diff --git a/.gitlab/ci/e2e.yml b/.gitlab/ci/e2e.yml index c1fd386e63..dc66bc7f9b 100644 --- a/.gitlab/ci/e2e.yml +++ b/.gitlab/ci/e2e.yml @@ -110,7 +110,7 @@ e2e request and config validation: .e2e-run: resource_group: e2e-${GROUP} - timeout: 2h + timeout: 3 hours image: name: ${RETINA_REGISTRY_PREFIX}/launcher:${RETINA_VERSION} entrypoint: ["/bin/sh", "-c"] @@ -178,11 +178,32 @@ e2e request and config validation: find . -iname "test_metrics.csv" -exec \ influx write --host $INFLUXDB_URL --token $INFLUXDB_TOKEN --org $INFLUXDB_ORG \ --bucket ci --file {} \; + # Artifact size + - echo -e "\e[0Ksection_start:`date +%s`:e2e_folder_section[collapsed=true]\r\e[0KLog folder's tree" + - | + print_tree() { + local dir="$1" + local prefix="$2" + + # List directories first + find "$dir" -mindepth 1 -maxdepth 1 -type d | while read -r subdir; do + local size=$(du -sh "$subdir" | awk '{print $1}') + echo "${prefix}├── $(basename "$subdir") [$size]" + print_tree "$subdir" "$prefix│ " + done + + # List files afterwards + find "$dir" -mindepth 1 -maxdepth 1 -type f | while read -r file; do + local size=$(du -sh "$file" | awk '{print $1}') + echo "${prefix}├── $(basename "$file") [$size]" + done + } + print_tree "tests/e2e/log/" "" + - echo -e "\e[0Ksection_end:`date +%s`:e2e_folder_section\r\e[0K" - | echo "*******************************************************************************************************************************" echo "Test report ---> https://softwareradiosystems.gitlab.io/-/$CI_PROJECT_NAME/-/jobs/$CI_JOB_ID/artifacts/tests/e2e/log//report.html" echo "*******************************************************************************************************************************" - - du -hs tests/e2e/log/ needs: - *retina-needs diff --git a/.gitlab/ci/e2e/.env b/.gitlab/ci/e2e/.env index 1e88bf0e34..d185555c50 100644 --- a/.gitlab/ci/e2e/.env +++ b/.gitlab/ci/e2e/.env @@ -1,6 +1,6 @@ SRSGNB_REGISTRY_URI=registry.gitlab.com/softwareradiosystems/srsgnb RETINA_REGISTRY_PREFIX=registry.gitlab.com/softwareradiosystems/ci/retina -RETINA_VERSION=0.51.7 +RETINA_VERSION=0.51.8 UBUNTU_VERSION=24.04 AMARISOFT_VERSION=2023-09-08 SRSUE_VERSION=23.11 diff --git a/.gitlab/ci/e2e/retina_request_viavi.yml b/.gitlab/ci/e2e/retina_request_viavi.yml index 31b2774f3b..169c43cad7 100644 --- a/.gitlab/ci/e2e/retina_request_viavi.yml +++ b/.gitlab/ci/e2e/retina_request_viavi.yml @@ -21,8 +21,8 @@ requests: 4Gi limits: 4Gi ephemeral-storage: - requests: "20G" - limits: "20G" + requests: "50G" + limits: "50G" taints: ["purpose=ci-amd64-avx512-onprem"] resources: - type: emulator diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index 1fccc42311..5a0fdc7069 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -71,6 +71,7 @@ #include "apps/services/application_message_banners.h" #include "apps/services/application_tracer.h" +#include "apps/services/buffer_pool/buffer_pool_manager.h" #include "apps/services/stdin_command_dispatcher.h" #include "apps/units/cu_cp/cu_cp_unit_config_yaml_writer.h" #include "apps/units/cu_up/cu_up_unit_config_yaml_writer.h" @@ -253,7 +254,7 @@ int main(int argc, char** argv) // TODO // Setup size of byte buffer pool. - init_byte_buffer_segment_pool(cu_cfg.buffer_pool_config.nof_segments, cu_cfg.buffer_pool_config.segment_size); + app_services::buffer_pool_manager buffer_pool_service(cu_cfg.buffer_pool_config); // Log CPU architecture. // TODO diff --git a/apps/cu/cu_appconfig.h b/apps/cu/cu_appconfig.h index a2fb65500e..cb1292bc3b 100644 --- a/apps/cu/cu_appconfig.h +++ b/apps/cu/cu_appconfig.h @@ -23,6 +23,7 @@ #pragma once #include "apps/gnb/gnb_appconfig.h" +#include "apps/services/buffer_pool/buffer_pool_appconfig.h" #include "apps/services/logger/logger_appconfig.h" #include diff --git a/apps/cu/cu_appconfig_cli11_schema.cpp b/apps/cu/cu_appconfig_cli11_schema.cpp index 90da41d189..a07e8ca878 100644 --- a/apps/cu/cu_appconfig_cli11_schema.cpp +++ b/apps/cu/cu_appconfig_cli11_schema.cpp @@ -21,6 +21,7 @@ */ #include "cu_appconfig_cli11_schema.h" +#include "apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.h" #include "apps/services/logger/logger_appconfig_cli11_schema.h" #include "cu_appconfig.h" #include "srsran/support/cli11_utils.h" @@ -43,19 +44,14 @@ static void configure_cli11_nru_args(CLI::App& app, srs_cu::cu_nru_appconfig& nr add_option(app, "--udp_max_rx_msgs", nru_cfg.udp_rx_max_msgs, "Maximum amount of messages RX in a single syscall"); } -static void configure_cli11_buffer_pool_args(CLI::App& app, buffer_pool_appconfig& config) -{ - app.add_option("--nof_segments", config.nof_segments, "Number of segments allocated by the buffer pool") - ->capture_default_str(); - app.add_option("--segment_size", config.segment_size, "Size of each buffer pool segment in bytes") - ->capture_default_str(); -} - void srsran::configure_cli11_with_cu_appconfig_schema(CLI::App& app, cu_appconfig& cu_cfg) { // Logging section. configure_cli11_with_logger_appconfig_schema(app, cu_cfg.log_cfg); + // Buffer pool section. + configure_cli11_with_buffer_pool_appconfig_schema(app, cu_cfg.buffer_pool_config); + // F1AP section. CLI::App* cu_cp_subcmd = add_subcommand(app, "cu_cp", "CU-UP parameters")->configurable(); CLI::App* f1ap_subcmd = add_subcommand(*cu_cp_subcmd, "f1ap", "F1AP parameters")->configurable(); @@ -65,8 +61,4 @@ void srsran::configure_cli11_with_cu_appconfig_schema(CLI::App& app, cu_appconfi CLI::App* cu_up_subcmd = add_subcommand(app, "cu_up", "CU-UP parameters")->configurable(); CLI::App* nru_subcmd = add_subcommand(*cu_up_subcmd, "nru", "NR-U parameters")->configurable(); configure_cli11_nru_args(*nru_subcmd, cu_cfg.nru_cfg); - - // Buffer pool section. - CLI::App* buffer_pool_subcmd = app.add_subcommand("buffer_pool", "Buffer pool configuration")->configurable(); - configure_cli11_buffer_pool_args(*buffer_pool_subcmd, cu_cfg.buffer_pool_config); } diff --git a/apps/du/du.cpp b/apps/du/du.cpp index 557132033d..cc7c14daeb 100644 --- a/apps/du/du.cpp +++ b/apps/du/du.cpp @@ -61,6 +61,7 @@ #include "apps/services/application_message_banners.h" #include "apps/services/application_tracer.h" +#include "apps/services/buffer_pool/buffer_pool_manager.h" #include "apps/services/core_isolation_manager.h" #include "apps/services/metrics_plotter_json.h" #include "apps/services/metrics_plotter_stdout.h" @@ -240,7 +241,7 @@ int main(int argc, char** argv) #endif // Setup size of byte buffer pool. - init_byte_buffer_segment_pool(du_cfg.buffer_pool_config.nof_segments, du_cfg.buffer_pool_config.segment_size); + app_services::buffer_pool_manager buffer_pool_service(du_cfg.buffer_pool_config); // Log CPU architecture. cpu_architecture_info::get().print_cpu_info(du_logger); diff --git a/apps/du/du_appconfig.h b/apps/du/du_appconfig.h index 981131b56c..767b1637a4 100644 --- a/apps/du/du_appconfig.h +++ b/apps/du/du_appconfig.h @@ -23,9 +23,9 @@ #pragma once #include "../gnb/gnb_appconfig.h" // TODO: Remove +#include "apps/services/buffer_pool/buffer_pool_appconfig.h" #include "apps/services/logger/logger_appconfig.h" #include "apps/services/os_sched_affinity_manager.h" -#include "srsran/adt/byte_buffer.h" #include "srsran/support/executors/unique_thread.h" #include diff --git a/apps/du/du_appconfig_cli11_schema.cpp b/apps/du/du_appconfig_cli11_schema.cpp index e8bce3add0..c361ae16e7 100644 --- a/apps/du/du_appconfig_cli11_schema.cpp +++ b/apps/du/du_appconfig_cli11_schema.cpp @@ -21,8 +21,10 @@ */ #include "du_appconfig_cli11_schema.h" +#include "apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.h" #include "apps/services/logger/logger_appconfig_cli11_schema.h" #include "du_appconfig.h" +#include "srsran/adt/interval.h" #include "srsran/support/cli11_utils.h" using namespace srsran; @@ -79,14 +81,6 @@ static void configure_cli11_e2_args(CLI::App& app, e2_appconfig& e2_params) add_option(app, "--e2sm_rc_enabled", e2_params.e2sm_rc_enabled, "Enable RC service module")->capture_default_str(); } -static void configure_cli11_buffer_pool_args(CLI::App& app, buffer_pool_appconfig& config) -{ - app.add_option("--nof_segments", config.nof_segments, "Number of segments allocated by the buffer pool") - ->capture_default_str(); - app.add_option("--segment_size", config.segment_size, "Size of each buffer pool segment in bytes") - ->capture_default_str(); -} - static error_type is_valid_cpu_index(unsigned cpu_idx) { std::string error_message = fmt::format("Invalid CPU core selected '{}'. Valid CPU ids: {}", @@ -268,6 +262,9 @@ void srsran::configure_cli11_with_du_appconfig_schema(CLI::App& app, du_appconfi // Loggers section. configure_cli11_with_logger_appconfig_schema(app, du_cfg.log_cfg); + // Buffer pool section. + configure_cli11_with_buffer_pool_appconfig_schema(app, du_cfg.buffer_pool_config); + // F1-C section. CLI::App* f1ap_subcmd = app.add_subcommand("f1ap", "F1AP interface configuration")->configurable(); configure_cli11_f1ap_args(*f1ap_subcmd, du_cfg.f1ap_cfg); @@ -284,10 +281,6 @@ void srsran::configure_cli11_with_du_appconfig_schema(CLI::App& app, du_appconfi CLI::App* e2_subcmd = add_subcommand(app, "e2", "E2 parameters")->configurable(); configure_cli11_e2_args(*e2_subcmd, du_cfg.e2_cfg); - // Buffer pool section. - CLI::App* buffer_pool_subcmd = app.add_subcommand("buffer_pool", "Buffer pool configuration")->configurable(); - configure_cli11_buffer_pool_args(*buffer_pool_subcmd, du_cfg.buffer_pool_config); - // Expert section. CLI::App* expert_subcmd = app.add_subcommand("expert_execution", "Expert execution configuration")->configurable(); configure_cli11_expert_execution_args(*expert_subcmd, du_cfg.expert_execution_cfg); diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp index 673768582d..306fef75d5 100644 --- a/apps/gnb/gnb.cpp +++ b/apps/gnb/gnb.cpp @@ -72,6 +72,7 @@ #include #include "apps/services/application_message_banners.h" +#include "apps/services/buffer_pool/buffer_pool_manager.h" #include "apps/services/core_isolation_manager.h" #include "apps/services/metrics_plotter_json.h" #include "apps/services/metrics_plotter_stdout.h" @@ -295,8 +296,8 @@ int main(int argc, char** argv) } #endif - // Setup size of byte buffer pool. - init_byte_buffer_segment_pool(gnb_cfg.buffer_pool_config.nof_segments, gnb_cfg.buffer_pool_config.segment_size); + // Buffer pool service. + app_services::buffer_pool_manager buffer_pool_service(gnb_cfg.buffer_pool_config); // Log CPU architecture. cpu_architecture_info::get().print_cpu_info(gnb_logger); diff --git a/apps/gnb/gnb_appconfig.h b/apps/gnb/gnb_appconfig.h index 486eb48355..431555e3fe 100644 --- a/apps/gnb/gnb_appconfig.h +++ b/apps/gnb/gnb_appconfig.h @@ -22,10 +22,9 @@ #pragma once +#include "apps/services/buffer_pool/buffer_pool_appconfig.h" #include "apps/services/logger/logger_appconfig.h" #include "apps/services/os_sched_affinity_manager.h" -#include "srsran/adt/byte_buffer.h" -#include "srsran/ran/direct_current_offset.h" #include "srsran/ran/gnb_id.h" #include "srsran/support/executors/unique_thread.h" #include @@ -66,11 +65,6 @@ struct metrics_appconfig { unsigned stdout_metrics_period = 1000; // Statistics report period in milliseconds }; -struct buffer_pool_appconfig { - std::size_t nof_segments = 1048576; - std::size_t segment_size = byte_buffer_segment_pool_default_segment_size(); -}; - /// CPU affinities configuration for the gNB app. struct cpu_affinities_appconfig { /// CPUs isolation. diff --git a/apps/gnb/gnb_appconfig_cli11_schema.cpp b/apps/gnb/gnb_appconfig_cli11_schema.cpp index d8d17b9365..83b7ee0f0c 100644 --- a/apps/gnb/gnb_appconfig_cli11_schema.cpp +++ b/apps/gnb/gnb_appconfig_cli11_schema.cpp @@ -21,8 +21,10 @@ */ #include "gnb_appconfig_cli11_schema.h" +#include "apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.h" #include "apps/services/logger/logger_appconfig_cli11_schema.h" #include "gnb_appconfig.h" +#include "srsran/adt/interval.h" #include "srsran/support/cli11_utils.h" #include "srsran/support/error_handling.h" #include "CLI/CLI11.hpp" @@ -84,14 +86,6 @@ static void configure_cli11_e2_args(CLI::App& app, e2_appconfig& e2_params) add_option(app, "--e2sm_rc_enabled", e2_params.e2sm_rc_enabled, "Enable RC service module")->capture_default_str(); } -static void configure_cli11_buffer_pool_args(CLI::App& app, buffer_pool_appconfig& config) -{ - app.add_option("--nof_segments", config.nof_segments, "Number of segments allocated by the buffer pool") - ->capture_default_str(); - app.add_option("--segment_size", config.segment_size, "Size of each buffer pool segment in bytes") - ->capture_default_str(); -} - static void configure_cli11_hal_args(CLI::App& app, std::optional& config) { config.emplace(); @@ -274,6 +268,9 @@ void srsran::configure_cli11_with_gnb_appconfig_schema(CLI::App& app, gnb_appcon // Loggers section. configure_cli11_with_logger_appconfig_schema(app, gnb_cfg.log_cfg); + // Buffer pool section. + configure_cli11_with_buffer_pool_appconfig_schema(app, gnb_cfg.buffer_pool_config); + // Metrics section. CLI::App* metrics_subcmd = app.add_subcommand("metrics", "Metrics configuration")->configurable(); configure_cli11_metrics_args(*metrics_subcmd, gnb_cfg.metrics_cfg); @@ -282,10 +279,6 @@ void srsran::configure_cli11_with_gnb_appconfig_schema(CLI::App& app, gnb_appcon CLI::App* e2_subcmd = add_subcommand(app, "e2", "E2 parameters")->configurable(); configure_cli11_e2_args(*e2_subcmd, gnb_cfg.e2_cfg); - // Buffer pool section. - CLI::App* buffer_pool_subcmd = app.add_subcommand("buffer_pool", "Buffer pool configuration")->configurable(); - configure_cli11_buffer_pool_args(*buffer_pool_subcmd, gnb_cfg.buffer_pool_config); - // Expert section. CLI::App* expert_subcmd = app.add_subcommand("expert_execution", "Expert execution configuration")->configurable(); configure_cli11_expert_execution_args(*expert_subcmd, gnb_cfg.expert_execution_cfg); diff --git a/apps/services/CMakeLists.txt b/apps/services/CMakeLists.txt index 42b174f7c7..f87cbfa389 100644 --- a/apps/services/CMakeLists.txt +++ b/apps/services/CMakeLists.txt @@ -18,6 +18,7 @@ # and at http://www.gnu.org/licenses/. # +add_subdirectory(buffer_pool) add_subdirectory(logger) set(SOURCES @@ -32,4 +33,4 @@ set(SOURCES add_library(srsran_app_services STATIC ${SOURCES}) target_include_directories(srsran_app_services PRIVATE ${CMAKE_SOURCE_DIR}) -target_link_libraries(srsran_app_services srsran_logger_app_service) +target_link_libraries(srsran_app_services srsran_logger_app_service srsran_buffer_pool_app_service) diff --git a/apps/services/buffer_pool/CMakeLists.txt b/apps/services/buffer_pool/CMakeLists.txt new file mode 100644 index 0000000000..4a287cf027 --- /dev/null +++ b/apps/services/buffer_pool/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN 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. +# +# srsRAN 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/. +# + +set(SOURCES + buffer_pool_appconfig_cli11_schema.cpp) + +add_library(srsran_buffer_pool_app_service STATIC ${SOURCES}) +target_include_directories(srsran_buffer_pool_app_service PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/apps/services/buffer_pool/buffer_pool_appconfig.h b/apps/services/buffer_pool/buffer_pool_appconfig.h new file mode 100644 index 0000000000..1a5574a228 --- /dev/null +++ b/apps/services/buffer_pool/buffer_pool_appconfig.h @@ -0,0 +1,35 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#pragma once + +#include "srsran/adt/byte_buffer.h" + +namespace srsran { + +/// Buffer pool application configuration. +struct buffer_pool_appconfig { + std::size_t nof_segments = 1048576; + std::size_t segment_size = byte_buffer_segment_pool_default_segment_size(); +}; + +} // namespace srsran diff --git a/apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.cpp b/apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.cpp new file mode 100644 index 0000000000..1b265ede27 --- /dev/null +++ b/apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.cpp @@ -0,0 +1,41 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#include "apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.h" +#include "apps/services/buffer_pool/buffer_pool_appconfig.h" + +using namespace srsran; + +static void configure_cli11_buffer_pool_args(CLI::App& app, buffer_pool_appconfig& config) +{ + app.add_option("--nof_segments", config.nof_segments, "Number of segments allocated by the buffer pool") + ->capture_default_str(); + app.add_option("--segment_size", config.segment_size, "Size of each buffer pool segment in bytes") + ->capture_default_str(); +} + +void srsran::configure_cli11_with_buffer_pool_appconfig_schema(CLI::App& app, buffer_pool_appconfig& config) +{ + // Buffer pool section. + CLI::App* buffer_pool_subcmd = app.add_subcommand("buffer_pool", "Buffer pool configuration")->configurable(); + configure_cli11_buffer_pool_args(*buffer_pool_subcmd, config); +} diff --git a/apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.h b/apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.h new file mode 100644 index 0000000000..0c1804504f --- /dev/null +++ b/apps/services/buffer_pool/buffer_pool_appconfig_cli11_schema.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#pragma once + +#include + +namespace srsran { + +struct buffer_pool_appconfig; + +/// Configures the given CLI11 application with the logger application configuration schema. +void configure_cli11_with_buffer_pool_appconfig_schema(CLI::App& app, buffer_pool_appconfig& config); + +} // namespace srsran diff --git a/apps/services/buffer_pool/buffer_pool_manager.h b/apps/services/buffer_pool/buffer_pool_manager.h new file mode 100644 index 0000000000..21b285ff38 --- /dev/null +++ b/apps/services/buffer_pool/buffer_pool_manager.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#pragma once + +#include "apps/services/buffer_pool/buffer_pool_appconfig.h" + +namespace srsran { +namespace app_services { + +/// Buffer pool manager for the applications. +class buffer_pool_manager +{ +public: + explicit buffer_pool_manager(const buffer_pool_appconfig& config) + { + init_byte_buffer_segment_pool(config.nof_segments, config.segment_size); + } +}; + +} // namespace app_services +} // namespace srsran diff --git a/apps/services/worker_manager.cpp b/apps/services/worker_manager.cpp index 75e2949014..399b03fb5e 100644 --- a/apps/services/worker_manager.cpp +++ b/apps/services/worker_manager.cpp @@ -261,7 +261,8 @@ void worker_manager::create_du_executors(bool is_blocking_m for (unsigned cell_id = 0; cell_id != nof_cells; ++cell_id) { const std::string cell_id_str = std::to_string(cell_id); - slot_workers[cell_id].executors.emplace_back("cell_exec#" + cell_id_str, task_priority::max - 1); + slot_workers[cell_id].executors.push_back( + {"cell_exec#" + cell_id_str, task_priority::max - 1, {}, std::nullopt, is_blocking_mode_active}); slot_workers[cell_id].executors.push_back( {"slot_exec#" + cell_id_str, task_priority::max, {}, std::nullopt, is_blocking_mode_active}); diff --git a/include/srsran/cu_cp/cu_cp_e1_handler.h b/include/srsran/cu_cp/cu_cp_e1_handler.h index f31ba6d625..15c4b15c21 100644 --- a/include/srsran/cu_cp/cu_cp_e1_handler.h +++ b/include/srsran/cu_cp/cu_cp_e1_handler.h @@ -54,13 +54,6 @@ class cu_cp_e1_handler /// the caller lets the returned object go out of scope, the CU-UP connection will be closed. virtual std::unique_ptr handle_new_cu_up_connection(std::unique_ptr e1ap_tx_pdu_notifier) = 0; - - /// \brief Handles a remove request. The corresponding CU-UP processor object will be removed. - /// \param[in] cu_up_index The index of the CU-UP processor object to delete. - virtual void handle_cu_up_remove_request(cu_up_index_t cu_up_index) = 0; - - /// \brief Get handler to a CU-UP connected to the CU-CP. - virtual cu_up_e1_handler& get_cu_up(cu_up_index_t cu_up_index) = 0; }; } // namespace srs_cu_cp diff --git a/include/srsran/cu_cp/cu_cp_types.h b/include/srsran/cu_cp/cu_cp_types.h index a0cb2e2aea..42cbb112e5 100644 --- a/include/srsran/cu_cp/cu_cp_types.h +++ b/include/srsran/cu_cp/cu_cp_types.h @@ -22,6 +22,7 @@ #pragma once +#include "srsran/adt/bounded_bitset.h" #include "srsran/adt/byte_buffer.h" #include "srsran/adt/optional.h" #include "srsran/adt/slotted_array.h" @@ -143,9 +144,42 @@ struct cu_cp_amf_identifier_t { }; struct cu_cp_five_g_s_tmsi { - uint16_t amf_set_id; - uint8_t amf_pointer; - uint32_t five_g_tmsi; + cu_cp_five_g_s_tmsi() = default; + + cu_cp_five_g_s_tmsi(const bounded_bitset<48>& five_g_s_tmsi_) : five_g_s_tmsi(five_g_s_tmsi_) + { + srsran_assert(five_g_s_tmsi_.size() == 48, "Invalid size for 5G-S-TMSI ({})", five_g_s_tmsi_.size()); + } + + cu_cp_five_g_s_tmsi(uint64_t amf_set_id, uint64_t amf_pointer, uint64_t five_g_tmsi) + { + five_g_s_tmsi.emplace(); + five_g_s_tmsi->resize(48); + five_g_s_tmsi->from_uint64((amf_set_id << 38U) + (amf_pointer << 32U) + five_g_tmsi); + } + + uint16_t get_amf_set_id() const + { + srsran_assert(five_g_s_tmsi.has_value(), "five_g_s_tmsi is not set"); + return five_g_s_tmsi.value().to_uint64() >> 38U; + }; + + uint8_t get_amf_pointer() const + { + srsran_assert(five_g_s_tmsi.has_value(), "five_g_s_tmsi is not set"); + return (five_g_s_tmsi.value().to_uint64() & 0x3f00000000) >> 32U; + }; + + uint32_t get_five_g_tmsi() const + { + srsran_assert(five_g_s_tmsi.has_value(), "five_g_s_tmsi is not set"); + return (five_g_s_tmsi.value().to_uint64() & 0xffffffff); + }; + + uint64_t to_number() const { return five_g_s_tmsi->to_uint64(); } + +private: + std::optional> five_g_s_tmsi; }; struct cu_cp_initial_ue_message { diff --git a/include/srsran/e1ap/gateways/e1_local_connector_factory.h b/include/srsran/e1ap/gateways/e1_local_connector_factory.h index f3880cf109..582b65f862 100644 --- a/include/srsran/e1ap/gateways/e1_local_connector_factory.h +++ b/include/srsran/e1ap/gateways/e1_local_connector_factory.h @@ -28,6 +28,7 @@ namespace srsran { class dlt_pcap; +class io_broker; class e1_local_connector : public srs_cu_up::e1_connection_client, public srs_cu_cp::e1_connection_server {}; @@ -41,4 +42,18 @@ struct e1_local_connector_config { /// E1AP PDUs or any socket send/recv. std::unique_ptr create_e1_local_connector(const e1_local_connector_config& cfg); +struct e1_local_sctp_connector_config { + /// PCAP writer for the E1AP messages. + dlt_pcap& pcap; + /// IO broker to handle the SCTP Rx data and notifications. + io_broker& broker; + /// Port to bind the SCTP socket. + int bind_port = 0; +}; + +/// Creates an E1 local connector using an SCTP socket as channel. +/// +/// Note: This class is useful for testing. +std::unique_ptr create_e1_local_connector(const e1_local_sctp_connector_config& cfg); + } // namespace srsran \ No newline at end of file diff --git a/include/srsran/e1ap/gateways/e1_network_client_factory.h b/include/srsran/e1ap/gateways/e1_network_client_factory.h new file mode 100644 index 0000000000..3c31a5d419 --- /dev/null +++ b/include/srsran/e1ap/gateways/e1_network_client_factory.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#pragma once + +#include "srsran/e1ap/gateways/e1_connection_client.h" +#include "srsran/gateways/sctp_network_gateway.h" + +namespace srsran { + +class dlt_pcap; +class io_broker; + +/// Configuration of an SCTP-based E1 Gateway in the CU-UP. +struct e1_cu_up_sctp_gateway_config { + /// SCTP configuration. + sctp_network_connector_config sctp; + /// IO broker responsible for handling SCTP Rx data and notifications. + io_broker& broker; + /// PCAP writer for the E1AP messages. + dlt_pcap& pcap; +}; + +/// \brief Create an E1 gateway connector that the CU-UP can use to connect to the CU-CP. +std::unique_ptr create_e1_gateway_client(const e1_cu_up_sctp_gateway_config& params); + +} // namespace srsran diff --git a/include/srsran/e1ap/gateways/e1_network_server_factory.h b/include/srsran/e1ap/gateways/e1_network_server_factory.h new file mode 100644 index 0000000000..1125f817a7 --- /dev/null +++ b/include/srsran/e1ap/gateways/e1_network_server_factory.h @@ -0,0 +1,48 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#pragma once + +#include "srsran/cu_cp/cu_cp_e1_handler.h" +#include "srsran/e1ap/gateways/e1_connection_server.h" +#include "srsran/gateways/sctp_network_gateway.h" + +namespace srsran { + +class dlt_pcap; +class io_broker; + +/// Configuration of an SCTP-based E1 Gateway in the CU-CP. +struct e1_cu_cp_sctp_gateway_config { + /// SCTP configuration. + sctp_network_gateway_config sctp; + /// IO broker responsible for handling SCTP Rx data and notifications. + io_broker& broker; + /// PCAP writer for the E1AP messages. + dlt_pcap& pcap; +}; + +/// Creates an E1 Gateway server that listens for incoming SCTP connections, packs/unpacks E1AP PDUs and forwards +/// them to the GW/CU-CP E1AP handler. +std::unique_ptr create_e1_gateway_server(const e1_cu_cp_sctp_gateway_config& params); + +} // namespace srsran diff --git a/include/srsran/gateways/sctp_network_gateway.h b/include/srsran/gateways/sctp_network_gateway.h index d636267f9b..b46450ff92 100644 --- a/include/srsran/gateways/sctp_network_gateway.h +++ b/include/srsran/gateways/sctp_network_gateway.h @@ -30,6 +30,7 @@ namespace srsran { constexpr uint16_t NGAP_PPID = 60; // NGAP PPID, see TS 38.412, section 7. constexpr uint16_t F1AP_PPID = 62; // F1AP PPID, see TS 38.472, section 7. +constexpr uint16_t E1AP_PPID = 64; // E1AP PPID, see TS 37.482, section 7. constexpr uint16_t E2_CP_PPID = 70; // E2-CP PPID assigned by IANA constexpr uint16_t E2_UP_PPID = 71; // E2-UP PPID assigned by IANA constexpr uint16_t E2_DU_PPID = 72; // E2-DU PPID assigned by IANA diff --git a/include/srsran/phy/generic_functions/precoding/channel_precoder.h b/include/srsran/phy/generic_functions/precoding/channel_precoder.h index 10e146ca93..ced6bca246 100644 --- a/include/srsran/phy/generic_functions/precoding/channel_precoder.h +++ b/include/srsran/phy/generic_functions/precoding/channel_precoder.h @@ -46,7 +46,7 @@ class channel_precoder /// of RE per layer of the input buffer. /// \remark An assertion is triggered if the precoding matrix dimensions do not match the number of layers of the /// input buffer and the number of antenna ports of the output buffer. - virtual void apply_precoding(re_buffer_writer<>& output, + virtual void apply_precoding(re_buffer_writer& output, const re_buffer_reader<>& input, const precoding_weight_matrix& precoding) const = 0; diff --git a/include/srsran/phy/support/resource_grid_writer.h b/include/srsran/phy/support/resource_grid_writer.h index d1ae404bfb..616627c12f 100644 --- a/include/srsran/phy/support/resource_grid_writer.h +++ b/include/srsran/phy/support/resource_grid_writer.h @@ -97,7 +97,7 @@ class resource_grid_writer : public resource_grid_base /// \param[in] symbols Symbols to be written into the resource grid. /// \note The RE positions given \c k_init, the number of elements in \c symbols and the \c stride shall be within the /// resource grid number of subcarriers. - virtual void put(unsigned port, unsigned l, unsigned k_init, unsigned stride, span symbols) = 0; + virtual void put(unsigned port, unsigned l, unsigned k_init, unsigned stride, span symbols) = 0; /// \brief Gets a read-write view of an OFDM symbol for a given port. /// diff --git a/lib/phy/upper/signal_processors/pucch/pucch_helper.h b/include/srsran/phy/upper/pucch_helper.h similarity index 100% rename from lib/phy/upper/signal_processors/pucch/pucch_helper.h rename to include/srsran/phy/upper/pucch_helper.h diff --git a/include/srsran/support/io/sctp_socket.h b/include/srsran/support/io/sctp_socket.h index 8f85871f76..2eea07a3fd 100644 --- a/include/srsran/support/io/sctp_socket.h +++ b/include/srsran/support/io/sctp_socket.h @@ -87,3 +87,34 @@ class sctp_socket }; } // namespace srsran + +namespace fmt { +template <> +struct formatter { + template + auto parse(ParseContext& ctx) + { + return ctx.begin(); + } + + template + auto format(const srsran::sctp_socket_params& cfg, FormatContext& ctx) + { + return format_to(ctx.out(), + "if_name={} ai_family={} ai_socktype={} reuse_addr={} non_blockin_mode={} rx_timeout={} " + "rto_initial={} rto_min={} rto_max={} init_max_attempts={} max_init_timeo={} no_delay={}", + cfg.if_name, + cfg.ai_family, + cfg.ai_socktype, + cfg.reuse_addr, + cfg.non_blocking_mode, + cfg.rx_timeout.count(), + cfg.rto_initial, + cfg.rto_min, + cfg.rto_max, + cfg.init_max_attempts, + cfg.max_init_timeo, + cfg.nodelay); + } +}; +} // namespace fmt diff --git a/lib/cu_cp/CMakeLists.txt b/lib/cu_cp/CMakeLists.txt index 81a8980081..2e68df4d85 100644 --- a/lib/cu_cp/CMakeLists.txt +++ b/lib/cu_cp/CMakeLists.txt @@ -29,6 +29,7 @@ set(SOURCES cu_cp_controller/cu_cp_controller.cpp cu_cp_controller/amf_connection_manager.cpp cu_cp_controller/du_connection_manager.cpp + cu_cp_controller/cu_up_connection_manager.cpp cu_up_processor/cu_up_processor_impl.cpp cu_up_processor/cu_up_processor_factory.cpp cu_up_processor/cu_up_processor_repository.cpp diff --git a/lib/cu_cp/cu_cp_controller/cu_cp_controller.cpp b/lib/cu_cp/cu_cp_controller/cu_cp_controller.cpp index fa8ba4d136..6e80223a4e 100644 --- a/lib/cu_cp/cu_cp_controller/cu_cp_controller.cpp +++ b/lib/cu_cp/cu_cp_controller/cu_cp_controller.cpp @@ -29,21 +29,21 @@ using namespace srsran; using namespace srs_cu_cp; -cu_cp_controller::cu_cp_controller(const cu_cp_configuration& config_, - cu_cp_routine_manager& routine_manager_, - ue_manager& ue_mng_, - ngap_connection_manager& ngap_conn_mng_, - const cu_up_processor_repository& cu_ups_, - du_processor_repository& dus_, - task_executor& ctrl_exec_) : +cu_cp_controller::cu_cp_controller(const cu_cp_configuration& config_, + cu_cp_routine_manager& routine_manager_, + ue_manager& ue_mng_, + ngap_connection_manager& ngap_conn_mng_, + cu_up_processor_repository& cu_ups_, + du_processor_repository& dus_, + task_executor& ctrl_exec_) : cfg(config_), ue_mng(ue_mng_), - cu_ups(cu_ups_), routine_mng(routine_manager_), ctrl_exec(ctrl_exec_), logger(srslog::fetch_basic_logger("CU-CP")), amf_mng(routine_manager_, cfg, ngap_conn_mng_), - du_mng(cfg.admission.max_nof_dus, dus_, ctrl_exec, routine_manager_) + du_mng(cfg.admission.max_nof_dus, dus_, ctrl_exec, routine_manager_), + cu_up_mng(cfg.admission.max_nof_cu_ups, cu_ups_, ctrl_exec, routine_manager_) { (void)ue_mng; } @@ -61,6 +61,9 @@ void cu_cp_controller::stop() // Stop and delete DU connections. du_mng.stop(); + // Stop and delete CU-UP connections. + cu_up_mng.stop(); + // Stop AMF connection. while (not ctrl_exec.defer([this]() { stop_impl(); })) { logger.warning("Failed to dispatch CU-CP stop task. Retrying..."); @@ -110,7 +113,7 @@ bool cu_cp_controller::request_ue_setup() const return false; } - if (cu_ups.get_nof_cu_ups() == 0) { + if (cu_up_mng.nof_cu_ups() == 0) { return false; } diff --git a/lib/cu_cp/cu_cp_controller/cu_cp_controller.h b/lib/cu_cp/cu_cp_controller/cu_cp_controller.h index 1c1c35e6d5..2cda26837a 100644 --- a/lib/cu_cp/cu_cp_controller/cu_cp_controller.h +++ b/lib/cu_cp/cu_cp_controller/cu_cp_controller.h @@ -23,6 +23,7 @@ #pragma once #include "amf_connection_manager.h" +#include "cu_up_connection_manager.h" #include "du_connection_manager.h" #include "node_connection_notifier.h" #include "srsran/cu_cp/cu_cp_configuration.h" @@ -45,13 +46,13 @@ class ue_manager; class cu_cp_controller { public: - cu_cp_controller(const cu_cp_configuration& config_, - cu_cp_routine_manager& routine_manager_, - ue_manager& ue_mng_, - ngap_connection_manager& ngap_conn_mng_, - const cu_up_processor_repository& cu_ups_, - du_processor_repository& dus_, - task_executor& ctrl_exec); + cu_cp_controller(const cu_cp_configuration& config_, + cu_cp_routine_manager& routine_manager_, + ue_manager& ue_mng_, + ngap_connection_manager& ngap_conn_mng_, + cu_up_processor_repository& cu_ups_, + du_processor_repository& dus_, + task_executor& ctrl_exec); void stop(); @@ -63,19 +64,20 @@ class cu_cp_controller bool request_ue_setup() const; cu_cp_f1c_handler& get_f1c_handler() { return du_mng; } + cu_cp_e1_handler& get_e1_handler() { return cu_up_mng; } private: void stop_impl(); - const cu_cp_configuration& cfg; - ue_manager& ue_mng; - const cu_up_processor_repository& cu_ups; - cu_cp_routine_manager& routine_mng; - task_executor& ctrl_exec; - srslog::basic_logger& logger; + const cu_cp_configuration& cfg; + ue_manager& ue_mng; + cu_cp_routine_manager& routine_mng; + task_executor& ctrl_exec; + srslog::basic_logger& logger; - amf_connection_manager amf_mng; - du_connection_manager du_mng; + amf_connection_manager amf_mng; + du_connection_manager du_mng; + cu_up_connection_manager cu_up_mng; std::mutex mutex; std::condition_variable cvar; diff --git a/lib/cu_cp/cu_cp_controller/cu_up_connection_manager.cpp b/lib/cu_cp/cu_cp_controller/cu_up_connection_manager.cpp new file mode 100644 index 0000000000..57f8683beb --- /dev/null +++ b/lib/cu_cp/cu_cp_controller/cu_up_connection_manager.cpp @@ -0,0 +1,248 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#include "cu_up_connection_manager.h" +#include "../cu_up_processor/cu_up_processor_repository.h" +#include "common_task_scheduler.h" +#include "srsran/e1ap/common/e1ap_message.h" +#include "srsran/support/executors/sync_task_executor.h" +#include + +using namespace srsran; +using namespace srs_cu_cp; + +/// Context of a CU-UP connection which is shared between the cu_up_connection_manager and the e1ap_message_notifier. +class cu_up_connection_manager::shared_cu_up_connection_context +{ +public: + shared_cu_up_connection_context(cu_up_connection_manager& parent_) : parent(parent_) {} + shared_cu_up_connection_context(const shared_cu_up_connection_context&) = delete; + shared_cu_up_connection_context(shared_cu_up_connection_context&&) = delete; + shared_cu_up_connection_context& operator=(const shared_cu_up_connection_context&) = delete; + shared_cu_up_connection_context& operator=(shared_cu_up_connection_context&&) = delete; + ~shared_cu_up_connection_context() { disconnect(); } + + /// Assign a CU-UP repository index to the context. This is called when the CU-UP repository is actually created. + void connect_cu_up(cu_up_index_t cu_up_idx_) + { + cu_up_idx = cu_up_idx_; + msg_handler = &parent.cu_ups.get_cu_up(cu_up_idx).get_message_handler(); + } + + /// Determines whether a CU-UP repository has been created for this connection. + bool connected() const { return msg_handler != nullptr; } + + /// Deletes the associated CU-UP repository, if it exists. + void disconnect() + { + if (not connected()) { + // CU-UP was never allocated or was already removed. + return; + } + + // Notify CU-UP that the connection is closed. + parent.handle_e1_gw_connection_closed(cu_up_idx); + + cu_up_idx = cu_up_index_t::invalid; + msg_handler = nullptr; + } + + /// Handle E1AP message coming from the CU-UP. + void handle_message(const e1ap_message& msg) + { + if (not connected()) { + parent.logger.warning("Discarding CU-UP E1AP message. Cause: CU-UP connection has been closed."); + } + + // Forward message. + msg_handler->handle_message(msg); + } + +private: + cu_up_connection_manager& parent; + cu_up_index_t cu_up_idx = cu_up_index_t::invalid; + e1ap_message_handler* msg_handler = nullptr; +}; + +/// Notifier used to forward Rx E1AP messages from the E1 GW to CU-CP in a thread safe manner. +class cu_up_connection_manager::e1_gw_to_cu_cp_pdu_adapter final : public e1ap_message_notifier +{ +public: + e1_gw_to_cu_cp_pdu_adapter(cu_up_connection_manager& parent_, + std::shared_ptr ctxt_) : + parent(parent_), ctxt(std::move(ctxt_)) + { + // Increment number of CU-UP connections. + parent.cu_up_count.fetch_add(1, std::memory_order_release); + } + + ~e1_gw_to_cu_cp_pdu_adapter() override + { + // Decrement the number of active CU-UP connections. + parent.cu_up_count.fetch_sub(1, std::memory_order_release); + + // Defer destruction of context to CU-CP execution context. + // Note: We make a copy of the shared_ptr of the context to extend its lifetime to when the defer callback actually + // gets executed. + // Note: We don't use move because the defer may fail. + while (not parent.cu_cp_exec.defer([ctxt_cpy = ctxt]() { ctxt_cpy->disconnect(); })) { + parent.logger.error("Failed to schedule CU-UP removal task. Retrying..."); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + + void on_new_message(const e1ap_message& msg) override + { + // Dispatch the E1AP Rx message handling to the CU-CP executor. + while (not parent.cu_cp_exec.execute([this, msg]() { ctxt->handle_message(msg); })) { + parent.logger.error("Failed to dispatch E1AP message to CU-CP. Retrying..."); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + +private: + cu_up_connection_manager& parent; + std::shared_ptr ctxt; +}; + +cu_up_connection_manager::cu_up_connection_manager(unsigned max_nof_cu_ups_, + cu_up_processor_repository& cu_ups_, + task_executor& cu_cp_exec_, + common_task_scheduler& common_task_sched_) : + max_nof_cu_ups(max_nof_cu_ups_), + cu_ups(cu_ups_), + cu_cp_exec(cu_cp_exec_), + common_task_sched(common_task_sched_), + logger(srslog::fetch_basic_logger("CU-CP")) +{ +} + +std::unique_ptr +cu_up_connection_manager::handle_new_cu_up_connection(std::unique_ptr e1ap_tx_pdu_notifier) +{ + // Note: This function may be called from a different execution context than the CU-CP. + + if (stopped.load(std::memory_order_acquire)) { + // CU-CP is in the process of being stopped. + return nullptr; + } + + // Verify that there is space for new CU-UP connection. + if (cu_up_count.load(std::memory_order_acquire) >= max_nof_cu_ups) { + logger.warning("Rejecting new CU-UP connection. Cause: Maximum number of CU-UPs {} reached.", max_nof_cu_ups); + return nullptr; + } + + // We create a "detached" notifier, that has no associated CU-UP processor yet. + auto shared_ctxt = std::make_shared(*this); + auto rx_pdu_notifier = std::make_unique(*this, shared_ctxt); + + // We dispatch the task to allocate a CU-UP processor and "attach" it to the notifier + while (not cu_cp_exec.execute([this, shared_ctxt, sender_notifier = std::move(e1ap_tx_pdu_notifier)]() mutable { + // Create a new CU-UP processor. + cu_up_index_t cu_up_index = cu_ups.add_cu_up(std::move(sender_notifier)); + if (cu_up_index == cu_up_index_t::invalid) { + logger.warning("Rejecting new CU-UP TNL connection. Cause: Failed to create a new CU-UP."); + return; + } + + // Register the allocated CU-UP processor index in the CU-UP connection context. + shared_ctxt->connect_cu_up(cu_up_index); + + if (not cu_up_connections.insert(std::make_pair(cu_up_index, std::move(shared_ctxt))).second) { + logger.error("Failed to store new CU-UP connection {}", cu_up_index); + return; + } + + logger.info("Added TNL connection to CU-UP {}", cu_up_index); + })) { + logger.debug("Failed to dispatch CU-CP CU-UP connection task. Retrying..."); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + return rx_pdu_notifier; +} + +void cu_up_connection_manager::handle_e1_gw_connection_closed(cu_up_index_t cu_up_idx) +{ + // Note: Called from within CU-CP execution context. + + common_task_sched.schedule_async_task(launch_async([this, cu_up_idx](coro_context>& ctx) { + CORO_BEGIN(ctx); + if (cu_up_connections.find(cu_up_idx) == cu_up_connections.end()) { + // CU-UP was already removed. + CORO_EARLY_RETURN(); + } + + // Await for clean removal of the CU-UP from the CU-UP repository. + CORO_AWAIT(cu_ups.remove_cu_up(cu_up_idx)); + + // Mark the connection as closed. + cu_up_connections.erase(cu_up_idx); + + // Flag that all CU-UPs got removed. + if (stopped and cu_up_connections.empty()) { + std::unique_lock lock(stop_mutex); + stop_completed = true; + stop_cvar.notify_one(); + } + + CORO_RETURN(); + })); +} + +void cu_up_connection_manager::stop() +{ + // Note: Called from outside of the CU-CP execution context. + stop_completed = false; + stopped = true; + + while (not cu_cp_exec.execute([this]() mutable { + if (cu_up_connections.empty()) { + // No CU-UPs connected. Notify completion. + std::unique_lock lock(stop_mutex); + stop_completed = true; + stop_cvar.notify_one(); + return; + } + + // For each created CU-UP connection context, launch the deletion routine. + std::vector cu_up_idxs; + cu_up_idxs.reserve(cu_up_connections.size()); + for (const auto& [cu_up_idx, ctxt] : cu_up_connections) { + cu_up_idxs.push_back(cu_up_idx); + } + for (cu_up_index_t cu_up_idx : cu_up_idxs) { + // Disconnect CU-UP notifier. + cu_up_connections[cu_up_idx]->disconnect(); + } + })) { + logger.debug("Failed to dispatch CU-CP CU-UP disconnection task. Retrying..."); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + // Wait for CU-UP stop to complete. + { + std::unique_lock lock(stop_mutex); + stop_cvar.wait(lock, [this] { return stop_completed; }); + } +} diff --git a/lib/cu_cp/cu_cp_controller/cu_up_connection_manager.h b/lib/cu_cp/cu_cp_controller/cu_up_connection_manager.h new file mode 100644 index 0000000000..142a19b910 --- /dev/null +++ b/lib/cu_cp/cu_cp_controller/cu_up_connection_manager.h @@ -0,0 +1,79 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#pragma once + +#include "srsran/cu_cp/cu_cp_e1_handler.h" +#include "srsran/cu_cp/cu_cp_types.h" +#include "srsran/support/executors/task_executor.h" +#include + +namespace srsran { +namespace srs_cu_cp { + +class cu_up_processor_repository; +class common_task_scheduler; + +/// \brief This class is responsible for allocating the resources in the CU-CP required to handle the establishment +/// or drop of E1 GW connections. +/// +/// This class acts as a facade, hiding the details associated with the dispatching of E1 GW events to the +/// the CU-CP through the appropriate task executors. +class cu_up_connection_manager : public cu_cp_e1_handler +{ +public: + cu_up_connection_manager(unsigned max_nof_cu_ups, + cu_up_processor_repository& cus_up_, + task_executor& cu_cp_exec_, + common_task_scheduler& common_task_sched_); + + std::unique_ptr + handle_new_cu_up_connection(std::unique_ptr e1ap_tx_pdu_notifier) override; + + void stop(); + + size_t nof_cu_ups() const { return cu_up_count.load(std::memory_order_relaxed); } + +private: + class shared_cu_up_connection_context; + class e1_gw_to_cu_cp_pdu_adapter; + + // Called by the E1 GW when it disconnects its PDU notifier endpoint. + void handle_e1_gw_connection_closed(cu_up_index_t cu_up_index); + + const unsigned max_nof_cu_ups; + cu_up_processor_repository& cu_ups; + task_executor& cu_cp_exec; + common_task_scheduler& common_task_sched; + srslog::basic_logger& logger; + + std::map> cu_up_connections; + std::atomic cu_up_count{0}; + + std::atomic stopped{false}; + std::mutex stop_mutex; + std::condition_variable stop_cvar; + bool stop_completed = false; +}; + +} // namespace srs_cu_cp +} // namespace srsran diff --git a/lib/cu_cp/cu_cp_impl.cpp b/lib/cu_cp/cu_cp_impl.cpp index 559c2bd98b..73158a54ad 100644 --- a/lib/cu_cp/cu_cp_impl.cpp +++ b/lib/cu_cp/cu_cp_impl.cpp @@ -247,6 +247,8 @@ async_task cu_cp_impl::handle_rrc_reestablishment_context_modification_req cu_up_db.find_cu_up_processor(uint_to_cu_up_index(0))->get_e1ap_bearer_context_manager(), du_db.get_du_processor(ue->get_du_index()).get_f1ap_interface().get_f1ap_ue_context_manager(), ue->get_rrc_ue_notifier(), + get_cu_cp_rrc_ue_interface(), + ue->get_task_sched(), ue->get_up_resource_manager()); } @@ -427,6 +429,8 @@ cu_cp_impl::handle_new_pdu_session_resource_setup_request(cu_cp_pdu_session_reso cu_up_db.find_cu_up_processor(uint_to_cu_up_index(0))->get_e1ap_bearer_context_manager(), du_db.get_du_processor(ue->get_du_index()).get_f1ap_interface().get_f1ap_ue_context_manager(), ue->get_rrc_ue_notifier(), + get_cu_cp_rrc_ue_interface(), + ue->get_task_sched(), ue->get_up_resource_manager()); } @@ -444,6 +448,8 @@ cu_cp_impl::handle_new_pdu_session_resource_modify_request(const cu_cp_pdu_sessi cu_up_db.find_cu_up_processor(uint_to_cu_up_index(0))->get_e1ap_bearer_context_manager(), du_db.get_du_processor(ue->get_du_index()).get_f1ap_interface().get_f1ap_ue_context_manager(), ue->get_rrc_ue_notifier(), + get_cu_cp_rrc_ue_interface(), + ue->get_task_sched(), ue->get_up_resource_manager()); } @@ -460,8 +466,8 @@ cu_cp_impl::handle_new_pdu_session_resource_release_command(const cu_cp_pdu_sess command, cu_up_db.find_cu_up_processor(uint_to_cu_up_index(0))->get_e1ap_bearer_context_manager(), du_db.get_du_processor(ue->get_du_index()).get_f1ap_interface().get_f1ap_ue_context_manager(), - ngap_entity->get_ngap_control_message_handler(), ue->get_rrc_ue_notifier(), + get_cu_cp_rrc_ue_interface(), ue->get_task_sched(), ue->get_up_resource_manager()); } diff --git a/lib/cu_cp/cu_cp_impl.h b/lib/cu_cp/cu_cp_impl.h index 02a698ad90..d9577eb0be 100644 --- a/lib/cu_cp/cu_cp_impl.h +++ b/lib/cu_cp/cu_cp_impl.h @@ -123,7 +123,7 @@ class cu_cp_impl final : public cu_cp, // cu_cp public interface cu_cp_f1c_handler& get_f1c_handler() override { return controller->get_f1c_handler(); } - cu_cp_e1_handler& get_e1_handler() override { return cu_up_db; } + cu_cp_e1_handler& get_e1_handler() override { return controller->get_e1_handler(); } cu_cp_e1ap_event_handler& get_cu_cp_e1ap_handler() override { return *this; } cu_cp_ng_handler& get_ng_handler() override { return *this; } cu_cp_ngap_handler& get_cu_cp_ngap_handler() override { return *this; } diff --git a/lib/cu_cp/cu_up_processor/cu_up_processor_repository.cpp b/lib/cu_cp/cu_up_processor/cu_up_processor_repository.cpp index 153505941b..83f709b8d5 100644 --- a/lib/cu_cp/cu_up_processor/cu_up_processor_repository.cpp +++ b/lib/cu_cp/cu_up_processor/cu_up_processor_repository.cpp @@ -29,39 +29,6 @@ using namespace srsran; using namespace srs_cu_cp; -namespace { - -class e1ap_rx_pdu_notifier final : public e1ap_message_notifier -{ -public: - e1ap_rx_pdu_notifier(cu_cp_e1_handler& parent_, cu_up_index_t cu_up_index_) : - parent(&parent_), - cu_up_index(cu_up_index_), - cached_msg_handler(parent->get_cu_up(cu_up_index).get_message_handler()) - { - } - e1ap_rx_pdu_notifier(const e1ap_rx_pdu_notifier&) = delete; - e1ap_rx_pdu_notifier(e1ap_rx_pdu_notifier&&) = delete; - e1ap_rx_pdu_notifier& operator=(const e1ap_rx_pdu_notifier&) = delete; - e1ap_rx_pdu_notifier& operator=(e1ap_rx_pdu_notifier&&) = delete; - - ~e1ap_rx_pdu_notifier() - { - if (parent != nullptr) { - parent->handle_cu_up_remove_request(cu_up_index); - } - } - - void on_new_message(const e1ap_message& msg) override { cached_msg_handler.handle_message(msg); } - -private: - cu_cp_e1_handler* parent; - cu_up_index_t cu_up_index; - e1ap_message_handler& cached_msg_handler; -}; - -} // namespace - cu_up_processor_repository::cu_up_processor_repository(cu_up_repository_config cfg_) : cfg(cfg_), logger(cfg.logger), @@ -72,26 +39,6 @@ cu_up_processor_repository::cu_up_processor_repository(cu_up_repository_config c { } -std::unique_ptr -cu_up_processor_repository::handle_new_cu_up_connection(std::unique_ptr e1ap_tx_pdu_notifier) -{ - cu_up_index_t cu_up_index = add_cu_up(std::move(e1ap_tx_pdu_notifier)); - if (cu_up_index == cu_up_index_t::invalid) { - logger.warning("Rejecting new CU-UP connection. Cause: Failed to create a new CU-UP"); - return nullptr; - } - - logger.info("Added CU-UP {}", cu_up_index); - - return std::make_unique(*this, cu_up_index); -} - -void cu_up_processor_repository::handle_cu_up_remove_request(cu_up_index_t cu_up_index) -{ - logger.debug("Removing CU-UP {}...", cu_up_index); - remove_cu_up(cu_up_index); -} - cu_up_index_t cu_up_processor_repository::add_cu_up(std::unique_ptr e1ap_tx_pdu_notifier) { cu_up_index_t cu_up_index = allocate_cu_up_index(); @@ -137,33 +84,32 @@ cu_up_index_t cu_up_processor_repository::allocate_cu_up_index() return cu_up_index_t::invalid; } -void cu_up_processor_repository::remove_cu_up(cu_up_index_t cu_up_index) +async_task cu_up_processor_repository::remove_cu_up(cu_up_index_t cu_up_index) { - // Note: The caller of this function can be a CU-UP procedure. Thus, we have to wait for the procedure to finish - // before safely removing the DU. This is achieved via a scheduled async task - srsran_assert(cu_up_index != cu_up_index_t::invalid, "Invalid cu_up_index={}", cu_up_index); - logger.debug("Scheduling cu_up_index={} deletion", cu_up_index); - - // Schedule CU-UP removal task - cu_up_task_sched.handle_cu_up_async_task( - cu_up_index, launch_async([this, cu_up_index](coro_context>& ctx) { - CORO_BEGIN(ctx); - auto du_it = cu_up_db.find(cu_up_index); - if (du_it == cu_up_db.end()) { - logger.warning("Remove CU-UP called for inexistent cu_up_index={}", cu_up_index); - CORO_EARLY_RETURN(); - } - - // Remove DU - // TODO - removed_cu_up_db.insert(std::make_pair(cu_up_index, std::move(cu_up_db.at(cu_up_index)))); - cu_up_db.erase(cu_up_index); - - logger.info("Removed CU-UP {}", cu_up_index); - - CORO_RETURN(); - })); + logger.debug("Removing CU-UP {}...", cu_up_index); + + return launch_async([this, cu_up_index](coro_context>& ctx) { + CORO_BEGIN(ctx); + + // Remove CU-UP + if (cu_up_db.find(cu_up_index) == cu_up_db.end()) { + logger.warning("Remove CU-UP called for non-existent cu_up_index={}", cu_up_index); + return; + } + + // Stop CU-UP activity, eliminating pending transactions for the CU-UP and respective UEs. + // TODO + + // Remove CU-UP + removed_cu_up_db.insert(std::make_pair(cu_up_index, std::move(cu_up_db.at(cu_up_index)))); + cu_up_db.erase(cu_up_index); + + // Remove CU-UP + logger.info("Removed CU-UP {}", cu_up_index); + + CORO_RETURN(); + }); } cu_up_e1_handler& cu_up_processor_repository::get_cu_up(cu_up_index_t cu_up_index) diff --git a/lib/cu_cp/cu_up_processor/cu_up_processor_repository.h b/lib/cu_cp/cu_up_processor/cu_up_processor_repository.h index 78a60a69d5..45dc561306 100644 --- a/lib/cu_cp/cu_up_processor/cu_up_processor_repository.h +++ b/lib/cu_cp/cu_up_processor/cu_up_processor_repository.h @@ -39,19 +39,22 @@ struct cu_up_repository_config { srslog::basic_logger& logger; }; -class cu_up_processor_repository : public cu_cp_e1_handler +class cu_up_processor_repository { public: explicit cu_up_processor_repository(cu_up_repository_config cfg_); - // CU-UP interface - std::unique_ptr - handle_new_cu_up_connection(std::unique_ptr e1ap_tx_pdu_notifier) override; - void handle_cu_up_remove_request(cu_up_index_t cu_up_index) override; + /// \brief Adds a CU-UP processor object to the CU-CP. + /// \return The CU-UP index of the added CU-UP processor object. + cu_up_index_t add_cu_up(std::unique_ptr e1ap_tx_pdu_notifier); + + /// \brief Removes the specified CU-UP processor object from the CU-CP. + /// \param[in] cu_up_index The index of the CU-UP processor to delete. + async_task remove_cu_up(cu_up_index_t cu_up_index); size_t get_nof_cu_ups() const { return cu_up_db.size(); } - cu_up_e1_handler& get_cu_up(cu_up_index_t cu_up_index) override; + cu_up_e1_handler& get_cu_up(cu_up_index_t cu_up_index); /// \brief Find a CU-UP object. /// \param[in] cu_up_index The index of the CU-UP processor object. @@ -70,14 +73,6 @@ class cu_up_processor_repository : public cu_cp_e1_handler e1ap_message_handler& get_message_handler() override; }; - /// \brief Adds a CU-UP processor object to the CU-CP. - /// \return The CU-UP index of the added CU-UP processor object. - cu_up_index_t add_cu_up(std::unique_ptr e1ap_tx_pdu_notifier); - - /// \brief Removes the specified CU-UP processor object from the CU-CP. - /// \param[in] cu_up_index The index of the CU-UP processor to delete. - void remove_cu_up(cu_up_index_t cu_up_index); - /// \brief Get the next available index from the CU-UP processor database. /// \return The CU-UP index. cu_up_index_t allocate_cu_up_index(); diff --git a/lib/cu_cp/routine_managers/cu_cp_routine_manager.cpp b/lib/cu_cp/routine_managers/cu_cp_routine_manager.cpp index b95e10eba9..b4f39013da 100644 --- a/lib/cu_cp/routine_managers/cu_cp_routine_manager.cpp +++ b/lib/cu_cp/routine_managers/cu_cp_routine_manager.cpp @@ -70,6 +70,8 @@ async_task cu_cp_routine_manager::sta e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng, f1ap_ue_context_manager& f1ap_ue_ctxt_mng, du_processor_rrc_ue_control_message_notifier& rrc_ue_ctrl_notifier, + cu_cp_rrc_ue_interface& cu_cp_notifier, + ue_task_scheduler& ue_task_sched, up_resource_manager& up_resource_mng) { return launch_async(setup_msg, @@ -79,6 +81,8 @@ async_task cu_cp_routine_manager::sta e1ap_bearer_ctxt_mng, f1ap_ue_ctxt_mng, rrc_ue_ctrl_notifier, + cu_cp_notifier, + ue_task_sched, up_resource_mng, logger); } @@ -89,10 +93,18 @@ cu_cp_routine_manager::start_pdu_session_resource_modification_routine( e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng, f1ap_ue_context_manager& f1ap_ue_ctxt_mng, du_processor_rrc_ue_control_message_notifier& rrc_ue_ctrl_notifier, + cu_cp_rrc_ue_interface& cu_cp_notifier, + ue_task_scheduler& ue_task_sched, up_resource_manager& up_resource_mng) { - return launch_async( - modify_msg, e1ap_bearer_ctxt_mng, f1ap_ue_ctxt_mng, rrc_ue_ctrl_notifier, up_resource_mng, logger); + return launch_async(modify_msg, + e1ap_bearer_ctxt_mng, + f1ap_ue_ctxt_mng, + rrc_ue_ctrl_notifier, + cu_cp_notifier, + ue_task_sched, + up_resource_mng, + logger); } async_task @@ -100,17 +112,17 @@ cu_cp_routine_manager::start_pdu_session_resource_release_routine( const cu_cp_pdu_session_resource_release_command& release_cmd, e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng, f1ap_ue_context_manager& f1ap_ue_ctxt_mng, - ngap_control_message_handler& ngap_handler, du_processor_rrc_ue_control_message_notifier& rrc_ue_ctrl_notifier, - ue_task_scheduler& task_sched, + cu_cp_rrc_ue_interface& cu_cp_notifier, + ue_task_scheduler& ue_task_sched, up_resource_manager& up_resource_mng) { return launch_async(release_cmd, e1ap_bearer_ctxt_mng, f1ap_ue_ctxt_mng, - ngap_handler, rrc_ue_ctrl_notifier, - task_sched, + cu_cp_notifier, + ue_task_sched, up_resource_mng, logger); } @@ -131,10 +143,19 @@ async_task cu_cp_routine_manager::start_reestablishment_context_modificati e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng, f1ap_ue_context_manager& f1ap_ue_ctxt_mng, du_processor_rrc_ue_control_message_notifier& rrc_ue_ctrl_notifier, + cu_cp_rrc_ue_interface& cu_cp_notifier, + ue_task_scheduler& ue_task_sched, up_resource_manager& ue_up_resource_manager) { - return launch_async( - ue_index, up_sec, e1ap_bearer_ctxt_mng, f1ap_ue_ctxt_mng, rrc_ue_ctrl_notifier, ue_up_resource_manager, logger); + return launch_async(ue_index, + up_sec, + e1ap_bearer_ctxt_mng, + f1ap_ue_ctxt_mng, + rrc_ue_ctrl_notifier, + cu_cp_notifier, + ue_task_sched, + ue_up_resource_manager, + logger); } async_task diff --git a/lib/cu_cp/routine_managers/cu_cp_routine_manager.h b/lib/cu_cp/routine_managers/cu_cp_routine_manager.h index a467ad5591..ea34081d1c 100644 --- a/lib/cu_cp/routine_managers/cu_cp_routine_manager.h +++ b/lib/cu_cp/routine_managers/cu_cp_routine_manager.h @@ -56,15 +56,17 @@ class cu_cp_routine_manager : public common_task_scheduler e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng, f1ap_ue_context_manager& f1ap_ue_ctxt_mng, du_processor_rrc_ue_control_message_notifier& rrc_ue_ctrl_notifier, + cu_cp_rrc_ue_interface& cu_cp_notifier, + ue_task_scheduler& ue_task_sched, up_resource_manager& up_resource_mng); async_task start_pdu_session_resource_release_routine(const cu_cp_pdu_session_resource_release_command& release_cmd, e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng, f1ap_ue_context_manager& f1ap_ue_ctxt_mng, - ngap_control_message_handler& ngap_handler, du_processor_rrc_ue_control_message_notifier& rrc_ue_ctrl_notifier, - ue_task_scheduler& task_sched, + cu_cp_rrc_ue_interface& cu_cp_notifier, + ue_task_scheduler& ue_task_sched, up_resource_manager& up_resource_mng); async_task @@ -72,6 +74,8 @@ class cu_cp_routine_manager : public common_task_scheduler e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng, f1ap_ue_context_manager& f1ap_ue_ctxt_mng, du_processor_rrc_ue_control_message_notifier& rrc_ue_ctrl_notifier, + cu_cp_rrc_ue_interface& cu_cp_notifier, + ue_task_scheduler& ue_task_sched, up_resource_manager& up_resource_mng); async_task @@ -86,6 +90,8 @@ class cu_cp_routine_manager : public common_task_scheduler e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng, f1ap_ue_context_manager& f1ap_ue_ctxt_mng, du_processor_rrc_ue_control_message_notifier& rrc_ue_ctrl_notifier, + cu_cp_rrc_ue_interface& cu_cp_notifier, + ue_task_scheduler& ue_task_sched, up_resource_manager& ue_up_resource_manager); async_task diff --git a/lib/cu_cp/routines/pdu_session_resource_modification_routine.cpp b/lib/cu_cp/routines/pdu_session_resource_modification_routine.cpp index acfbe3078b..c0720f34fe 100644 --- a/lib/cu_cp/routines/pdu_session_resource_modification_routine.cpp +++ b/lib/cu_cp/routines/pdu_session_resource_modification_routine.cpp @@ -22,6 +22,7 @@ #include "pdu_session_resource_modification_routine.h" #include "pdu_session_routine_helpers.h" +#include "srsran/cu_cp/ue_task_scheduler.h" using namespace srsran; using namespace srsran::srs_cu_cp; @@ -57,12 +58,16 @@ pdu_session_resource_modification_routine::pdu_session_resource_modification_rou e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng_, f1ap_ue_context_manager& f1ap_ue_ctxt_mng_, du_processor_rrc_ue_control_message_notifier& rrc_ue_notifier_, + cu_cp_rrc_ue_interface& cu_cp_notifier_, + ue_task_scheduler& ue_task_sched_, up_resource_manager& up_resource_mng_, srslog::basic_logger& logger_) : modify_request(modify_request_), e1ap_bearer_ctxt_mng(e1ap_bearer_ctxt_mng_), f1ap_ue_ctxt_mng(f1ap_ue_ctxt_mng_), rrc_ue_notifier(rrc_ue_notifier_), + cu_cp_notifier(cu_cp_notifier_), + ue_task_sched(ue_task_sched_), up_resource_mng(up_resource_mng_), logger(logger_) { @@ -182,6 +187,9 @@ void pdu_session_resource_modification_routine::operator()( // Handle RRC Reconfiguration result. if (handle_procedure_response(response_msg, modify_request, rrc_reconfig_result, logger) == false) { logger.warning("ue={}: \"{}\" RRC reconfiguration failed", modify_request.ue_index, name()); + // Notify NGAP to request UE context release from AMF + ue_task_sched.schedule_async_task(cu_cp_notifier.handle_ue_context_release( + {modify_request.ue_index, {}, ngap_cause_radio_network_t::release_due_to_ngran_generated_reason})); CORO_EARLY_RETURN(generate_pdu_session_resource_modify_response(false)); } } diff --git a/lib/cu_cp/routines/pdu_session_resource_modification_routine.h b/lib/cu_cp/routines/pdu_session_resource_modification_routine.h index c71b824b65..c2177bfd57 100644 --- a/lib/cu_cp/routines/pdu_session_resource_modification_routine.h +++ b/lib/cu_cp/routines/pdu_session_resource_modification_routine.h @@ -22,8 +22,10 @@ #pragma once +#include "../cu_cp_impl_interface.h" #include "../du_processor/du_processor.h" #include "../up_resource_manager/up_resource_manager_impl.h" +#include "srsran/cu_cp/ue_task_scheduler.h" #include "srsran/e1ap/cu_cp/e1ap_cu_cp.h" #include "srsran/support/async/async_task.h" @@ -39,6 +41,8 @@ class pdu_session_resource_modification_routine e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng_, f1ap_ue_context_manager& f1ap_ue_ctxt_mng_, du_processor_rrc_ue_control_message_notifier& rrc_ue_notifier_, + cu_cp_rrc_ue_interface& cu_cp_notifier_, + ue_task_scheduler& ue_task_sched_, up_resource_manager& up_resource_mng_, srslog::basic_logger& logger_); @@ -60,6 +64,8 @@ class pdu_session_resource_modification_routine e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng; // to trigger bearer context setup at CU-UP f1ap_ue_context_manager& f1ap_ue_ctxt_mng; // to trigger UE context modification at DU du_processor_rrc_ue_control_message_notifier& rrc_ue_notifier; // to trigger RRC Reconfiguration at UE + cu_cp_rrc_ue_interface& cu_cp_notifier; // to trigger UE release at CU-CP + ue_task_scheduler& ue_task_sched; // to schedule UE release request up_resource_manager& up_resource_mng; // to get RRC DRB config srslog::basic_logger& logger; diff --git a/lib/cu_cp/routines/pdu_session_resource_release_routine.cpp b/lib/cu_cp/routines/pdu_session_resource_release_routine.cpp index 572b55427a..38f5e5ddcc 100644 --- a/lib/cu_cp/routines/pdu_session_resource_release_routine.cpp +++ b/lib/cu_cp/routines/pdu_session_resource_release_routine.cpp @@ -31,16 +31,16 @@ pdu_session_resource_release_routine::pdu_session_resource_release_routine( const cu_cp_pdu_session_resource_release_command& release_cmd_, e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng_, f1ap_ue_context_manager& f1ap_ue_ctxt_mng_, - ngap_control_message_handler& ngap_handler_, du_processor_rrc_ue_control_message_notifier& rrc_ue_notifier_, + cu_cp_rrc_ue_interface& cu_cp_notifier_, ue_task_scheduler& task_sched_, up_resource_manager& up_resource_mng_, srslog::basic_logger& logger_) : release_cmd(release_cmd_), e1ap_bearer_ctxt_mng(e1ap_bearer_ctxt_mng_), f1ap_ue_ctxt_mng(f1ap_ue_ctxt_mng_), - ngap_handler(ngap_handler_), rrc_ue_notifier(rrc_ue_notifier_), + cu_cp_notifier(cu_cp_notifier_), task_sched(task_sched_), up_resource_mng(up_resource_mng_), logger(logger_) @@ -190,13 +190,8 @@ pdu_session_resource_release_routine::handle_pdu_session_resource_release_respon logger.info("ue={}: \"{}\" failed", release_cmd.ue_index, name()); // Trigger UE context release request. - cu_cp_ue_context_release_request req{release_cmd.ue_index}; - req.cause = ngap_cause_radio_network_t::radio_conn_with_ue_lost; - task_sched.schedule_async_task(launch_async([ngap_notif = &ngap_handler, req](coro_context>& ctx) { - CORO_BEGIN(ctx); - CORO_AWAIT(ngap_notif->handle_ue_context_release_request(req)); - CORO_RETURN(); - })); + task_sched.schedule_async_task(cu_cp_notifier.handle_ue_context_release( + {release_cmd.ue_index, {}, ngap_cause_radio_network_t::radio_conn_with_ue_lost})); } return response_msg; diff --git a/lib/cu_cp/routines/pdu_session_resource_release_routine.h b/lib/cu_cp/routines/pdu_session_resource_release_routine.h index 3f89240778..7c49b9e4ce 100644 --- a/lib/cu_cp/routines/pdu_session_resource_release_routine.h +++ b/lib/cu_cp/routines/pdu_session_resource_release_routine.h @@ -22,11 +22,11 @@ #pragma once +#include "../cu_cp_impl_interface.h" #include "../du_processor/du_processor.h" #include "../up_resource_manager/up_resource_manager_impl.h" #include "srsran/cu_cp/ue_task_scheduler.h" #include "srsran/e1ap/cu_cp/e1ap_cu_cp.h" -#include "srsran/ngap/ngap.h" #include "srsran/support/async/async_task.h" namespace srsran { @@ -40,8 +40,8 @@ class pdu_session_resource_release_routine pdu_session_resource_release_routine(const cu_cp_pdu_session_resource_release_command& release_cmd_, e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng_, f1ap_ue_context_manager& f1ap_ue_ctxt_mng_, - ngap_control_message_handler& ngap_handler_, du_processor_rrc_ue_control_message_notifier& rrc_ue_notifier_, + cu_cp_rrc_ue_interface& cu_cp_notifier_, ue_task_scheduler& task_sched_, up_resource_manager& up_resource_mng_, srslog::basic_logger& logger_); @@ -62,9 +62,9 @@ class pdu_session_resource_release_routine e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng; // to trigger bearer context setup at CU-UP f1ap_ue_context_manager& f1ap_ue_ctxt_mng; // to trigger UE context modification at DU - ngap_control_message_handler& ngap_handler; // to request UE release du_processor_rrc_ue_control_message_notifier& rrc_ue_notifier; // to trigger RRC Reconfiguration at UE - ue_task_scheduler& task_sched; // to schedule UE release request (over NGAP) + cu_cp_rrc_ue_interface& cu_cp_notifier; // to trigger UE release at CU-CP + ue_task_scheduler& task_sched; // to schedule UE release request up_resource_manager& up_resource_mng; // to get RRC DRB config srslog::basic_logger& logger; diff --git a/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp b/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp index de0746f245..4385efce42 100644 --- a/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp +++ b/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp @@ -72,6 +72,8 @@ pdu_session_resource_setup_routine::pdu_session_resource_setup_routine( e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng_, f1ap_ue_context_manager& f1ap_ue_ctxt_mng_, du_processor_rrc_ue_control_message_notifier& rrc_ue_notifier_, + cu_cp_rrc_ue_interface& cu_cp_notifier_, + ue_task_scheduler& ue_task_sched_, up_resource_manager& up_resource_mng_, srslog::basic_logger& logger_) : setup_msg(setup_msg_), @@ -81,6 +83,8 @@ pdu_session_resource_setup_routine::pdu_session_resource_setup_routine( e1ap_bearer_ctxt_mng(e1ap_bearer_ctxt_mng_), f1ap_ue_ctxt_mng(f1ap_ue_ctxt_mng_), rrc_ue_notifier(rrc_ue_notifier_), + cu_cp_notifier(cu_cp_notifier_), + ue_task_sched(ue_task_sched_), up_resource_mng(up_resource_mng_), logger(logger_) { @@ -239,6 +243,9 @@ void pdu_session_resource_setup_routine::operator()( // Handle RRC Reconfiguration Response if (!handle_procedure_response(response_msg, setup_msg, rrc_reconfig_result, logger)) { logger.warning("ue={}: \"{}\" RRC reconfiguration failed", setup_msg.ue_index, name()); + // Notify NGAP to request UE context release from AMF + ue_task_sched.schedule_async_task(cu_cp_notifier.handle_ue_context_release( + {setup_msg.ue_index, {}, ngap_cause_radio_network_t::release_due_to_ngran_generated_reason})); CORO_EARLY_RETURN(handle_pdu_session_resource_setup_result(false)); } } diff --git a/lib/cu_cp/routines/pdu_session_resource_setup_routine.h b/lib/cu_cp/routines/pdu_session_resource_setup_routine.h index bee23fbc9f..7a1bccdbc3 100644 --- a/lib/cu_cp/routines/pdu_session_resource_setup_routine.h +++ b/lib/cu_cp/routines/pdu_session_resource_setup_routine.h @@ -22,9 +22,11 @@ #pragma once +#include "../cu_cp_impl_interface.h" #include "../du_processor/du_processor.h" #include "../up_resource_manager/up_resource_manager_impl.h" #include "srsran/cu_cp/ue_configuration.h" +#include "srsran/cu_cp/ue_task_scheduler.h" #include "srsran/e1ap/cu_cp/e1ap_cu_cp.h" #include "srsran/support/async/async_task.h" @@ -58,6 +60,8 @@ class pdu_session_resource_setup_routine e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng_, f1ap_ue_context_manager& f1ap_ue_ctxt_mng_, du_processor_rrc_ue_control_message_notifier& rrc_ue_notifier_, + cu_cp_rrc_ue_interface& cu_cp_notifier_, + ue_task_scheduler& ue_task_sched_, up_resource_manager& up_resource_mng_, srslog::basic_logger& logger_); @@ -81,6 +85,8 @@ class pdu_session_resource_setup_routine e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng; // to trigger bearer context setup at CU-UP f1ap_ue_context_manager& f1ap_ue_ctxt_mng; // to trigger UE context modification at DU du_processor_rrc_ue_control_message_notifier& rrc_ue_notifier; // to trigger RRC Reconfiguration at UE + cu_cp_rrc_ue_interface& cu_cp_notifier; // to trigger UE release at CU-CP + ue_task_scheduler& ue_task_sched; // to schedule UE release request up_resource_manager& up_resource_mng; // to get RRC DRB config srslog::basic_logger& logger; diff --git a/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp b/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp index 5885dd9168..35858688cf 100644 --- a/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp +++ b/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp @@ -34,6 +34,8 @@ reestablishment_context_modification_routine::reestablishment_context_modificati e1ap_bearer_context_manager& e1ap_bearer_ctxt_mng_, f1ap_ue_context_manager& f1ap_ue_ctxt_mng_, du_processor_rrc_ue_control_message_notifier& rrc_ue_notifier_, + cu_cp_rrc_ue_interface& cu_cp_notifier_, + ue_task_scheduler& ue_task_sched_, up_resource_manager& up_resource_mng_, srslog::basic_logger& logger_) : ue_index(ue_index_), @@ -41,6 +43,8 @@ reestablishment_context_modification_routine::reestablishment_context_modificati e1ap_bearer_ctxt_mng(e1ap_bearer_ctxt_mng_), f1ap_ue_ctxt_mng(f1ap_ue_ctxt_mng_), rrc_ue_notifier(rrc_ue_notifier_), + cu_cp_notifier(cu_cp_notifier_), + ue_task_sched(ue_task_sched_), up_resource_mng(up_resource_mng_), logger(logger_) { @@ -148,6 +152,9 @@ void reestablishment_context_modification_routine::operator()(coro_contexthandle_dl_buffer_state_update(mac_dl_buffer_state_indication_message{ue_index, prev_lcid, 0}); + mac->handle_dl_buffer_state_update(mac_dl_buffer_state_indication_message{ue_index, lcid, 0}); } } @@ -253,18 +252,19 @@ class rlc_tx_mac_buffer_state_updater : public rlc_tx_lower_layer_notifier srsran_assert(mac != nullptr, "RLC Tx Buffer State notifier is disconnected"); mac_dl_buffer_state_indication_message bs{}; bs.ue_index = ue_index; - bs.lcid = lcid.load(std::memory_order_relaxed); + bs.lcid = lcid; bs.bs = bsr; - if (SRSRAN_UNLIKELY(bs.lcid == INVALID_LCID)) { + if (SRSRAN_UNLIKELY(not connected.load(std::memory_order_relaxed))) { // Discard. - return; + bs.bs = 0; } mac->handle_dl_buffer_state_update(bs); } private: du_ue_index_t ue_index = INVALID_DU_UE_INDEX; - std::atomic lcid{INVALID_LCID}; + std::atomic connected{true}; + lcid_t lcid; mac_ue_control_information_handler* mac = nullptr; }; diff --git a/lib/e1ap/gateways/CMakeLists.txt b/lib/e1ap/gateways/CMakeLists.txt index 48aaf95ab2..398ad0176c 100644 --- a/lib/e1ap/gateways/CMakeLists.txt +++ b/lib/e1ap/gateways/CMakeLists.txt @@ -18,5 +18,8 @@ # and at http://www.gnu.org/licenses/. # -add_library(srsran_e1_gateway e1_local_connector_factory.cpp) -target_link_libraries(srsran_e1_gateway srsran_support srsran_e1ap_common e1ap_asn1) +add_library(srsran_e1_gateway + e1_local_connector_factory.cpp + e1_network_client_factory.cpp + e1_network_server_factory.cpp) +target_link_libraries(srsran_e1_gateway srsran_support srsran_e1ap_common e1ap_asn1 srsran_pcap) diff --git a/lib/e1ap/gateways/e1_local_connector_factory.cpp b/lib/e1ap/gateways/e1_local_connector_factory.cpp index 9591ac4e60..9dbe89b43b 100644 --- a/lib/e1ap/gateways/e1_local_connector_factory.cpp +++ b/lib/e1ap/gateways/e1_local_connector_factory.cpp @@ -23,6 +23,8 @@ #include "srsran/e1ap/gateways/e1_local_connector_factory.h" #include "srsran/cu_cp/cu_cp_e1_handler.h" #include "srsran/e1ap/common/e1ap_message.h" +#include "srsran/e1ap/gateways/e1_network_client_factory.h" +#include "srsran/e1ap/gateways/e1_network_server_factory.h" #include "srsran/pcap/dlt_pcap.h" using namespace srsran; @@ -76,7 +78,7 @@ class e1_local_connector_impl final : public e1_local_connector { report_fatal_error_if_not(cu_cp_e1_mng != nullptr, "CU-CP has not been attached to E1 gateway."); - // Decorate DU RX notifier with pcap writing. + // Decorate CU-UP RX notifier with pcap writing. if (pcap_writer.is_write_enabled()) { cu_up_notifier = std::make_unique( std::move(cu_up_notifier), pcap_writer, srslog::fetch_basic_logger("CU-UP-E1")); @@ -99,9 +101,64 @@ class e1_local_connector_impl final : public e1_local_connector srs_cu_cp::cu_cp_e1_handler* cu_cp_e1_mng = nullptr; }; +/// Implementation of a CU-UP and CU-CP E1 SCTP-based gateway for the case that the CU-UP and CU-CP are co-located. +/// +/// Note: This class should only be used for testing purposes. +class e1_sctp_connector_impl final : public e1_local_connector +{ +public: + e1_sctp_connector_impl(const e1_local_sctp_connector_config& cfg) : broker(cfg.broker), pcap_writer(cfg.pcap) + { + // Create SCTP server. + sctp_network_gateway_config sctp; + sctp.if_name = "E1"; + sctp.ppid = E1AP_PPID; + sctp.bind_address = "127.0.0.1"; + // Use any bind port available. + sctp.bind_port = cfg.bind_port; + server = create_e1_gateway_server(e1_cu_cp_sctp_gateway_config{sctp, broker, pcap_writer}); + } + + void attach_cu_cp(srs_cu_cp::cu_cp_e1_handler& cu_e1_handler_) override + { + server->attach_cu_cp(cu_e1_handler_); + + // Create SCTP client. + sctp_network_connector_config sctp_client; + sctp_client.if_name = "E1"; + sctp_client.dest_name = "CU-CP"; + sctp_client.connect_address = "127.0.0.1"; + sctp_client.connect_port = server->get_listen_port().value(); + sctp_client.ppid = E1AP_PPID; + // Note: We only need to save the PCAPs in one side of the connection. + client = create_e1_gateway_client(e1_cu_up_sctp_gateway_config{sctp_client, broker, *null_pcap_writer}); + } + + std::optional get_listen_port() const override { return server->get_listen_port(); } + + std::unique_ptr + handle_cu_up_connection_request(std::unique_ptr cu_up_rx_pdu_notifier) override + { + // Connect client to server automatically. + return client->handle_cu_up_connection_request(std::move(cu_up_rx_pdu_notifier)); + } + +private: + io_broker& broker; + dlt_pcap& pcap_writer; + std::unique_ptr null_pcap_writer = create_null_dlt_pcap(); + std::unique_ptr server; + std::unique_ptr client; +}; + } // namespace std::unique_ptr srsran::create_e1_local_connector(const e1_local_connector_config& cfg) { return std::make_unique(cfg); } + +std::unique_ptr srsran::create_e1_local_connector(const e1_local_sctp_connector_config& cfg) +{ + return std::make_unique(cfg); +} diff --git a/lib/e1ap/gateways/e1_network_client_factory.cpp b/lib/e1ap/gateways/e1_network_client_factory.cpp new file mode 100644 index 0000000000..800aa1685e --- /dev/null +++ b/lib/e1ap/gateways/e1_network_client_factory.cpp @@ -0,0 +1,170 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#include "srsran/e1ap/gateways/e1_network_client_factory.h" +#include "srsran/asn1/e1ap/e1ap.h" +#include "srsran/e1ap/common/e1ap_message.h" +#include "srsran/gateways/sctp_network_client_factory.h" +#include "srsran/pcap/dlt_pcap.h" +#include "srsran/support/io/io_broker.h" + +using namespace srsran; + +namespace { + +/// \brief Notifier for converting packed E1AP PDUs coming from the E1 GW into unpacked E1AP PDUs and forward them to +/// the CU-UP. +class sctp_to_e1_pdu_notifier final : public sctp_association_sdu_notifier +{ +public: + sctp_to_e1_pdu_notifier(std::unique_ptr cu_up_rx_pdu_notifier_, + dlt_pcap& pcap_writer_, + srslog::basic_logger& logger_) : + cu_up_rx_pdu_notifier(std::move(cu_up_rx_pdu_notifier_)), pcap_writer(pcap_writer_), logger(logger_) + { + } + + bool on_new_sdu(byte_buffer sdu) override + { + // Unpack E1AP PDU. + asn1::cbit_ref bref(sdu); + e1ap_message msg; + if (msg.pdu.unpack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Couldn't unpack E1AP PDU"); + return false; + } + + // Forward Rx PDU to pcap, if enabled. + if (pcap_writer.is_write_enabled()) { + pcap_writer.push_pdu(sdu.copy()); + } + + // Forward unpacked Rx PDU to the CU-UP. + cu_up_rx_pdu_notifier->on_new_message(msg); + + return true; + } + +private: + std::unique_ptr cu_up_rx_pdu_notifier; + dlt_pcap& pcap_writer; + srslog::basic_logger& logger; +}; + +/// \brief Notifier for converting unpacked E1AP PDUs coming from the CU-UP into packed E1AP PDUs and forward them to +/// the F1C-GW. +class e1_to_sctp_pdu_notifier final : public e1ap_message_notifier +{ +public: + e1_to_sctp_pdu_notifier(std::unique_ptr sctp_rx_pdu_notifier_, + dlt_pcap& pcap_writer_, + srslog::basic_logger& logger_) : + sctp_rx_pdu_notifier(std::move(sctp_rx_pdu_notifier_)), pcap_writer(pcap_writer_), logger(logger_) + { + } + + void on_new_message(const e1ap_message& msg) override + { + // pack E1AP PDU into SCTP SDU. + byte_buffer tx_sdu{byte_buffer::fallback_allocation_tag{}}; + asn1::bit_ref bref(tx_sdu); + if (msg.pdu.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack E1AP PDU"); + return; + } + + // Push Tx PDU to pcap. + if (pcap_writer.is_write_enabled()) { + pcap_writer.push_pdu(tx_sdu.copy()); + } + + // Forward packed Tx PDU to SCTP gateway. + sctp_rx_pdu_notifier->on_new_sdu(std::move(tx_sdu)); + } + +private: + std::unique_ptr sctp_rx_pdu_notifier; + dlt_pcap& pcap_writer; + srslog::basic_logger& logger; +}; + +class e1_sctp_gateway_client final : public srs_cu_up::e1_connection_client +{ +public: + e1_sctp_gateway_client(const e1_cu_up_sctp_gateway_config& params) : + pcap_writer(params.pcap), broker(params.broker), sctp_params(params.sctp) + { + // Create SCTP network adapter. + sctp_gateway = create_sctp_network_client(sctp_network_client_config{params.sctp, broker}); + report_error_if_not(sctp_gateway != nullptr, "Failed to create SCTP gateway client.\n"); + } + + std::unique_ptr + handle_cu_up_connection_request(std::unique_ptr cu_up_rx_pdu_notifier) override + { + srsran_assert(cu_up_rx_pdu_notifier != nullptr, "CU-UP Rx PDU notifier is null"); + + logger.debug( + "Establishing TNL connection to CU-CP ({}:{})...", sctp_params.connect_address, sctp_params.connect_port); + std::unique_ptr sctp_sender = sctp_gateway->connect_to( + "CU-CP", + sctp_params.connect_address, + sctp_params.connect_port, + std::make_unique(std::move(cu_up_rx_pdu_notifier), pcap_writer, logger)); + if (sctp_sender == nullptr) { + logger.error("Failed to establish E1 TNL connection to CU-CP on {}:{}.\n", + sctp_params.connect_address, + sctp_params.connect_port); + return nullptr; + } + logger.info("{}: TNL connection to {} on {}:{} accepted", + sctp_params.if_name, + sctp_params.dest_name, + sctp_params.connect_address, + sctp_params.connect_port); + fmt::print("{}: Connection to {} on {}:{} completed\n", + sctp_params.if_name, + sctp_params.dest_name, + sctp_params.connect_address, + sctp_params.connect_port); + + // Return the Tx PDU notifier to the CU-UP. + return std::make_unique(std::move(sctp_sender), pcap_writer, logger); + } + +private: + dlt_pcap& pcap_writer; + io_broker& broker; + srsran::sctp_network_connector_config sctp_params; + srslog::basic_logger& logger = srslog::fetch_basic_logger("CU-UP-E1"); + + // SCTP network gateway + std::unique_ptr sctp_gateway; +}; + +} // namespace + +std::unique_ptr +srsran::create_e1_gateway_client(const e1_cu_up_sctp_gateway_config& params) +{ + return std::make_unique(params); +} diff --git a/lib/e1ap/gateways/e1_network_server_factory.cpp b/lib/e1ap/gateways/e1_network_server_factory.cpp new file mode 100644 index 0000000000..aca4462558 --- /dev/null +++ b/lib/e1ap/gateways/e1_network_server_factory.cpp @@ -0,0 +1,166 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#include "srsran/e1ap/gateways/e1_network_server_factory.h" +#include "srsran/asn1/e1ap/e1ap.h" +#include "srsran/e1ap/common/e1ap_message.h" +#include "srsran/gateways/sctp_network_server_factory.h" +#include "srsran/pcap/dlt_pcap.h" +#include "srsran/support/error_handling.h" + +using namespace srsran; + +namespace { + +/// Notifier passed to the CU-CP, which the CU-CP will use to send E1AP Tx PDUs. +class e1_to_gw_pdu_notifier final : public e1ap_message_notifier +{ +public: + e1_to_gw_pdu_notifier(std::unique_ptr sctp_sender_, + dlt_pcap& pcap_writer_, + srslog::basic_logger& logger_) : + sctp_sender(std::move(sctp_sender_)), pcap_writer(pcap_writer_), logger(logger_) + { + } + + /// Handle unpacked Tx E1AP PDU by packing and forwarding it into the SCTP GW. + void on_new_message(const e1ap_message& msg) override + { + // pack E1AP PDU into SCTP SDU. + byte_buffer tx_sdu{byte_buffer::fallback_allocation_tag{}}; + asn1::bit_ref bref(tx_sdu); + if (msg.pdu.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack E1AP PDU"); + return; + } + + // Push Tx PDU to pcap. + if (pcap_writer.is_write_enabled()) { + pcap_writer.push_pdu(tx_sdu.copy()); + } + + // Forward packed E1AP Tx PDU to SCTP gateway. + sctp_sender->on_new_sdu(std::move(tx_sdu)); + } + +private: + std::unique_ptr sctp_sender; + dlt_pcap& pcap_writer; + srslog::basic_logger& logger; +}; + +/// Notifier passed to the SCTP GW, which the GW will use to forward E1AP Rx PDUs to the CU-CP. +class gw_to_e1_pdu_notifier final : public sctp_association_sdu_notifier +{ +public: + gw_to_e1_pdu_notifier(std::unique_ptr e1ap_notifier_, + dlt_pcap& pcap_writer_, + srslog::basic_logger& logger_) : + e1ap_notifier(std::move(e1ap_notifier_)), pcap_writer(pcap_writer_), logger(logger_) + { + } + + bool on_new_sdu(byte_buffer sdu) override + { + // Unpack SCTP SDU into E1AP PDU. + asn1::cbit_ref bref(sdu); + e1ap_message msg; + if (msg.pdu.unpack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Couldn't unpack E1AP PDU"); + return false; + } + + // Forward SCTP Rx SDU to pcap, if enabled. + if (pcap_writer.is_write_enabled()) { + pcap_writer.push_pdu(sdu.copy()); + } + + // Forward unpacked Rx PDU to the CU-CP. + e1ap_notifier->on_new_message(msg); + + return true; + } + +private: + std::unique_ptr e1ap_notifier; + dlt_pcap& pcap_writer; + srslog::basic_logger& logger; +}; + +/// Adapter of the SCTP server to the E1 interface of the CU-CP. +class e1_sctp_server final : public srs_cu_cp::e1_connection_server, public sctp_network_association_factory +{ +public: + e1_sctp_server(const e1_cu_cp_sctp_gateway_config& params_) : params(params_) + { + // Create SCTP server. + sctp_server = create_sctp_network_server(sctp_network_server_config{params.sctp, params.broker, *this}); + report_error_if_not(sctp_server != nullptr, "Failed to create SCTP server"); + } + + void attach_cu_cp(srs_cu_cp::cu_cp_e1_handler& cu_e1_handler_) override + { + cu_e1_handler = &cu_e1_handler_; + + // Start listening for new CU-UP SCTP connections. + bool result = sctp_server->listen(); + report_error_if_not(result, "Failed to start SCTP server.\n"); + fmt::print("{}: Listening for new connections on {}:{}...\n", + params.sctp.if_name, + params.sctp.bind_address, + params.sctp.bind_port); + } + + std::optional get_listen_port() const override { return sctp_server->get_listen_port(); } + + std::unique_ptr + create(std::unique_ptr sctp_send_notifier) override + { + // Create an unpacked E1AP PDU notifier and pass it to the CU-CP. + auto e1_sender = std::make_unique(std::move(sctp_send_notifier), params.pcap, logger); + + std::unique_ptr e1_receiver = + cu_e1_handler->handle_new_cu_up_connection(std::move(e1_sender)); + + // Wrap the received E1AP Rx PDU notifier in an SCTP notifier and return it. + if (e1_receiver == nullptr) { + return nullptr; + } + + return std::make_unique(std::move(e1_receiver), params.pcap, logger); + } + +private: + const e1_cu_cp_sctp_gateway_config params; + srslog::basic_logger& logger = srslog::fetch_basic_logger("CU-CP-E1"); + srs_cu_cp::cu_cp_e1_handler* cu_e1_handler = nullptr; + + std::unique_ptr sctp_server; +}; + +} // namespace + +std::unique_ptr +srsran::create_e1_gateway_server(const e1_cu_cp_sctp_gateway_config& cfg) +{ + return std::make_unique(cfg); +} diff --git a/lib/f1ap/cu_cp/f1ap_asn1_converters.h b/lib/f1ap/cu_cp/f1ap_asn1_converters.h index 020e93424e..84ceb2b95d 100644 --- a/lib/f1ap/cu_cp/f1ap_asn1_converters.h +++ b/lib/f1ap/cu_cp/f1ap_asn1_converters.h @@ -569,14 +569,6 @@ f1ap_rrc_recfg_complete_ind_to_asn1(const f1ap_rrc_recfg_complete_ind& rrc_recfg return asn1_rrc_recfg_complete_ind; } -/// \brief Calculate the 5G-S-TMSI from the common type 5G-S-TMSI struct. -inline uint64_t five_g_s_tmsi_struct_to_number(const cu_cp_five_g_s_tmsi& five_g_s_tmsi) -{ - // 5G-S-TMSI is a 48 bit string consisting of <5G-TMSI (32 bit)> - return ((uint64_t)five_g_s_tmsi.amf_set_id << 38) + ((uint64_t)five_g_s_tmsi.amf_pointer << 32) + - five_g_s_tmsi.five_g_tmsi; -} - /// \brief Convert F1AP ASN.1 to \c cu_cp_tx_bw. /// \param[in] asn1_tx_bw The ASN.1 type tx bw. /// \return The common type tx bw. diff --git a/lib/f1ap/cu_cp/f1ap_asn1_helpers.h b/lib/f1ap/cu_cp/f1ap_asn1_helpers.h index 62d3d6a953..8d499fdada 100644 --- a/lib/f1ap/cu_cp/f1ap_asn1_helpers.h +++ b/lib/f1ap/cu_cp/f1ap_asn1_helpers.h @@ -480,7 +480,7 @@ inline void fill_f1ap_ue_context_modification_response(f1ap_ue_context_modificat inline void fill_asn1_paging_message(asn1::f1ap::paging_s& asn1_paging, const cu_cp_paging_message& paging) { // Add ue id idx value - uint64_t five_g_s_tmsi = five_g_s_tmsi_struct_to_number(paging.ue_paging_id); + uint64_t five_g_s_tmsi = paging.ue_paging_id.to_number(); // UE Identity Index value is defined as: UE_ID 5G-S-TMSI mod 1024 (see TS 38.304 section 7.1) asn1_paging->ue_id_idx_value.set_idx_len10().from_number(five_g_s_tmsi % 1024); diff --git a/lib/f1ap/du/procedures/f1ap_du_ue_context_setup_procedure.cpp b/lib/f1ap/du/procedures/f1ap_du_ue_context_setup_procedure.cpp index 7f7dc89bdc..f2f7601d8a 100644 --- a/lib/f1ap/du/procedures/f1ap_du_ue_context_setup_procedure.cpp +++ b/lib/f1ap/du/procedures/f1ap_du_ue_context_setup_procedure.cpp @@ -26,6 +26,7 @@ #include "proc_logger.h" #include "srsran/asn1/f1ap/common.h" #include "srsran/f1ap/common/f1ap_message.h" +#include "srsran/support/async/async_no_op_task.h" using namespace srsran; using namespace srs_du; @@ -103,7 +104,7 @@ void f1ap_du_ue_context_setup_procedure::operator()(coro_context If the UE CONTEXT SETUP REQUEST message contains the RRC-Container IE, the gNB-DU shall send the corresponding // RRC message to the UE via SRB1. if (msg->rrc_container_present and not msg->rrc_container.empty()) { - CORO_AWAIT(ue->bearers.find_srb(srb_id_t::srb1)->handle_pdu_and_await_transmission(msg->rrc_container.copy())); + CORO_AWAIT(handle_rrc_container()); } // Respond back to CU-CP with success. @@ -112,6 +113,16 @@ void f1ap_du_ue_context_setup_procedure::operator()(coro_context f1ap_du_ue_context_setup_procedure::handle_rrc_container() +{ + f1c_bearer* srb1 = ue->bearers.find_srb(srb_id_t::srb1); + if (srb1 != nullptr) { + return srb1->handle_pdu_and_await_transmission(msg->rrc_container.copy()); + } + logger.error("{}: Failed to find SRB1 bearer to send RRC container.", f1ap_log_prefix{ue->context, name()}); + return launch_no_op_task(); +} + async_task f1ap_du_ue_context_setup_procedure::request_du_ue_config() { // Construct DU request. diff --git a/lib/f1ap/du/procedures/f1ap_du_ue_context_setup_procedure.h b/lib/f1ap/du/procedures/f1ap_du_ue_context_setup_procedure.h index 9a5280edd7..4f19830e6e 100644 --- a/lib/f1ap/du/procedures/f1ap_du_ue_context_setup_procedure.h +++ b/lib/f1ap/du/procedures/f1ap_du_ue_context_setup_procedure.h @@ -51,6 +51,8 @@ class f1ap_du_ue_context_setup_procedure // Send UE Context Setup Failure to CU. void send_ue_context_setup_failure(); + async_task handle_rrc_container(); + const char* name() const { return "UE Context Setup"; } const asn1::f1ap::ue_context_setup_request_s msg; diff --git a/lib/f1ap/du/ue_context/f1c_du_bearer_impl.cpp b/lib/f1ap/du/ue_context/f1c_du_bearer_impl.cpp index 75caae1f4a..36673c5aff 100644 --- a/lib/f1ap/du/ue_context/f1c_du_bearer_impl.cpp +++ b/lib/f1ap/du/ue_context/f1c_du_bearer_impl.cpp @@ -138,8 +138,11 @@ f1c_other_srb_du_bearer::f1c_other_srb_du_bearer(f1ap_ue_context& ue_ctxt_ logger(srslog::fetch_basic_logger("DU-F1")) { // Mark all event entries as free. - for (unsigned i = 0; i != pending_delivery_event_pool.size(); ++i) { - pending_delivery_event_pool[i].first = -1; + for (auto& event : pending_delivery_event_pool) { + event.first = -1; + } + for (auto& event : pending_transmission_event_pool) { + event.first = -1; } } @@ -216,12 +219,18 @@ async_task f1c_other_srb_du_bearer::handle_pdu_and_await_delivery(byte_buf void f1c_other_srb_du_bearer::handle_transmit_notification(uint32_t highest_pdcp_sn) { - handle_notification(highest_pdcp_sn, true); + if (not ue_exec.defer([this, highest_pdcp_sn]() { handle_notification(highest_pdcp_sn, true); })) { + logger.warning("Discarded transmit notification for SRB{} because the task executor queue is full.", + srb_id_to_uint(srb_id)); + } } void f1c_other_srb_du_bearer::handle_delivery_notification(uint32_t highest_pdcp_sn) { - handle_notification(highest_pdcp_sn, false); + if (not ue_exec.defer([this, highest_pdcp_sn]() { handle_notification(highest_pdcp_sn, false); })) { + logger.warning("Discarded delivery notification for SRB{} because the task executor queue is full.", + srb_id_to_uint(srb_id)); + } } async_task f1c_other_srb_du_bearer::handle_pdu_and_await(byte_buffer pdu, bool tx_or_delivery) diff --git a/lib/ngap/gateways/n2_connection_client_factory.cpp b/lib/ngap/gateways/n2_connection_client_factory.cpp index 12a94ff7c9..b35afe5434 100644 --- a/lib/ngap/gateways/n2_connection_client_factory.cpp +++ b/lib/ngap/gateways/n2_connection_client_factory.cpp @@ -244,6 +244,7 @@ class n2_sctp_gateway_client : public n2_connection_client sctp_cfg.connect_address, sctp_cfg.connect_port, std::make_unique(std::move(cu_cp_rx_pdu_notifier), pcap_writer, logger)); + if (sctp_sender == nullptr) { logger.error( "Failed to establish N2 TNL connection to AMF on {}:{}.\n", sctp_cfg.connect_address, sctp_cfg.connect_port); @@ -287,12 +288,17 @@ srsran::srs_cu_cp::create_n2_connection_client(const n2_connection_client_config // Connection to AMF through SCTP. const auto& nw_mode = std::get(params.mode); srsran::sctp_network_connector_config sctp_cfg; - sctp_cfg.dest_name = "AMF"; - sctp_cfg.if_name = "N2"; - sctp_cfg.connect_address = nw_mode.amf_address; - sctp_cfg.connect_port = nw_mode.amf_port; - sctp_cfg.bind_address = nw_mode.bind_address; - sctp_cfg.bind_interface = nw_mode.bind_interface; - sctp_cfg.ppid = NGAP_PPID; + sctp_cfg.dest_name = "AMF"; + sctp_cfg.if_name = "N2"; + sctp_cfg.connect_address = nw_mode.amf_address; + sctp_cfg.connect_port = nw_mode.amf_port; + sctp_cfg.bind_address = nw_mode.bind_address; + sctp_cfg.bind_interface = nw_mode.bind_interface; + sctp_cfg.rto_initial = nw_mode.rto_initial; + sctp_cfg.rto_min = nw_mode.rto_min; + sctp_cfg.rto_max = nw_mode.rto_max; + sctp_cfg.init_max_attempts = nw_mode.init_max_attempts; + sctp_cfg.max_init_timeo = nw_mode.max_init_timeo; + sctp_cfg.ppid = NGAP_PPID; return std::make_unique(nw_mode.broker, sctp_cfg, params.pcap); } diff --git a/lib/ngap/ngap_asn1_converters.h b/lib/ngap/ngap_asn1_converters.h index d99475dd9e..18703ac683 100644 --- a/lib/ngap/ngap_asn1_converters.h +++ b/lib/ngap/ngap_asn1_converters.h @@ -848,5 +848,18 @@ inline bool target_to_source_transport_container_to_asn1( return true; } +/// \brief Convert NGAP ASN.1 to \c cu_cp_five_g_s_tmsi. +/// \param[in] asn1_ue_id The ASN.1 type ue paging ID. +/// \return The common type cu_cp_five_g_s_tmsi. +inline cu_cp_five_g_s_tmsi ngap_asn1_to_ue_paging_id(const asn1::ngap::ue_paging_id_c& asn1_ue_id) +{ + srsran_assert(asn1_ue_id.type() == asn1::ngap::ue_paging_id_c::types_opts::five_g_s_tmsi, + "Invalid UE paging ID type"); + + return cu_cp_five_g_s_tmsi{asn1_ue_id.five_g_s_tmsi().amf_set_id.to_number(), + asn1_ue_id.five_g_s_tmsi().amf_pointer.to_number(), + asn1_ue_id.five_g_s_tmsi().five_g_tmsi.to_number()}; +} + } // namespace srs_cu_cp } // namespace srsran diff --git a/lib/ngap/ngap_asn1_helpers.h b/lib/ngap/ngap_asn1_helpers.h index 635af4f3ed..26788da165 100644 --- a/lib/ngap/ngap_asn1_helpers.h +++ b/lib/ngap/ngap_asn1_helpers.h @@ -214,9 +214,9 @@ inline void fill_asn1_initial_ue_message(asn1::ngap::init_ue_msg_s& asn1_ms if (msg.five_g_s_tmsi.has_value()) { // TS 23.003 - 5G-S-TMSI contains AMF Set ID, AMF Pointer and 5G TMSI. asn1_msg->five_g_s_tmsi_present = true; - asn1_msg->five_g_s_tmsi.amf_set_id.from_number(msg.five_g_s_tmsi.value().amf_set_id); - asn1_msg->five_g_s_tmsi.amf_pointer.from_number(msg.five_g_s_tmsi.value().amf_pointer); - asn1_msg->five_g_s_tmsi.five_g_tmsi.from_number(msg.five_g_s_tmsi.value().five_g_tmsi); + asn1_msg->five_g_s_tmsi.amf_set_id.from_number(msg.five_g_s_tmsi.value().get_amf_set_id()); + asn1_msg->five_g_s_tmsi.amf_pointer.from_number(msg.five_g_s_tmsi.value().get_amf_pointer()); + asn1_msg->five_g_s_tmsi.five_g_tmsi.from_number(msg.five_g_s_tmsi.value().get_five_g_tmsi()); } if (msg.amf_set_id.has_value()) { @@ -866,9 +866,7 @@ inline void fill_asn1_ue_context_release_complete(asn1::ngap::ue_context_release inline void fill_cu_cp_paging_message(cu_cp_paging_message& paging, const asn1::ngap::paging_s& asn1_paging) { // add ue paging id - paging.ue_paging_id.amf_set_id = asn1_paging->ue_paging_id.five_g_s_tmsi().amf_set_id.to_number(); - paging.ue_paging_id.amf_pointer = asn1_paging->ue_paging_id.five_g_s_tmsi().amf_pointer.to_number(); - paging.ue_paging_id.five_g_tmsi = asn1_paging->ue_paging_id.five_g_s_tmsi().five_g_tmsi.to_number(); + paging.ue_paging_id = ngap_asn1_to_ue_paging_id(asn1_paging->ue_paging_id); // add paging drx if (asn1_paging->paging_drx_present) { diff --git a/lib/phy/generic_functions/precoding/channel_precoder_avx2.cpp b/lib/phy/generic_functions/precoding/channel_precoder_avx2.cpp index ec46b86bfa..1021af7854 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_avx2.cpp +++ b/lib/phy/generic_functions/precoding/channel_precoder_avx2.cpp @@ -55,9 +55,26 @@ simd_cf_interleaved operator*(const simd_cf_interleaved& re, const simd_cf_t& we return _mm256_fmaddsub_ps(re, weight.re, _mm256_mul_ps(_mm256_shuffle_ps(re, re, 0xb1), weight.im)); } +inline __m128i ps_to_cbf16(simd_cf_interleaved in) +{ + const __m256i bias = _mm256_set1_epi32(0x7fff); + const __m256i one = _mm256_set1_epi32(0x1); + + __m256i a_i32 = _mm256_castps_si256(in); + + // Round to nearest even. + a_i32 = _mm256_add_epi32(a_i32, _mm256_add_epi32(bias, _mm256_and_si256(_mm256_srli_epi32(a_i32, 16), one))); + + // Shift right 16 bits. + a_i32 = _mm256_srai_epi32(a_i32, 16); + + // Pack both parts in 32-bit registers. + return _mm_packs_epi32(_mm256_extractf128_si256(a_i32, 0), _mm256_extractf128_si256(a_i32, 1)); +} + } // namespace -void channel_precoder_avx2::apply_precoding_port(span port_re, +void channel_precoder_avx2::apply_precoding_port(span port_re, const re_buffer_reader<>& input_re, span port_weights) const { @@ -96,15 +113,16 @@ void channel_precoder_avx2::apply_precoding_port(span port_ } // Store. - _mm256_storeu_ps(reinterpret_cast(&port_re[i_re]), re_out); + _mm_storeu_si128(reinterpret_cast<__m128i*>(&port_re[i_re]), ps_to_cbf16(re_out)); } for (; i_re != nof_re; ++i_re) { - port_re[i_re] = layer_re_view_list[0][i_re] * port_weights[0]; + cf_t sum = layer_re_view_list[0][i_re] * port_weights[0]; for (unsigned i_layer = 1; i_layer != nof_layers; ++i_layer) { - port_re[i_re] += layer_re_view_list[i_layer][i_re] * port_weights[i_layer]; + sum += layer_re_view_list[i_layer][i_re] * port_weights[i_layer]; } + port_re[i_re] = sum; } } @@ -193,23 +211,6 @@ static inline void layer4_map_and_ci8_to_cf(simd_cf_interleaved& out_l0, from_ci8_to_cf(out_l0, out_l1, out_l2, out_l3, tmp); } -inline __m128i ps_to_cbf16(simd_cf_interleaved in) -{ - const __m256i bias = _mm256_set1_epi32(0x7fff); - const __m256i one = _mm256_set1_epi32(0x1); - - __m256i a_i32 = _mm256_castps_si256(in); - - // Round to nearest even. - a_i32 = _mm256_add_epi32(a_i32, _mm256_add_epi32(bias, _mm256_and_si256(_mm256_srli_epi32(a_i32, 16), one))); - - // Shift right 16 bits. - a_i32 = _mm256_srai_epi32(a_i32, 16); - - // Pack both parts in 32-bit registers. - return _mm_packs_epi32(_mm256_extractf128_si256(a_i32, 0), _mm256_extractf128_si256(a_i32, 1)); -} - void channel_precoder_avx2::apply_layer_map_and_precoding(re_buffer_writer& output, span input, const precoding_weight_matrix& precoding) const diff --git a/lib/phy/generic_functions/precoding/channel_precoder_avx2.h b/lib/phy/generic_functions/precoding/channel_precoder_avx2.h index c7bcfd2bf5..03d498fe8b 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_avx2.h +++ b/lib/phy/generic_functions/precoding/channel_precoder_avx2.h @@ -34,7 +34,7 @@ class channel_precoder_avx2 : public channel_precoder_impl { public: // See interface for documentation. - void apply_precoding_port(span port_re, + void apply_precoding_port(span port_re, const re_buffer_reader<>& input_re, span port_weights) const override; diff --git a/lib/phy/generic_functions/precoding/channel_precoder_avx512.cpp b/lib/phy/generic_functions/precoding/channel_precoder_avx512.cpp index 8ebfdcfa91..06464852a1 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_avx512.cpp +++ b/lib/phy/generic_functions/precoding/channel_precoder_avx512.cpp @@ -49,6 +49,27 @@ struct simd_cf_t { // Type to hold a set of complex numbers using an AVX512 register, with interleaved real and imaginary parts. using simd_cf_interleaved = __m512; +inline __m256i ps_to_cbf16(simd_cf_interleaved in) +{ +#if __AVX512BF16__ + return (__m256i)_mm512_cvtneps_pbh(in); +#else // __AVX512BF16__ + const __m512i bias = _mm512_set1_epi32(0x7fff); + const __m512i one = _mm512_set1_epi32(0x1); + + __m512i a_i32 = _mm512_castps_si512(in); + + // Round to nearest even. + a_i32 = _mm512_add_epi32(a_i32, _mm512_add_epi32(bias, _mm512_and_si512(_mm512_srli_epi32(a_i32, 16), one))); + + // Shift right 16 bits. + a_i32 = _mm512_srli_epi32(a_i32, 16); + + // Pack both parts in 32-bit registers. + return _mm512_cvtepi32_epi16(a_i32); +#endif // __AVX512BF16__ +} + } // namespace // Multiplication operator for the precoding weights. @@ -215,28 +236,7 @@ static inline void layer4_map_and_ci8_to_cf(simd_cf_interleaved& out0, from_ci8_to_cf(out0, out1, out2, out3, tmp); } -inline __m256i ps_to_cbf16(simd_cf_interleaved in) -{ -#if __AVX512BF16__ - return (__m256i)_mm512_cvtneps_pbh(in); -#else // __AVX512BF16__ - const __m512i bias = _mm512_set1_epi32(0x7fff); - const __m512i one = _mm512_set1_epi32(0x1); - - __m512i a_i32 = _mm512_castps_si512(in); - - // Round to nearest even. - a_i32 = _mm512_add_epi32(a_i32, _mm512_add_epi32(bias, _mm512_and_si512(_mm512_srli_epi32(a_i32, 16), one))); - - // Shift right 16 bits. - a_i32 = _mm512_srli_epi32(a_i32, 16); - - // Pack both parts in 32-bit registers. - return _mm512_cvtepi32_epi16(a_i32); -#endif // __AVX512BF16__ -} - -void channel_precoder_avx512::apply_precoding_port(span port_re, +void channel_precoder_avx512::apply_precoding_port(span port_re, const re_buffer_reader<>& input_re, span port_weights) const { @@ -275,15 +275,15 @@ void channel_precoder_avx512::apply_precoding_port(span por } // Store. - _mm512_storeu_ps(reinterpret_cast(&port_re[i_re]), re_out); + _mm256_storeu_si256(reinterpret_cast<__m256i*>(&port_re[i_re]), ps_to_cbf16(re_out)); } for (; i_re != nof_re; ++i_re) { - port_re[i_re] = layer_re_view_list[0][i_re] * port_weights[0]; - + cf_t sum = layer_re_view_list[0][i_re] * port_weights[0]; for (unsigned i_layer = 1; i_layer != nof_layers; ++i_layer) { - port_re[i_re] += layer_re_view_list[i_layer][i_re] * port_weights[i_layer]; + sum += layer_re_view_list[i_layer][i_re] * port_weights[i_layer]; } + port_re[i_re] = sum; } } diff --git a/lib/phy/generic_functions/precoding/channel_precoder_avx512.h b/lib/phy/generic_functions/precoding/channel_precoder_avx512.h index 38cf587a08..c9c661511f 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_avx512.h +++ b/lib/phy/generic_functions/precoding/channel_precoder_avx512.h @@ -33,7 +33,7 @@ namespace srsran { class channel_precoder_avx512 : public channel_precoder_impl { // See interface for documentation. - void apply_precoding_port(span port_re, + void apply_precoding_port(span port_re, const re_buffer_reader<>& input_re, span port_weights) const override; diff --git a/lib/phy/generic_functions/precoding/channel_precoder_generic.cpp b/lib/phy/generic_functions/precoding/channel_precoder_generic.cpp index 8be7c71469..57a3709f58 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_generic.cpp +++ b/lib/phy/generic_functions/precoding/channel_precoder_generic.cpp @@ -24,7 +24,7 @@ using namespace srsran; -void channel_precoder_generic::apply_precoding_port(span port_re, +void channel_precoder_generic::apply_precoding_port(span port_re, const re_buffer_reader<>& input_re, span port_weights) const { @@ -38,12 +38,13 @@ void channel_precoder_generic::apply_precoding_port(span po for (unsigned i_re = 0; i_re != nof_re; ++i_re) { // Set the port RE to the contribution of the first layer. - port_re[i_re] = layer_re_view_list[0][i_re] * port_weights[0]; + cf_t sum = layer_re_view_list[0][i_re] * port_weights[0]; for (unsigned i_layer = 1; i_layer != nof_layers; ++i_layer) { // Accumulate the contributions of all other layers. - port_re[i_re] += layer_re_view_list[i_layer][i_re] * port_weights[i_layer]; + sum += layer_re_view_list[i_layer][i_re] * port_weights[i_layer]; } + port_re[i_re] = sum; } } diff --git a/lib/phy/generic_functions/precoding/channel_precoder_generic.h b/lib/phy/generic_functions/precoding/channel_precoder_generic.h index acbe10cb44..254a561625 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_generic.h +++ b/lib/phy/generic_functions/precoding/channel_precoder_generic.h @@ -33,7 +33,7 @@ namespace srsran { class channel_precoder_generic : public channel_precoder_impl { // See interface for documentation. - void apply_precoding_port(span port_re, + void apply_precoding_port(span port_re, const re_buffer_reader<>& input_re, span port_weights) const override; diff --git a/lib/phy/generic_functions/precoding/channel_precoder_impl.cpp b/lib/phy/generic_functions/precoding/channel_precoder_impl.cpp index 762a228d9b..2c1f3ac67d 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_impl.cpp +++ b/lib/phy/generic_functions/precoding/channel_precoder_impl.cpp @@ -24,7 +24,7 @@ using namespace srsran; -void channel_precoder_impl::apply_precoding(re_buffer_writer<>& output, +void channel_precoder_impl::apply_precoding(re_buffer_writer& output, const re_buffer_reader<>& input, const precoding_weight_matrix& precoding) const { @@ -58,7 +58,7 @@ void channel_precoder_impl::apply_precoding(re_buffer_writer<>& outpu for (unsigned i_port = 0; i_port != nof_tx_ports; ++i_port) { // View of the output RE for a single antenna port. - span port_re_view = output.get_slice(i_port); + span port_re_view = output.get_slice(i_port); // View of the precoding weights applicable to a single antenna port, i.e., the coefficients applied to each // layer for the antenna port. diff --git a/lib/phy/generic_functions/precoding/channel_precoder_impl.h b/lib/phy/generic_functions/precoding/channel_precoder_impl.h index 7ad6f14391..29c03821be 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_impl.h +++ b/lib/phy/generic_functions/precoding/channel_precoder_impl.h @@ -37,7 +37,7 @@ class channel_precoder_impl : public channel_precoder explicit channel_precoder_impl() = default; // See interface for documentation. - void apply_precoding(re_buffer_writer<>& output, + void apply_precoding(re_buffer_writer& output, const re_buffer_reader<>& input, const precoding_weight_matrix& precoding) const override; @@ -47,8 +47,9 @@ class channel_precoder_impl : public channel_precoder /// \param[out] port_re View over the RE of a single antenna port. /// \param[in] input Input symbols, indexed by RE and transmit layer. /// \param[in] precoding Precoding coefficients, indexed by layer. - virtual void - apply_precoding_port(span port_re, const re_buffer_reader<>& input_re, span port_weights) const = 0; + virtual void apply_precoding_port(span port_re, + const re_buffer_reader<>& input_re, + span port_weights) const = 0; }; } // namespace srsran diff --git a/lib/phy/generic_functions/precoding/channel_precoder_neon.cpp b/lib/phy/generic_functions/precoding/channel_precoder_neon.cpp index 1a3bbbea41..e38c36d826 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_neon.cpp +++ b/lib/phy/generic_functions/precoding/channel_precoder_neon.cpp @@ -74,9 +74,30 @@ simd_cf_interleaved add_mul(const simd_cf_interleaved& sum, const simd_cf_interl return ret; } +inline uint16x8_t cf_to_cbf16(simd_cf_interleaved in) +{ + const uint32x4_t bias = vdupq_n_u32(0x7fff); + const uint32x4_t one = vdupq_n_u32(0x1); + + // Reinterpret the 32-bit single-precision input as unsigned 32-bit integer. + uint32x4_t a_u32 = vreinterpretq_u32_f32(in.val[0]); + uint32x4_t b_u32 = vreinterpretq_u32_f32(in.val[1]); + + // Round to nearest even. + a_u32 = vaddq_u32(a_u32, vaddq_u32(bias, vandq_u32(vshrq_n_u32(a_u32, 16), one))); + b_u32 = vaddq_u32(b_u32, vaddq_u32(bias, vandq_u32(vshrq_n_u32(b_u32, 16), one))); + + // Remove the 16 least significant bits of the fractional part. + a_u32 = vshrq_n_u32(a_u32, 16); + b_u32 = vandq_u32(b_u32, vdupq_n_u32(0xffff0000)); + + // Combine real and imaginary parts. + return vreinterpretq_u16_u32(vorrq_u32(a_u32, b_u32)); +} + } // namespace -void channel_precoder_neon::apply_precoding_port(span port_re, +void channel_precoder_neon::apply_precoding_port(span port_re, const re_buffer_reader<>& input_re, span port_weights) const { @@ -115,15 +136,17 @@ void channel_precoder_neon::apply_precoding_port(span port_ } // Store. - vst2q_f32(reinterpret_cast(&port_re[i_re]), re_out); + vst1q_u16(reinterpret_cast(&port_re[i_re]), cf_to_cbf16(re_out)); } for (; i_re != nof_re; ++i_re) { - port_re[i_re] = layer_re_view_list[0][i_re] * port_weights[0]; + cf_t sum = layer_re_view_list[0][i_re] * port_weights[0]; for (unsigned i_layer = 1; i_layer != nof_layers; ++i_layer) { - port_re[i_re] += layer_re_view_list[i_layer][i_re] * port_weights[i_layer]; + sum += layer_re_view_list[i_layer][i_re] * port_weights[i_layer]; } + + port_re[i_re] = sum; } } @@ -174,27 +197,6 @@ static inline void from_ci8_to_cf(simd_cf_interleaved& out0, from_ci32_to_cf(out3, in_ci32_3); } -inline uint16x8_t cf_to_cbf16(simd_cf_interleaved in) -{ - const uint32x4_t bias = vdupq_n_u32(0x7fff); - const uint32x4_t one = vdupq_n_u32(0x1); - - // Reinterpret the 32-bit single-precision input as unsigned 32-bit integer. - uint32x4_t a_u32 = vreinterpretq_u32_f32(in.val[0]); - uint32x4_t b_u32 = vreinterpretq_u32_f32(in.val[1]); - - // Round to nearest even. - a_u32 = vaddq_u32(a_u32, vaddq_u32(bias, vandq_u32(vshrq_n_u32(a_u32, 16), one))); - b_u32 = vaddq_u32(b_u32, vaddq_u32(bias, vandq_u32(vshrq_n_u32(b_u32, 16), one))); - - // Remove the 16 least significant bits of the fractional part. - a_u32 = vshrq_n_u32(a_u32, 16); - b_u32 = vandq_u32(b_u32, vdupq_n_u32(0xffff0000)); - - // Combine real and imaginary parts. - return vreinterpretq_u16_u32(vorrq_u32(a_u32, b_u32)); -} - // Applies layer mapping for two layers and converts the symbols to cf_t. static inline void layer2_map_and_ci8_to_cf(simd_cf_interleaved& out0_l0, simd_cf_interleaved& out0_l1, diff --git a/lib/phy/generic_functions/precoding/channel_precoder_neon.h b/lib/phy/generic_functions/precoding/channel_precoder_neon.h index 33ec236a63..b2f8f4527e 100644 --- a/lib/phy/generic_functions/precoding/channel_precoder_neon.h +++ b/lib/phy/generic_functions/precoding/channel_precoder_neon.h @@ -34,7 +34,7 @@ class channel_precoder_neon : public channel_precoder_impl { public: // See interface for documentation. - void apply_precoding_port(span port_re, + void apply_precoding_port(span port_re, const re_buffer_reader<>& input_re, span port_weights) const override; diff --git a/lib/phy/support/resource_grid_mapper_impl.cpp b/lib/phy/support/resource_grid_mapper_impl.cpp index 878d9463fe..f9f283c5d0 100644 --- a/lib/phy/support/resource_grid_mapper_impl.cpp +++ b/lib/phy/support/resource_grid_mapper_impl.cpp @@ -27,7 +27,7 @@ using namespace srsran; -using precoding_buffer_type = static_re_buffer; +using precoding_buffer_type = static_re_buffer; // Resource element allocation patterns within a resource block for PDSCH DM-RS type 1. static const re_prb_mask& get_re_mask_type_1(unsigned cdm_group_id) @@ -58,7 +58,6 @@ static void map_dmrs_type1_contiguous(resource_grid_writer& writer, static constexpr unsigned re_stride = 2; static constexpr unsigned nof_dmrs_re_prb = NRE / re_stride; - unsigned nof_layers = precoding.get_nof_layers(); unsigned nof_precoding_ports = precoding.get_nof_ports(); // PRG size in number of RB. @@ -90,19 +89,6 @@ static void map_dmrs_type1_contiguous(resource_grid_writer& writer, continue; } - // Bypass precoding if it has no effect on the signal. - if ((nof_layers == 1) && (nof_precoding_ports == 1) && (precoding.get_nof_prg() == 1) && - (precoding.get_coefficient(0, 0, 0) == 1.0F)) { - // View over the input RE belonging to the current symbol. - re_buffer_reader_view input_re_symbol(input, i_re_buffer, nof_re_symbol); - - // Map directly to the grid. - span port_data = input_re_symbol.get_slice(0); - writer.put(0, i_symbol, first_subcarrier, re_stride, port_data); - i_re_buffer += nof_re_symbol; - continue; - } - // Counter for the number of precoded REs for the current symbol. unsigned i_precoding_buffer = 0; // First PRG in the allocation pattern. @@ -121,8 +107,8 @@ static void map_dmrs_type1_contiguous(resource_grid_writer& writer, unsigned nof_re_prg = (prg_prb_end - prg_prb_start) * nof_dmrs_re_prb; // Views of the input and precoder buffers for the REs belonging to the current PRG. - re_buffer_reader_view input_re_prg(input, i_re_buffer, nof_re_prg); - re_buffer_writer_view output_re_prg(precoding_buffer, i_precoding_buffer, nof_re_prg); + re_buffer_reader_view input_re_prg(input, i_re_buffer, nof_re_prg); + re_buffer_writer_view output_re_prg(precoding_buffer, i_precoding_buffer, nof_re_prg); // Apply precoding. precoder.apply_precoding(output_re_prg, input_re_prg, prg_weights); @@ -140,7 +126,7 @@ static void map_dmrs_type1_contiguous(resource_grid_writer& writer, for (unsigned i_tx_port = 0; i_tx_port != nof_precoding_ports; ++i_tx_port) { // Map the precoded REs to each port for the current symbol. - span port_data = precoding_buffer.get_slice(i_tx_port); + span port_data = precoding_buffer.get_slice(i_tx_port); writer.put(i_tx_port, i_symbol, first_subcarrier, re_stride, port_data); } } @@ -255,8 +241,8 @@ void resource_grid_mapper_impl::map(const re_buffer_reader<>& input, unsigned nof_re_prg = prg_re_mask.count(); // Views of the input and precoder buffers for the REs belonging to the current PRG. - re_buffer_reader_view input_re_prg(input, i_re_buffer, nof_re_prg); - re_buffer_writer_view output_re_prg(precoding_buffer, i_precoding_buffer, nof_re_prg); + re_buffer_reader_view input_re_prg(input, i_re_buffer, nof_re_prg); + re_buffer_writer_view output_re_prg(precoding_buffer, i_precoding_buffer, nof_re_prg); // Apply precoding. precoder->apply_precoding(output_re_prg, input_re_prg, prg_weights); @@ -277,8 +263,8 @@ void resource_grid_mapper_impl::map(const re_buffer_reader<>& input, for (unsigned i_tx_port = 0; i_tx_port != nof_precoding_ports; ++i_tx_port) { // Map the precoded REs to each port for the current symbol. - span port_data = precoding_buffer.get_slice(i_tx_port); - span unmapped = writer.put(i_tx_port, i_symbol, 0, symbol_re_mask, port_data); + span port_data = precoding_buffer.get_slice(i_tx_port); + span unmapped = writer.put(i_tx_port, i_symbol, 0, symbol_re_mask, port_data); srsran_assert(unmapped.empty(), "Not all REs have been mapped to the grid."); } } diff --git a/lib/phy/support/resource_grid_writer_impl.cpp b/lib/phy/support/resource_grid_writer_impl.cpp index 45a76ef5ee..ab29dfc52b 100644 --- a/lib/phy/support/resource_grid_writer_impl.cpp +++ b/lib/phy/support/resource_grid_writer_impl.cpp @@ -145,11 +145,11 @@ void resource_grid_writer_impl::put(unsigned port, unsigned l, unsigned k_init, clear_empty(port); } -void resource_grid_writer_impl::put(unsigned port, - unsigned l, - unsigned k_init, - unsigned stride, - span symbols) +void resource_grid_writer_impl::put(unsigned port, + unsigned l, + unsigned k_init, + unsigned stride, + span symbols) { unsigned nof_symbols = symbols.size(); srsran_assert( diff --git a/lib/phy/support/resource_grid_writer_impl.h b/lib/phy/support/resource_grid_writer_impl.h index c7aaaca5a2..568eb2ff58 100644 --- a/lib/phy/support/resource_grid_writer_impl.h +++ b/lib/phy/support/resource_grid_writer_impl.h @@ -62,7 +62,7 @@ class resource_grid_writer_impl : public resource_grid_writer void put(unsigned port, unsigned l, unsigned k_init, span symbols) override; // See interface for documentation. - void put(unsigned port, unsigned l, unsigned k_init, unsigned stride, span symbols) override; + void put(unsigned port, unsigned l, unsigned k_init, unsigned stride, span symbols) override; // See interface for documentation. span get_view(unsigned port, unsigned l) override; diff --git a/lib/phy/support/support_factories.cpp b/lib/phy/support/support_factories.cpp index 00750f07a5..97f5c7e418 100644 --- a/lib/phy/support/support_factories.cpp +++ b/lib/phy/support/support_factories.cpp @@ -145,7 +145,7 @@ class channel_precoder_dummy : public channel_precoder { public: // See interface for documentation. - void apply_precoding(re_buffer_writer<>& output, + void apply_precoding(re_buffer_writer& output, const re_buffer_reader<>& input, const precoding_weight_matrix& precoding) const override { diff --git a/lib/phy/upper/channel_coding/ldpc/ldpc_decoder_impl.h b/lib/phy/upper/channel_coding/ldpc/ldpc_decoder_impl.h index b15db41c27..519114cf37 100644 --- a/lib/phy/upper/channel_coding/ldpc/ldpc_decoder_impl.h +++ b/lib/phy/upper/channel_coding/ldpc/ldpc_decoder_impl.h @@ -190,9 +190,9 @@ class ldpc_decoder_impl : public ldpc_decoder private: /// Soft bits clamp lower bound. - static constexpr log_likelihood_ratio soft_bits_clamp_low = -32; + static constexpr log_likelihood_ratio soft_bits_clamp_low = -64; /// Soft bits clamp higher bound. - static constexpr log_likelihood_ratio soft_bits_clamp_high = 32; + static constexpr log_likelihood_ratio soft_bits_clamp_high = 64; /// Pointer to the Tanner graph (~ parity check matrix) used by the encoding algorithm. const ldpc_graph_impl* current_graph = nullptr; /// Total number of base graph variable nodes in the current graph. diff --git a/lib/phy/upper/channel_processors/pucch_detector_format0.h b/lib/phy/upper/channel_processors/pucch_detector_format0.h index 5b2af59793..0646c713e6 100644 --- a/lib/phy/upper/channel_processors/pucch_detector_format0.h +++ b/lib/phy/upper/channel_processors/pucch_detector_format0.h @@ -22,8 +22,8 @@ #pragma once -#include "../signal_processors/pucch/pucch_helper.h" #include "srsran/phy/upper/channel_processors/pucch_detector.h" +#include "srsran/phy/upper/pucch_helper.h" #include "srsran/phy/upper/sequence_generators/low_papr_sequence_collection.h" #include "srsran/ran/pucch/pucch_constants.h" diff --git a/lib/phy/upper/channel_processors/pucch_detector_impl.h b/lib/phy/upper/channel_processors/pucch_detector_impl.h index 15739a7596..a0f6f897bf 100644 --- a/lib/phy/upper/channel_processors/pucch_detector_impl.h +++ b/lib/phy/upper/channel_processors/pucch_detector_impl.h @@ -25,13 +25,13 @@ #pragma once -#include "../signal_processors/pucch/pucch_helper.h" #include "pucch_detector_format0.h" #include "srsran/phy/support/re_buffer.h" #include "srsran/phy/support/resource_grid_reader.h" #include "srsran/phy/upper/channel_processors/pucch_detector.h" #include "srsran/phy/upper/equalization/channel_equalizer.h" #include "srsran/phy/upper/equalization/dynamic_ch_est_list.h" +#include "srsran/phy/upper/pucch_helper.h" #include "srsran/phy/upper/sequence_generators/low_papr_sequence_collection.h" #include "srsran/phy/upper/sequence_generators/pseudo_random_generator.h" diff --git a/lib/phy/upper/signal_processors/dmrs_pbch_processor_impl.cpp b/lib/phy/upper/signal_processors/dmrs_pbch_processor_impl.cpp index 33521bfb8f..40721e0b83 100644 --- a/lib/phy/upper/signal_processors/dmrs_pbch_processor_impl.cpp +++ b/lib/phy/upper/signal_processors/dmrs_pbch_processor_impl.cpp @@ -22,7 +22,7 @@ #include "dmrs_pbch_processor_impl.h" #include "srsran/phy/support/resource_grid_writer.h" -#include "srsran/srsvec/sc_prod.h" +#include "srsran/srsvec/conversion.h" using namespace srsran; @@ -60,8 +60,12 @@ void dmrs_pbch_processor_impl::mapping(const std::array& r, // For each port... for (unsigned port : args.ports) { + // Convert symbols to complex BF16. + std::array symbols_cbf16; + srsvec::convert(symbols_cbf16, r); + // Create view with the symbols. - span symbols = r; + span symbols = symbols_cbf16; // Put sequence in symbol 1 (0 + v , 4 + v , 8 + v ,..., 236 + v). grid.put(port, l0 + 1, k0 + v, stride, symbols.first(nof_dmrs_full_symbol)); diff --git a/lib/phy/upper/signal_processors/pucch/dmrs_pucch_processor_format1_impl.cpp b/lib/phy/upper/signal_processors/pucch/dmrs_pucch_processor_format1_impl.cpp index 8a89185de7..c737efde4d 100644 --- a/lib/phy/upper/signal_processors/pucch/dmrs_pucch_processor_format1_impl.cpp +++ b/lib/phy/upper/signal_processors/pucch/dmrs_pucch_processor_format1_impl.cpp @@ -21,7 +21,7 @@ */ #include "dmrs_pucch_processor_format1_impl.h" -#include "pucch_helper.h" +#include "srsran/phy/upper/pucch_helper.h" #include "srsran/srsvec/add.h" #include "srsran/srsvec/sc_prod.h" diff --git a/lib/phy/upper/signal_processors/pucch/dmrs_pucch_processor_format1_impl.h b/lib/phy/upper/signal_processors/pucch/dmrs_pucch_processor_format1_impl.h index 90a1580266..95ce5796bb 100644 --- a/lib/phy/upper/signal_processors/pucch/dmrs_pucch_processor_format1_impl.h +++ b/lib/phy/upper/signal_processors/pucch/dmrs_pucch_processor_format1_impl.h @@ -22,8 +22,8 @@ #pragma once -#include "pucch_helper.h" #include "pucch_orthogonal_sequence.h" +#include "srsran/phy/upper/pucch_helper.h" #include "srsran/phy/upper/sequence_generators/low_papr_sequence_collection.h" #include "srsran/phy/upper/signal_processors/dmrs_pucch_processor.h" #include "srsran/phy/upper/signal_processors/port_channel_estimator.h" diff --git a/lib/phy/upper/signal_processors/pucch/pucch_orthogonal_sequence.h b/lib/phy/upper/signal_processors/pucch/pucch_orthogonal_sequence.h index 10dea3329e..592e4b46ba 100644 --- a/lib/phy/upper/signal_processors/pucch/pucch_orthogonal_sequence.h +++ b/lib/phy/upper/signal_processors/pucch/pucch_orthogonal_sequence.h @@ -22,7 +22,9 @@ #pragma once +#include "srsran/adt/complex.h" #include "srsran/ran/pucch/pucch_constants.h" +#include "srsran/support/math_utils.h" #include "srsran/support/srsran_assert.h" #include diff --git a/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.cpp b/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.cpp index b1405219f0..fa362e3144 100644 --- a/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.cpp +++ b/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.cpp @@ -31,16 +31,12 @@ using namespace asn1::rrc_nr; rrc_reconfiguration_procedure::rrc_reconfiguration_procedure(rrc_ue_context_t& context_, const rrc_reconfiguration_procedure_request& args_, rrc_ue_reconfiguration_proc_notifier& rrc_ue_notifier_, - rrc_ue_context_update_notifier& cu_cp_notifier_, - rrc_ue_cu_cp_ue_notifier& cu_cp_ue_notifier_, rrc_ue_event_manager& event_mng_, rrc_ue_srb_handler& srb_notifier_, rrc_ue_logger& logger_) : context(context_), args(args_), rrc_ue(rrc_ue_notifier_), - cu_cp_notifier(cu_cp_notifier_), - cu_cp_ue_notifier(cu_cp_ue_notifier_), event_mng(event_mng_), srb_notifier(srb_notifier_), logger(logger_) @@ -78,9 +74,6 @@ void rrc_reconfiguration_procedure::operator()(coro_context>& c procedure_result = true; } else { logger.log_warning("\"{}\" timed out after {}ms", name(), context.cfg.rrc_procedure_timeout_ms.count()); - // Notify NGAP to request UE context release from AMF - cu_cp_ue_notifier.schedule_async_task(cu_cp_notifier.on_ue_release_required( - {context.ue_index, {}, ngap_cause_radio_network_t::release_due_to_ngran_generated_reason})); } CORO_RETURN(procedure_result); diff --git a/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.h b/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.h index 30ea718969..b6bdda080d 100644 --- a/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.h +++ b/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.h @@ -40,8 +40,6 @@ class rrc_reconfiguration_procedure rrc_reconfiguration_procedure(rrc_ue_context_t& context_, const rrc_reconfiguration_procedure_request& args_, rrc_ue_reconfiguration_proc_notifier& rrc_ue_notifier_, - rrc_ue_context_update_notifier& cu_cp_notifier_, - rrc_ue_cu_cp_ue_notifier& cu_cp_ue_notifier_, rrc_ue_event_manager& event_mng_, rrc_ue_srb_handler& srb_notifier_, rrc_ue_logger& logger_); @@ -57,11 +55,9 @@ class rrc_reconfiguration_procedure rrc_ue_context_t& context; const rrc_reconfiguration_procedure_request args; - rrc_ue_reconfiguration_proc_notifier& rrc_ue; // handler to the parent RRC UE object - rrc_ue_context_update_notifier& cu_cp_notifier; // to release the UE if the reconfiguration fails - rrc_ue_cu_cp_ue_notifier& cu_cp_ue_notifier; // to schedule the UE release - rrc_ue_event_manager& event_mng; // event manager for the RRC UE entity - rrc_ue_srb_handler& srb_notifier; // For creating SRBs + rrc_ue_reconfiguration_proc_notifier& rrc_ue; // handler to the parent RRC UE object + rrc_ue_event_manager& event_mng; // event manager for the RRC UE entity + rrc_ue_srb_handler& srb_notifier; // For creating SRBs rrc_ue_logger& logger; rrc_transaction transaction; diff --git a/lib/rrc/ue/procedures/rrc_setup_procedure.cpp b/lib/rrc/ue/procedures/rrc_setup_procedure.cpp index dd72a76bec..e9e0b8730f 100644 --- a/lib/rrc/ue/procedures/rrc_setup_procedure.cpp +++ b/lib/rrc/ue/procedures/rrc_setup_procedure.cpp @@ -23,6 +23,7 @@ #include "rrc_setup_procedure.h" #include "../rrc_asn1_helpers.h" #include "srsran/asn1/rrc_nr/dl_ccch_msg.h" +#include using namespace srsran; using namespace srsran::srs_cu_cp; @@ -104,27 +105,39 @@ void rrc_setup_procedure::send_rrc_setup() rrc_ue.on_new_dl_ccch(dl_ccch_msg); } -void rrc_setup_procedure::send_initial_ue_msg(const asn1::rrc_nr::rrc_setup_complete_s& rrc_setup_complete) +void rrc_setup_procedure::send_initial_ue_msg(const asn1::rrc_nr::rrc_setup_complete_s& rrc_setup_complete_msg) { cu_cp_initial_ue_message init_ue_msg = {}; + const auto& rrc_setup_complete = rrc_setup_complete_msg.crit_exts.rrc_setup_complete(); + init_ue_msg.ue_index = context.ue_index; - init_ue_msg.nas_pdu = rrc_setup_complete.crit_exts.rrc_setup_complete().ded_nas_msg.copy(); + init_ue_msg.nas_pdu = rrc_setup_complete.ded_nas_msg.copy(); init_ue_msg.establishment_cause = static_cast(context.connection_cause.value); init_ue_msg.user_location_info.nr_cgi = context.cell.cgi; init_ue_msg.user_location_info.tai.plmn_id = context.cell.cgi.plmn_id; init_ue_msg.user_location_info.tai.tac = context.cell.tac; - cu_cp_five_g_s_tmsi five_g_s_tmsi; - if (context.five_g_tmsi.has_value()) { - five_g_s_tmsi.five_g_tmsi = context.five_g_tmsi.value(); - // amf_pointer and amf_set_id will be set by NGAP - init_ue_msg.five_g_s_tmsi = five_g_s_tmsi; + if (rrc_setup_complete.ng_5_g_s_tmsi_value_present) { + if (rrc_setup_complete.ng_5_g_s_tmsi_value.type() == + asn1::rrc_nr::rrc_setup_complete_ies_s::ng_5_g_s_tmsi_value_c_::types_opts::options::ng_5_g_s_tmsi) { + context.five_g_s_tmsi = asn1_to_five_g_s_tmsi(rrc_setup_complete.ng_5_g_s_tmsi_value.ng_5_g_s_tmsi()); + } else { + if (!std::holds_alternative>(context.setup_ue_id)) { + logger.log_warning("5G-S-TMSI part 1 is missing"); + } else { + context.five_g_s_tmsi = asn1_to_five_g_s_tmsi(std::get>(context.setup_ue_id), + rrc_setup_complete.ng_5_g_s_tmsi_value.ng_5_g_s_tmsi_part2()); + } + } + } + + if (context.five_g_s_tmsi.has_value()) { + init_ue_msg.five_g_s_tmsi = context.five_g_s_tmsi.value(); } - if (rrc_setup_complete.crit_exts.rrc_setup_complete().registered_amf_present) { - cu_cp_amf_identifier_t amf_id = - asn1_to_amf_identifier(rrc_setup_complete.crit_exts.rrc_setup_complete().registered_amf.amf_id); + if (rrc_setup_complete.registered_amf_present) { + cu_cp_amf_identifier_t amf_id = asn1_to_amf_identifier(rrc_setup_complete.registered_amf.amf_id); init_ue_msg.amf_set_id = amf_id.amf_set_id; // TODO: Handle PLMN ID diff --git a/lib/rrc/ue/rrc_asn1_converters.h b/lib/rrc/ue/rrc_asn1_converters.h index f24ad59b91..58efb4b4d9 100644 --- a/lib/rrc/ue/rrc_asn1_converters.h +++ b/lib/rrc/ue/rrc_asn1_converters.h @@ -22,6 +22,7 @@ #pragma once +#include "srsran/adt/bounded_bitset.h" #include "srsran/asn1/asn1_utils.h" #include "srsran/asn1/rrc_nr/ul_dcch_msg_ies.h" #include "srsran/cu_cp/cu_cp_types.h" @@ -248,6 +249,23 @@ integrity_prot_algorithm_to_rrc_asn1(const security::integrity_algorithm& integr return asn1_integrity_prot_algo; } +inline cu_cp_five_g_s_tmsi asn1_to_five_g_s_tmsi(const asn1::fixed_bitstring<48>& asn1_five_g_s_tmsi) +{ + bounded_bitset<48> five_g_s_tmsi(48); + five_g_s_tmsi.from_uint64(asn1_five_g_s_tmsi.to_number()); + + return cu_cp_five_g_s_tmsi{five_g_s_tmsi}; +} + +inline cu_cp_five_g_s_tmsi asn1_to_five_g_s_tmsi(const asn1::fixed_bitstring<39>& asn1_five_g_s_tmsi_part1, + const asn1::fixed_bitstring<9>& asn1_five_g_s_tmsi_part2) +{ + bounded_bitset<48> five_g_s_tmsi(48); + five_g_s_tmsi.from_uint64((asn1_five_g_s_tmsi_part2.to_number() << 39) + asn1_five_g_s_tmsi_part1.to_number()); + + return cu_cp_five_g_s_tmsi{five_g_s_tmsi}; +} + inline cu_cp_amf_identifier_t asn1_to_amf_identifier(const asn1::fixed_bitstring<24>& asn1_amf_id) { cu_cp_amf_identifier_t amf_id; diff --git a/lib/rrc/ue/rrc_ue_context.h b/lib/rrc/ue/rrc_ue_context.h index 7390dc9b58..4b43cda38a 100644 --- a/lib/rrc/ue/rrc_ue_context.h +++ b/lib/rrc/ue/rrc_ue_context.h @@ -45,17 +45,18 @@ class rrc_ue_context_t const rrc_ue_cfg_t& cfg_, std::optional rrc_context_); - const ue_index_t ue_index; // UE index assigned by the DU processor - const rnti_t c_rnti; // current C-RNTI - const rrc_cell_context cell; // current cell - const rrc_ue_cfg_t cfg; - rrc_state state = rrc_state::idle; - std::optional meas_cfg; - std::optional five_g_tmsi; - uint64_t setup_ue_id; - asn1::rrc_nr::establishment_cause_opts connection_cause; - std::map srbs; - std::optional capabilities; + const ue_index_t ue_index; // UE index assigned by the DU processor + const rnti_t c_rnti; // current C-RNTI + const rrc_cell_context cell; // current cell + const rrc_ue_cfg_t cfg; + rrc_state state = rrc_state::idle; + std::optional meas_cfg; + std::optional five_g_s_tmsi; + std::variant> + setup_ue_id; ///< this is either a random value or the 5G-S-TMSI-PART1 + asn1::rrc_nr::establishment_cause_opts connection_cause; + std::map srbs; + std::optional capabilities; std::optional capabilities_list; std::optional transfer_context; // Context of old UE when created through mobility. bool reestablishment_ongoing = false; diff --git a/lib/rrc/ue/rrc_ue_message_handlers.cpp b/lib/rrc/ue/rrc_ue_message_handlers.cpp index 939a958446..8f7aaa31d1 100644 --- a/lib/rrc/ue/rrc_ue_message_handlers.cpp +++ b/lib/rrc/ue/rrc_ue_message_handlers.cpp @@ -88,13 +88,7 @@ void rrc_ue_impl::handle_rrc_setup_request(const asn1::rrc_nr::rrc_setup_request const rrc_setup_request_ies_s& request_ies = request_msg.rrc_setup_request; switch (request_ies.ue_id.type().value) { case init_ue_id_c::types_opts::ng_5_g_s_tmsi_part1: { - context.setup_ue_id = request_ies.ue_id.ng_5_g_s_tmsi_part1().to_number(); - - // As per TS 23.003 section 2.10.1 the last 32Bits of the 5G-S-TMSI are the 5G-TMSI - unsigned shift_bits = - request_ies.ue_id.ng_5_g_s_tmsi_part1().length() - 32; // calculate the number of bits to shift - context.five_g_tmsi = ((request_ies.ue_id.ng_5_g_s_tmsi_part1().to_number() << shift_bits) >> shift_bits); - + context.setup_ue_id = request_ies.ue_id.ng_5_g_s_tmsi_part1(); break; } case asn1::rrc_nr::init_ue_id_c::types_opts::random_value: @@ -388,8 +382,7 @@ byte_buffer rrc_ue_impl::get_packed_ue_radio_access_cap_info() const async_task rrc_ue_impl::handle_rrc_reconfiguration_request(const rrc_reconfiguration_procedure_request& msg) { - return launch_async( - context, msg, *this, cu_cp_notifier, cu_cp_ue_notifier, *event_mng, get_rrc_ue_srb_handler(), logger); + return launch_async(context, msg, *this, *event_mng, get_rrc_ue_srb_handler(), logger); } rrc_ue_handover_reconfiguration_context diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index 1650c1a9cc..f24ed8f5e8 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -704,7 +704,7 @@ alloc_result ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gra if (not is_retx) { // [Implementation-defined] Check whether max. UL grants per slot is reached if PUSCH for current UE succeeds. If // so, allocate remaining RBs to the current UE only if it's a new Tx. - if (pusch_pdu_rem_space == 1) { + if (pusch_pdu_rem_space == 1 and not u.has_pending_sr()) { mcs_prbs.n_prbs = rb_helper::find_empty_interval_of_length(used_crbs, used_crbs.size(), 0).length(); } // Due to the pre-allocated UCI bits, MCS 0 and PRB 1 would not leave any space for the payload on the TBS, as diff --git a/lib/support/network/sctp_socket.cpp b/lib/support/network/sctp_socket.cpp index 6ddfa3d4ca..ac5ee90ba5 100644 --- a/lib/support/network/sctp_socket.cpp +++ b/lib/support/network/sctp_socket.cpp @@ -21,10 +21,11 @@ */ #include "srsran/support/io/sctp_socket.h" +#include "srsran/adt/optional.h" #include "srsran/srslog/srslog.h" #include "srsran/support/error_handling.h" -#include "srsran/support/format_utils.h" #include "srsran/support/io/sockets.h" +#include "srsran/support/srsran_assert.h" #include #include #include @@ -43,10 +44,7 @@ bool sctp_subscribe_to_events(const unique_fd& fd) events.sctp_shutdown_event = 1; events.sctp_association_event = 1; - if (::setsockopt(fd.value(), IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events)) != 0) { - return false; - } - return true; + return ::setsockopt(fd.value(), IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events)) == 0; } /// \brief Modify SCTP default parameters for quicker detection of broken links. @@ -155,10 +153,7 @@ bool sctp_set_nodelay(const unique_fd& fd, std::optional nodelay) } int optval = nodelay.value() ? 1 : 0; - if (::setsockopt(fd.value(), IPPROTO_SCTP, SCTP_NODELAY, &optval, sizeof(optval)) != 0) { - return false; - } - return true; + return ::setsockopt(fd.value(), IPPROTO_SCTP, SCTP_NODELAY, &optval, sizeof(optval)) == 0; } } // namespace @@ -311,6 +306,7 @@ bool sctp_socket::set_non_blocking() bool sctp_socket::set_sockopts(const sctp_socket_params& params) { + logger.debug("Setting socket options. params=[{}]", params); if (not sctp_subscribe_to_events(sock_fd)) { logger.error( "{}: SCTP failed to be created. Cause: Subscribing to SCTP events failed: {}", if_name, strerror(errno)); diff --git a/tests/benchmarks/phy/upper/precoding/channel_precoder_benchmark.cpp b/tests/benchmarks/phy/upper/precoding/channel_precoder_benchmark.cpp index 89ebcd0617..e79c483723 100644 --- a/tests/benchmarks/phy/upper/precoding/channel_precoder_benchmark.cpp +++ b/tests/benchmarks/phy/upper/precoding/channel_precoder_benchmark.cpp @@ -108,8 +108,7 @@ int main(int argc, char** argv) // Create input and output RE buffers. dynamic_re_buffer input_re(nof_layers, nof_re); - dynamic_re_buffer precoded_re(nof_ports, nof_re); - dynamic_re_buffer precoded_re_cbf16(nof_ports, nof_re); + dynamic_re_buffer precoded_re(nof_ports, nof_re); std::vector input_symbols(nof_layers * nof_re); precoding_weight_matrix weights(nof_layers, nof_ports); @@ -141,8 +140,8 @@ int main(int argc, char** argv) precoder->apply_precoding(precoded_re, input_re, weights); }); - perf_meas_ci8.new_measure(meas_descr, nof_re, [&precoded_re_cbf16, &input_symbols, &weights, &precoder]() { - precoder->apply_layer_map_and_precoding(precoded_re_cbf16, input_symbols, weights); + perf_meas_ci8.new_measure(meas_descr, nof_re, [&precoded_re, &input_symbols, &weights, &precoder]() { + precoder->apply_layer_map_and_precoding(precoded_re, input_symbols, weights); }); } diff --git a/tests/e2e/tests/viavi/test_declaration.yml b/tests/e2e/tests/viavi/test_declaration.yml index e782850be1..b3e057bd62 100644 --- a/tests/e2e/tests/viavi/test_declaration.yml +++ b/tests/e2e/tests/viavi/test_declaration.yml @@ -33,8 +33,8 @@ campaign_filename: &campaign_filename "C:\\ci\\CI 4x4 ORAN-FH-complete.xml" gnb_extra_commands: &gnb_extra_commands "" -expected_dl_bitrate_high: &expected_dl_bitrate_high 1400000000 -expected_ul_bitrate_high: &expected_ul_bitrate_high 100000000 +expected_dl_bitrate_high: &expected_dl_bitrate_high 1200000000 +expected_ul_bitrate_high: &expected_ul_bitrate_high 80000000 expected_dl_bitrate_low: &expected_dl_bitrate_low 14000 expected_ul_bitrate_low: &expected_ul_bitrate_low 1000 test_timeout: &test_timeout 2700 # 45 * 60 diff --git a/tests/integrationtests/du_high/du_high_test.cpp b/tests/integrationtests/du_high/du_high_test.cpp index f6c50e51d1..917359e77b 100644 --- a/tests/integrationtests/du_high/du_high_test.cpp +++ b/tests/integrationtests/du_high/du_high_test.cpp @@ -170,12 +170,19 @@ TEST_F(du_high_tester, when_ue_context_setup_release_starts_then_drb_activity_st this->test_logger.info("STATUS: RRC Release started being scheduled..."); // Ensure that DRBs stop being scheduled at this point, even if it takes a while for the UE release to complete. + unsigned drb_data_count = 0; while (cu_notifier.last_f1ap_msgs.empty()) { run_slot(); const dl_msg_alloc* pdsch = find_ue_pdsch(rnti, phy.cells[0].last_dl_res.value().dl_res->ue_grants); if (pdsch != nullptr) { - // PDSCH scheduled. Ensure it was for SRB1 (DRB1 might fill the rest of the TB though). - ASSERT_NE(find_ue_pdsch_with_lcid(rnti, LCID_SRB1, phy.cells[0].last_dl_res.value().dl_res->ue_grants), nullptr); + // PDSCH scheduled. Ensure it was for SRB1. + // Note: There might be at most one single DRB1 PDSCH that smuggles in after the RRC Release due to race + // conditions. + auto* drb_pdsch = find_ue_pdsch_with_lcid(rnti, LCID_MIN_DRB, phy.cells[0].last_dl_res.value().dl_res->ue_grants); + if (drb_pdsch != nullptr) { + drb_data_count++; + ASSERT_LT(drb_data_count, 2) << "More than 1 PDSCH grant for DRB data was scheduled after RRC Release"; + } } } } diff --git a/tests/unittests/cu_cp/cu_cp_test.cpp b/tests/unittests/cu_cp/cu_cp_test.cpp index 207af159f6..aab96ef03a 100644 --- a/tests/unittests/cu_cp/cu_cp_test.cpp +++ b/tests/unittests/cu_cp/cu_cp_test.cpp @@ -519,10 +519,7 @@ TEST_F(cu_cp_test, when_handover_request_received_then_handover_notify_is_sent) // Inject E1AP Bearer Context Setup Response e1ap_message bearer_ctxt_setup_resp = generate_bearer_context_setup_response(int_to_gnb_cu_cp_ue_e1ap_id(0), int_to_gnb_cu_up_ue_e1ap_id(0)); - cu_cp_obj->get_e1_handler() - .get_cu_up(uint_to_cu_up_index(0)) - .get_message_handler() - .handle_message(bearer_ctxt_setup_resp); + e1ap_gw.get_cu_up(0).on_new_message(bearer_ctxt_setup_resp); // Check that the UE Context Setup Request Message was sent to the DU ASSERT_EQ(f1c_gw.last_tx_pdus(0).back().pdu.type(), asn1::f1ap::f1ap_pdu_c::types_opts::options::init_msg); @@ -544,10 +541,7 @@ TEST_F(cu_cp_test, when_handover_request_received_then_handover_notify_is_sent) // Inject E1AP Bearer Context Modification Response e1ap_message bearer_ctxt_mod_resp = generate_bearer_context_modification_response(int_to_gnb_cu_cp_ue_e1ap_id(0), int_to_gnb_cu_up_ue_e1ap_id(0)); - cu_cp_obj->get_e1_handler() - .get_cu_up(uint_to_cu_up_index(0)) - .get_message_handler() - .handle_message(bearer_ctxt_mod_resp); + e1ap_gw.get_cu_up(0).on_new_message(bearer_ctxt_mod_resp); // Check that the Handover Request Ack was sent to the AMF ASSERT_EQ(n2_gw.last_ngap_msgs.back().pdu.type(), asn1::ngap::ngap_pdu_c::types_opts::options::successful_outcome); diff --git a/tests/unittests/cu_cp/cu_cp_test_helpers.cpp b/tests/unittests/cu_cp/cu_cp_test_helpers.cpp index 6ced4654a5..1b6530f595 100644 --- a/tests/unittests/cu_cp/cu_cp_test_helpers.cpp +++ b/tests/unittests/cu_cp/cu_cp_test_helpers.cpp @@ -279,7 +279,7 @@ void cu_cp_test::test_e1ap_attach() // Pass E1SetupRequest to the CU-CP e1ap_message e1setup_msg = generate_valid_cu_up_e1_setup_request(); - cu_cp_obj->get_e1_handler().get_cu_up(uint_to_cu_up_index(0)).get_message_handler().handle_message(e1setup_msg); + e1ap_gw.get_cu_up(0).on_new_message(e1setup_msg); } void cu_cp_test::test_du_attach(du_index_t du_index, gnb_du_id_t gnb_du_id, nr_cell_identity nrcell_id, pci_t pci) @@ -326,10 +326,7 @@ void cu_cp_test::add_pdu_sessions(std::vector psis, // Inject Bearer Context Setup Response e1ap_message bearer_context_setup_resp = generate_bearer_context_setup_response(cu_cp_ue_e1ap_id, cu_up_ue_e1ap_id); - cu_cp_obj->get_e1_handler() - .get_cu_up(uint_to_cu_up_index(0)) - .get_message_handler() - .handle_message(bearer_context_setup_resp); + e1ap_gw.get_cu_up(0).on_new_message(bearer_context_setup_resp); } else { // check that the Bearer Context Modification was sent to the CU-UP ASSERT_EQ(e1ap_gw.last_tx_pdus(0).back().pdu.type(), asn1::e1ap::e1ap_pdu_c::types_opts::options::init_msg); @@ -338,10 +335,7 @@ void cu_cp_test::add_pdu_sessions(std::vector psis, // Inject Bearer Context Modification Response e1ap_message bearer_context_mod_resp = generate_bearer_context_modification_response(cu_cp_ue_e1ap_id, cu_up_ue_e1ap_id); - cu_cp_obj->get_e1_handler() - .get_cu_up(uint_to_cu_up_index(0)) - .get_message_handler() - .handle_message(bearer_context_mod_resp); + e1ap_gw.get_cu_up(0).on_new_message(bearer_context_mod_resp); } // check that the UE Context Modification Request was sent to the DU @@ -370,10 +364,7 @@ void cu_cp_test::add_pdu_sessions(std::vector psis, // Inject Bearer Context Modification Response e1ap_message bearer_context_mod_resp = generate_bearer_context_modification_response(cu_cp_ue_e1ap_id, cu_up_ue_e1ap_id); - cu_cp_obj->get_e1_handler() - .get_cu_up(uint_to_cu_up_index(0)) - .get_message_handler() - .handle_message(bearer_context_mod_resp); + e1ap_gw.get_cu_up(0).on_new_message(bearer_context_mod_resp); // check that the RRC Reconfiguration was sent to the DU ASSERT_EQ(f1c_gw.last_tx_pdus(0).back().pdu.type(), asn1::f1ap::f1ap_pdu_c::types_opts::options::init_msg); diff --git a/tests/unittests/cu_cp/mobility/inter_du_handover_routine_test.cpp b/tests/unittests/cu_cp/mobility/inter_du_handover_routine_test.cpp index f0c36b0249..88045ce2a8 100644 --- a/tests/unittests/cu_cp/mobility/inter_du_handover_routine_test.cpp +++ b/tests/unittests/cu_cp/mobility/inter_du_handover_routine_test.cpp @@ -116,10 +116,7 @@ class inter_du_handover_routine_test : public mobility_test { e1ap_message bearer_context_modification_fail = generate_bearer_context_modification_failure(int_to_gnb_cu_cp_ue_e1ap_id(0), int_to_gnb_cu_up_ue_e1ap_id(0)); - cu_cp_obj->get_e1_handler() - .get_cu_up(uint_to_cu_up_index(0)) - .get_message_handler() - .handle_message(bearer_context_modification_fail); + e1ap_gw.get_cu_up(0).on_new_message(bearer_context_modification_fail); } /// \brief Inject Bearer Context Modification Response. @@ -127,10 +124,7 @@ class inter_du_handover_routine_test : public mobility_test { e1ap_message bearer_context_modification_resp = generate_bearer_context_modification_response(int_to_gnb_cu_cp_ue_e1ap_id(0), int_to_gnb_cu_up_ue_e1ap_id(0)); - cu_cp_obj->get_e1_handler() - .get_cu_up(uint_to_cu_up_index(0)) - .get_message_handler() - .handle_message(bearer_context_modification_resp); + e1ap_gw.get_cu_up(0).on_new_message(bearer_context_modification_resp); } /// \brief Inject Bearer Context Release Complete. @@ -138,10 +132,7 @@ class inter_du_handover_routine_test : public mobility_test { e1ap_message bearer_context_release_complete = generate_bearer_context_release_complete(int_to_gnb_cu_cp_ue_e1ap_id(0), int_to_gnb_cu_up_ue_e1ap_id(0)); - cu_cp_obj->get_e1_handler() - .get_cu_up(uint_to_cu_up_index(0)) - .get_message_handler() - .handle_message(bearer_context_release_complete); + e1ap_gw.get_cu_up(0).on_new_message(bearer_context_release_complete); } /// \brief Inject UE Context Modification Response. diff --git a/tests/unittests/cu_cp/routines/cu_cp_routine_manager_test_helpers.h b/tests/unittests/cu_cp/routines/cu_cp_routine_manager_test_helpers.h index 227ade642b..5d1e2b2839 100644 --- a/tests/unittests/cu_cp/routines/cu_cp_routine_manager_test_helpers.h +++ b/tests/unittests/cu_cp/routines/cu_cp_routine_manager_test_helpers.h @@ -56,7 +56,7 @@ class cu_cp_routine_manager_test : public ::testing::Test ue_manager ue_mng{cu_cp_cfg}; dummy_du_processor_rrc_ue_control_message_notifier rrc_ue_ctrl_notifier; dummy_du_processor_rrc_ue_srb_control_notifier rrc_ue_srb_ctrl_notifier; - dummy_ngap_ue_context_removal_handler ngap_ue_removal_handler; + dummy_cu_cp_rrc_ue_interface cu_cp_notifier; dummy_cu_cp_ue_removal_handler ue_removal_handler{&ue_mng}; std::unique_ptr routine_mng; }; diff --git a/tests/unittests/cu_cp/routines/pdu_session_resource_modification_routine_test.cpp b/tests/unittests/cu_cp/routines/pdu_session_resource_modification_routine_test.cpp index 8b25d64ef9..37cd274d7b 100644 --- a/tests/unittests/cu_cp/routines/pdu_session_resource_modification_routine_test.cpp +++ b/tests/unittests/cu_cp/routines/pdu_session_resource_modification_routine_test.cpp @@ -52,6 +52,8 @@ class pdu_session_resource_modification_test : public pdu_session_resource_routi e1ap_bearer_ctxt_mng, f1ap_ue_ctxt_mng, rrc_ue_ctrl_notifier, + cu_cp_notifier, + ue_mng.find_ue(msg.ue_index)->get_task_sched(), ue_mng.find_ue(msg.ue_index)->get_up_resource_manager()); t_launcher.emplace(t); } @@ -82,6 +84,8 @@ class pdu_session_resource_modification_test : public pdu_session_resource_routi e1ap_bearer_ctxt_mng, f1ap_ue_ctxt_mng, rrc_ue_ctrl_notifier, + cu_cp_notifier, + ue_mng.find_ue(request.ue_index)->get_task_sched(), ue_mng.find_ue(request.ue_index)->get_up_resource_manager()); lazy_task_launcher setup_launcher(setup_task); @@ -132,6 +136,8 @@ class pdu_session_resource_modification_test : public pdu_session_resource_routi e1ap_bearer_ctxt_mng, f1ap_ue_ctxt_mng, rrc_ue_ctrl_notifier, + cu_cp_notifier, + ue_mng.find_ue(request.ue_index)->get_task_sched(), ue_mng.find_ue(request.ue_index)->get_up_resource_manager()); lazy_task_launcher modify_launcher(modify_task); diff --git a/tests/unittests/cu_cp/routines/pdu_session_resource_release_routine_test.cpp b/tests/unittests/cu_cp/routines/pdu_session_resource_release_routine_test.cpp index 9e12aca9e9..1cd6206d85 100644 --- a/tests/unittests/cu_cp/routines/pdu_session_resource_release_routine_test.cpp +++ b/tests/unittests/cu_cp/routines/pdu_session_resource_release_routine_test.cpp @@ -46,8 +46,8 @@ class pdu_session_resource_release_test : public pdu_session_resource_routine_te msg, e1ap_bearer_ctxt_mng, f1ap_ue_ctxt_mng, - ngap_control_handler, rrc_ue_ctrl_notifier, + cu_cp_notifier, ue_task_sched, ue_mng.find_ue(msg.ue_index)->get_up_resource_manager()); t_launcher.emplace(t); @@ -94,6 +94,8 @@ class pdu_session_resource_release_test : public pdu_session_resource_routine_te e1ap_bearer_ctxt_mng, f1ap_ue_ctxt_mng, rrc_ue_ctrl_notifier, + cu_cp_notifier, + ue_mng.find_ue(request.ue_index)->get_task_sched(), ue_mng.find_ue(request.ue_index)->get_up_resource_manager()); setup_launcher.emplace(setup_task); } diff --git a/tests/unittests/cu_cp/routines/pdu_session_resource_setup_routine_test.cpp b/tests/unittests/cu_cp/routines/pdu_session_resource_setup_routine_test.cpp index d37d16a2c2..53977dd422 100644 --- a/tests/unittests/cu_cp/routines/pdu_session_resource_setup_routine_test.cpp +++ b/tests/unittests/cu_cp/routines/pdu_session_resource_setup_routine_test.cpp @@ -52,6 +52,8 @@ class pdu_session_resource_setup_test : public pdu_session_resource_routine_test e1ap_bearer_ctxt_mng, f1ap_ue_ctxt_mng, rrc_ue_ctrl_notifier, + cu_cp_notifier, + ue_mng.find_ue(msg.ue_index)->get_task_sched(), ue_mng.find_ue(msg.ue_index)->get_up_resource_manager()); t_launcher.emplace(t); } diff --git a/tests/unittests/cu_cp/test_helpers.h b/tests/unittests/cu_cp/test_helpers.h index f5b62e14ff..3c88ca0b3d 100644 --- a/tests/unittests/cu_cp/test_helpers.h +++ b/tests/unittests/cu_cp/test_helpers.h @@ -707,5 +707,68 @@ struct dummy_ue_task_scheduler : public ue_task_scheduler { timer_manager& timer_db; task_executor& exec; }; + +class dummy_cu_cp_rrc_ue_interface : public cu_cp_rrc_ue_interface +{ +public: + void add_ue_context(rrc_ue_reestablishment_context_response context) { reest_context = context; } + + bool next_ue_setup_response = true; + + rrc_ue_reestablishment_context_response + handle_rrc_reestablishment_request(pci_t old_pci, rnti_t old_c_rnti, ue_index_t ue_index) override + { + logger.info("ue={} old_pci={} old_c-rnti={}: Received RRC Reestablishment Request", ue_index, old_pci, old_c_rnti); + + return reest_context; + } + + async_task handle_rrc_reestablishment_context_modification_required(ue_index_t ue_index) override + { + logger.info("ue={}: Received Reestablishment Context Modification Required"); + + return launch_async([](coro_context>& ctx) mutable { + CORO_BEGIN(ctx); + CORO_RETURN(true); + }); + } + + void handle_rrc_reestablishment_failure(const cu_cp_ue_context_release_request& request) override + { + logger.info("ue={}: Received RRC Reestablishment failure notification", request.ue_index); + } + + void handle_rrc_reestablishment_complete(ue_index_t old_ue_index) override + { + logger.info("ue={}: Received RRC Reestablishment complete notification", old_ue_index); + } + + async_task handle_ue_context_transfer(ue_index_t ue_index, ue_index_t old_ue_index) override + { + logger.info("ue={}: Requested a UE context transfer from old_ue={}", ue_index, old_ue_index); + return launch_async([](coro_context>& ctx) mutable { + CORO_BEGIN(ctx); + CORO_RETURN(true); + }); + } + + async_task handle_ue_context_release(const cu_cp_ue_context_release_request& request) override + { + logger.info("ue={}: Requested a UE release", request.ue_index); + last_cu_cp_ue_context_release_request = request; + + return launch_async([](coro_context>& ctx) mutable { + CORO_BEGIN(ctx); + CORO_RETURN(); + }); + } + + cu_cp_ue_context_release_request last_cu_cp_ue_context_release_request; + +private: + rrc_ue_reestablishment_context_response reest_context = {}; + srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST"); +}; + } // namespace srs_cu_cp } // namespace srsran diff --git a/tests/unittests/du_manager/du_ue/ue_manager_test.cpp b/tests/unittests/du_manager/du_ue/ue_manager_test.cpp index dcb7d75a20..ba30e59c63 100644 --- a/tests/unittests/du_manager/du_ue/ue_manager_test.cpp +++ b/tests/unittests/du_manager/du_ue/ue_manager_test.cpp @@ -289,7 +289,7 @@ TEST_F(du_ue_manager_tester, when_ue_is_being_removed_then_ue_notifiers_get_disc // TEST: UE notifiers are disconnected. mac_dummy.last_dl_bs.reset(); srb1.on_buffer_state_update(10); - ASSERT_FALSE(mac_dummy.last_dl_bs.has_value()); + ASSERT_TRUE(not mac_dummy.last_dl_bs.has_value() or mac_dummy.last_dl_bs.value().bs == 0); } class du_ue_manager_rlf_tester : public du_ue_manager_tester diff --git a/tests/unittests/e1ap/CMakeLists.txt b/tests/unittests/e1ap/CMakeLists.txt index 5d0e59593d..72bb086d60 100644 --- a/tests/unittests/e1ap/CMakeLists.txt +++ b/tests/unittests/e1ap/CMakeLists.txt @@ -25,3 +25,4 @@ include_directories(../../..) add_subdirectory(common) add_subdirectory(cu_cp) add_subdirectory(cu_up) +add_subdirectory(gateways) diff --git a/tests/unittests/e1ap/cu_cp/e1ap_cu_cp_test_helpers.h b/tests/unittests/e1ap/cu_cp/e1ap_cu_cp_test_helpers.h index 0870f2a793..366b8eea64 100644 --- a/tests/unittests/e1ap/cu_cp/e1ap_cu_cp_test_helpers.h +++ b/tests/unittests/e1ap/cu_cp/e1ap_cu_cp_test_helpers.h @@ -95,6 +95,8 @@ class dummy_cu_cp_e1ap_gateway cu_up_tx_notifiers.erase(cu_up_tx_notifiers.begin() + connection_idx); } + e1ap_message_notifier& get_cu_up(size_t connection_idx) { return *cu_up_tx_notifiers.at(connection_idx); } + span last_rx_pdus(size_t connection_idx) const { return local_e1ap_gw.get_last_cu_cp_rx_pdus(connection_idx); diff --git a/tests/unittests/e1ap/gateways/CMakeLists.txt b/tests/unittests/e1ap/gateways/CMakeLists.txt new file mode 100644 index 0000000000..2363a2d051 --- /dev/null +++ b/tests/unittests/e1ap/gateways/CMakeLists.txt @@ -0,0 +1,33 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN 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. +# +# srsRAN 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/. +# + +add_executable(e1_gateway_test e1_gateway_test.cpp) +target_link_libraries(e1_gateway_test + srsran_e1_gateway + srsran_gateway + e1ap_test_helpers + srsran_e1ap_common + srsran_support + srsran_network + srslog + e1ap_asn1 + gtest + gtest_main) +add_test(e1_gateway_test e1_gateway_test) diff --git a/tests/unittests/e1ap/gateways/e1_gateway_test.cpp b/tests/unittests/e1ap/gateways/e1_gateway_test.cpp new file mode 100644 index 0000000000..7ca8ff0bdb --- /dev/null +++ b/tests/unittests/e1ap/gateways/e1_gateway_test.cpp @@ -0,0 +1,291 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN 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. + * + * srsRAN 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/. + * + */ + +#include "srsran/adt/blocking_queue.h" +#include "srsran/asn1/e1ap/common.h" +#include "srsran/asn1/e1ap/e1ap_pdu_contents.h" +#include "srsran/cu_cp/cu_cp_e1_handler.h" +#include "srsran/e1ap/common/e1ap_message.h" +#include "srsran/e1ap/gateways/e1_local_connector_factory.h" +#include "srsran/pcap/dlt_pcap.h" +#include "srsran/support/io/io_broker_factory.h" +#include +#include + +using namespace srsran; + +class dummy_dlt_pcap final : public dlt_pcap +{ +public: + bool enabled = false; + bool closed = false; + blocking_queue last_sdus{16}; + + void close() override { closed = true; } + bool is_write_enabled() const override { return enabled; } + void push_pdu(const_span pdu) override { last_sdus.push_blocking(byte_buffer::create(pdu).value()); } + virtual void push_pdu(byte_buffer pdu) override { last_sdus.push_blocking(std::move(pdu)); } +}; + +class e1_link : public srs_cu_cp::cu_cp_e1_handler +{ +public: + class rx_pdu_notifier : public e1ap_message_notifier + { + public: + rx_pdu_notifier(const std::string& name_, + blocking_queue& rx_pdus_, + std::promise eof_received_) : + name(name_), rx_pdus(rx_pdus_), eof_received(std::move(eof_received_)) + { + } + ~rx_pdu_notifier() override + { + eof_received.set_value(); + logger.info("{}: RX PDU notifier destroyed", name); + } + + void on_new_message(const e1ap_message& msg) override { rx_pdus.push_blocking(msg); } + + const std::string name; + blocking_queue& rx_pdus; + std::promise eof_received; + srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST"); + }; + + e1_link(bool use_sctp, bool pcap_enabled) + { + pcap.enabled = pcap_enabled; + + if (use_sctp) { + broker = create_io_broker(io_broker_type::epoll); + connector = create_e1_local_connector(e1_local_sctp_connector_config{pcap, *broker}); + } else { + connector = create_e1_local_connector(e1_local_connector_config{pcap}); + } + + connector->attach_cu_cp(*this); + + // Connect client to server. + connect_client(); + } + + std::unique_ptr + handle_new_cu_up_connection(std::unique_ptr e1ap_tx_pdu_notifier) override + { + // Note: May be called from io broker thread. + cu_cp_tx_pdu_notifier = std::move(e1ap_tx_pdu_notifier); + std::promise eof_signal; + cu_cp_gw_assoc_close_signaled = eof_signal.get_future(); + + logger.info("CU-CP handled new DU connection"); + connection_complete_signal.set_value(); + + return std::make_unique("CU-CP", cu_rx_pdus, std::move(eof_signal)); + } + + std::unique_ptr broker; + dummy_dlt_pcap pcap; + std::unique_ptr connector; + srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST"); + + blocking_queue cu_rx_pdus{128}; + blocking_queue cu_up_rx_pdus{128}; + + std::future cu_cp_gw_assoc_close_signaled; + std::future cu_up_gw_assoc_close_signaled; + std::unique_ptr cu_cp_tx_pdu_notifier; + std::unique_ptr cu_up_tx_pdu_notifier; + +protected: + void connect_client() + { + // Connect client to server. + std::promise eof_signal; + cu_up_gw_assoc_close_signaled = eof_signal.get_future(); + cu_up_tx_pdu_notifier = connector->handle_cu_up_connection_request( + std::make_unique("CU-UP", cu_up_rx_pdus, std::move(eof_signal))); + + // Wait for server to receive connection. + std::future connection_completed = connection_complete_signal.get_future(); + connection_completed.wait(); + logger.info("CU-UP connection to CU-CP is complete"); + } + + std::promise connection_complete_signal; +}; + +class e1_gateway_link_test : public ::testing::TestWithParam +{ +protected: + e1_gateway_link_test() + { + srslog::init(); + logger.set_level(srslog::basic_levels::debug); + srslog::fetch_basic_logger("SCTP-GW").set_level(srslog::basic_levels::debug); + srslog::fetch_basic_logger("CU-CP-E1").set_level(srslog::basic_levels::debug); + srslog::fetch_basic_logger("CU-UP-E1").set_level(srslog::basic_levels::debug); + } + ~e1_gateway_link_test() override { srslog::flush(); } + + void create_link(bool pcap_enabled = false) + { + bool use_sctp = GetParam(); + link = std::make_unique(use_sctp, pcap_enabled); + } + + void send_to_cu_up(const e1ap_message& msg) { link->cu_cp_tx_pdu_notifier->on_new_message(msg); } + + void send_to_cu_cp(const e1ap_message& msg) { link->cu_up_tx_pdu_notifier->on_new_message(msg); } + + bool pop_cu_rx_pdu(e1ap_message& msg) + { + bool res; + msg = link->cu_rx_pdus.pop_blocking(&res); + return res; + } + + bool pop_cu_up_rx_pdu(e1ap_message& msg) + { + bool res; + msg = link->cu_up_rx_pdus.pop_blocking(&res); + return res; + } + + srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST"); + std::unique_ptr link; +}; + +static e1ap_message create_test_message() +{ + e1ap_message msg; + msg.pdu.set_init_msg().load_info_obj(ASN1_E1AP_ID_BEARER_CONTEXT_MOD); + asn1::e1ap::bearer_context_mod_request_s& bearer_mod = msg.pdu.init_msg().value.bearer_context_mod_request(); + bearer_mod->gnb_cu_cp_ue_e1ap_id = 0; + bearer_mod->gnb_cu_up_ue_e1ap_id = 1; + return msg; +} + +static byte_buffer pack(const e1ap_message& msg) +{ + byte_buffer pdu; + { + asn1::bit_ref bref{pdu}; + report_fatal_error_if_not(msg.pdu.pack(bref) == asn1::SRSASN_SUCCESS, "Failed to pack E1AP PDU"); + } + return pdu; +} + +static bool is_equal(const e1ap_message& lhs, const e1ap_message& rhs) +{ + byte_buffer lhs_pdu = pack(lhs); + byte_buffer rhs_pdu = pack(rhs); + return lhs_pdu == rhs_pdu; +} + +TEST_P(e1_gateway_link_test, when_cu_up_sends_msg_then_cu_receives_msg) +{ + create_link(); + + e1ap_message orig_msg = create_test_message(); + send_to_cu_cp(orig_msg); + + e1ap_message dest_msg; + ASSERT_TRUE(pop_cu_rx_pdu(dest_msg)); + ASSERT_TRUE(is_equal(orig_msg, dest_msg)); +} + +TEST_P(e1_gateway_link_test, when_cu_cp_sends_msg_then_cu_up_receives_msg) +{ + create_link(); + + e1ap_message orig_msg = create_test_message(); + send_to_cu_up(orig_msg); + + e1ap_message dest_msg; + ASSERT_TRUE(pop_cu_up_rx_pdu(dest_msg)); + ASSERT_TRUE(is_equal(orig_msg, dest_msg)); +} + +TEST_P(e1_gateway_link_test, when_pcap_writer_disabled_then_no_pcap_is_written) +{ + create_link(false); + + e1ap_message orig_msg = create_test_message(); + send_to_cu_up(orig_msg); + e1ap_message dest_msg; + ASSERT_TRUE(pop_cu_up_rx_pdu(dest_msg)); + byte_buffer sdu; + ASSERT_FALSE(link->pcap.last_sdus.try_pop(sdu)); + + send_to_cu_cp(orig_msg); + ASSERT_TRUE(pop_cu_rx_pdu(dest_msg)); + ASSERT_FALSE(link->pcap.last_sdus.try_pop(sdu)); +} + +TEST_P(e1_gateway_link_test, when_pcap_writer_enabled_then_pcap_is_written) +{ + create_link(true); + + e1ap_message orig_msg = create_test_message(); + + send_to_cu_up(orig_msg); + e1ap_message dest_msg; + ASSERT_TRUE(pop_cu_up_rx_pdu(dest_msg)); + bool popped = false; + byte_buffer sdu = link->pcap.last_sdus.pop_blocking(&popped); + ASSERT_TRUE(popped); + ASSERT_FALSE(link->pcap.last_sdus.try_pop(sdu)); + + send_to_cu_cp(orig_msg); + ASSERT_TRUE(pop_cu_rx_pdu(dest_msg)); + popped = false; + sdu = link->pcap.last_sdus.pop_blocking(&popped); + ASSERT_TRUE(popped); + ASSERT_FALSE(link->pcap.last_sdus.try_pop(sdu)); +} + +TEST_P(e1_gateway_link_test, when_cu_tx_pdu_notifier_is_closed_then_connection_closes) +{ + create_link(); + + // The CU-CP resets its E1 Tx notifier. + logger.info("Closing CU-CP Tx path..."); + link->cu_cp_tx_pdu_notifier.reset(); + + // Wait for GW to report to CU-UP that the association is closed. + link->cu_up_gw_assoc_close_signaled.wait(); +} + +TEST_P(e1_gateway_link_test, when_cu_up_tx_pdu_notifier_is_closed_then_connection_closes) +{ + create_link(); + + // The CU-UP resets its E1 Tx notifier. + logger.info("Closing CU-UP Tx path..."); + link->cu_up_tx_pdu_notifier.reset(); + + // Wait for GW to report to CU that the association is closed. + link->cu_cp_gw_assoc_close_signaled.wait(); +} + +INSTANTIATE_TEST_SUITE_P(e1_gateway_link_tests, e1_gateway_link_test, ::testing::Values(true, false)); diff --git a/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp b/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp index df67d8d673..ee0d31e669 100644 --- a/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp +++ b/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp @@ -458,9 +458,9 @@ cu_cp_paging_message srsran::srs_cu_cp::generate_paging_message() cu_cp_paging_message paging_msg; // add ue paging id - paging_msg.ue_paging_id.amf_set_id = 1; - paging_msg.ue_paging_id.amf_pointer = 0; - paging_msg.ue_paging_id.five_g_tmsi = 4211117727; + bounded_bitset<48> five_g_s_tmsi(48); + five_g_s_tmsi.from_uint64(((uint64_t)1U << 38U) + ((uint64_t)0U << 32U) + 4211117727); + paging_msg.ue_paging_id = cu_cp_five_g_s_tmsi{five_g_s_tmsi}; // add paging drx paging_msg.paging_drx = 64; diff --git a/tests/unittests/f1ap/du/f1ap_du_test_helpers.cpp b/tests/unittests/f1ap/du/f1ap_du_test_helpers.cpp index 765e125c04..8c8f2408e8 100644 --- a/tests/unittests/f1ap/du/f1ap_du_test_helpers.cpp +++ b/tests/unittests/f1ap/du/f1ap_du_test_helpers.cpp @@ -27,6 +27,7 @@ #include "srsran/asn1/f1ap/common.h" #include "srsran/asn1/f1ap/f1ap_pdu_contents_ue.h" #include "srsran/du/du_cell_config_helpers.h" +#include "srsran/pdcp/pdcp_sn_util.h" #include "srsran/support/async/async_test_utils.h" #include "srsran/support/test_utils.h" @@ -85,38 +86,6 @@ asn1::f1ap::drbs_to_be_setup_item_s srsran::srs_du::generate_drb_am_setup_item(d return drb; } -f1ap_message srsran::srs_du::generate_ue_context_setup_request(const std::initializer_list& drbs_to_add) -{ - using namespace asn1::f1ap; - f1ap_message msg; - - msg.pdu.set_init_msg().load_info_obj(ASN1_F1AP_ID_UE_CONTEXT_SETUP); - ue_context_setup_request_s& dl_msg = msg.pdu.init_msg().value.ue_context_setup_request(); - dl_msg->gnb_cu_ue_f1ap_id = 0; - dl_msg->gnb_du_ue_f1ap_id_present = true; - dl_msg->gnb_du_ue_f1ap_id = 0; - dl_msg->srbs_to_be_setup_list_present = true; - dl_msg->srbs_to_be_setup_list.resize(1); - dl_msg->srbs_to_be_setup_list[0].load_info_obj(ASN1_F1AP_ID_SRBS_SETUP_ITEM); - srbs_to_be_setup_item_s& srb2 = dl_msg->srbs_to_be_setup_list[0]->srbs_to_be_setup_item(); - srb2.srb_id = 2; - - dl_msg->drbs_to_be_setup_list_present = drbs_to_add.size() > 0; - dl_msg->drbs_to_be_setup_list.resize(drbs_to_add.size()); - unsigned count = 0; - for (drb_id_t drbid : drbs_to_add) { - dl_msg->drbs_to_be_setup_list[count].load_info_obj(ASN1_F1AP_ID_DRB_INFO); - dl_msg->drbs_to_be_setup_list[count]->drbs_to_be_setup_item() = generate_drb_am_setup_item(drbid); - ++count; - } - - dl_msg->rrc_container_present = true; - EXPECT_TRUE( - dl_msg->rrc_container.append(test_rgen::random_vector(test_rgen::uniform_int(3, 100)))); - - return msg; -} - asn1::f1ap::drbs_to_be_setup_mod_item_s srsran::srs_du::generate_drb_am_mod_item(drb_id_t drbid) { using namespace asn1::f1ap; @@ -380,6 +349,11 @@ void f1ap_du_test::run_ue_context_setup_procedure(du_ue_index_t ue_index, const for (const auto& created_srb : f1ap_du_cfg_handler.last_ue_cfg_response->f1c_bearers_added) { ue.f1c_bearers[srb_id_to_uint(created_srb.srb_id)].bearer = created_srb.bearer; } + + // Report transmission notification back to F1AP. + std::optional pdcp_sn = get_pdcp_sn(f1ap_req->rrc_container, pdcp_sn_size::size12bits, true, test_logger); + ue.f1c_bearers[LCID_SRB1].bearer->handle_transmit_notification(pdcp_sn.value()); + this->ctrl_worker.run_pending_tasks(); } f1ap_ue_configuration_response f1ap_du_test::update_f1ap_ue_config(du_ue_index_t ue_index, diff --git a/tests/unittests/f1ap/du/f1ap_du_test_helpers.h b/tests/unittests/f1ap/du/f1ap_du_test_helpers.h index e25a32613e..e6c911f195 100644 --- a/tests/unittests/f1ap/du/f1ap_du_test_helpers.h +++ b/tests/unittests/f1ap/du/f1ap_du_test_helpers.h @@ -145,9 +145,6 @@ f1_setup_request_message generate_f1_setup_request_message(); /// \brief Generate F1AP ASN.1 DRB AM Setup configuration. asn1::f1ap::drbs_to_be_setup_item_s generate_drb_am_setup_item(drb_id_t drbid); -/// \brief Generate an F1AP UE Context Setup Request message with specified list of DRBs. -f1ap_message generate_ue_context_setup_request(const std::initializer_list& drbs_to_add); - /// \brief Generate F1AP ASN.1 DRB AM Setup configuration. asn1::f1ap::drbs_to_be_setup_mod_item_s generate_drb_am_mod_item(drb_id_t drbid); diff --git a/tests/unittests/f1ap/du/f1ap_du_ue_context_modification_test.cpp b/tests/unittests/f1ap/du/f1ap_du_ue_context_modification_test.cpp index 5dad262036..44ee62dca0 100644 --- a/tests/unittests/f1ap/du/f1ap_du_ue_context_modification_test.cpp +++ b/tests/unittests/f1ap/du/f1ap_du_ue_context_modification_test.cpp @@ -37,7 +37,9 @@ class f1ap_du_ue_context_modification_test : public f1ap_du_test // Test Preamble. run_f1_setup_procedure(); run_f1ap_ue_create(test_ue_index); - run_ue_context_setup_procedure(test_ue_index, generate_ue_context_setup_request({})); + f1ap_message msg = + test_helpers::create_ue_context_setup_request(gnb_cu_ue_f1ap_id_t{0}, gnb_du_ue_f1ap_id_t{0}, 1, {}); + run_ue_context_setup_procedure(test_ue_index, msg); } void start_procedure(const std::initializer_list& drbs, byte_buffer rrc_container = {}) diff --git a/tests/unittests/f1ap/du/f1ap_du_ue_context_release_test.cpp b/tests/unittests/f1ap/du/f1ap_du_ue_context_release_test.cpp index 6a697768fc..8cb5e24a6e 100644 --- a/tests/unittests/f1ap/du/f1ap_du_ue_context_release_test.cpp +++ b/tests/unittests/f1ap/du/f1ap_du_ue_context_release_test.cpp @@ -37,7 +37,9 @@ class f1ap_du_ue_context_release_test : public f1ap_du_test run_f1_setup_procedure(); du_ue_index_t ue_index = to_du_ue_index(test_rgen::uniform_int(0, MAX_DU_UE_INDEX)); test_ue = run_f1ap_ue_create(ue_index); - run_ue_context_setup_procedure(ue_index, generate_ue_context_setup_request({})); + f1ap_message msg = + test_helpers::create_ue_context_setup_request(gnb_cu_ue_f1ap_id_t{0}, gnb_du_ue_f1ap_id_t{0}, 1, {}); + run_ue_context_setup_procedure(ue_index, msg); } void start_procedure(const f1ap_message& msg = generate_ue_context_release_command()) diff --git a/tests/unittests/f1ap/du/f1ap_du_ue_context_setup_procedure_test.cpp b/tests/unittests/f1ap/du/f1ap_du_ue_context_setup_procedure_test.cpp index 068fbe33a9..82631db5d4 100644 --- a/tests/unittests/f1ap/du/f1ap_du_ue_context_setup_procedure_test.cpp +++ b/tests/unittests/f1ap/du/f1ap_du_ue_context_setup_procedure_test.cpp @@ -21,6 +21,7 @@ */ #include "f1ap_du_test_helpers.h" +#include "tests/test_doubles/f1ap/f1ap_test_messages.h" #include "srsran/support/test_utils.h" #include @@ -90,6 +91,19 @@ class f1ap_du_ue_context_setup_test : public f1ap_du_test } f1ap->handle_message(msg); + + if (not ue_ctx_setup.gnb_du_ue_f1ap_id_present) { + report_fatal_error_if_not(this->f1ap_du_cfg_handler.last_ue_creation_response.has_value(), + "UE should have been created"); + test_ue->f1c_bearers[srb_id_to_uint(srb_id_t::srb1)].bearer = + this->f1ap_du_cfg_handler.last_ue_creation_response.value().f1c_bearers_added[0]; + } + } + + void on_rrc_container_transmitted(uint32_t highest_pdcp_sn) + { + this->test_ue->f1c_bearers[LCID_SRB1].bearer->handle_transmit_notification(highest_pdcp_sn); + this->ctrl_worker.run_pending_tasks(); } ue_test_context* test_ue = nullptr; @@ -98,7 +112,8 @@ class f1ap_du_ue_context_setup_test : public f1ap_du_test TEST_F(f1ap_du_ue_context_setup_test, when_f1ap_receives_request_then_f1ap_notifies_du_of_ue_context_update) { du_creates_f1_logical_connection(); - start_procedure(generate_ue_context_setup_request({drb_id_t::drb1})); + start_procedure(test_helpers::create_ue_context_setup_request( + gnb_cu_ue_f1ap_id_t{0}, gnb_du_ue_f1ap_id_t{0}, 1, {drb_id_t::drb1})); // DU manager receives UE Context Update Request. ASSERT_TRUE(this->f1ap_du_cfg_handler.last_ue_context_update_req.has_value()); @@ -116,9 +131,14 @@ TEST_F(f1ap_du_ue_context_setup_test, when_f1ap_receives_request_then_f1ap_notif TEST_F(f1ap_du_ue_context_setup_test, when_f1ap_receives_request_then_f1ap_responds_back_with_ue_context_setup_response) { du_creates_f1_logical_connection(); - auto msg = generate_ue_context_setup_request({drb_id_t::drb1}); + f1ap_message msg = test_helpers::create_ue_context_setup_request( + gnb_cu_ue_f1ap_id_t{0}, gnb_du_ue_f1ap_id_t{0}, 1, {drb_id_t::drb1}); start_procedure(msg); + // Lower layers handle RRC container. + this->f1c_gw.last_tx_f1ap_pdu = {}; + on_rrc_container_transmitted(1); + // F1AP sends UE CONTEXT SETUP RESPONSE to CU-CP. ASSERT_EQ(this->f1c_gw.last_tx_f1ap_pdu.pdu.type().value, f1ap_pdu_c::types_opts::successful_outcome); ASSERT_EQ(this->f1c_gw.last_tx_f1ap_pdu.pdu.successful_outcome().value.type().value, @@ -146,7 +166,8 @@ TEST_F(f1ap_du_ue_context_setup_test, when_f1ap_receives_request_then_f1ap_respo TEST_F(f1ap_du_ue_context_setup_test, when_f1ap_receives_request_then_the_rrc_container_is_sent_dl_via_srb1) { du_creates_f1_logical_connection(); - f1ap_message msg = generate_ue_context_setup_request({drb_id_t::drb1}); + f1ap_message msg = test_helpers::create_ue_context_setup_request( + gnb_cu_ue_f1ap_id_t{0}, gnb_du_ue_f1ap_id_t{0}, 1, {drb_id_t::drb1}); start_procedure(msg); // F1AP sends RRC Container present in UE CONTEXT SETUP REQUEST via SRB1. @@ -157,7 +178,9 @@ TEST_F(f1ap_du_ue_context_setup_test, when_f1ap_receives_request_then_the_rrc_co TEST_F(f1ap_du_ue_context_setup_test, when_f1ap_receives_request_then_new_srbs_become_active) { du_creates_f1_logical_connection(); - run_ue_context_setup_procedure(test_ue->ue_index, generate_ue_context_setup_request({drb_id_t::drb1})); + f1ap_message msg = test_helpers::create_ue_context_setup_request( + gnb_cu_ue_f1ap_id_t{0}, gnb_du_ue_f1ap_id_t{0}, 1, {drb_id_t::drb1}); + run_ue_context_setup_procedure(test_ue->ue_index, msg); // UL data through created SRB2 reaches F1-C. ASSERT_EQ(this->f1ap_du_cfg_handler.last_ue_cfg_response->f1c_bearers_added.size(), 1); @@ -174,8 +197,8 @@ TEST_F(f1ap_du_ue_context_setup_test, when_f1ap_receives_request_then_new_srbs_b TEST_F(f1ap_du_ue_context_setup_test, when_f1ap_receives_request_without_gnb_du_ue_f1ap_id_then_ue_is_created) { - f1ap_message msg = generate_ue_context_setup_request({drb_id_t::drb1}); - msg.pdu.init_msg().value.ue_context_setup_request()->gnb_du_ue_f1ap_id_present = false; + f1ap_message msg = + test_helpers::create_ue_context_setup_request(gnb_cu_ue_f1ap_id_t{0}, std::nullopt, 1, {drb_id_t::drb1}); start_procedure(msg); @@ -185,8 +208,8 @@ TEST_F(f1ap_du_ue_context_setup_test, when_f1ap_receives_request_without_gnb_du_ TEST_F(f1ap_du_ue_context_setup_test, when_f1ap_receives_request_without_gnb_du_ue_f1ap_id_then_ue_context_is_updated) { - f1ap_message msg = generate_ue_context_setup_request({drb_id_t::drb1}); - msg.pdu.init_msg().value.ue_context_setup_request()->gnb_du_ue_f1ap_id_present = false; + f1ap_message msg = + test_helpers::create_ue_context_setup_request(gnb_cu_ue_f1ap_id_t{0}, std::nullopt, 1, {drb_id_t::drb1}); start_procedure(msg); @@ -203,10 +226,11 @@ TEST_F( f1ap_du_ue_context_setup_test, when_f1ap_receives_request_without_gnb_du_ue_f1ap_id_then_ue_context_setup_response_is_sent_to_cu_cp_with_crnti_ie) { - f1ap_message msg = generate_ue_context_setup_request({drb_id_t::drb1}); - msg.pdu.init_msg().value.ue_context_setup_request()->gnb_du_ue_f1ap_id_present = false; + f1ap_message msg = + test_helpers::create_ue_context_setup_request(gnb_cu_ue_f1ap_id_t{0}, std::nullopt, 1, {drb_id_t::drb1}); start_procedure(msg); + on_rrc_container_transmitted(1); // F1AP sends UE CONTEXT SETUP RESPONSE to CU-CP. ASSERT_EQ(this->f1c_gw.last_tx_f1ap_pdu.pdu.type().value, f1ap_pdu_c::types_opts::successful_outcome); diff --git a/tests/unittests/f1ap/du/f1ap_du_ul_rrc_message_transfer_test.cpp b/tests/unittests/f1ap/du/f1ap_du_ul_rrc_message_transfer_test.cpp index a41d6c8f1a..7ea072aff9 100644 --- a/tests/unittests/f1ap/du/f1ap_du_ul_rrc_message_transfer_test.cpp +++ b/tests/unittests/f1ap/du/f1ap_du_ul_rrc_message_transfer_test.cpp @@ -22,6 +22,7 @@ #include "f1ap_du_test_helpers.h" #include "lib/f1ap/du/ue_context/f1c_du_bearer_impl.h" +#include "tests/test_doubles/f1ap/f1ap_test_messages.h" #include "srsran/asn1/f1ap/common.h" #include "srsran/support/test_utils.h" #include @@ -35,7 +36,9 @@ TEST_F(f1ap_du_test, when_sdu_is_received_then_sdu_is_forwarded_to_tx_pdu_notifi // Run Test Preamble. run_f1_setup_procedure(); ue_test_context* ue = run_f1ap_ue_create(to_du_ue_index(0)); - run_ue_context_setup_procedure(ue->ue_index, generate_ue_context_setup_request({})); + f1ap_message msg = + test_helpers::create_ue_context_setup_request(gnb_cu_ue_f1ap_id_t{0}, gnb_du_ue_f1ap_id_t{0}, 1, {}); + run_ue_context_setup_procedure(ue->ue_index, msg); this->f1c_gw.last_tx_f1ap_pdu.pdu = {}; std::vector bytes = test_rgen::random_vector(test_rgen::uniform_int(1, 4000)); diff --git a/tests/unittests/ngap/ngap_paging_test.cpp b/tests/unittests/ngap/ngap_paging_test.cpp index a24258e1e0..594abde744 100644 --- a/tests/unittests/ngap/ngap_paging_test.cpp +++ b/tests/unittests/ngap/ngap_paging_test.cpp @@ -32,17 +32,19 @@ class ngap_paging_test : public ngap_test bool was_minimal_conversion_successful() const { // check ue paging id - if (cu_cp_paging_notifier.last_msg.ue_paging_id.amf_set_id != 1) { - test_logger.error("AMF Set ID mismatch {} != {}", cu_cp_paging_notifier.last_msg.ue_paging_id.amf_set_id, 1); + if (cu_cp_paging_notifier.last_msg.ue_paging_id.get_amf_set_id() != 1) { + test_logger.error( + "AMF Set ID mismatch {} != {}", cu_cp_paging_notifier.last_msg.ue_paging_id.get_amf_set_id(), 1); return false; } - if (cu_cp_paging_notifier.last_msg.ue_paging_id.amf_pointer != 0) { - test_logger.error("AMF Pointer mismatch {} != {}", cu_cp_paging_notifier.last_msg.ue_paging_id.amf_pointer, 0); + if (cu_cp_paging_notifier.last_msg.ue_paging_id.get_amf_pointer() != 0) { + test_logger.error( + "AMF Pointer mismatch {} != {}", cu_cp_paging_notifier.last_msg.ue_paging_id.get_amf_pointer(), 0); return false; } - if (cu_cp_paging_notifier.last_msg.ue_paging_id.five_g_tmsi != 4211117727) { + if (cu_cp_paging_notifier.last_msg.ue_paging_id.get_five_g_tmsi() != 4211117727) { test_logger.error( - "FiveG TMSI mismatch {} != {}", cu_cp_paging_notifier.last_msg.ue_paging_id.five_g_tmsi, 4211117727); + "FiveG TMSI mismatch {} != {}", cu_cp_paging_notifier.last_msg.ue_paging_id.get_five_g_tmsi(), 4211117727); return false; } diff --git a/tests/unittests/ofh/receiver/helpers.h b/tests/unittests/ofh/receiver/helpers.h index c8aaa21fde..a0d016fcec 100644 --- a/tests/unittests/ofh/receiver/helpers.h +++ b/tests/unittests/ofh/receiver/helpers.h @@ -110,7 +110,7 @@ class resource_grid_writer_bool_spy : public resource_grid_writer nof_prbs_written += symbols.size() / NOF_SUBCARRIERS_PER_RB; } - void put(unsigned port, unsigned l, unsigned k_init, unsigned stride, span symbols) override + void put(unsigned port, unsigned l, unsigned k_init, unsigned stride, span symbols) override { grid_written = true; nof_prbs_written += divide_ceil(symbols.size() * stride, NOF_SUBCARRIERS_PER_RB); diff --git a/tests/unittests/ofh/transmitter/ofh_uplink_request_handler_impl_test.cpp b/tests/unittests/ofh/transmitter/ofh_uplink_request_handler_impl_test.cpp index 5a090fbf5d..4fdbd29123 100644 --- a/tests/unittests/ofh/transmitter/ofh_uplink_request_handler_impl_test.cpp +++ b/tests/unittests/ofh/transmitter/ofh_uplink_request_handler_impl_test.cpp @@ -127,8 +127,8 @@ class resource_grid_dummy : public resource_grid return {}; } - void put(unsigned port, unsigned l, unsigned k_init, span symbols) override {} - void put(unsigned port, unsigned l, unsigned k_init, unsigned stride, span symbols) override {} + void put(unsigned port, unsigned l, unsigned k_init, span symbols) override {} + void put(unsigned port, unsigned l, unsigned k_init, unsigned stride, span symbols) override {} span get_view(unsigned port, unsigned l) override { return {}; } }; diff --git a/tests/unittests/phy/generic_functions/precoding/channel_precoder_test.cpp b/tests/unittests/phy/generic_functions/precoding/channel_precoder_test.cpp index 2fb204836e..8b25c78a65 100644 --- a/tests/unittests/phy/generic_functions/precoding/channel_precoder_test.cpp +++ b/tests/unittests/phy/generic_functions/precoding/channel_precoder_test.cpp @@ -44,27 +44,12 @@ using MultiplePRGParams = std::tuple< namespace srsran { -static float ASSERT_MAX_ERROR = 1e-4; - static std::ostream& operator<<(std::ostream& os, span data) { fmt::print(os, "{}", data); return os; } -static std::ostream& operator<<(std::ostream& os, span data) -{ - fmt::print(os, "{}", data); - return os; -} - -static bool operator==(span lhs, span rhs) -{ - return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](cf_t lhs_val, cf_t rhs_val) { - return (std::abs(lhs_val - rhs_val) < ASSERT_MAX_ERROR); - }); -} - static bool operator==(span lhs, span rhs) { static constexpr float max_relative_error_cbf16 = 1.0F / 256.0F; @@ -250,8 +235,8 @@ TEST_P(PrecodingFixture, RandomWeightsCft) unsigned nof_re = nof_rb * NRE; // Buffer to hold the precoded RE. - static_re_buffer precoding_buffer(nof_ports, - nof_re); + static_re_buffer precoding_buffer( + nof_ports, nof_re); for (unsigned nof_layers = 1; nof_layers <= nof_ports; ++nof_layers) { // Generate random RE arranged by layers. const re_buffer_reader<>& input_data = generate_random_data(nof_layers, nof_re); @@ -267,7 +252,7 @@ TEST_P(PrecodingFixture, RandomWeightsCft) // For each antenna port, compare the precoded RE with the golden sequence for all RE and PRG. for (unsigned i_port = 0; i_port != nof_ports; ++i_port) { - ASSERT_EQ(span(golden.get_slice(i_port)), span(precoding_buffer.get_slice(i_port))); + ASSERT_EQ(span(golden.get_slice(i_port)), span(precoding_buffer.get_slice(i_port))); } } } diff --git a/tests/unittests/phy/support/resource_grid_test_doubles.h b/tests/unittests/phy/support/resource_grid_test_doubles.h index 10c61f41fd..07f9272a41 100644 --- a/tests/unittests/phy/support/resource_grid_test_doubles.h +++ b/tests/unittests/phy/support/resource_grid_test_doubles.h @@ -139,13 +139,11 @@ class resource_grid_writer_spy : public resource_grid_writer } } - void put(unsigned port, unsigned l, unsigned k_init, unsigned stride, span symbols) override + void put(unsigned port, unsigned l, unsigned k_init, unsigned stride, span symbols) override { ++count; for (unsigned i_symb = 0; i_symb != symbols.size(); ++i_symb) { - if ((symbols[i_symb].real() != 0) || (symbols[i_symb].imag() != 0)) { - put(port, l, k_init + (i_symb * stride), symbols[i_symb]); - } + data[{k_init + i_symb * stride, l, port}] = symbols[i_symb]; } } diff --git a/tests/unittests/rrc/rrc_asn1_helpers_test.cpp b/tests/unittests/rrc/rrc_asn1_helpers_test.cpp index 53fd1c4a96..cbd4641dcb 100644 --- a/tests/unittests/rrc/rrc_asn1_helpers_test.cpp +++ b/tests/unittests/rrc/rrc_asn1_helpers_test.cpp @@ -21,12 +21,46 @@ */ #include "lib/rrc/ue/rrc_asn1_converters.h" +#include "srsran/asn1/asn1_utils.h" #include "srsran/cu_cp/cu_cp_types.h" +#include #include using namespace srsran; using namespace srsran::srs_cu_cp; +/// Test five-g-s-tmsi conversion +TEST(rrc_asn1_helpers_test, test_five_g_s_tmsi_converter_for_valid_five_g_s_tmsi) +{ + // use known a Five-G-S-TMSI + asn1::fixed_bitstring<48> asn1_five_g_s_tmsi; + asn1_five_g_s_tmsi.from_number(278099133963U); + + srs_cu_cp::cu_cp_five_g_s_tmsi five_g_s_tmsi = asn1_to_five_g_s_tmsi(asn1_five_g_s_tmsi); + + ASSERT_EQ(1U, five_g_s_tmsi.get_amf_set_id()); + ASSERT_EQ(0U, five_g_s_tmsi.get_amf_pointer()); + ASSERT_EQ(3221227019U, five_g_s_tmsi.get_five_g_tmsi()); +} + +/// Test five-g-s-tmsi conversion with concatenation +TEST(rrc_asn1_helpers_test, test_five_g_s_tmsi_concatenation_for_valid_five_g_s_tmsi) +{ + // use known Five-G-S-TMSI-Par1 and Five-G-S-TMSI-Part2 + asn1::fixed_bitstring<39> asn1_five_g_s_tmsi_part1; + asn1_five_g_s_tmsi_part1.from_number(278099133963); + + asn1::fixed_bitstring<9> asn1_five_g_s_tmsi_part_2; + asn1_five_g_s_tmsi_part_2.from_number(0); + + srs_cu_cp::cu_cp_five_g_s_tmsi five_g_s_tmsi = + asn1_to_five_g_s_tmsi(asn1_five_g_s_tmsi_part1, asn1_five_g_s_tmsi_part_2); + + ASSERT_EQ(1U, five_g_s_tmsi.get_amf_set_id()); + ASSERT_EQ(0U, five_g_s_tmsi.get_amf_pointer()); + ASSERT_EQ(3221227019U, five_g_s_tmsi.get_five_g_tmsi()); +} + /// Test amf-identifier decoding TEST(rrc_asn1_helpers_test, test_amf_identifier_converter_for_valid_amf_id) { diff --git a/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp b/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp index 5272149547..3ef2bd894b 100644 --- a/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp @@ -329,6 +329,30 @@ TEST_P(ue_grid_allocator_tester, allocates_pusch_restricted_to_recommended_max_n ASSERT_EQ(find_ue_pusch(u1.crnti, res_grid[0].result.ul)->pusch_cfg.rbs.type1().length(), grant1.max_nof_rbs); } +TEST_P(ue_grid_allocator_tester, does_not_allocate_pusch_with_all_remaining_rbs_if_its_a_sr_indication) +{ + sched_ue_creation_request_message ue_creation_req = + test_helpers::create_default_sched_ue_creation_request(this->cfg_builder_params); + ue_creation_req.ue_index = to_du_ue_index(0); + ue_creation_req.crnti = to_rnti(0x4601); + ue& u1 = add_ue(ue_creation_req); + // Trigger a SR indication. + u1.handle_sr_indication(); + + const ue_pusch_grant grant1{.user = &u1, + .cell_index = to_du_cell_index(0), + .h_id = to_harq_id(0), + .recommended_nof_bytes = u1.pending_ul_newtx_bytes()}; + + const crb_interval cell_crbs = {cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.crbs.start(), + cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.crbs.stop()}; + + ASSERT_TRUE(run_until([&]() { return alloc.allocate_ul_grant(grant1).status == alloc_status::success; })); + ASSERT_TRUE(run_until([&]() { return find_ue_pusch(u1.crnti, res_grid[0].result.ul) != nullptr; })); + // Successfully allocates PUSCH corresponding to the grant. + ASSERT_LT(find_ue_pusch(u1.crnti, res_grid[0].result.ul)->pusch_cfg.rbs.type1().length(), cell_crbs.length()); +} + TEST_P(ue_grid_allocator_tester, no_two_pdschs_are_allocated_in_same_slot_for_a_ue) { static const unsigned nof_bytes_to_schedule = 400U;