diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index edcb720f49..2b0fe65911 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -160,6 +160,7 @@ full-code-format: KUBERNETES_CPU_LIMIT: 6 KUBERNETES_MEMORY_REQUEST: 12Gi KUBERNETES_MEMORY_LIMIT: 12Gi + CHECK_PER_FILE_TIMEOUT: 600 before_script: - | echo " @@ -180,7 +181,21 @@ full-code-format: cd build || exit cmake -DASSERT_LEVEL=PARANOID -DAUTO_DETECT_ISA=False -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DBUILD_TESTS=False .. make srsran_build_info # needed to generate hashes.h + - | + monitor_child_process() { + while true; do + ps -eo comm,pid,etimes | grep ${ANALYZER} | while read comm pid etimes; do + if [ $etimes -gt $CHECK_PER_FILE_TIMEOUT ]; then + echo "Killing child analysis process" + kill $pid + fi + done + sleep 30 + done + } + export -f monitor_child_process script: + - nohup bash -c monitor_child_process & - static-analyzer.sh -i /tmp/codechecker_skip --jobs ${KUBERNETES_CPU_REQUEST} --analyzers ${ANALYZER} ${ANALYZER_ARGS} $CI_PROJECT_DIR after_script: - mv codechecker_html codechecker-${ANALYZER}-html @@ -191,7 +206,6 @@ full-code-format: - codechecker-${ANALYZER}-html${ARTIFACT_EXTRA_PATH} when: always expire_in: 10 minutes - timeout: 8 hours # clangsa is slow clang-tidy: extends: .codechecker @@ -211,6 +225,7 @@ cppcheck: rules: - if: $CI_DESCRIPTION =~ /Nightly Build Unit Tests/ interruptible: false + retry: 2 variables: ANALYZER: cppcheck ANALYZER_ARGS: --cppcheck-max-template-recursion 5 @@ -231,6 +246,7 @@ clangsa: variables: ANALYZER: clangsa ANALYZER_ARGS: --ctu + timeout: 8 hours .coverity_base: image: $CR_REGISTRY_URI/coverity_image/2022.6.0:1.0.0 @@ -452,7 +468,7 @@ disable current schedule: - if: $CI_DESCRIPTION =~ /Alternative OSs/ when: always - if: $CI_DESCRIPTION =~ /Weekly/ - when: always + when: always interruptible: false variables: ENABLE: "false" diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index 48246fd0c7..a99c25ce7d 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -8,7 +8,7 @@ include: - project: softwareradiosystems/ci/srsran_project_packaging - ref: "3" + ref: "4" file: .gitlab/ci-shared/package.yml - local: .gitlab/ci/src_cache.yml @@ -17,7 +17,9 @@ variables: AMD64_AVX2_TAG: amd64-avx2 AMD64_AVX512_TAG: amd64-avx2-avx512 ARM64_TAG: arm64 + AMD64_LIB_BUILDER_TAG: on-prem-amd64 + AMD64_VIAVI_BUILDER_TAG: on-prem-amd64-avx2-avx512 INFRASTRUCTURE_TAG: description: Computer architecture and supported instruction sets @@ -632,7 +634,7 @@ smoke release update cache: - *cache_build_set artifacts: <<: *build_artifacts - expire_in: 1 day + expire_in: 3 day smoke relwithdeb update cache: extends: .smoke relwithdeb @@ -709,7 +711,7 @@ smoke valgrind update cache: SAVE_ARTIFACTS: "True" # Valgrind generates extra files artifacts: <<: *build_artifacts - expire_in: 1 day + expire_in: 3 day smoke asan: extends: .build_and_unit @@ -739,7 +741,7 @@ package: - if: $CI_DESCRIPTION =~ /Nightly Build Unit Tests/ when: delayed start_in: 30 minutes - variables: + variables: &package_variables PROJECT_NAME: srsran-project RELEASE_VERSION: "99.9" KUBERNETES_CPU_REQUEST: 7 @@ -1716,7 +1718,20 @@ basic relwithdeb: SAVE_ARTIFACTS: "True" artifacts: <<: *build_artifacts - expire_in: 1 day + expire_in: 3 day + +basic package: + extends: .deb-package + stage: build and unit tests + rules: + - if: $CI_DESCRIPTION =~ /Nightly E2E Tests/ + retry: 2 + interruptible: false + variables: + <<: *package_variables + OS_VERSION: "24.04" + tags: ["${AMD64_LIB_BUILDER_TAG}"] + needs: [] basic tsan: extends: .smoke tsan @@ -1729,7 +1744,7 @@ basic tsan: SAVE_ARTIFACTS: "True" artifacts: <<: *build_artifacts - expire_in: 1 day + expire_in: 3 day basic asan: extends: smoke asan @@ -1743,7 +1758,7 @@ basic asan: tags: ["${AMD64_AVX2_TAG}"] artifacts: <<: *build_artifacts - expire_in: 1 day + expire_in: 3 day basic memcheck: extends: .smoke valgrind @@ -1756,7 +1771,7 @@ basic memcheck: SAVE_ARTIFACTS: "True" artifacts: <<: *build_artifacts - expire_in: 1 day + expire_in: 3 day basic avx512 dpdk: extends: .build_and_unit @@ -1783,10 +1798,10 @@ basic avx512 dpdk: KUBERNETES_CPU_LIMIT: 14 KUBERNETES_MEMORY_REQUEST: 20Gi KUBERNETES_MEMORY_LIMIT: 20Gi - tags: ["on-prem-amd64-avx2-avx512"] + tags: ["${AMD64_VIAVI_BUILDER_TAG}"] artifacts: <<: *build_artifacts - expire_in: 1 day + expire_in: 3 day ####### # Web # diff --git a/.gitlab/ci/e2e.yml b/.gitlab/ci/e2e.yml index 4d56301277..49f81799ad 100644 --- a/.gitlab/ci/e2e.yml +++ b/.gitlab/ci/e2e.yml @@ -16,6 +16,7 @@ variables: description: Retina Testbed Description options: - "zmq" + - "zmq_deb" - "zmq_single_ue" - "zmq_4x4_mimo" - "zmq_srsue" @@ -143,6 +144,7 @@ e2e request validation: - retina-delete-orchestration-network --user-name ^ci_${GROUP} --regex # Add extra secret env variables to the .env file - | + echo "" >> .gitlab/ci/e2e/.env cat $RETINA_SECRET_ENV >> .gitlab/ci/e2e/.env # Set username for retina - | @@ -235,7 +237,7 @@ amari 1UE: TESTBED: "zmq_single_ue" MARKERS: "zmq_single_ue" E2E_LOG_LEVEL: "info" - RETINA_ARGS: "gnb.all.pcap=True gnb.all.mac_enable=True gnb.all.rlc_enable=True" + RETINA_ARGS: "gnb.all.pcap=True gnb.all.rlc_enable=True" needs: - job: "basic relwithdeb" artifacts: true @@ -254,11 +256,24 @@ amari 1UE 4x4 mimo: - *txrx-lib - *retina-needs +amari 4UE deb: + extends: .zmq + variables: + TESTBED: "zmq_deb" + MARKERS: "smoke" + E2E_LOG_LEVEL: "info" + needs: + - job: "basic package" + artifacts: true + - *txrx-lib + - *retina-needs + amari 32UE: extends: .zmq variables: MARKERS: "zmq and not smoke" E2E_LOG_LEVEL: "info" + RETINA_ARGS: "gnb.all.pcap=True gnb.all.rlc_enable=False" needs: - job: "basic relwithdeb" artifacts: true @@ -266,7 +281,15 @@ amari 32UE: - *retina-needs parallel: matrix: - - KEYWORDS: ["attach_detach", "ping", "iperf and udp", "iperf and tcp"] + - KEYWORDS: + [ + "attach_detach", + "ping", + "iperf and udp and band:3", + "iperf and udp and not band:3", + "iperf and tcp and band:3", + "iperf and tcp and not band:3", + ] amari 32UE [reestablishment]: extends: .zmq @@ -274,6 +297,7 @@ amari 32UE [reestablishment]: MARKERS: "zmq and not smoke" E2E_LOG_LEVEL: "info" KEYWORDS: reestablishment + RETINA_ARGS: "gnb.all.pcap=True gnb.all.mac_enable=True gnb.all.rlc_enable=False" allow_failure: true needs: - job: "basic relwithdeb" @@ -431,8 +455,6 @@ test mode ru memcheck: amari b200 asan: extends: .rf - when: manual - allow_failure: true variables: MARKERS: "rf_not_crash" needs: @@ -442,15 +464,11 @@ amari b200 asan: validate b200 config: extends: .rf - when: manual - allow_failure: true variables: MARKERS: "rf_b200" validate n300 config: extends: .rf - when: manual - allow_failure: true variables: MARKERS: "rf_n300" TESTBED: "android_n300" @@ -476,8 +494,6 @@ android b200: android x300: stage: rf extends: .e2e-run - when: manual - allow_failure: true variables: GROUP: "rf" TESTBED: "android_x300" diff --git a/.gitlab/ci/e2e/.env b/.gitlab/ci/e2e/.env index 17c0aa238c..fa24f2da95 100644 --- a/.gitlab/ci/e2e/.env +++ b/.gitlab/ci/e2e/.env @@ -1,9 +1,12 @@ SRSGNB_REGISTRY_URI=registry.gitlab.com/softwareradiosystems/srsgnb RETINA_REGISTRY_PREFIX=registry.gitlab.com/softwareradiosystems/ci/retina -RETINA_VERSION=0.43.0 -AMARISOFT_VERSION=2023-03-17 +RETINA_VERSION=0.44.2 +UBUNTU_VERSION=24.04 +AMARISOFT_VERSION=2023-09-08 SRSUE_VERSION=23.11 -OPEN5GS_VERSION=2.6.1 +OPEN5GS_VERSION=2.6.6 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -METRICS_SERVER_VERSION=1.5.2 +METRICS_SERVER_VERSION=1.7.0 DPDK_VERSION=23.11 +ZMQ_HOSTLABEL_0=kubernetes.io/hostname=k8s-worker-vm2 +ZMQ_HOSTLABEL_1=kubernetes.io/hostname=k8s-worker-vm2 diff --git a/.gitlab/ci/e2e/retina_request_test_mode.yml b/.gitlab/ci/e2e/retina_request_test_mode.yml index ffa7047339..0b16ca52c0 100644 --- a/.gitlab/ci/e2e/retina_request_test_mode.yml +++ b/.gitlab/ci/e2e/retina_request_test_mode.yml @@ -9,6 +9,8 @@ - name: srs-gnb type: gnb image: ${RETINA_REGISTRY_PREFIX}/srsgnb:${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_0} requirements: arch: amd64 cpu: @@ -41,3 +43,5 @@ requests: "6G" limits: "6G" image: ${RETINA_REGISTRY_PREFIX}/open5gs:${OPEN5GS_VERSION}_${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_0} diff --git a/.gitlab/ci/e2e/retina_request_zmq.yml b/.gitlab/ci/e2e/retina_request_zmq.yml index 3eca2f3699..12b5fea347 100644 --- a/.gitlab/ci/e2e/retina_request_zmq.yml +++ b/.gitlab/ci/e2e/retina_request_zmq.yml @@ -9,6 +9,8 @@ - name: amarisoft-ue type: ue image: ${RETINA_REGISTRY_PREFIX}/amarisoftue:${AMARISOFT_VERSION}_${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} nof_ports: 32 requirements: arch: amd64 @@ -31,6 +33,8 @@ - name: srs-gnb type: gnb image: ${RETINA_REGISTRY_PREFIX}/srsgnb:${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} requirements: arch: amd64 cpu: @@ -62,3 +66,5 @@ requests: "6G" limits: "6G" image: ${RETINA_REGISTRY_PREFIX}/open5gs:${OPEN5GS_VERSION}_${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} diff --git a/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml b/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml index b9ae6f43d3..1602839879 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml @@ -9,6 +9,8 @@ - name: amarisoft-ue type: ue image: ${RETINA_REGISTRY_PREFIX}/amarisoftue:${AMARISOFT_VERSION}_${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} requirements: arch: amd64 cpu: @@ -31,6 +33,8 @@ - name: srs-gnb type: gnb image: ${RETINA_REGISTRY_PREFIX}/srsgnb:${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} requirements: arch: amd64 cpu: @@ -63,3 +67,5 @@ requests: "6G" limits: "6G" image: ${RETINA_REGISTRY_PREFIX}/open5gs:${OPEN5GS_VERSION}_${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} diff --git a/.gitlab/ci/e2e/retina_request_zmq_deb.yml b/.gitlab/ci/e2e/retina_request_zmq_deb.yml new file mode 100644 index 0000000000..f7bc886105 --- /dev/null +++ b/.gitlab/ci/e2e/retina_request_zmq_deb.yml @@ -0,0 +1,68 @@ +# +# Copyright 2013-2024 Software Radio Systems Limited +# +# By using this file, you agree to the terms and conditions set +# forth in the LICENSE file which can be found at the top level of +# the distribution. +# + +- name: amarisoft-ue + type: ue + image: ${RETINA_REGISTRY_PREFIX}/amarisoftue:${AMARISOFT_VERSION}_${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} + nof_ports: 4 + requirements: + arch: amd64 + cpu: + requests: 1 + memory: + requests: "8G" + ephemeral-storage: + requests: "6G" + limits: "6G" + resources: + - type: zmq + - type: license + model: amarisoft-5g + shared_files: + - local_path: ../../build_trx_srsran/libtrx_srsran.so + remote_path: /opt/lteue/trx_srsran.so + is_executable: true + +- name: srs-gnb + type: gnb + image: ${RETINA_REGISTRY_PREFIX}/agent-ubuntu-${UBUNTU_VERSION}:${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} + requirements: + arch: amd64 + cpu: + requests: 1 + memory: + requests: "8G" + limits: "8G" + ephemeral-storage: + requests: "6G" + limits: "6G" + resources: + - type: zmq + shared_files: + - local_path: ../../srsran-project_99.9-0ubuntu1ppa1~24.04_amd64.deb + remote_path: /tmp/retina-libs/srsran-project.deb + is_executable: false + +- name: open5gs + type: 5gc + requirements: + arch: amd64 + cpu: + requests: 1 + memory: + requests: "4G" + ephemeral-storage: + requests: "6G" + limits: "6G" + image: ${RETINA_REGISTRY_PREFIX}/open5gs:${OPEN5GS_VERSION}_${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} diff --git a/.gitlab/ci/e2e/retina_request_zmq_single_ue.yml b/.gitlab/ci/e2e/retina_request_zmq_single_ue.yml index 9ce41af239..4f6dd88928 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_single_ue.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_single_ue.yml @@ -9,6 +9,8 @@ - name: amarisoft-ue type: ue image: ${RETINA_REGISTRY_PREFIX}/amarisoftue:${AMARISOFT_VERSION}_${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} requirements: arch: amd64 cpu: @@ -30,6 +32,8 @@ - name: srs-gnb type: gnb image: ${RETINA_REGISTRY_PREFIX}/srsgnb:${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} requirements: arch: amd64 cpu: @@ -61,3 +65,5 @@ requests: "6G" limits: "6G" image: ${RETINA_REGISTRY_PREFIX}/open5gs:${OPEN5GS_VERSION}_${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} diff --git a/.gitlab/ci/e2e/retina_request_zmq_srsue.yml b/.gitlab/ci/e2e/retina_request_zmq_srsue.yml index 640663ea18..5c78097cef 100644 --- a/.gitlab/ci/e2e/retina_request_zmq_srsue.yml +++ b/.gitlab/ci/e2e/retina_request_zmq_srsue.yml @@ -9,6 +9,8 @@ - name: srs-ue type: ue image: ${RETINA_REGISTRY_PREFIX}/srsue:${SRSUE_VERSION}_${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} requirements: arch: amd64 cpu: @@ -22,6 +24,8 @@ - name: srs-gnb type: gnb image: ${RETINA_REGISTRY_PREFIX}/srsgnb:${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} requirements: arch: amd64 cpu: @@ -53,3 +57,5 @@ requests: "6G" limits: "6G" image: ${RETINA_REGISTRY_PREFIX}/open5gs:${OPEN5GS_VERSION}_${RETINA_VERSION} + labels: + - ${ZMQ_HOSTLABEL_1} diff --git a/.gitlab/ci/schedules.yml b/.gitlab/ci/schedules.yml index 90c605717c..ada7e26243 100644 --- a/.gitlab/ci/schedules.yml +++ b/.gitlab/ci/schedules.yml @@ -35,7 +35,7 @@ Nightly Build Unit Tests: value: "#ci_gnb" Nightly E2E Tests: - cron: "15 23 * * 0-5" + cron: "00 23 * * 0-5" cron_timezone: "Europe/Madrid" ref: dev variables: diff --git a/.gitlab/ci/trx.yml b/.gitlab/ci/trx.yml index 5fe4d53036..572387c292 100644 --- a/.gitlab/ci/trx.yml +++ b/.gitlab/ci/trx.yml @@ -19,7 +19,7 @@ build trx driver: extends: .build_and_unit variables: AMARISOFT_PACKAGE_REGISTRY: ${CI_API_V4_URL}/projects/44296988/packages/generic/amarisoft - AMARISOFT_VERSION: "2023-03-17" + AMARISOFT_VERSION: "2023-09-08" KUBERNETES_CPU_REQUEST: 2 KUBERNETES_CPU_LIMIT: 2 KUBERNETES_MEMORY_REQUEST: 2.5Gi diff --git a/CMakeLists.txt b/CMakeLists.txt index cb4de50f1f..a0b3087240 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,7 @@ option(ENABLE_UHD "Enable UHD" ON) option(ENABLE_ZEROMQ "Enable ZeroMQ" OFF) option(ENABLE_FFTW "Enable FFTW" ON) option(ENABLE_DPDK "Enable DPDK" OFF) +option(ENABLE_LIBNUMA "Enable LibNUMA" OFF) option(ENABLE_EXPORT "Enable PIC and export libraries" OFF) option(ENABLE_TRX_DRIVER "Enable Amarisoft TRX driver library" OFF) option(AUTO_DETECT_ISA "Enable automatic ISA detection" ON) @@ -263,6 +264,14 @@ else (ENABLE_ZEROMQ) unset(ZEROMQ_FOUND CACHE) endif (ENABLE_ZEROMQ) +if (ENABLE_LIBNUMA) + find_package(NUMA) + if (NUMA_FOUND) + include_directories(${NUMA_INCLUDE_DIRS}) + add_definitions(-DNUMA_SUPPORT) + endif (NUMA_FOUND) +endif (ENABLE_LIBNUMA) + # DPDK if (ENABLE_DPDK) set(DPDK_MIN_VERSION "22.11") diff --git a/apps/examples/du/du_example.cpp b/apps/examples/du/du_example.cpp index 2887442b00..06c0f7663e 100644 --- a/apps/examples/du/du_example.cpp +++ b/apps/examples/du/du_example.cpp @@ -259,7 +259,10 @@ class dummy_cu_cp_handler : public f1c_connection_client // Store the packed RRC setup message in the RRC container field of the F1 DL RRC Message that is sent to the // DU. - resp->rrc_container.resize(msg4_pdu.length()); + if (!resp->rrc_container.resize(msg4_pdu.length())) { + du_logger.warning("Unable to resize RRC PDU to {} bytes", msg4_pdu.length()); + return; + } std::copy(msg4_pdu.begin(), msg4_pdu.end(), resp->rrc_container.begin()); } else if (msg.pdu.init_msg().value.type().value == asn1::f1ap::f1ap_elem_procs_o::init_msg_c::types_opts::f1_setup_request) { diff --git a/apps/gnb/adapters/f1c_gateway_local_connector.cpp b/apps/gnb/adapters/f1c_gateway_local_connector.cpp index 5806b23a32..368fcd7cb0 100644 --- a/apps/gnb/adapters/f1c_gateway_local_connector.cpp +++ b/apps/gnb/adapters/f1c_gateway_local_connector.cpp @@ -42,8 +42,6 @@ class f1ap_pdu_pcap_notifier final : public f1ap_message_notifier void on_new_message(const f1ap_message& msg) override { - logger.debug("Received a PDU of type {}", msg.pdu.type().to_string()); - if (pcap_writer.is_write_enabled()) { byte_buffer buf; asn1::bit_ref bref(buf); diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp index e70f9de759..538d7d4620 100644 --- a/apps/gnb/gnb.cpp +++ b/apps/gnb/gnb.cpp @@ -333,6 +333,9 @@ int main(int argc, char** argv) // Log build info gnb_logger.info("Built in {} mode using {}", get_build_mode(), get_build_info()); + // Log CPU architecture. + cpu_architecture_info::get().print_cpu_info(gnb_logger); + // Check and log included CPU features and check support by current CPU if (cpu_supports_included_features()) { gnb_logger.debug("Required CPU features: {}", get_cpu_feature_info()); @@ -518,6 +521,9 @@ int main(int argc, char** argv) variant_get(ru_cfg.config), gnb_cfg.log_cfg, workers, ru_ul_adapt, ru_timing_adapt); ru_object = create_generic_ru(variant_get(ru_cfg.config)); + + // Set the generic RU controller for the GNB console. + console.set_ru_controller(ru_object->get_controller()); } else { ru_dummy_dependencies ru_dependencies; configure_ru_dummy_executors_and_notifiers(variant_get(ru_cfg.config), diff --git a/apps/gnb/gnb_appconfig.h b/apps/gnb/gnb_appconfig.h index 0bef56adf3..833a3b0b22 100644 --- a/apps/gnb/gnb_appconfig.h +++ b/apps/gnb/gnb_appconfig.h @@ -44,6 +44,7 @@ #include "srsran/ran/sib/system_info_config.h" #include "srsran/ran/slot_pdu_capacity_constants.h" #include "srsran/ran/subcarrier_spacing.h" +#include "srsran/support/cpu_architecture_info.h" #include "srsran/support/unique_thread.h" #include "srsran/support/units.h" #include @@ -729,10 +730,10 @@ struct security_appconfig { }; struct cu_cp_appconfig { - uint16_t max_nof_dus = 6; - uint16_t max_nof_cu_ups = 6; - int inactivity_timer = 7200; // in seconds - unsigned ue_context_setup_timeout_s = 3; // in seconds (must be larger than T310) + uint16_t max_nof_dus = 6; + uint16_t max_nof_cu_ups = 6; + int inactivity_timer = 5; // in seconds + unsigned pdu_session_setup_timeout = 3; // in seconds (must be larger than T310) mobility_appconfig mobility_config; rrc_appconfig rrc_config; security_appconfig security_config; @@ -933,6 +934,17 @@ struct ru_sdr_expert_appconfig { /// \note Powering up the transmitter ahead of time requires starting the transmission earlier, and reduces the time /// window for the radio to transmit the provided samples. float power_ramping_time_us = 0.0F; + /// \brief Lower PHY downlink baseband buffer size policy. + /// + /// Selects the size policy of the baseband buffers that pass DL samples from the lower PHY to the radio. + /// Available options: + /// - auto: the size policy is automatically selected based on the SDR front-end. + /// - single-packet: the buffer size matches the optimal buffer size indicated by the SDR front-end. + /// - half-slot: the buffer size matches the number of samples per half-slot. + /// - slot: the buffer size matches the number of samples per slot. + /// - optimal-slot: the buffer size is equal to the greatest multiple of the optimal buffer size indicated by the + /// SDR front-end that results in a buffer size smaller than the number of samples per slot. + std::string dl_buffer_size_policy = "auto"; }; /// gNB app SDR Radio Unit configuration. @@ -981,21 +993,21 @@ struct ru_ofh_base_cell_appconfig { /// Set this option when the operating bandwidth of the RU is larger than the configured bandwidth of the cell. optional ru_operating_bw; /// T1a maximum parameter for downlink Control-Plane in microseconds. - unsigned T1a_max_cp_dl = 500U; + std::chrono::microseconds T1a_max_cp_dl{500}; /// T1a minimum parameter for downlink Control-Plane in microseconds. - unsigned T1a_min_cp_dl = 258U; + std::chrono::microseconds T1a_min_cp_dl{258}; /// T1a maximum parameter for uplink Control-Plane in microseconds. - unsigned T1a_max_cp_ul = 500U; + std::chrono::microseconds T1a_max_cp_ul{500}; /// T1a minimum parameter for uplink Control-Plane in microseconds. - unsigned T1a_min_cp_ul = 285U; + std::chrono::microseconds T1a_min_cp_ul{285}; /// T1a maximum parameter for downlink User-Plane in microseconds. - unsigned T1a_max_up = 300U; + std::chrono::microseconds T1a_max_up{300}; /// T1a minimum parameter for downlink User-Plane in microseconds. - unsigned T1a_min_up = 85U; + std::chrono::microseconds T1a_min_up{85}; /// Ta4 maximum parameter for uplink User-Plane in microseconds. - unsigned Ta4_max = 300U; + std::chrono::microseconds Ta4_max{300}; /// Ta4 minimum parameter for uplink User-Plane in microseconds. - unsigned Ta4_min = 85U; + std::chrono::microseconds Ta4_min{85}; /// Enables the Control-Plane PRACH message signalling. bool is_prach_control_plane_enabled = true; /// \brief Downlink broadcast flag. @@ -1159,7 +1171,7 @@ struct ofh_threads_appconfig { struct expert_threads_appconfig { expert_threads_appconfig() { - unsigned nof_threads = compute_host_nof_hardware_threads(); + unsigned nof_threads = cpu_architecture_info::get().get_host_nof_available_cpus(); if (nof_threads < 4) { upper_threads.nof_ul_threads = 1; diff --git a/apps/gnb/gnb_appconfig_cli11_schema.cpp b/apps/gnb/gnb_appconfig_cli11_schema.cpp index 9352981081..299e45d2a7 100644 --- a/apps/gnb/gnb_appconfig_cli11_schema.cpp +++ b/apps/gnb/gnb_appconfig_cli11_schema.cpp @@ -434,11 +434,10 @@ static void configure_cli11_cu_cp_args(CLI::App& app, cu_cp_appconfig& cu_cp_par ->capture_default_str() ->check(CLI::Range(1, 7200)); - app.add_option( - "--ue_context_setup_timeout_s", - cu_cp_params.ue_context_setup_timeout_s, - "Timeout for the reception of an InitialContextSetupRequest after an InitialUeMessage was sent to the " - "core, in seconds. The timeout must be larger than T310. If the value is reached, the UE will be released") + app.add_option("--pdu_session_setup_timeout", + cu_cp_params.pdu_session_setup_timeout, + "Timeout for the setup of a PDU session after an InitialUeMessage was sent to the core, in " + "seconds. The timeout must be larger than T310. If the value is reached, the UE will be released") ->capture_default_str(); CLI::App* mobility_subcmd = app.add_subcommand("mobility", "Mobility configuration"); @@ -974,6 +973,14 @@ static void configure_cli11_si_sched_info(CLI::App& app, sib_appconfig::si_sched ->check(CLI::IsMember({2, 19})); } +static void configure_cli11_epoch_time(CLI::App& app, epoch_time_t& epoch_time) +{ + app.add_option("--sfn", epoch_time.sfn, "SFN Part")->capture_default_str()->check(CLI::Range(0, 1023)); + app.add_option("--subframe_number", epoch_time.subframe_number, "Sub-frame number Part") + ->capture_default_str() + ->check(CLI::Range(0, 9)); +} + static void configure_cli11_ephemeris_info_ecef(CLI::App& app, ecef_coordinates_t& ephemeris_info) { app.add_option("--pos_x", ephemeris_info.position_x, "X Position of the satellite") @@ -1010,6 +1017,7 @@ static void configure_cli11_ephemeris_info_orbital(CLI::App& app, orbital_coordi } static void configure_cli11_ntn_args(CLI::App& app, optional& ntn, + epoch_time_t& epoch_time, orbital_coordinates_t& orbital_coordinates, ecef_coordinates_t& ecef_coordinates) { @@ -1021,6 +1029,10 @@ static void configure_cli11_ntn_args(CLI::App& app, ntn.value().ta_info.emplace(); app.add_option("--ta_common", ntn->ta_info->ta_common, "TA common offset"); + // epoch time. + CLI::App* epoch_time_subcmd = app.add_subcommand("epoch_time", "Epoch time for the NTN assistance information"); + configure_cli11_epoch_time(*epoch_time_subcmd, epoch_time); + // ephemeris configuration. CLI::App* ephem_subcmd_ecef = app.add_subcommand("ephemeris_info_ecef", "ephermeris information of the satellite in ecef coordinates"); @@ -1665,6 +1677,14 @@ static void configure_cli11_test_mode_args(CLI::App& app, test_mode_appconfig& t static void configure_cli11_ru_sdr_expert_args(CLI::App& app, ru_sdr_expert_appconfig& config) { + auto buffer_size_policy_check = [](const std::string& value) -> std::string { + if (value == "auto" || value == "single-packet" || value == "half-slot" || value == "slot" || + value == "optimal-slot") { + return {}; + } + return "Invalid DL buffer size policy. Accepted values [auto,single-packet,half-slot,slot,optimal-slot]"; + }; + app.add_option("--low_phy_dl_throttling", config.lphy_dl_throttling, "Throttles the lower PHY DL baseband generation. The range is (0, 1). Set it to zero to disable it.") @@ -1678,6 +1698,12 @@ static void configure_cli11_ru_sdr_expert_args(CLI::App& app, ru_sdr_expert_appc "Specifies the power ramping time in microseconds, it proactively initiates the transmission and " "mitigates transient effects.") ->capture_default_str(); + app.add_option( + "--dl_buffer_size_policy", + config.dl_buffer_size_policy, + "Selects the size policy of the baseband buffers that pass DL samples from the lower PHY to the radio.") + ->capture_default_str() + ->check(buffer_size_policy_check); } static void configure_cli11_ru_sdr_args(CLI::App& app, ru_sdr_appconfig& config) @@ -2311,13 +2337,19 @@ static void manage_hal_optional(CLI::App& app, gnb_appconfig& gnb_cfg) static void manage_ntn_optional(CLI::App& app, gnb_appconfig& gnb_cfg, + epoch_time_t& epoch_time, orbital_coordinates_t orbital_coordinates, ecef_coordinates_t ecef_coordinates) { auto ntn_app = app.get_subcommand_ptr("ntn"); + unsigned nof_epoch_entries = ntn_app->get_subcommand("epoch_time")->count_all(); unsigned nof_ecef_entries = ntn_app->get_subcommand("ephemeris_info_ecef")->count_all(); unsigned nof_orbital_entries = ntn_app->get_subcommand("ephemeris_orbital")->count_all(); + if (nof_epoch_entries) { + gnb_cfg.ntn_cfg.value().epoch_time = epoch_time; + } + if (nof_ecef_entries) { gnb_cfg.ntn_cfg.value().ephemeris_info = ecef_coordinates; } else if (nof_orbital_entries) { @@ -2426,9 +2458,10 @@ void srsran::configure_cli11_with_gnb_appconfig_schema(CLI::App& app, gnb_parsed // NTN section. CLI::App* ntn_subcmd = app.add_subcommand("ntn", "NTN parameters")->configurable(); + static epoch_time_t epoch_time; static ecef_coordinates_t ecef_coordinates; static orbital_coordinates_t orbital_coordinates; - configure_cli11_ntn_args(*ntn_subcmd, gnb_cfg.ntn_cfg, orbital_coordinates, ecef_coordinates); + configure_cli11_ntn_args(*ntn_subcmd, gnb_cfg.ntn_cfg, epoch_time, orbital_coordinates, ecef_coordinates); // NOTE: CLI11 needs that the life of the variable lasts longer than the call of this function. As both options need // to be added and a variant is used to store the Radio Unit configuration, the configuration is parsed in a helper @@ -2562,7 +2595,7 @@ void srsran::configure_cli11_with_gnb_appconfig_schema(CLI::App& app, gnb_parsed app.callback([&]() { manage_ru_variant(app, gnb_cfg, sdr_cfg, ofh_cfg, dummy_cfg); manage_hal_optional(app, gnb_cfg); - manage_ntn_optional(app, gnb_cfg, orbital_coordinates, ecef_coordinates); + manage_ntn_optional(app, gnb_cfg, epoch_time, orbital_coordinates, ecef_coordinates); manage_processing_delay(app, gnb_cfg); manage_expert_execution_threads(app, gnb_cfg); }); diff --git a/apps/gnb/gnb_appconfig_translators.cpp b/apps/gnb/gnb_appconfig_translators.cpp index 0061029ae7..64f4e520c7 100644 --- a/apps/gnb/gnb_appconfig_translators.cpp +++ b/apps/gnb/gnb_appconfig_translators.cpp @@ -141,9 +141,9 @@ srs_cu_cp::cu_cp_configuration srsran::generate_cu_cp_config(const gnb_appconfig config.cu_cp_cfg.security_config.confidentiality_protection); } - out_cfg.ue_config.inactivity_timer = std::chrono::seconds{config.cu_cp_cfg.inactivity_timer}; - out_cfg.ue_config.max_nof_supported_ues = config.cu_cp_cfg.max_nof_dus * srsran::srs_cu_cp::MAX_NOF_UES_PER_DU; - out_cfg.ngap_config.ue_context_setup_timeout = std::chrono::seconds{config.cu_cp_cfg.ue_context_setup_timeout_s}; + out_cfg.ue_config.inactivity_timer = std::chrono::seconds{config.cu_cp_cfg.inactivity_timer}; + out_cfg.ue_config.max_nof_supported_ues = config.cu_cp_cfg.max_nof_dus * srsran::srs_cu_cp::MAX_NOF_UES_PER_DU; + out_cfg.ngap_config.pdu_session_setup_timeout = std::chrono::seconds{config.cu_cp_cfg.pdu_session_setup_timeout}; out_cfg.statistics_report_period = std::chrono::seconds{config.metrics_cfg.cu_cp_statistics_report_period}; out_cfg.mobility_config.mobility_manager_config.trigger_handover_from_measurements = @@ -444,7 +444,11 @@ static sib19_info create_sib19_info(const gnb_appconfig& config) variant_get(sib19.ephemeris_info.value()).position_x /= 1.3; variant_get(sib19.ephemeris_info.value()).position_y /= 1.3; variant_get(sib19.ephemeris_info.value()).position_z /= 1.3; + variant_get(sib19.ephemeris_info.value()).velocity_vx /= 0.06; + variant_get(sib19.ephemeris_info.value()).velocity_vy /= 0.06; + variant_get(sib19.ephemeris_info.value()).velocity_vz /= 0.06; } else if (variant_holds_alternative(sib19.ephemeris_info.value())) { + variant_get(sib19.ephemeris_info.value()).semi_major_axis -= 6500000; variant_get(sib19.ephemeris_info.value()).semi_major_axis /= 0.004249; variant_get(sib19.ephemeris_info.value()).eccentricity /= 0.00000001431; variant_get(sib19.ephemeris_info.value()).periapsis /= 0.00000002341; @@ -1229,21 +1233,35 @@ static void generate_low_phy_config(lower_phy_configuration& out_cfg, out_cfg.time_alignment_calibration = 0; } - // Select buffer size policy. + // Select RX buffer size policy. if (ru_cfg.device_driver == "zmq") { - out_cfg.baseband_tx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::half_slot; out_cfg.baseband_rx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::half_slot; } else if (low_phy_threads_cfg.execution_profile == lower_phy_thread_profile::single) { // For single executor, the same executor processes uplink and downlink. In this case, the processing is blocked // by the signal reception. The buffers must be smaller than a slot duration considering the downlink baseband // samples must arrive to the baseband device before the transmission time passes. - out_cfg.baseband_tx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::single_packet; out_cfg.baseband_rx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::single_packet; } else { - out_cfg.baseband_tx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::slot; out_cfg.baseband_rx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::single_packet; } + // Select TX buffer size policy. + if (ru_cfg.expert_cfg.dl_buffer_size_policy == "auto") { + if (ru_cfg.device_driver == "zmq") { + out_cfg.baseband_tx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::half_slot; + } else if (low_phy_threads_cfg.execution_profile == lower_phy_thread_profile::single) { + // For single executor, the same executor processes uplink and downlink. In this case, the processing is blocked + // by the signal reception. The buffers must be smaller than a slot duration considering the downlink baseband + // samples must arrive to the baseband device before the transmission time passes. + out_cfg.baseband_tx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::single_packet; + } else { + out_cfg.baseband_tx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::slot; + } + } else { + // Manual selection of the TX buffer size policy. + out_cfg.baseband_tx_buffer_size_policy = to_buffer_size_policy(ru_cfg.expert_cfg.dl_buffer_size_policy); + } + // Get lower PHY system time throttling. out_cfg.system_time_throttling = ru_cfg.expert_cfg.lphy_dl_throttling; @@ -1447,6 +1465,40 @@ static bool parse_mac_address(const std::string& mac_str, span mac) return true; } +/// Converts reception window timing parameters from microseconds to number of symbols given the symbol duration. +static ofh::rx_window_timing_parameters +rx_timing_window_params_us_to_symbols(std::chrono::microseconds Ta4_max, + std::chrono::microseconds Ta4_min, + std::chrono::duration symbol_duration) +{ + ofh::rx_window_timing_parameters rx_window_timing_params; + rx_window_timing_params.sym_start = std::ceil(Ta4_min / symbol_duration); + rx_window_timing_params.sym_end = std::ceil(Ta4_max / symbol_duration); + + return rx_window_timing_params; +} + +/// Converts transmission window timing parameters from microseconds to number of symbols given the symbol duration. +static ofh::tx_window_timing_parameters +tx_timing_window_params_us_to_symbols(std::chrono::microseconds T1a_max_cp_dl, + std::chrono::microseconds T1a_min_cp_dl, + std::chrono::microseconds T1a_max_cp_ul, + std::chrono::microseconds T1a_min_cp_ul, + std::chrono::microseconds T1a_max_up, + std::chrono::microseconds T1a_min_up, + std::chrono::duration symbol_duration) +{ + ofh::tx_window_timing_parameters tx_window_timing_params; + tx_window_timing_params.sym_cp_dl_start = std::floor(T1a_max_cp_dl / symbol_duration); + tx_window_timing_params.sym_cp_dl_end = std::ceil(T1a_min_cp_dl / symbol_duration); + tx_window_timing_params.sym_cp_ul_start = std::floor(T1a_max_cp_ul / symbol_duration); + tx_window_timing_params.sym_cp_ul_end = std::ceil(T1a_min_cp_ul / symbol_duration); + tx_window_timing_params.sym_up_dl_start = std::floor(T1a_max_up / symbol_duration); + tx_window_timing_params.sym_up_dl_end = std::ceil(T1a_min_up / symbol_duration); + + return tx_window_timing_params; +} + static void generate_ru_ofh_config(ru_ofh_configuration& out_cfg, const gnb_appconfig& config, span du_cells) { @@ -1480,6 +1532,9 @@ generate_ru_ofh_config(ru_ofh_configuration& out_cfg, const gnb_appconfig& confi srsran_terminate("Invalid Radio Unit MAC address"); } + std::chrono::duration symbol_duration( + (1e6 / (get_nsymb_per_slot(cyclic_prefix::NORMAL) * get_nof_slots_per_subframe(cell.common_scs)))); + sector_cfg.cp = cyclic_prefix::NORMAL; sector_cfg.scs = cell.common_scs; sector_cfg.bw = cell.channel_bw_mhz; @@ -1487,26 +1542,27 @@ generate_ru_ofh_config(ru_ofh_configuration& out_cfg, const gnb_appconfig& confi sector_cfg.ru_operating_bw = cell_cfg.cell.ru_operating_bw; sector_cfg.is_uplink_static_comp_hdr_enabled = cell_cfg.cell.is_uplink_static_comp_hdr_enabled; sector_cfg.is_downlink_static_comp_hdr_enabled = cell_cfg.cell.is_downlink_static_comp_hdr_enabled; - sector_cfg.tx_window_timing_params = {std::chrono::microseconds(cell_cfg.cell.T1a_max_cp_dl), - std::chrono::microseconds(cell_cfg.cell.T1a_min_cp_dl), - std::chrono::microseconds(cell_cfg.cell.T1a_max_cp_ul), - std::chrono::microseconds(cell_cfg.cell.T1a_min_cp_ul), - std::chrono::microseconds(cell_cfg.cell.T1a_max_up), - std::chrono::microseconds(cell_cfg.cell.T1a_min_up)}; - sector_cfg.rx_window_timing_params = {std::chrono::microseconds(cell_cfg.cell.Ta4_max), - std::chrono::microseconds(cell_cfg.cell.Ta4_min)}; - sector_cfg.is_prach_control_plane_enabled = cell_cfg.cell.is_prach_control_plane_enabled; - sector_cfg.is_downlink_broadcast_enabled = cell_cfg.cell.is_downlink_broadcast_enabled; - sector_cfg.ignore_ecpri_payload_size_field = cell_cfg.cell.ignore_ecpri_payload_size_field; - sector_cfg.ignore_ecpri_seq_id_field = cell_cfg.cell.ignore_ecpri_seq_id_field; - sector_cfg.warn_unreceived_ru_frames = cell_cfg.cell.warn_unreceived_ru_frames; - sector_cfg.ul_compression_params = {ofh::to_compression_type(cell_cfg.cell.compression_method_ul), - cell_cfg.cell.compression_bitwidth_ul}; - sector_cfg.dl_compression_params = {ofh::to_compression_type(cell_cfg.cell.compression_method_dl), - cell_cfg.cell.compression_bitwidth_dl}; - sector_cfg.prach_compression_params = {ofh::to_compression_type(cell_cfg.cell.compression_method_prach), - cell_cfg.cell.compression_bitwidth_prach}; - sector_cfg.iq_scaling = cell_cfg.cell.iq_scaling; + sector_cfg.tx_window_timing_params = tx_timing_window_params_us_to_symbols(cell_cfg.cell.T1a_max_cp_dl, + cell_cfg.cell.T1a_min_cp_dl, + cell_cfg.cell.T1a_max_cp_ul, + cell_cfg.cell.T1a_min_cp_ul, + cell_cfg.cell.T1a_max_up, + cell_cfg.cell.T1a_min_up, + symbol_duration); + sector_cfg.rx_window_timing_params = + rx_timing_window_params_us_to_symbols(cell_cfg.cell.Ta4_max, cell_cfg.cell.Ta4_min, symbol_duration); + sector_cfg.is_prach_control_plane_enabled = cell_cfg.cell.is_prach_control_plane_enabled; + sector_cfg.is_downlink_broadcast_enabled = cell_cfg.cell.is_downlink_broadcast_enabled; + sector_cfg.ignore_ecpri_payload_size_field = cell_cfg.cell.ignore_ecpri_payload_size_field; + sector_cfg.ignore_ecpri_seq_id_field = cell_cfg.cell.ignore_ecpri_seq_id_field; + sector_cfg.warn_unreceived_ru_frames = cell_cfg.cell.warn_unreceived_ru_frames; + sector_cfg.ul_compression_params = {ofh::to_compression_type(cell_cfg.cell.compression_method_ul), + cell_cfg.cell.compression_bitwidth_ul}; + sector_cfg.dl_compression_params = {ofh::to_compression_type(cell_cfg.cell.compression_method_dl), + cell_cfg.cell.compression_bitwidth_dl}; + sector_cfg.prach_compression_params = {ofh::to_compression_type(cell_cfg.cell.compression_method_prach), + cell_cfg.cell.compression_bitwidth_prach}; + sector_cfg.iq_scaling = cell_cfg.cell.iq_scaling; sector_cfg.tci = cell_cfg.vlan_tag; sector_cfg.prach_eaxc.assign(cell_cfg.ru_prach_port_id.begin(), cell_cfg.ru_prach_port_id.end()); diff --git a/apps/gnb/gnb_appconfig_validators.cpp b/apps/gnb/gnb_appconfig_validators.cpp index f40a862dd3..7835ee32a5 100644 --- a/apps/gnb/gnb_appconfig_validators.cpp +++ b/apps/gnb/gnb_appconfig_validators.cpp @@ -141,6 +141,13 @@ template return std::unique(temp_ports.begin(), temp_ports.end()) == temp_ports.end(); } +static bool validate_transmission_window(std::chrono::duration symbol_duration, + std::chrono::microseconds window_start, + std::chrono::microseconds window_end) +{ + return ((window_end - window_start) > symbol_duration); +} + /// Validates the given Open Fronthaul Radio Unit application configuration. Returns true on success, otherwise /// false. static bool validate_ru_ofh_appconfig(const gnb_appconfig& config) @@ -151,6 +158,30 @@ static bool validate_ru_ofh_appconfig(const gnb_appconfig& config) const ru_ofh_cell_appconfig& ofh_cell = ofh_cfg.cells[i]; const base_cell_appconfig& cell_cfg = config.cells_cfg[i].cell; + const std::chrono::duration symbol_duration( + (1e3 / (get_nsymb_per_slot(cyclic_prefix::NORMAL) * get_nof_slots_per_subframe(cell_cfg.common_scs)))); + + if (!validate_transmission_window(symbol_duration, ofh_cell.cell.T1a_min_cp_dl, ofh_cell.cell.T1a_max_cp_dl)) { + fmt::print("Transmission timing window length for DL Control-Plane must be bigger than the symbol duration " + "({:.2f}us).\n", + symbol_duration.count()); + return false; + } + + if (!validate_transmission_window(symbol_duration, ofh_cell.cell.T1a_min_cp_ul, ofh_cell.cell.T1a_max_cp_ul)) { + fmt::print("Transmission timing window length for UL Control-Plane must be bigger than the symbol duration " + "({:.2f}us).\n", + symbol_duration.count()); + return false; + } + + if (!validate_transmission_window(symbol_duration, ofh_cell.cell.T1a_min_up, ofh_cell.cell.T1a_max_up)) { + fmt::print( + "Transmission timing window length for DL User-Plane must be bigger than the symbol duration ({:.2f}us).\n", + symbol_duration.count()); + return false; + } + if (!ofh_cell.cell.is_downlink_broadcast_enabled && cell_cfg.nof_antennas_dl != ofh_cell.ru_dl_port_id.size()) { fmt::print("RU number of downlink ports={} must match the number of transmission antennas={}\n", ofh_cell.ru_dl_port_id.size(), @@ -622,10 +653,10 @@ static bool validate_amf_appconfig(const amf_appconfig& config) /// Validates the given CU-CP configuration. Returns true on success, otherwise false. static bool validate_cu_cp_appconfig(const cu_cp_appconfig& config, const sib_appconfig& sib_cfg) { - // only check if the ue_context_setup_timout is larger than T310 - if (config.ue_context_setup_timeout_s * 1000 < sib_cfg.ue_timers_and_constants.t310) { - fmt::print("ue_context_setup_timeout_s ({}ms) must be larger than T310 ({}ms)\n", - config.ue_context_setup_timeout_s * 1000, + // only check if the pdu_session_setup_timout is larger than T310 + if (config.pdu_session_setup_timeout * 1000 < sib_cfg.ue_timers_and_constants.t310) { + fmt::print("pdu_session_setup_timeout ({}ms) must be larger than T310 ({}ms)\n", + config.pdu_session_setup_timeout * 1000, sib_cfg.ue_timers_and_constants.t310); return false; } @@ -1208,6 +1239,15 @@ static bool validate_expert_execution_appconfig(const gnb_appconfig& config) return false; } + if (variant_holds_alternative(config.ru_cfg)) { + auto& sdr_cfg = variant_get(config.ru_cfg); + if ((config.expert_execution_cfg.threads.lower_threads.execution_profile == lower_phy_thread_profile::single) && + (sdr_cfg.expert_cfg.dl_buffer_size_policy != "auto")) { + fmt::print("DL buffer size policy must be set to auto when single thread lower PHY profile is used.\n"); + return false; + } + } + // Configure more cells for expert execution than the number of cells is an error. if (config.expert_execution_cfg.cell_affinities.size() > config.cells_cfg.size()) { fmt::print("Using more cells for expert execution '{}' than the number of defined cells '{}'\n", diff --git a/apps/gnb/gnb_du_factory.cpp b/apps/gnb/gnb_du_factory.cpp index 16e438ed91..8fc70cc430 100644 --- a/apps/gnb/gnb_du_factory.cpp +++ b/apps/gnb/gnb_du_factory.cpp @@ -166,8 +166,9 @@ std::vector> srsran::make_gnb_dus(const gnb_appconfig& du_hi_cfg.rlc_p = &rlc_p; du_hi_cfg.gnb_du_id = du_insts.size() + 1; du_hi_cfg.gnb_du_name = fmt::format("srsdu{}", du_hi_cfg.gnb_du_id); - du_hi_cfg.du_bind_addr = {fmt::format("127.0.0.{}", du_hi_cfg.gnb_du_id)}; - du_hi_cfg.mac_cfg = generate_mac_expert_config(gnb_cfg); + du_hi_cfg.du_bind_addr = + transport_layer_address::create_from_string(fmt::format("127.0.0.{}", du_hi_cfg.gnb_du_id)); + du_hi_cfg.mac_cfg = generate_mac_expert_config(gnb_cfg); // Assign different initial C-RNTIs to different DUs. du_hi_cfg.mac_cfg.initial_crnti = to_rnti(0x4601 + (0x1000 * du_insts.size())); du_hi_cfg.sched_ue_metrics_notifier = metrics_hub.get_scheduler_ue_metrics_source("DU " + std::to_string(i)); diff --git a/apps/gnb/gnb_worker_manager.cpp b/apps/gnb/gnb_worker_manager.cpp index 5823434246..fc4196e57a 100644 --- a/apps/gnb/gnb_worker_manager.cpp +++ b/apps/gnb/gnb_worker_manager.cpp @@ -164,7 +164,7 @@ void worker_manager::create_du_cu_executors(const gnb_appconfig& appcfg) concurrent_queue_policy::lockfree_mpmc, appcfg.cu_up_cfg.gtpu_queue_size}, // TODO: Consider separate param for size of UL queue if needed. {fmt::format("ue_up_dl_exec#{}", i), - concurrent_queue_policy::lockfree_spsc, + concurrent_queue_policy::lockfree_mpmc, appcfg.cu_up_cfg.gtpu_queue_size}}}); } @@ -178,14 +178,11 @@ void worker_manager::create_du_cu_executors(const gnb_appconfig& appcfg) const std::string cell_id_str = std::to_string(cell_id); const priority_multiqueue_worker du_cell_worker{ "du_cell#" + cell_id_str, - {{concurrent_queue_policy::lockfree_spsc, 4}, - {concurrent_queue_policy::lockfree_spsc, 8}, - {concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}, + {{concurrent_queue_policy::lockfree_spsc, 4}, {concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}, std::chrono::microseconds{10}, // Create Cell and slot indication executors. In case of ZMQ, we make the slot indication executor // synchronous. - {{"cell_exec#" + cell_id_str, task_priority::max - 2}, - {"err_ind#" + cell_id_str, task_priority ::max - 1}, + {{"cell_exec#" + cell_id_str, task_priority::max - 1}, {"slot_exec#" + cell_id_str, task_priority::max, {}, nullopt, is_blocking_mode_active}}, os_thread_realtime_priority::max() - 2, affinity_mng[cell_id].calcute_affinity_mask(gnb_sched_affinity_mask_types::l2_cell)}; @@ -247,8 +244,7 @@ void worker_manager::create_du_cu_executors(const gnb_appconfig& appcfg) // DU-high executor mapper. using exec_list = std::initializer_list; auto cell_exec_mapper = std::make_unique(exec_list{exec_map.at("cell_exec#" + cell_id_str)}, - exec_list{exec_map.at("slot_exec#" + cell_id_str)}, - exec_list{exec_map.at("err_ind#" + cell_id_str)}); + exec_list{exec_map.at("slot_exec#" + cell_id_str)}); auto ue_exec_mapper = std::make_unique(exec_list{exec_map.at("ue_up_ctrl_exec#0")}, exec_list{exec_map.at("ue_up_ul_exec#0")}, exec_list{exec_map.at("ue_up_dl_exec#0")}); diff --git a/apps/gnb/helpers/gnb_console_helper.cpp b/apps/gnb/helpers/gnb_console_helper.cpp index 77ece73d17..ac4ff02d75 100644 --- a/apps/gnb/helpers/gnb_console_helper.cpp +++ b/apps/gnb/helpers/gnb_console_helper.cpp @@ -27,12 +27,36 @@ #include "srsran/support/build_info/build_info.h" #include "srsran/support/io/io_broker.h" #include -#include #include #include using namespace srsran; +// Parses integer values from a console command. +template +static expected parse_int(const std::string& value) +{ + try { + return std::stoi(value); + } catch (const std::invalid_argument& e) { + return {e.what()}; + } catch (const std::out_of_range& e) { + return {e.what()}; + } +} + +// Parses floating point values from a console command and attempts to store them in a double. +static expected parse_double(const std::string& value) +{ + try { + return std::stod(value); + } catch (const std::invalid_argument& e) { + return {e.what()}; + } catch (const std::out_of_range& e) { + return {e.what()}; + } +} + gnb_console_helper::gnb_console_helper(io_broker& io_broker_, srslog::log_channel& log_chan_, bool autostart_stdout_metrics_) : @@ -101,12 +125,92 @@ void gnb_console_helper::stdin_handler(int fd) } } +void gnb_console_helper::handle_tx_gain_command(const std::list& gain_args) +{ + if (!radio_controller.has_value()) { + fmt::print("Interactive radio control is not supported for the current GNB configuration.\n"); + return; + } + + if (gain_args.size() != 2) { + fmt::print("Invalid TX gain command structure. Usage: tx_gain \n"); + return; + } + + expected port_id = parse_int(gain_args.front()); + if (port_id.is_error()) { + fmt::print("Invalid port ID.\n"); + return; + } + expected gain_dB = parse_double(gain_args.back()); + if (gain_dB.is_error()) { + fmt::print("Invalid gain value.\n"); + return; + } + + if (!radio_controller.value()->set_tx_gain(port_id.value(), gain_dB.value())) { + fmt::print("Setting TX gain was not successful. The radio may not support this feature.\n"); + return; + } + + fmt::print("Tx gain set to {} dB for port {}.\n", gain_dB.value(), port_id.value()); +} + +void gnb_console_helper::handle_rx_gain_command(const std::list& gain_args) +{ + if (!radio_controller.has_value()) { + fmt::print("Interactive radio control is not supported for the current GNB configuration.\n"); + return; + } + + if (gain_args.size() != 2) { + fmt::print("Invalid RX gain command structure. Usage: rx_gain \n"); + return; + } + + expected port_id = parse_int(gain_args.front()); + if (port_id.is_error()) { + fmt::print("Invalid port ID.\n"); + return; + } + expected gain_dB = parse_double(gain_args.back()); + if (gain_dB.is_error()) { + fmt::print("Invalid gain value.\n"); + return; + } + + if (!radio_controller.value()->set_rx_gain(port_id.value(), gain_dB.value())) { + fmt::print("Setting RX gain was not successful. The radio may not support this feature.\n"); + return; + } + + fmt::print("Rx gain set to {} dB for port {}.\n", gain_dB.value(), port_id.value()); +} + void gnb_console_helper::handle_command(const std::string& command) { + // Print help message if the command is empty. + if (command.empty()) { + print_help(); + return; + } + + // Break the command into a list of arguments. + std::list arg_list; + srsran::string_parse_list(command, ' ', arg_list); + + srsran_assert(!arg_list.empty(), "Parsing empty command argument list"); + if (command == "q") { raise(SIGTERM); } else if (command == "t") { metrics_plotter.toggle_print(); + } else if (arg_list.front() == "tx_gain") { + arg_list.pop_front(); + handle_tx_gain_command(arg_list); + } else if (arg_list.front() == "rx_gain") { + arg_list.pop_front(); + handle_rx_gain_command(arg_list); } else { print_help(); } @@ -115,8 +219,10 @@ void gnb_console_helper::handle_command(const std::string& command) void gnb_console_helper::print_help() { fmt::print("Available commands:\n"); - fmt::print("\tt: start/stop console trace\n"); - fmt::print("\tq: quit application\n"); + fmt::print("\tt: start/stop console trace\n"); + fmt::print("\tq: quit application\n"); + fmt::print("\ttx_gain : set Tx gain\n"); + fmt::print("\trx_gain : set Rx gain\n"); fmt::print("\n"); } @@ -125,6 +231,11 @@ void gnb_console_helper::set_cells(const span& cells_) cells = {cells_.begin(), cells_.end()}; } +void gnb_console_helper::set_ru_controller(ru_controller& controller) +{ + radio_controller.emplace(&controller); +} + void gnb_console_helper::on_app_starting() { fmt::print("\n--== srsRAN gNB (commit {}) ==--\n\n", get_build_hash()); diff --git a/apps/gnb/helpers/gnb_console_helper.h b/apps/gnb/helpers/gnb_console_helper.h index 95c66a38ca..5619101e90 100644 --- a/apps/gnb/helpers/gnb_console_helper.h +++ b/apps/gnb/helpers/gnb_console_helper.h @@ -25,7 +25,9 @@ #include "metrics_plotter_json.h" #include "metrics_plotter_stdout.h" #include "srsran/du/du_cell_config.h" +#include "srsran/ru/ru_controller.h" #include "srsran/scheduler/scheduler_metrics.h" +#include namespace srsran { @@ -59,6 +61,10 @@ class gnb_console_helper : public app_state_notifier void on_app_stopping() override; void set_cells(const span& cells_); + void set_ru_controller(ru_controller& controller); + + void handle_tx_gain_command(const std::list& gain_args); + void handle_rx_gain_command(const std::list& gain_args); private: void stdin_handler(int fd); @@ -70,6 +76,7 @@ class gnb_console_helper : public app_state_notifier metrics_plotter_stdout metrics_plotter; metrics_plotter_json metrics_json; std::vector cells; + optional radio_controller; bool autostart_stdout_metrics = false; }; diff --git a/cmake/modules/FindNUMA.cmake b/cmake/modules/FindNUMA.cmake new file mode 100644 index 0000000000..615744657d --- /dev/null +++ b/cmake/modules/FindNUMA.cmake @@ -0,0 +1,40 @@ +# +# 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/. +# + +find_package(PkgConfig REQUIRED) + +find_path( + NUMA_INCLUDE_DIRS + NAMES numa.h numaif.h + PATHS /usr/local/include + /usr/include +) + +find_library( + NUMA_LIBRARIES + NAMES numa + PATHS /usr/local/lib + /usr/lib + /usr/lib/x86_64-linux-gnu +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(NUMA DEFAULT_MSG NUMA_LIBRARIES NUMA_INCLUDE_DIRS) +MARK_AS_ADVANCED(NUMA_LIBRARIES NUMA_INCLUDE_DIRS) diff --git a/docker/README.md b/docker/README.md index f8de8bec06..c7ded8a34b 100644 --- a/docker/README.md +++ b/docker/README.md @@ -12,6 +12,13 @@ To launch the full multi-container solution, please run: docker compose -f docker/docker-compose.yml up ``` +or + +```bash +cd docker/ +docker compose up +``` + - To force a new build of the containers (including a new build of srsRAN gnb), please add a `--build` flag at the end of the previous command. - To run it in background, please add a `-d` flag at the end of the previous command. - For more options, check `docker compose up --help` diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index b72d577c20..875f554c8d 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -28,7 +28,7 @@ services: target: open5gs args: OS_VERSION: "22.04" - OPEN5GS_VERSION: "v2.6.1" + OPEN5GS_VERSION: "v2.6.6" env_file: - ${OPEN_5GS_ENV_FILE:-open5gs/open5gs.env} privileged: true diff --git a/docker/grafana/dashboards/home.json b/docker/grafana/dashboards/home.json index 95647029e7..f594693244 100644 --- a/docker/grafana/dashboards/home.json +++ b/docker/grafana/dashboards/home.json @@ -57,7 +57,6 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": null, "links": [], "liveNow": false, "panels": [ @@ -97,30 +96,29 @@ "color": { "mode": "thresholds" }, + "decimals": 0, "mappings": [], + "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] - } + }, + "unit": "none" }, "overrides": [] }, "gridPos": { "h": 4, - "w": 3, + "w": 2, "x": 4, "y": 0 }, - "id": 12, + "id": 19, "options": { "colorMode": "value", "graphMode": "none", @@ -128,12 +126,14 @@ "orientation": "auto", "reduceOptions": { "calcs": [ - "last" + "lastNotNull" ], "fields": "", "values": false }, - "textMode": "auto" + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true }, "pluginVersion": "10.2.0-61719", "targets": [ @@ -142,12 +142,20 @@ "type": "influxdb", "uid": "JOSE3g9KVz" }, - "query": "from(bucket: \"srsran\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"ue_count\")\n |> filter(fn: (r) => r[\"testbed\"] == \"default\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> last()", - "refId": "A" + "query": "from(bucket: \"srsran\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"ue_info\")\n |> filter(fn: (r) => r[\"testbed\"] == \"default\")\n |> filter(fn: (r) => r[\"_field\"] == \"dl_brate\")\n |> window(every: 2s)\n |> group(columns: [\"_stop\"])\n |> unique(column: \"pci\")\n |> count(column: \"pci\")\n |> map(fn: (r) => ({ r with _value: r[\"pci\"] }))\n |> drop(columns: [\"pci\"])\n |> group()\n", + "refId": "Downlink" + } + ], + "title": "Num Cells", + "transformations": [ + { + "id": "renameByRegex", + "options": { + "regex": ".*", + "renamePattern": "Num Cells" + } } ], - "title": "Active UEs", - "transparent": true, "type": "stat" }, { @@ -159,42 +167,7 @@ "fieldConfig": { "defaults": { "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": 30000, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 7, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "dashed" - } + "mode": "thresholds" }, "decimals": 0, "mappings": [], @@ -214,30 +187,35 @@ }, "gridPos": { "h": 4, - "w": 5, - "x": 7, + "w": 6, + "x": 6, "y": 0 }, "id": 14, "options": { - "legend": { - "calcs": [], - "displayMode": "table", - "placement": "right", - "showLegend": false + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true }, + "pluginVersion": "10.2.0-61719", "targets": [ { "datasource": { "type": "influxdb", "uid": "JOSE3g9KVz" }, - "query": "from(bucket: \"srsran\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"ue_count\")\n |> filter(fn: (r) => r[\"testbed\"] == \"default\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")", + "query": "from(bucket: \"srsran\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"ue_info\")\n |> filter(fn: (r) => r[\"testbed\"] == \"default\")\n |> filter(fn: (r) => r[\"_field\"] == \"dl_brate\")\n |> map(fn: (r) => ({ r with ue_id: r[\"pci\"]+\".\"+r[\"rnti\"]}))\n |> window(every: 2s)\n |> group(columns: [\"_stop\"])\n |> unique(column: \"ue_id\")\n |> count(column: \"ue_id\")\n |> map(fn: (r) => ({ r with _value: r[\"ue_id\"] }))\n |> drop(columns: [\"ue_id\"])\n |> group()\n", "refId": "Downlink" } ], @@ -251,7 +229,7 @@ } } ], - "type": "timeseries" + "type": "stat" }, { "datasource": { @@ -297,7 +275,9 @@ "fields": "", "values": false }, - "textMode": "auto" + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true }, "pluginVersion": "10.2.0-62263", "targets": [ @@ -306,23 +286,12 @@ "type": "influxdb", "uid": "JOSE3g9KVz" }, - "query": "from(bucket: \"srsran\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"ue_info\")\n |> filter(fn: (r) => r[\"testbed\"] == \"default\")\n |> filter(fn: (r) => r[\"_field\"] == \"dl_brate\")", + "query": "from(bucket: \"srsran\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"ue_info\")\n |> filter(fn: (r) => r[\"testbed\"] == \"default\")\n |> filter(fn: (r) => r[\"_field\"] == \"dl_brate\")\n |> window(every: 1s)\n |> group(columns: [\"_stop\"])\n |> sum(column: \"_value\")\n |> group()\n |> movingAverage(n: 2)\n ", "refId": "A" } ], "title": "Current Total Downlink Bitrate", - "transformations": [ - { - "id": "calculateField", - "options": { - "mode": "reduceRow", - "reduce": { - "reducer": "sum" - }, - "replaceFields": true - } - } - ], + "transformations": [], "type": "stat" }, { @@ -369,7 +338,9 @@ "fields": "", "values": false }, - "textMode": "auto" + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true }, "pluginVersion": "10.2.0-61719", "targets": [ @@ -378,23 +349,12 @@ "type": "influxdb", "uid": "JOSE3g9KVz" }, - "query": "from(bucket: \"srsran\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"ue_info\")\n |> filter(fn: (r) => r[\"testbed\"] == \"default\")\n |> filter(fn: (r) => r[\"_field\"] == \"dl_brate\")", + "query": "from(bucket: \"srsran\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"ue_info\")\n |> filter(fn: (r) => r[\"testbed\"] == \"default\")\n |> filter(fn: (r) => r[\"_field\"] == \"dl_brate\")\n |> window(every: 1s)\n |> group(columns: [\"_stop\"])\n |> sum(column: \"_value\")\n |> group()\n |> movingAverage(n: 2)\n ", "refId": "A" } ], "title": "Maximum Total Downlink Bitrate", - "transformations": [ - { - "id": "calculateField", - "options": { - "mode": "reduceRow", - "reduce": { - "reducer": "sum" - }, - "replaceFields": true - } - } - ], + "transformations": [], "type": "stat" }, { @@ -409,7 +369,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -491,8 +450,8 @@ { "id": "renameByRegex", "options": { - "regex": ".*rnti=\"(\\w+).*$", - "renamePattern": "UE $1" + "regex": ".*pci=\"(\\w+).*rnti=\"(\\w+).*$", + "renamePattern": "UE $2 [$1]" } } ], @@ -510,7 +469,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -597,8 +555,8 @@ { "id": "renameByRegex", "options": { - "regex": ".*rnti=\"(\\w+).*$", - "renamePattern": "UE $1" + "regex": ".*pci=\"(\\w+).*rnti=\"(\\w+).*$", + "renamePattern": "UE $2 [$1]" } } ], @@ -616,7 +574,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -698,8 +655,8 @@ { "id": "renameByRegex", "options": { - "regex": ".*rnti=\"(\\w+).*$", - "renamePattern": "UE $1" + "regex": ".*pci=\"(\\w+).*rnti=\"(\\w+).*$", + "renamePattern": "UE $2 [$1]" } } ], @@ -717,7 +674,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -804,8 +760,8 @@ { "id": "renameByRegex", "options": { - "regex": ".*rnti=\"(\\w+).*$", - "renamePattern": "UE $1" + "regex": ".*pci=\"(\\w+).*rnti=\"(\\w+).*$", + "renamePattern": "UE $2 [$1]" } } ], @@ -823,7 +779,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -910,8 +865,8 @@ { "id": "renameByRegex", "options": { - "regex": ".*rnti=\"(\\w+).*$", - "renamePattern": "UE $1" + "regex": ".*pci=\"(\\w+).*rnti=\"(\\w+).*$", + "renamePattern": "UE $2 [$1]" } } ], @@ -929,7 +884,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -1016,8 +970,8 @@ { "id": "renameByRegex", "options": { - "regex": ".*rnti=\"(\\w+).*$", - "renamePattern": "UE $1" + "regex": ".*pci=\"(\\w+).*rnti=\"(\\w+).*$", + "renamePattern": "UE $2 [$1]" } } ], @@ -1053,6 +1007,6 @@ "timezone": "", "title": "srsRAN Project Metrics", "uid": "a8a69f8a-1dd1-4496-8f3f-33de8988a823", - "version": 1, + "version": 2, "weekStart": "" } \ No newline at end of file diff --git a/docker/metrics_server/pyproject.toml b/docker/metrics_server/pyproject.toml index 4ea5769537..58e8d8b325 100644 --- a/docker/metrics_server/pyproject.toml +++ b/docker/metrics_server/pyproject.toml @@ -43,7 +43,7 @@ description = "srsRAN Metrics Server" name = "srs_metrics_server" readme = "README.md" requires-python = ">=3.7" -version = "1.5.2" +version = "1.7.0" [project.scripts] metrics-server = "metrics_server.__main__:main" diff --git a/docker/metrics_server/src/metrics_server/__main__.py b/docker/metrics_server/src/metrics_server/__main__.py index 9d06cace13..4cce8ac29d 100644 --- a/docker/metrics_server/src/metrics_server/__main__.py +++ b/docker/metrics_server/src/metrics_server/__main__.py @@ -167,30 +167,18 @@ def _publish_data( # Currently we only support ue_list metric if "ue_list" in metric: timestamp = datetime.utcfromtimestamp(metric["timestamp"]).isoformat() - # Number of UEs measurement - _influx_push( - write_api, - bucket=bucket, - record={ - "measurement": "ue_count", - "tags": { - "testbed": testbed, - }, - "fields": {"value": len(metric["ue_list"])}, - "time": timestamp, - }, - record_time_key="time", - ) # UE Info measurement for ue_info in metric["ue_list"]: ue_container = ue_info["ue_container"] rnti = ue_container.pop("rnti") + pci = ue_container.pop("pci") _influx_push( write_api, bucket=bucket, record={ "measurement": "ue_info", "tags": { + "pci": pci, "rnti": f"{rnti:x}", "testbed": testbed, }, diff --git a/docker/open5gs/Dockerfile b/docker/open5gs/Dockerfile index b5ccd71c21..5e7d7c0a06 100644 --- a/docker/open5gs/Dockerfile +++ b/docker/open5gs/Dockerfile @@ -62,7 +62,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-ins && apt-get autoremove && apt-get clean # To set a fixed version of open5gs use -ARG OPEN5GS_VERSION=v2.6.1 +ARG OPEN5GS_VERSION=v2.6.6 RUN echo $OPEN5GS_VERSION > ./open5gsversion # get latest open5gs tag (must be stored in a file, because docker does not allow to use the return value directly) # RUN git ls-remote --tags https://github.com/open5gs/open5gs | sort -t '/' -k 3 -V | awk -F/ '{ print $3 }' | awk '!/\^\{\}/' | tail -n 1 > ./open5gsversion diff --git a/docker/open5gs/README.md b/docker/open5gs/README.md index 90a5fd4ee6..b8539a207b 100644 --- a/docker/open5gs/README.md +++ b/docker/open5gs/README.md @@ -1,4 +1,4 @@ -This is a all-in-one Docker container for Open5GS. At build, the container will use the specified version of the open5gs repository (default v2.6.1 +This is a all-in-one Docker container for Open5GS. At build, the container will use the specified version of the open5gs repository (default v2.6.6 ). To run the latest tag of the open5gs repository (), line 51 and 52 in .Dockerfile ``` # get latest open5gs tag (must be stored in a file, because docker does not allow to use the return value directly) @@ -54,7 +54,7 @@ Build the Docker container using: `docker build --target open5gs -t open5gs-docker .` -You can overwrite open5gs version by adding `--build-arg OPEN5GS_VERSION=v2.6.1` +You can overwrite open5gs version by adding `--build-arg OPEN5GS_VERSION=v2.6.6` Then run the docker container with: diff --git a/docker/open5gs/open5gs-5gc.yml b/docker/open5gs/open5gs-5gc.yml index a4d79ea6fa..d8d1e67041 100644 --- a/docker/open5gs/open5gs-5gc.yml +++ b/docker/open5gs/open5gs-5gc.yml @@ -26,7 +26,6 @@ parameter: # no_sgwu: true # no_pcrf: true # no_hss: true -no_ipv6: true sbi: server: @@ -242,3 +241,8 @@ udr: sbi: - addr: 127.0.0.20 port: 7777 +time: + t3412: + value: 540 # 9 minutes * 60 = 540 seconds + t3512: + value: 540 # 9 minutes * 60 = 540 seconds diff --git a/include/srsran/adt/byte_buffer.h b/include/srsran/adt/byte_buffer.h index 8d444596d5..f453f7dec9 100644 --- a/include/srsran/adt/byte_buffer.h +++ b/include/srsran/adt/byte_buffer.h @@ -23,6 +23,8 @@ #pragma once #include "srsran/adt/detail/byte_buffer_range_helpers.h" +#include "srsran/adt/detail/intrusive_ptr.h" +#include "srsran/adt/expected.h" #include "fmt/format.h" namespace srsran { @@ -40,6 +42,7 @@ void init_byte_buffer_segment_pool(std::size_t nof_segments, std::size_t memory_block_size = byte_buffer_segment_pool_default_segment_size()); /// \brief Non-owning view to a byte sequence. +/// /// The underlying byte sequence is not contiguous in memory. Instead, it is represented as an intrusive linked list of /// byte buffer segments, where each segment contains a span of bytes. class byte_buffer_view @@ -109,8 +112,7 @@ class byte_buffer_view const_byte_buffer_segment_span_range segments() const { return {it, length()}; } /// Returns a non-owning list of segments that compose the byte_buffer. - /// The segments are not const, so that the callee can modify the bytes, - /// but not layout of the buffer. + /// The segments are not const, so that the callee can modify the bytes, but not layout of the buffer. byte_buffer_segment_span_range modifiable_segments() { return {it, length()}; }; /// \brief Equality comparison between byte buffer view and another range. @@ -143,9 +145,11 @@ class byte_buffer_view class byte_buffer_slice; /// \brief Byte sequence, which represents its data in memory via an intrusive linked list of memory chunks. +/// /// This container is not contiguous in memory. -/// Default copy ctor and assignment is disabled in this container. The user should instead std::move to transfer -/// ownership, .copy() for shallow copies with shared ownership and .deep_copy() for byte-wise copies. +/// Default copy ctor, assignment and explicit construction is disabled in this container. The user should use the +/// provided factory methods to create objects, use std::move to transfer ownership, .copy() for shallow copies with +/// shared ownership and .deep_copy() for byte-wise copies. class byte_buffer { /// Node of linked list of byte buffer segments. @@ -160,10 +164,25 @@ class byte_buffer size_t pkt_len = 0; /// One of the segments shares the same memory block with the byte_buffer control block. node_t* segment_in_cb_memory_block = nullptr; + /// Intrusive ptr reference counter. + intrusive_ptr_atomic_ref_counter ref_count; + /// Whether failures to allocate segments using pool should fallback to malloc. + bool malloc_fallback = false; void destroy_node(node_t* node) const; ~control_block(); + + private: + friend void intrusive_ptr_inc_ref(control_block* ptr) { ptr->ref_count.inc_ref(); } + friend void intrusive_ptr_dec_ref(control_block* ptr) + { + if (ptr->ref_count.dec_ref()) { + ptr->destroy_cb(); + } + } + + void destroy_cb(); }; /// Headroom given to the first segment of the byte_buffer. @@ -174,6 +193,8 @@ class byte_buffer using iterator = detail::byte_buffer_segment_list_byte_iterator; using const_iterator = detail::byte_buffer_segment_list_byte_const_iterator; + struct fallback_allocation_tag {}; + /// Creates an empty byte_buffer. byte_buffer() noexcept = default; @@ -187,9 +208,23 @@ class byte_buffer clear(); } } + /// Creates a byte_buffer with contents provided by a span of bytes. + static expected create(span bytes) + { + byte_buffer buf; + if (not buf.append(bytes)) { + return default_error_t{}; + } + return buf; + } - /// Creates a byte_buffer with data intialized via a initializer list. + /// Creates a byte_buffer with data initialized via a initializer list. byte_buffer(std::initializer_list lst) : byte_buffer(span{lst.begin(), lst.size()}) {} + /// Creates a byte_buffer with data initialized via a initializer list. + static expected create(std::initializer_list lst) + { + return create(span(lst.begin(), lst.size())); + } /// Creates a byte_buffer with data assigned from a range of bytes. template @@ -199,11 +234,24 @@ class byte_buffer clear(); } } + /// Creates a byte_buffer with data assigned from a range of bytes. + template + static expected create(It other_begin, It other_end) + { + byte_buffer buf; + if (not buf.append(other_begin, other_end)) { + return default_error_t{}; + } + return buf; + } /// Move constructor. byte_buffer(byte_buffer&& other) noexcept = default; - /// copy assignment is disabled. Use std::move, .copy() or .deep_copy() instead. + byte_buffer(fallback_allocation_tag tag, span other = {}) noexcept; + byte_buffer(fallback_allocation_tag tag, const byte_buffer& other) noexcept; + + /// Copy assignment is disabled. Use std::move, .copy() or .deep_copy() instead. byte_buffer& operator=(const byte_buffer&) noexcept = delete; /// Move assignment of byte_buffer. It avoids unnecessary reference counting increment. @@ -260,7 +308,7 @@ class byte_buffer /// Appends an initializer list of bytes. SRSRAN_NODISCARD bool append(const std::initializer_list& bytes) { - return append(span{bytes.begin(), bytes.size()}); + return append(span(bytes.begin(), bytes.size())); } /// Appends bytes from another byte_buffer. This function may allocate new segments. @@ -283,9 +331,9 @@ class byte_buffer } /// Appends a view of bytes into current byte buffer. - bool append(const byte_buffer_view& view) + SRSRAN_NODISCARD bool append(const byte_buffer_view& view) { - // append segment by segment. + // Append segment by segment. auto view_segs = view.segments(); for (span seg : view_segs) { if (not append(seg)) { @@ -296,17 +344,17 @@ class byte_buffer } /// Appends an owning view of bytes into current byte buffer. - bool append(const byte_buffer_slice& view); + SRSRAN_NODISCARD bool append(const byte_buffer_slice& view); /// Prepends bytes to byte_buffer. This function may allocate new segments. - bool prepend(span bytes); + SRSRAN_NODISCARD bool prepend(span bytes); /// \brief Prepend data of byte buffer to this byte buffer. - bool prepend(const byte_buffer& other); + SRSRAN_NODISCARD bool prepend(const byte_buffer& other); /// \brief Prepend data of r-value byte buffer to this byte buffer. The segments of the provided byte buffer can get /// "stolen" if the byte buffer is the last reference to the segments. - bool prepend(byte_buffer&& other); + SRSRAN_NODISCARD bool prepend(byte_buffer&& other); /// Prepends space in byte_buffer. This function may allocate new segments. /// \param nof_bytes Number of bytes to reserve at header. @@ -353,20 +401,19 @@ class byte_buffer bool is_contiguous() const { return empty() or ctrl_blk_ptr->segments.head == ctrl_blk_ptr->segments.tail; } /// Moves the bytes stored in different segments of the byte_buffer into first segment. - bool linearize(); + SRSRAN_NODISCARD bool linearize(); /// Set byte_buffer length. Note: It doesn't initialize newly created bytes. - bool resize(size_t new_sz); + SRSRAN_NODISCARD bool resize(size_t new_sz); /// Returns a non-owning list of segments that compose the byte_buffer. byte_buffer_segment_span_range segments() { - return byte_buffer_segment_span_range(ctrl_blk_ptr != nullptr ? ctrl_blk_ptr->segments.head : nullptr, 0, length()); + return {ctrl_blk_ptr != nullptr ? ctrl_blk_ptr->segments.head : nullptr, 0, length()}; } const_byte_buffer_segment_span_range segments() const { - return const_byte_buffer_segment_span_range( - ctrl_blk_ptr != nullptr ? ctrl_blk_ptr->segments.head : nullptr, 0, length()); + return {ctrl_blk_ptr != nullptr ? ctrl_blk_ptr->segments.head : nullptr, 0, length()}; } /// \brief Equality comparison between byte buffer view and another range. @@ -394,9 +441,9 @@ class byte_buffer private: bool has_ctrl_block() const { return ctrl_blk_ptr != nullptr; } - SRSRAN_NODISCARD node_t* create_head_segment(size_t headroom); + SRSRAN_NODISCARD node_t* add_head_segment(size_t headroom, bool use_fallback = false); - static SRSRAN_NODISCARD node_t* create_segment(size_t headroom); + SRSRAN_NODISCARD node_t* create_segment(size_t headroom); SRSRAN_NODISCARD bool append_segment(size_t headroom_suggestion); @@ -423,17 +470,16 @@ class byte_buffer static void warn_alloc_failure(); - // TODO: Optimize. shared_ptr<> has a lot of boilerplate we don't need. It is also hard to determine the size - // of the shared_ptr control block allocation and how much we need to discount in the segment. - std::shared_ptr ctrl_blk_ptr = nullptr; + intrusive_ptr ctrl_blk_ptr; }; /// \brief This class represents a sub-interval or make_slice of a potentially larger byte_buffer. +/// /// Like byte_buffer and byte_buffer_view, the represented bytes by this class are not contiguous in memory. -/// Contrarily to byte_buffer_view, this class retains shared ownership of the segments held by the byte_buffer -/// which it references. -/// Due to the shared ownership model, the usage of this class may involve additional overhead associated with -/// reference counting, which does not take place when using byte_buffer_view. +/// Contrarily to byte_buffer_view, this class retains shared ownership of the segments held by the byte_buffer which it +/// references. +/// Due to the shared ownership model, the usage of this class may involve additional overhead associated with reference +/// counting, which does not take place when using byte_buffer_view. class byte_buffer_slice { public: @@ -448,8 +494,26 @@ class byte_buffer_slice explicit byte_buffer_slice(const byte_buffer_slice&) noexcept = default; byte_buffer_slice(byte_buffer_slice&&) noexcept = default; + byte_buffer_slice(span bytes) : byte_buffer_slice(byte_buffer{bytes}) {} + static expected create(span bytes) + { + auto buf = byte_buffer::create(bytes); + if (not buf) { + return default_error_t{}; + } + return byte_buffer_slice(std::move(buf.value())); + } + byte_buffer_slice(std::initializer_list bytes) : byte_buffer_slice(byte_buffer{bytes}) {} + static expected create(std::initializer_list bytes) + { + auto buf = byte_buffer::create(bytes); + if (not buf) { + return default_error_t{}; + } + return byte_buffer_slice(std::move(buf.value())); + } /// Conversion from byte_buffer to byte_buffer_slice via move. byte_buffer_slice(byte_buffer&& buf_) : buf(std::move(buf_)), sliced_view(buf) {} @@ -611,13 +675,14 @@ class byte_buffer_writer /// Appends span of bytes. SRSRAN_NODISCARD bool append(span bytes) { return buffer->append(bytes); } - /// Appends single byte. + /// Appends a single byte. SRSRAN_NODISCARD bool append(uint8_t byte) { return buffer->append(byte); } + /// Appends the specified amount of zeros. SRSRAN_NODISCARD bool append_zeros(size_t nof_zeros) { // TODO: optimize. - for (size_t i = 0; i < nof_zeros; ++i) { + for (size_t i = 0; i != nof_zeros; ++i) { if (not buffer->append(0)) { return false; } @@ -642,10 +707,11 @@ class byte_buffer_writer inline byte_buffer make_byte_buffer(const std::string& hex_str) { srsran_assert(hex_str.size() % 2 == 0, "The number of hex digits must be even"); + byte_buffer ret; - for (size_t i = 0; i < hex_str.size(); i += 2) { + for (size_t i = 0, e = hex_str.size(); i != e; i += 2) { uint8_t val; - sscanf(hex_str.data() + i, "%02hhX", &val); + std::sscanf(hex_str.data() + i, "%02hhX", &val); if (not ret.append(val)) { ret.clear(); break; @@ -654,7 +720,7 @@ inline byte_buffer make_byte_buffer(const std::string& hex_str) return ret; } -/// Perfoms a segment-wise copy of the byte_buffer into a span object. +/// Performs a segment-wise copy of the byte_buffer into a span object. /// The length is limited by the length of src and dst, whichever is smaller. /// /// \param src Source byte_buffer. @@ -691,16 +757,17 @@ inline size_t copy_segments(const ByteBufferType& src, span dst) /// \return A contiguous view of the byte_buffer inline span to_span(const byte_buffer& src, span tmp_mem) { - // empty buffer + // Empty buffer. if (src.empty()) { return {}; } - // is contiguous: shortcut without copy + // Is contiguous: shortcut without copy. if (src.is_contiguous()) { return *src.segments().begin(); } - // non-contiguous: copy required + + // Non-contiguous: copy required. srsran_assert(src.length() <= tmp_mem.size(), "Insufficient temporary memory to fit the byte_buffer. buffer_size={}, tmp_size={}", src.length(), diff --git a/include/srsran/adt/byte_buffer_chain.h b/include/srsran/adt/byte_buffer_chain.h index b38bcbdc67..894e1759bf 100644 --- a/include/srsran/adt/byte_buffer_chain.h +++ b/include/srsran/adt/byte_buffer_chain.h @@ -143,8 +143,9 @@ class byte_buffer_chain using iterator = iter_impl; using const_iterator = iter_impl; - /// \brief Creates an empty byte_buffer_chain. + /// Creates an empty byte_buffer_chain. byte_buffer_chain(); + static expected create() { return byte_buffer_chain{}; } ~byte_buffer_chain() { @@ -167,16 +168,47 @@ class byte_buffer_chain byte_buffer_chain(const byte_buffer_chain&) = delete; /// Conversion from byte_buffer to byte_buffer_chain. - explicit byte_buffer_chain(byte_buffer buf_) : byte_buffer_chain() { append(std::move(buf_)); } + explicit byte_buffer_chain(byte_buffer buf_) : byte_buffer_chain() + { + bool ret = append(std::move(buf_)); + (void)ret; + } + /// Creates a byte_buffer_chain from a byte_buffer. + static expected create(byte_buffer buf_) + { + byte_buffer_chain buf; + if (not buf.append(std::move(buf_))) { + return default_error_t{}; + } + return buf; + } /// Conversion from byte_buffer_slice to byte_buffer_chain. - byte_buffer_chain(byte_buffer_slice&& buf_) : byte_buffer_chain() { append(std::move(buf_)); } + byte_buffer_chain(byte_buffer_slice&& buf_) : byte_buffer_chain() + { + bool ret = append(std::move(buf_)); + (void)ret; + } + /// Creates a byte_buffer_chain from a byte_buffer_slice. + static expected create(byte_buffer_slice buf_) + { + byte_buffer_chain buf; + if (not buf.append(std::move(buf_))) { + return default_error_t{}; + } + return buf; + } /// Conversion from byte_buffer with specified offset and size to byte_buffer_chain. byte_buffer_chain(byte_buffer buf_, size_t start, size_t sz) : byte_buffer_chain(byte_buffer_slice(std::move(buf_), start, sz)) { } + /// Creates a byte_buffer_chain from byte_buffer with specified offset and size to byte_buffer_chain. + static expected create(byte_buffer buf_, size_t start, size_t sz) + { + return create(byte_buffer_slice(std::move(buf_), start, sz)); + } /// Default move assignment operator. byte_buffer_chain& operator=(byte_buffer_chain&& other) noexcept @@ -233,7 +265,7 @@ class byte_buffer_chain /// /// \param obj Slice to append to the byte_buffer_chain. /// \return true if operation was successful, false otherwise. - bool append(byte_buffer_slice obj) noexcept + SRSRAN_NODISCARD bool append(byte_buffer_slice obj) noexcept { if (obj.empty()) { return true; @@ -249,11 +281,11 @@ class byte_buffer_chain /// Appends a byte_buffer to the end of the byte_buffer_chain. /// \return true if operation was successful, false otherwise. - bool append(byte_buffer buf) { return append(byte_buffer_slice{std::move(buf)}); } + SRSRAN_NODISCARD bool append(byte_buffer buf) { return append(byte_buffer_slice{std::move(buf)}); } /// Appends the contents of another byte_buffer_chain to the end of this byte_buffer_chain. /// \return true if operation was successful, false otherwise. - bool append(byte_buffer_chain other) + SRSRAN_NODISCARD bool append(byte_buffer_chain other) { if (nof_slices() + other.nof_slices() > max_nof_slices()) { return false; @@ -269,7 +301,7 @@ class byte_buffer_chain /// Prepends a byte_buffer_slice to the beginning of the byte_buffer_chain. This operation has O(N) complexity. /// \return true if operation was successful, false otherwise. - bool prepend(byte_buffer_slice slice) + SRSRAN_NODISCARD bool prepend(byte_buffer_slice slice) { if (slice.empty()) { return true; @@ -288,14 +320,14 @@ class byte_buffer_chain } byte_count += slice.length(); slice_count++; - // Store slice in the first (now empty) position + // Store slice in the first (now empty) position. *slices_ptr = std::move(slice); return true; } /// Prepends a byte_buffer to the beginning of the byte_buffer_chain. /// \return true if operation was successful, false otherwise. - bool prepend(byte_buffer buf) { return prepend(byte_buffer_slice{std::move(buf)}); } + SRSRAN_NODISCARD bool prepend(byte_buffer buf) { return prepend(byte_buffer_slice{std::move(buf)}); } /// Release all the byte buffer slices held by the byte_buffer_chain. void clear() @@ -360,21 +392,22 @@ class byte_buffer_chain void operator()(void* p); }; - // Total number of bytes stored in this container. + /// Total number of bytes stored in this container. size_t byte_count = 0; - // Memory block managed by a memory pool, where the slices are stored. + /// Memory block managed by a memory pool, where the slices are stored. std::unique_ptr mem_block; - // Maximum number of byte_buffer_slices that this container can hold. + /// Maximum number of byte_buffer_slices that this container can hold. size_t max_slices = 0; - // Total number of byte_buffer_slices stored in this container. + /// Total number of byte_buffer_slices stored in this container. size_t slice_count = 0; - // Array where byte_buffer_slices are stored. This array is a view to the \c mem_block. + /// Array where byte_buffer_slices are stored. This array is a view to the \c mem_block. byte_buffer_slice* slices_ptr = nullptr; }; } // namespace srsran namespace fmt { + /// \brief Custom formatter for byte_buffer_chain. template <> struct formatter : public formatter { diff --git a/include/srsran/adt/detail/byte_buffer_segment_list.h b/include/srsran/adt/detail/byte_buffer_segment_list.h index 8c1a5a30c1..8fca512eaa 100644 --- a/include/srsran/adt/detail/byte_buffer_segment_list.h +++ b/include/srsran/adt/detail/byte_buffer_segment_list.h @@ -140,13 +140,12 @@ class byte_buffer_segment_byte_iterator_impl using iterator_category = std::forward_iterator_tag; byte_buffer_segment_byte_iterator_impl() = default; - byte_buffer_segment_byte_iterator_impl(byte_buffer_segment_list::node_t* start_segment) : - current_segment(start_segment) - { - } - byte_buffer_segment_byte_iterator_impl(byte_buffer_segment_list::node_t* start_segment, size_t offset_) : + byte_buffer_segment_byte_iterator_impl(byte_buffer_segment_list::node_t* start_segment, size_t offset_ = 0) : current_segment(start_segment), offset(offset_) { + while (current_segment != nullptr and current_segment->length() == 0) { + current_segment = current_segment->next; + } } /// Conversion from iterators of uint8_t (iterator) to const uint8_t (const_iterator). @@ -250,6 +249,9 @@ class byte_buffer_segment_list_span_iterator_impl byte_buffer_segment_list_span_iterator_impl(NodeSegmentType* seg, size_t offset_, size_t size_) : current_segment(seg), offset(offset_), rem_bytes(size_) { + while (current_segment != nullptr and current_segment->length() == 0) { + current_segment = current_segment->next; + } srsran_assert(current_segment != nullptr or (offset == 0 and rem_bytes == 0), "Positive offset or length for empty segment"); srsran_assert(current_segment == nullptr or offset <= current_segment->length(), "Invalid offset"); @@ -259,6 +261,9 @@ class byte_buffer_segment_list_span_iterator_impl byte_buffer_segment_list_span_iterator_impl(const byte_buffer_segment_byte_iterator_impl& it, size_t size_) : byte_buffer_segment_list_span_iterator_impl(it.current_segment, it.offset, size_) { + while (current_segment != nullptr and current_segment->length() == 0) { + current_segment = current_segment->next; + } } /// Copy constructor. diff --git a/include/srsran/adt/detail/intrusive_ptr.h b/include/srsran/adt/detail/intrusive_ptr.h new file mode 100644 index 0000000000..48e2d1ac5c --- /dev/null +++ b/include/srsran/adt/detail/intrusive_ptr.h @@ -0,0 +1,161 @@ +/* + * + * 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 { + +/// \brief Atomic reference counter that can be used as a member of objects T of a intrusive_ptr. +class intrusive_ptr_atomic_ref_counter +{ +public: + void inc_ref() { ref_count.fetch_add(1, std::memory_order_relaxed); } + + bool unique() const { return ref_count.load(std::memory_order_relaxed) == 1; } + + bool dec_ref() { return ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1; } + +private: + std::atomic ref_count{0}; +}; + +/// \brief Smart pointer type where a reference counter for the managed object is stored in the object itself. +/// +/// The advantage of this class over a shared_ptr is its simplicity, avoiding two different memory regions for the +/// reference counter and the managed object. +template +class intrusive_ptr +{ +public: + intrusive_ptr() = default; + + intrusive_ptr(T* ptr_, bool add_ref = true) : ptr(ptr_) + { + if (ptr != nullptr && add_ref) { + intrusive_ptr_inc_ref(ptr); + } + } + + intrusive_ptr(const intrusive_ptr& other) noexcept : ptr(other.ptr) + { + if (ptr != nullptr) { + intrusive_ptr_inc_ref(ptr); + } + } + + intrusive_ptr(intrusive_ptr&& other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } + + ~intrusive_ptr() + { + if (ptr != nullptr) { + intrusive_ptr_dec_ref(ptr); + } + } + + intrusive_ptr& operator=(intrusive_ptr& other) noexcept + { + if (ptr != other.ptr) { + T* temp = ptr; + if (other.ptr != nullptr) { + intrusive_ptr_inc_ref(other.ptr); + } + ptr = other.ptr; + if (temp != nullptr) { + instrusive_ptr_dec_ref(temp); + } + } + } + + intrusive_ptr& operator=(intrusive_ptr&& other) noexcept + { + intrusive_ptr{std::move(other)}.swap(*this); + return *this; + } + + T& operator*() const { return *ptr; } + + T* operator->() const { return ptr; } + + T* get() const { return ptr; } + + void reset() { intrusive_ptr{}.swap(*this); } + + void reset(T* other) { intrusive_ptr{other}.swap(*this); } + + void swap(intrusive_ptr& other) noexcept + { + T* temp = ptr; + ptr = other.ptr; + other.ptr = temp; + } + + bool unique() const { return intrusive_ptr_is_unique(ptr); } + +private: + T* ptr = nullptr; +}; + +template +inline void swap(intrusive_ptr& lhs, intrusive_ptr& rhs) +{ + lhs.swap(rhs); +} + +template +inline bool operator==(const intrusive_ptr& lhs, const intrusive_ptr& rhs) +{ + return (lhs.get() == rhs.get()); +} + +template +inline bool operator==(const intrusive_ptr& lhs, U* rhs) +{ + return (lhs.get() == rhs); +} + +template +inline bool operator==(const intrusive_ptr& lhs, std::nullptr_t rhs) +{ + return (lhs.get() == rhs); +} + +template +inline bool operator!=(const intrusive_ptr& lhs, const intrusive_ptr& rhs) +{ + return (lhs.get() != rhs.get()); +} + +template +inline bool operator!=(const intrusive_ptr& lhs, U* rhs) +{ + return (lhs.get() != rhs); +} + +template +inline bool operator!=(const intrusive_ptr& lhs, std::nullptr_t rhs) +{ + return (lhs.get() != rhs); +} + +} // namespace srsran \ No newline at end of file diff --git a/include/srsran/asn1/asn1_utils.h b/include/srsran/asn1/asn1_utils.h index 901796b318..f6c5ad244c 100644 --- a/include/srsran/asn1/asn1_utils.h +++ b/include/srsran/asn1/asn1_utils.h @@ -829,7 +829,7 @@ class bounded_octstring { HANDLE_CODE(pack_length(bref, size(), LB, UB, aligned)); if (aligned) { - bref.align_bytes_zero(); + HANDLE_CODE(bref.align_bytes_zero()); } for (uint32_t i = 0; i < size(); ++i) { HANDLE_CODE(bref.pack(octets_[i], 8)); @@ -842,7 +842,7 @@ class bounded_octstring HANDLE_CODE(unpack_length(len, bref, LB, UB, aligned)); resize(len); if (aligned) { - bref.align_bytes(); + HANDLE_CODE(bref.align_bytes()); } for (uint32_t i = 0; i < size(); ++i) { HANDLE_CODE(bref.unpack(octets_[i], 8)); @@ -869,7 +869,7 @@ class unbounded_octstring : public srsran::byte_buffer using srsran::byte_buffer::byte_buffer; unbounded_octstring(byte_buffer other) noexcept : srsran::byte_buffer(std::move(other)) {} - unbounded_octstring(const unbounded_octstring& other) noexcept : srsran::byte_buffer(other.deep_copy()) {} + unbounded_octstring(const unbounded_octstring& other) noexcept; unbounded_octstring(unbounded_octstring&& other) noexcept : srsran::byte_buffer(std::move(other)) {} unbounded_octstring& operator=(byte_buffer other) noexcept diff --git a/include/srsran/cu_cp/cu_cp_types.h b/include/srsran/cu_cp/cu_cp_types.h index bc0fda3b4d..588e427db8 100644 --- a/include/srsran/cu_cp/cu_cp_types.h +++ b/include/srsran/cu_cp/cu_cp_types.h @@ -26,7 +26,7 @@ #include "srsran/adt/optional.h" #include "srsran/adt/slotted_array.h" #include "srsran/pdcp/pdcp_config.h" -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/ngap_cause.h" #include "srsran/ran/crit_diagnostics.h" #include "srsran/ran/cu_types.h" #include "srsran/ran/lcid.h" @@ -327,7 +327,7 @@ struct cu_cp_associated_qos_flow { }; struct cu_cp_qos_flow_with_cause_item { qos_flow_id_t qos_flow_id = qos_flow_id_t::invalid; - cause_t cause; + ngap_cause_t cause; }; using cu_cp_qos_flow_failed_to_setup_item = cu_cp_qos_flow_with_cause_item; @@ -351,7 +351,7 @@ struct cu_cp_pdu_session_res_setup_response_item { }; struct cu_cp_pdu_session_resource_setup_unsuccessful_transfer { - cause_t cause; + ngap_cause_t cause; optional crit_diagnostics; }; @@ -367,7 +367,7 @@ struct cu_cp_pdu_session_resource_setup_response { }; struct cu_cp_pdu_session_res_release_cmd_transfer { - cause_t cause; + ngap_cause_t cause; }; struct cu_cp_pdu_session_res_to_release_item_rel_cmd { @@ -478,14 +478,14 @@ struct cu_cp_pdu_session_resource_modify_response { }; struct cu_cp_ue_context_release_command { - ue_index_t ue_index = ue_index_t::invalid; - cause_t cause; + ue_index_t ue_index = ue_index_t::invalid; + ngap_cause_t cause; }; struct cu_cp_ue_context_release_request { ue_index_t ue_index = ue_index_t::invalid; std::vector pdu_session_res_list_cxt_rel_req; - cause_t cause; + ngap_cause_t cause; }; struct cu_cp_recommended_cell_item { diff --git a/include/srsran/du_high/du_high_executor_mapper.h b/include/srsran/du_high/du_high_executor_mapper.h index d8e51bf89b..e31d88791d 100644 --- a/include/srsran/du_high/du_high_executor_mapper.h +++ b/include/srsran/du_high/du_high_executor_mapper.h @@ -1,3 +1,24 @@ +/* + * + * 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 @@ -17,9 +38,6 @@ class du_high_cell_executor_mapper /// \brief Executor to handle slot_indication events for a given cell. virtual task_executor& slot_ind_executor(du_cell_index_t cell_index) = 0; - - /// \brief Executor to handle error_indication events for a given cell. - virtual task_executor& error_ind_executor(du_cell_index_t cell_index) = 0; }; /// This interface is used to allow the DU to choose between different UE-specific task executors. diff --git a/include/srsran/du_manager/du_manager.h b/include/srsran/du_manager/du_manager.h index 7b686fab5f..c2553b7da7 100644 --- a/include/srsran/du_manager/du_manager.h +++ b/include/srsran/du_manager/du_manager.h @@ -52,6 +52,9 @@ class du_manager_configurator /// \brief Remove UE context from the DU. virtual async_task handle_ue_delete_request(const f1ap_ue_delete_request& request) = 0; + /// \brief Deactivate DRB activity for a given UE. + virtual async_task handle_ue_deactivation_request(du_ue_index_t ue_index) = 0; + /// \brief Handle the transfer of resources of old UE to new Reestablishing UE and deletion of the old UE context. virtual void handle_ue_reestablishment(du_ue_index_t new_ue_index, du_ue_index_t old_ue_index) = 0; }; diff --git a/include/srsran/e1ap/common/e1_setup_messages.h b/include/srsran/e1ap/common/e1_setup_messages.h index 18c03335cc..fe57b493d7 100644 --- a/include/srsran/e1ap/common/e1_setup_messages.h +++ b/include/srsran/e1ap/common/e1_setup_messages.h @@ -22,7 +22,7 @@ #pragma once -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/e1ap_cause.h" #include "srsran/ran/crit_diagnostics.h" #include "srsran/ran/cu_types.h" @@ -49,7 +49,7 @@ struct cu_up_e1_setup_response { optional gnb_cu_cp_name; // e1 setup failure - optional cause; + optional cause; optional crit_diagnostics; }; diff --git a/include/srsran/e1ap/common/e1ap_types.h b/include/srsran/e1ap/common/e1ap_types.h index 0cdbcf1a91..d0ed46ede6 100644 --- a/include/srsran/e1ap/common/e1ap_types.h +++ b/include/srsran/e1ap/common/e1ap_types.h @@ -26,7 +26,7 @@ #include "srsran/adt/optional.h" #include "srsran/adt/slotted_array.h" #include "srsran/pdcp/pdcp_config.h" -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/e1ap_cause.h" #include "srsran/ran/cu_types.h" #include "srsran/ran/lcid.h" #include "srsran/ran/up_transport_layer_info.h" @@ -216,7 +216,7 @@ struct e1ap_qos_flow_item { struct e1ap_qos_flow_failed_item { qos_flow_id_t qos_flow_id = qos_flow_id_t::invalid; - cause_t cause; + e1ap_cause_t cause; }; struct e1ap_data_forwarding_info { @@ -233,8 +233,8 @@ struct e1ap_drb_setup_item_ng_ran { }; struct e1ap_drb_failed_item_ng_ran { - drb_id_t drb_id = drb_id_t::invalid; - cause_t cause; + drb_id_t drb_id = drb_id_t::invalid; + e1ap_cause_t cause; }; struct e1ap_pdu_session_resource_setup_modification_item { @@ -249,7 +249,7 @@ struct e1ap_pdu_session_resource_setup_modification_item { struct e1ap_pdu_session_resource_failed_item { pdu_session_id_t pdu_session_id = pdu_session_id_t::invalid; - cause_t cause; + e1ap_cause_t cause; }; struct e1ap_crit_diagnostics_item { diff --git a/include/srsran/e1ap/cu_cp/e1ap_cu_cp_bearer_context_update.h b/include/srsran/e1ap/cu_cp/e1ap_cu_cp_bearer_context_update.h index 592cf6ea1a..9197bdc78c 100644 --- a/include/srsran/e1ap/cu_cp/e1ap_cu_cp_bearer_context_update.h +++ b/include/srsran/e1ap/cu_cp/e1ap_cu_cp_bearer_context_update.h @@ -50,7 +50,7 @@ struct e1ap_bearer_context_setup_response { slotted_id_vector pdu_session_resource_failed_list; // Bearer Context Setup Failure - optional cause; + optional cause; // Common optional crit_diagnostics; @@ -81,15 +81,15 @@ struct e1ap_bearer_context_modification_response { slotted_id_vector pdu_session_resource_failed_to_modify_list; // Bearer Context Modification Failure - optional cause; + optional cause; // Common optional crit_diagnostics; }; struct e1ap_bearer_context_release_command { - ue_index_t ue_index = ue_index_t::invalid; - cause_t cause; + ue_index_t ue_index = ue_index_t::invalid; + e1ap_cause_t cause; }; } // namespace srs_cu_cp diff --git a/include/srsran/e1ap/cu_up/e1ap_cu_up_bearer_context_update.h b/include/srsran/e1ap/cu_up/e1ap_cu_up_bearer_context_update.h index cc16d72670..33dc6ad52b 100644 --- a/include/srsran/e1ap/cu_up/e1ap_cu_up_bearer_context_update.h +++ b/include/srsran/e1ap/cu_up/e1ap_cu_up_bearer_context_update.h @@ -52,7 +52,7 @@ struct e1ap_bearer_context_setup_response { slotted_id_vector pdu_session_resource_failed_list; // Bearer Context Setup Failure - optional cause; + optional cause; // Common optional crit_diagnostics; @@ -87,7 +87,7 @@ struct e1ap_bearer_context_modification_response { slotted_id_vector pdu_session_resource_failed_to_modify_list; // Bearer Context Modification Failure - optional cause; + optional cause; // Common optional crit_diagnostics; @@ -95,8 +95,8 @@ struct e1ap_bearer_context_modification_response { /// \brief Request to release a bearer context. struct e1ap_bearer_context_release_command { - ue_index_t ue_index = INVALID_UE_INDEX; - cause_t cause; // Cause of the release. + ue_index_t ue_index = INVALID_UE_INDEX; + e1ap_cause_t cause; // Cause of the release. }; } // namespace srs_cu_up diff --git a/include/srsran/f1ap/cu_cp/du_setup_notifier.h b/include/srsran/f1ap/cu_cp/du_setup_notifier.h index 9e9e75d9b1..19f3a20191 100644 --- a/include/srsran/f1ap/cu_cp/du_setup_notifier.h +++ b/include/srsran/f1ap/cu_cp/du_setup_notifier.h @@ -24,7 +24,7 @@ #include "srsran/adt/optional.h" #include "srsran/cu_cp/cu_cp_types.h" -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/f1ap_cause.h" #include "srsran/ran/crit_diagnostics.h" #include "srsran/ran/gnb_du_id.h" #include "srsran/ran/nr_cgi.h" @@ -57,8 +57,8 @@ struct du_setup_result { uint8_t gnb_cu_rrc_version; }; struct rejected { - cause_t cause; - std::string cause_str; + f1ap_cause_t cause; + std::string cause_str; }; variant result; diff --git a/include/srsran/f1ap/cu_cp/f1ap_cu.h b/include/srsran/f1ap/cu_cp/f1ap_cu.h index 7269795e8c..1489a3d204 100644 --- a/include/srsran/f1ap/cu_cp/f1ap_cu.h +++ b/include/srsran/f1ap/cu_cp/f1ap_cu.h @@ -54,7 +54,7 @@ class f1ap_rrc_message_handler struct f1ap_ue_context_release_command { ue_index_t ue_index = ue_index_t::invalid; - cause_t cause; + f1ap_cause_t cause; byte_buffer rrc_release_pdu; optional srb_id; }; diff --git a/include/srsran/f1ap/cu_cp/f1ap_cu_ue_context_update.h b/include/srsran/f1ap/cu_cp/f1ap_cu_ue_context_update.h index d1e6db998d..0bbf34f646 100644 --- a/include/srsran/f1ap/cu_cp/f1ap_cu_ue_context_update.h +++ b/include/srsran/f1ap/cu_cp/f1ap_cu_ue_context_update.h @@ -24,7 +24,7 @@ #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/f1ap/common/f1ap_ue_id.h" -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/f1ap_cause.h" #include "srsran/ran/cu_types.h" #include "srsran/ran/lcid.h" #include "srsran/ran/nr_cgi.h" @@ -175,18 +175,18 @@ struct f1ap_drbs_setup_mod_item { }; struct f1ap_srbs_failed_to_be_setup_mod_item { - srb_id_t srb_id = srb_id_t::nulltype; - optional cause; + srb_id_t srb_id = srb_id_t::nulltype; + optional cause; }; struct f1ap_drbs_failed_to_be_setup_mod_item { - drb_id_t drb_id = drb_id_t::invalid; - optional cause; + drb_id_t drb_id = drb_id_t::invalid; + optional cause; }; struct f1ap_scell_failed_to_setup_mod_item { - nr_cell_global_id_t scell_id; - optional cause; + nr_cell_global_id_t scell_id; + optional cause; }; struct f1ap_srbs_setup_mod_item { @@ -216,7 +216,7 @@ struct f1ap_ue_context_setup_response { slotted_id_vector srbs_setup_list; // max size = 8 // UE Context Setup Failure - optional cause; + optional cause; std::vector potential_sp_cell_list; // max size = 64 // Common @@ -300,7 +300,7 @@ struct f1ap_ue_context_modification_response { optional full_cfg; // UE Context Modification Failure - optional cause; + optional cause; // Common optional crit_diagnostics; @@ -308,8 +308,8 @@ struct f1ap_ue_context_modification_response { /// \brief Request Command for F1AP UE CONTEXT Release Request. struct f1ap_ue_context_release_request { - ue_index_t ue_index; - cause_t cause; + ue_index_t ue_index; + f1ap_cause_t cause; }; } // namespace srs_cu_cp diff --git a/include/srsran/f1ap/du/f1ap_du.h b/include/srsran/f1ap/du/f1ap_du.h index d4e7e9693b..ca9a22475e 100644 --- a/include/srsran/f1ap/du/f1ap_du.h +++ b/include/srsran/f1ap/du/f1ap_du.h @@ -185,6 +185,13 @@ class f1ap_du_configurator : public f1ap_task_scheduler /// \brief Request the update of the UE configuration in the DU. virtual async_task request_ue_removal(const f1ap_ue_delete_request& request) = 0; + /// \brief Request by the F1AP to the DU to deactivate the DRB activity for a given UE. + /// + /// This is generally called when the DU receives a request to remove a UE context, but it needs to flush first + /// any pending SRB PDUs. + /// \param ue_index Index of the UE for which the DRB deactivation is requested. + virtual async_task request_ue_drb_deactivation(du_ue_index_t ue_index) = 0; + /// \brief Notify DU that a given UE is performing RRC Reestablishment. virtual void notify_reestablishment_of_old_ue(du_ue_index_t new_ue_index, du_ue_index_t old_ue_index) = 0; diff --git a/include/srsran/f1u/cu_up/f1u_bearer_factory.h b/include/srsran/f1u/cu_up/f1u_bearer_factory.h index 313d268064..c0e7b9ff80 100644 --- a/include/srsran/f1u/cu_up/f1u_bearer_factory.h +++ b/include/srsran/f1u/cu_up/f1u_bearer_factory.h @@ -39,7 +39,9 @@ std::unique_ptr create_f1u_bearer(uint32_t ue_ f1u_tx_pdu_notifier& tx_pdu_notifier, f1u_rx_delivery_notifier& rx_delivery_notifier, f1u_rx_sdu_notifier& rx_sdu_notifier, - timer_factory timers, + timer_factory ue_dl_timer_factory, + unique_timer& ue_inactivity_timer, + task_executor& ul_exec, f1u_bearer_disconnector& disconnector); } // namespace srs_cu_up diff --git a/include/srsran/f1u/cu_up/f1u_gateway.h b/include/srsran/f1u/cu_up/f1u_gateway.h index 63e6b1f7b0..d165b74ec6 100644 --- a/include/srsran/f1u/cu_up/f1u_gateway.h +++ b/include/srsran/f1u/cu_up/f1u_gateway.h @@ -49,7 +49,9 @@ class f1u_cu_up_gateway : public srs_cu_up::f1u_bearer_disconnector const up_transport_layer_info& ul_up_tnl_info, srs_cu_up::f1u_rx_delivery_notifier& rx_delivery_notifier, srs_cu_up::f1u_rx_sdu_notifier& rx_sdu_notifier, - timer_factory timers) = 0; + task_executor& ul_exec, + timer_factory ue_dl_timer_factory, + unique_timer& ue_inactivity_timer) = 0; virtual void attach_dl_teid(const up_transport_layer_info& ul_up_tnl_info, const up_transport_layer_info& dl_up_tnl_info) = 0; diff --git a/include/srsran/f1u/du/f1u_bearer_factory.h b/include/srsran/f1u/du/f1u_bearer_factory.h index 4b64895e07..158cff5e22 100644 --- a/include/srsran/f1u/du/f1u_bearer_factory.h +++ b/include/srsran/f1u/du/f1u_bearer_factory.h @@ -41,6 +41,7 @@ struct f1u_bearer_creation_message { f1u_rx_sdu_notifier* rx_sdu_notifier; f1u_tx_pdu_notifier* tx_pdu_notifier; timer_factory timers; + task_executor* ue_executor; }; /// \brief Creates an F1-U bearer for the DU. diff --git a/include/srsran/f1u/du/f1u_gateway.h b/include/srsran/f1u/du/f1u_gateway.h index fd11b48a5f..92bdfebaef 100644 --- a/include/srsran/f1u/du/f1u_gateway.h +++ b/include/srsran/f1u/du/f1u_gateway.h @@ -25,7 +25,6 @@ #include "srsran/f1u/du/f1u_bearer.h" #include "srsran/f1u/du/f1u_config.h" #include "srsran/f1u/du/f1u_rx_sdu_notifier.h" -#include "srsran/gtpu/gtpu_teid.h" #include "srsran/ran/lcid.h" #include "srsran/ran/up_transport_layer_info.h" #include "srsran/support/timers.h" @@ -51,7 +50,8 @@ class f1u_du_gateway const up_transport_layer_info& dl_up_tnl_info, const up_transport_layer_info& ul_up_tnl_info, srs_du::f1u_rx_sdu_notifier& du_rx, - timer_factory timers) = 0; + timer_factory timers, + task_executor& ue_executor) = 0; virtual void remove_du_bearer(const up_transport_layer_info& dl_up_tnl_info) = 0; }; diff --git a/include/srsran/f1u/local_connector/f1u_local_bearer_adapter.h b/include/srsran/f1u/local_connector/f1u_local_bearer_adapter.h index 457e2401a9..1d249f7b29 100644 --- a/include/srsran/f1u/local_connector/f1u_local_bearer_adapter.h +++ b/include/srsran/f1u/local_connector/f1u_local_bearer_adapter.h @@ -39,34 +39,39 @@ class f1u_dl_local_adapter : public srs_cu_up::f1u_tx_pdu_notifier logger("CU-F1-U", {ue_index, drb_id, ul_tnl_info}) { } + void attach_du_handler(srs_du::f1u_rx_pdu_handler& handler_, const up_transport_layer_info& dl_tnl_info_) { handler = &handler_; dl_tnl_info.emplace(dl_tnl_info_); } + void detach_du_handler(const up_transport_layer_info& dl_tnl_info_) { if (dl_tnl_info == dl_tnl_info_) { handler = nullptr; dl_tnl_info.reset(); } else { - logger.log_info("Cannot dettach DU handler: DL-TEID does not match."); + logger.log_info("Cannot dettach DU bearer, DL-FTEID does not match. F-TEID={}, requested F-TEID={}", + dl_tnl_info, + dl_tnl_info_); } } + void on_new_pdu(nru_dl_message msg) override { if (handler == nullptr) { - logger.log_info("Cannot handle NR-U DL message: DU handler not attached."); + logger.log_info("Cannot handle NR-U DL message. DU bearer does not exist."); return; } - logger.log_debug("Passing PDU to DU handler. {}", dl_tnl_info); + logger.log_debug("Passing PDU to DU bearer. {}", dl_tnl_info); handler->handle_pdu(std::move(msg)); }; private: srs_cu_up::f1u_bearer_logger logger; - srs_du::f1u_rx_pdu_handler* handler = nullptr; - optional dl_tnl_info = {}; + srs_du::f1u_rx_pdu_handler* handler = nullptr; + optional dl_tnl_info; }; class f1u_tx_delivery_local_adapter : public srs_cu_up::f1u_rx_delivery_notifier @@ -93,7 +98,7 @@ class f1u_ul_local_adapter : public srs_du::f1u_tx_pdu_notifier void on_new_pdu(nru_ul_message msg) override { if (handler == nullptr) { - srslog::fetch_basic_logger("DU-F1-U").info("Cannot handle NR-U UL message: CU handler not attached."); + logger.log_info("Cannot handle NR-U UL message. CU-UP bearer does not exist."); return; } handler->handle_pdu(std::move(msg)); diff --git a/include/srsran/f1u/local_connector/f1u_local_connector.h b/include/srsran/f1u/local_connector/f1u_local_connector.h index 0f19cd50de..364dd2b443 100644 --- a/include/srsran/f1u/local_connector/f1u_local_connector.h +++ b/include/srsran/f1u/local_connector/f1u_local_connector.h @@ -76,7 +76,9 @@ class f1u_local_connector final : public srs_du::f1u_du_gateway, public f1u_cu_u const up_transport_layer_info& ul_up_tnl_info, srs_cu_up::f1u_rx_delivery_notifier& rx_delivery_notifier, srs_cu_up::f1u_rx_sdu_notifier& rx_sdu_notifier, - timer_factory timers) override; + task_executor& ul_exec, + timer_factory ue_dl_timer_factory, + unique_timer& ue_inactivity_timer) override; void attach_dl_teid(const up_transport_layer_info& ul_up_tnl_info, const up_transport_layer_info& dl_up_tnl_info) override; @@ -89,7 +91,8 @@ class f1u_local_connector final : public srs_du::f1u_du_gateway, public f1u_cu_u const up_transport_layer_info& dl_up_tnl_info, const up_transport_layer_info& ul_up_tnl_info, srs_du::f1u_rx_sdu_notifier& du_rx, - timer_factory timers) override; + timer_factory timers, + task_executor& ue_executor) override; void remove_du_bearer(const up_transport_layer_info& dl_up_tnl_info) override; diff --git a/include/srsran/gtpu/gtpu_tunnel_ngu_factory.h b/include/srsran/gtpu/gtpu_tunnel_ngu_factory.h index 45c1f27b51..56308d2e9f 100644 --- a/include/srsran/gtpu/gtpu_tunnel_ngu_factory.h +++ b/include/srsran/gtpu/gtpu_tunnel_ngu_factory.h @@ -39,7 +39,7 @@ struct gtpu_tunnel_ngu_creation_message { dlt_pcap* gtpu_pcap; gtpu_tunnel_ngu_rx_lower_layer_notifier* rx_lower; gtpu_tunnel_tx_upper_layer_notifier* tx_upper; - timer_factory timers; + timer_factory ue_dl_timer_factory; }; /// Creates an instance of a GTP-U entity. diff --git a/include/srsran/hal/dpdk/bbdev/bbdev_acc.h b/include/srsran/hal/dpdk/bbdev/bbdev_acc.h index 8ccd4dddc3..da6149c2cb 100644 --- a/include/srsran/hal/dpdk/bbdev/bbdev_acc.h +++ b/include/srsran/hal/dpdk/bbdev/bbdev_acc.h @@ -90,8 +90,8 @@ class bbdev_acc unsigned get_nof_fft_cores() const { return nof_fft_lcores; } /// Returns the size of the (external) HARQ buffer size embedded in the hardware-accelerator. - /// \return HARQ buffer size (in bytes). - units::bytes get_harq_buff_size() const { return units::bytes(1024 * info.drv.harq_buffer_size); } + /// \return HARQ buffer size in bytes. Note that 64 bits are used to enable sizes >= 4GB. + uint64_t get_harq_buff_size_bytes() const { return static_cast(info.drv.harq_buffer_size) * 1024; } /// Returns the size of each mbuf used to exchange unencoded and unrate-matched messages with the accelerator. /// \return Unencoded and unrate-matched mbuf size (in bytes). diff --git a/include/srsran/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository.h b/include/srsran/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository.h index 336fa858cb..819785396a 100644 --- a/include/srsran/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository.h +++ b/include/srsran/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository.h @@ -26,6 +26,7 @@ #pragma once #include "srsran/support/srsran_assert.h" +#include "srsran/support/units.h" #include namespace srsran { @@ -33,7 +34,7 @@ namespace hal { /// Fixed CB-offset increment used in the accelerator's HARQ memory. /// Note that it is assumed that the HARQ memory is organized in N slots of HARQ_INCR bytes. -static constexpr unsigned HARQ_INCR = 32768; +static constexpr units::bytes HARQ_INCR{32768}; /// External HARQ buffer context. struct ext_harq_buffer_context_entry { @@ -51,10 +52,10 @@ class ext_harq_buffer_context_repository /// \param[in] nof_codeblocks_ Indicates the number of codeblocks to store in the repository. /// \param[in] ext_harq_buff_size Size of the external HARQ buffer (in bytes). /// \param[in] debug_mode_ Requests to implement the debug mode (meant for unittesting). - explicit ext_harq_buffer_context_repository(unsigned nof_codeblocks_, unsigned ext_harq_buff_size, bool debug_mode_) : + explicit ext_harq_buffer_context_repository(unsigned nof_codeblocks_, uint64_t ext_harq_buff_size, bool debug_mode_) : nof_codeblocks(nof_codeblocks_) { - unsigned requested_size = nof_codeblocks_ * HARQ_INCR; + uint64_t requested_size = static_cast(nof_codeblocks_) * static_cast(HARQ_INCR.value()); srsran_assert(requested_size <= ext_harq_buff_size, "Requested size ({} bytes) for {} codeblocks exceeds external HARQ buffer capacity ({} bytes).", requested_size, diff --git a/include/srsran/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository_factory.h b/include/srsran/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository_factory.h index 2ab53c6b1c..2d9262db0b 100644 --- a/include/srsran/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository_factory.h +++ b/include/srsran/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository_factory.h @@ -30,7 +30,7 @@ namespace hal { /// Returns a ext_harq_buffer_context_repository instance on success, otherwise returns nullptr. std::shared_ptr create_ext_harq_buffer_context_repository(unsigned nof_codeblocks, - unsigned ext_harq_buff_size, + uint64_t ext_harq_buff_size, bool debug_mode = false); } // namespace hal diff --git a/include/srsran/ngap/ngap_configuration.h b/include/srsran/ngap/ngap_configuration.h index 5c5e34b347..d2d6fb0b45 100644 --- a/include/srsran/ngap/ngap_configuration.h +++ b/include/srsran/ngap/ngap_configuration.h @@ -38,7 +38,7 @@ struct ngap_configuration { std::string plmn; /// Full PLMN as string (without possible filler digit) e.g. "00101" unsigned tac; std::vector slice_configurations; - std::chrono::seconds ue_context_setup_timeout; // timeout for ue context setup in seconds + std::chrono::seconds pdu_session_setup_timeout; // timeout for pdu session setup in seconds }; } // namespace srs_cu_cp diff --git a/include/srsran/ngap/ngap_configuration_helpers.h b/include/srsran/ngap/ngap_configuration_helpers.h index 7ff7dd40c3..414035e637 100644 --- a/include/srsran/ngap/ngap_configuration_helpers.h +++ b/include/srsran/ngap/ngap_configuration_helpers.h @@ -39,8 +39,8 @@ inline srs_cu_cp::ngap_configuration make_default_ngap_config() cfg.plmn = "00101"; cfg.tac = 7; s_nssai_t slice_cfg; - slice_cfg.sst = 1; - cfg.ue_context_setup_timeout = std::chrono::seconds{2}; + slice_cfg.sst = 1; + cfg.pdu_session_setup_timeout = std::chrono::seconds{2}; cfg.slice_configurations.push_back(slice_cfg); return cfg; diff --git a/include/srsran/ngap/ngap_handover.h b/include/srsran/ngap/ngap_handover.h index 8a749da8d4..b484644cbd 100644 --- a/include/srsran/ngap/ngap_handover.h +++ b/include/srsran/ngap/ngap_handover.h @@ -76,11 +76,11 @@ struct ngap_cell_type { }; struct ngap_last_visited_ngran_cell_info { - nr_cell_global_id_t global_cell_id; - ngap_cell_type cell_type; - uint16_t time_ue_stayed_in_cell; - optional time_ue_stayed_in_cell_enhanced_granularity; - optional ho_cause_value; + nr_cell_global_id_t global_cell_id; + ngap_cell_type cell_type; + uint16_t time_ue_stayed_in_cell; + optional time_ue_stayed_in_cell_enhanced_granularity; + optional ho_cause_value; }; struct ngap_last_visited_cell_item { @@ -99,7 +99,7 @@ struct ngap_source_ngran_node_to_target_ngran_node_transparent_container { struct ngap_handover_request { ue_index_t ue_index = ue_index_t::invalid; ngap_handov_type handov_type; - cause_t cause; + ngap_cause_t cause; ngap_ue_aggr_max_bit_rate ue_aggr_max_bit_rate; // TODO: Add optional core_network_assist_info_for_inactive security::security_context security_context; @@ -158,7 +158,7 @@ struct ngap_handover_resource_allocation_response { ngap_target_ngran_node_to_source_ngran_node_transparent_container target_to_source_transparent_container; // handover request failure - cause_t cause; + ngap_cause_t cause; // common optional crit_diagnostics; diff --git a/include/srsran/ngap/ngap_init_context_setup.h b/include/srsran/ngap/ngap_init_context_setup.h index 5cc93cd83a..692c5f5961 100644 --- a/include/srsran/ngap/ngap_init_context_setup.h +++ b/include/srsran/ngap/ngap_init_context_setup.h @@ -54,7 +54,7 @@ struct ngap_init_context_setup_request { }; struct ngap_init_context_setup_failure { - cause_t cause; + ngap_cause_t cause; slotted_id_vector pdu_session_res_failed_to_setup_items; optional crit_diagnostics; }; diff --git a/include/srsran/ngap/ngap_setup.h b/include/srsran/ngap/ngap_setup.h index f12cb88f88..0ed4b12e31 100644 --- a/include/srsran/ngap/ngap_setup.h +++ b/include/srsran/ngap/ngap_setup.h @@ -74,7 +74,7 @@ struct ngap_ng_setup_response { }; struct ngap_ng_setup_failure { - cause_t cause; + ngap_cause_t cause; optional crit_diagnostics; }; diff --git a/include/srsran/ofh/ethernet/ethernet_frame_pool.h b/include/srsran/ofh/ethernet/ethernet_frame_pool.h index c3a58d7e8c..90798f71e1 100644 --- a/include/srsran/ofh/ethernet/ethernet_frame_pool.h +++ b/include/srsran/ofh/ethernet/ethernet_frame_pool.h @@ -187,6 +187,7 @@ class frame_buffer_array buffer.status = frame_buffer::frame_buffer_status::free; } } + used_buffers.clear(); } // Returns a vector of pointers to the buffers ready for sending. diff --git a/include/srsran/ofh/ofh_sector_config.h b/include/srsran/ofh/ofh_sector_config.h index 05274eaecd..47484edb66 100644 --- a/include/srsran/ofh/ofh_sector_config.h +++ b/include/srsran/ofh/ofh_sector_config.h @@ -58,9 +58,9 @@ struct sector_configuration { uint16_t tci; /// DU transmission window timing parameters. - du_tx_window_timing_parameters tx_window_timing_params; + tx_window_timing_parameters tx_window_timing_params; /// Reception window timing parameters. - du_rx_window_timing_parameters rx_window_timing_params; + rx_window_timing_parameters rx_window_timing_params; /// Cyclic prefix. cyclic_prefix cp; diff --git a/include/srsran/ofh/receiver/ofh_receiver.h b/include/srsran/ofh/receiver/ofh_receiver.h index 3160d77e1e..b5ca7e1a5b 100644 --- a/include/srsran/ofh/receiver/ofh_receiver.h +++ b/include/srsran/ofh/receiver/ofh_receiver.h @@ -35,8 +35,8 @@ class receiver public: virtual ~receiver() = default; - /// Returns the OTA symbol boundary notifier of this Open Fronthaul receiver. - virtual ota_symbol_boundary_notifier& get_ota_symbol_boundary_notifier() = 0; + /// Returns an OTA symbol boundary notifier of this Open Fronthaul receiver or nullptr if not present. + virtual ota_symbol_boundary_notifier* get_ota_symbol_boundary_notifier() = 0; /// Returns the controller of this Open Fronthaul receiver. virtual controller& get_controller() = 0; diff --git a/include/srsran/ofh/receiver/ofh_receiver_configuration.h b/include/srsran/ofh/receiver/ofh_receiver_configuration.h index ea1e3e7c96..14fc019ad1 100644 --- a/include/srsran/ofh/receiver/ofh_receiver_configuration.h +++ b/include/srsran/ofh/receiver/ofh_receiver_configuration.h @@ -51,7 +51,7 @@ struct receiver_config { /// Tag control information field. uint16_t tci; /// Reception window timing parameters. - du_rx_window_timing_parameters rx_timing_params; + rx_window_timing_parameters rx_timing_params; /// \brief RU operating bandwidth. /// /// Set this option when the operating bandwidth of the RU is larger than the configured bandwidth of the cell. diff --git a/include/srsran/ofh/receiver/ofh_receiver_timing_parameters.h b/include/srsran/ofh/receiver/ofh_receiver_timing_parameters.h index 3284669dd6..0f2a2eb190 100644 --- a/include/srsran/ofh/receiver/ofh_receiver_timing_parameters.h +++ b/include/srsran/ofh/receiver/ofh_receiver_timing_parameters.h @@ -23,16 +23,19 @@ #pragma once #include +#include namespace srsran { namespace ofh { -/// \brief Structure storing the reception window timing parameters. -struct du_rx_window_timing_parameters { - /// Offset from the current OTA symbol to the end of UL User-Plane reception window. - std::chrono::microseconds Ta4_max; - /// Offset from the current OTA symbol to the start of UL User-Plane reception window. - std::chrono::microseconds Ta4_min; +/// \brief Structure storing the reception window timing parameters expressed in a number of symbols. +struct rx_window_timing_parameters { + /// Offset from the current OTA symbol to the first symbol at which UL User-Plane message can be received within its + /// reception window. Must be calculated based on \c Ta4_min parameter. + unsigned sym_start; + /// Offset from the current OTA symbol to the last symbol at which UL User-Plane message can be received within its + /// reception window. Must be calculated based on \c Ta4_max parameter. + unsigned sym_end; }; } // namespace ofh diff --git a/include/srsran/ofh/timing/slot_symbol_point.h b/include/srsran/ofh/timing/slot_symbol_point.h index 04dd66f0d5..09a0002f2c 100644 --- a/include/srsran/ofh/timing/slot_symbol_point.h +++ b/include/srsran/ofh/timing/slot_symbol_point.h @@ -59,8 +59,8 @@ class slot_symbol_point /// Numerology index (0..4). unsigned get_numerology() const { return numerology; } - /// System slot. - uint32_t system_slot() const { return count_val; } + /// Conversion of slot symbol point to a raw counter. + uint32_t to_uint() const { return count_val; } /// Implementation of the sum operator, where \c jump is represented in number of symbols. slot_symbol_point& operator+=(int jump) @@ -153,7 +153,7 @@ inline int operator-(slot_symbol_point lhs, slot_symbol_point rhs) get_nof_slots_per_subframe(to_subcarrier_spacing(lhs.get_numerology())) * lhs.get_nof_symbols(); - int tmp = static_cast(lhs.system_slot()) - static_cast(rhs.system_slot()); + int tmp = static_cast(lhs.to_uint()) - static_cast(rhs.to_uint()); if (tmp > (nof_symbols_per_slot_wrap / 2)) { return (tmp - nof_symbols_per_slot_wrap); } diff --git a/include/srsran/ofh/transmitter/ofh_transmitter_configuration.h b/include/srsran/ofh/transmitter/ofh_transmitter_configuration.h index 20269363c6..e499317f6a 100644 --- a/include/srsran/ofh/transmitter/ofh_transmitter_configuration.h +++ b/include/srsran/ofh/transmitter/ofh_transmitter_configuration.h @@ -31,6 +31,7 @@ #include "srsran/ofh/ofh_constants.h" #include "srsran/ofh/serdes/ofh_cplane_message_builder.h" #include "srsran/ofh/serdes/ofh_uplane_message_builder.h" +#include "srsran/ofh/transmitter/ofh_transmitter_timing_parameters.h" #include "srsran/ran/bs_channel_bandwidth.h" #include "srsran/ran/cyclic_prefix.h" #include "srsran/ran/tdd/tdd_ul_dl_config.h" @@ -38,32 +39,6 @@ namespace srsran { namespace ofh { -/// \brief Structure storing the transmission window timing parameters. -struct du_tx_window_timing_parameters { - /// Offset from the current OTA symbol to the start of DL Control-Plane transmission window. - std::chrono::microseconds T1a_max_cp_dl; - /// Offset from the current OTA symbol to the end of DL Control-Plane transmission window. - std::chrono::microseconds T1a_min_cp_dl; - /// Offset from the current OTA symbol to the start of UL Control-Plane transmission window. - std::chrono::microseconds T1a_max_cp_ul; - /// Offset from the current OTA symbol to the end of UL Control-Plane transmission window. - std::chrono::microseconds T1a_min_cp_ul; - /// Offset from the current OTA symbol to the start of DL User-Plane transmission window. - std::chrono::microseconds T1a_max_up; - /// Offset from the current OTA symbol to the end of DL User-Plane transmission window. - std::chrono::microseconds T1a_min_up; -}; - -/// Configuration used by ofh_symbol_handler implementations. -struct symbol_handler_config { - /// Transmission window timing parameters for delay management. - du_tx_window_timing_parameters tx_timing_params; - /// Number of symbols per slot. - unsigned symbols_per_slot; - /// Highest subcarrier spacing. - subcarrier_spacing scs; -}; - /// Open Fronthaul transmitter configuration. struct transmitter_config { /// Radio sector identifier. @@ -74,6 +49,8 @@ struct transmitter_config { subcarrier_spacing scs; /// Cyclic prefix. cyclic_prefix cp; + /// Transmission window timing parameters. + tx_window_timing_parameters tx_timing_params; /// Downlink eAxC. static_vector dl_eaxc; /// Uplink eAxC. @@ -96,8 +73,6 @@ struct transmitter_config { units::bytes mtu_size; /// RU working bandwidth. bs_channel_bandwidth_fr1 ru_working_bw; - /// Open Fronthaul symbol handler configuration. - symbol_handler_config symbol_handler_cfg; /// Downlink compression parameters. ru_compression_params dl_compr_params; /// Uplink compression parameters. diff --git a/include/srsran/ofh/transmitter/ofh_transmitter_timing_parameters.h b/include/srsran/ofh/transmitter/ofh_transmitter_timing_parameters.h new file mode 100644 index 0000000000..7a9d01da8b --- /dev/null +++ b/include/srsran/ofh/transmitter/ofh_transmitter_timing_parameters.h @@ -0,0 +1,55 @@ +/* + * + * 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 +#include + +namespace srsran { +namespace ofh { + +/// Structure storing the transmission window timing parameters expressed in a number of symbols. +struct tx_window_timing_parameters { + /// Offset from the current OTA symbol to the first symbol at which DL Control-Plane message can be sent, or in + /// other words it is the offset to the start of DL Control-Plane transmission window. Must be calculated based on + /// \c T1a_max_cp_dl parameter. + unsigned sym_cp_dl_start; + /// Offset from the current OTA symbol to the last symbol at which DL Control-Plane message can be sent within its + /// transmission window. Must be calculated based on \c T1a_min_cp_dl parameter. + unsigned sym_cp_dl_end; + /// Offset from the current OTA symbol to the first symbol at which UL Control-Plane message can be sent within its + /// transmission window. Must be calculated based on \c T1a_max_cp_ul parameter. + unsigned sym_cp_ul_start; + /// Offset from the current OTA symbol to the last symbol at which UL Control-Plane message can be sent within its + /// transmission window. Must be calculated based on \c T1a_min_cp_ul parameter. + unsigned sym_cp_ul_end; + /// Offset from the current OTA symbol to the first symbol at which DL User-Plane message can be sent within its + /// transmission window. Must be calculated based on \c T1a_max_up parameter. + unsigned sym_up_dl_start; + /// Offset from the current OTA symbol to the last symbol at which DL User-Plane message can be sent within its + /// transmission window. Must be calculated based on \c T1a_min_up parameter. + unsigned sym_up_dl_end; +}; + +} // namespace ofh +} // namespace srsran diff --git a/include/srsran/pdcp/pdcp_factory.h b/include/srsran/pdcp/pdcp_factory.h index 9eed5e7cef..8931e0f310 100644 --- a/include/srsran/pdcp/pdcp_factory.h +++ b/include/srsran/pdcp/pdcp_factory.h @@ -44,7 +44,9 @@ struct pdcp_entity_creation_message { pdcp_tx_upper_control_notifier* tx_upper_cn; pdcp_rx_upper_data_notifier* rx_upper_dn; pdcp_rx_upper_control_notifier* rx_upper_cn; - timer_factory timers; + timer_factory ue_dl_timer_factory; + timer_factory ue_ul_timer_factory; + timer_factory ue_ctrl_timer_factory; }; /// Creates an instance of a PDCP entity. diff --git a/include/srsran/phy/lower/lower_phy_configuration.h b/include/srsran/phy/lower/lower_phy_configuration.h index 3e08251ebf..d82b2becd2 100644 --- a/include/srsran/phy/lower/lower_phy_configuration.h +++ b/include/srsran/phy/lower/lower_phy_configuration.h @@ -133,6 +133,24 @@ struct lower_phy_configuration { task_executor* prach_async_executor; }; +/// Converts a string into a baseband buffer size policy. +inline lower_phy_baseband_buffer_size_policy to_buffer_size_policy(const std::string& str) +{ + if (str == "single-packet") { + return lower_phy_baseband_buffer_size_policy::single_packet; + } + if (str == "half-slot") { + return lower_phy_baseband_buffer_size_policy::half_slot; + } + if (str == "slot") { + return lower_phy_baseband_buffer_size_policy::slot; + } + if (str == "optimal-slot") { + return lower_phy_baseband_buffer_size_policy::optimal_slot; + } + report_error("Invalid lower PHY baseband buffer size policy '{}'.", str); +} + /// Returns true if the given lower PHY configuration is valid, otherwise false. inline bool is_valid_lower_phy_config(const lower_phy_configuration& config) { diff --git a/include/srsran/ran/cause/common.h b/include/srsran/ran/cause/common.h new file mode 100644 index 0000000000..52e21de545 --- /dev/null +++ b/include/srsran/ran/cause/common.h @@ -0,0 +1,62 @@ +/* + * + * 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 { + +enum class cause_protocol_t : uint8_t { + transfer_syntax_error = 0, + abstract_syntax_error_reject, + abstract_syntax_error_ignore_and_notify, + msg_not_compatible_with_receiver_state, + semantic_error, + abstract_syntax_error_falsely_constructed_msg, + unspecified +}; + +enum class cause_misc_t : uint8_t { + ctrl_processing_overload = 0, + not_enough_user_plane_processing_res, + hardware_fail, + om_intervention, + unspecified +}; + +// Establishment cause + +enum class establishment_cause_t : uint8_t { + emergency = 0, + high_prio_access, + mt_access, + mo_sig, + mo_data, + mo_voice_call, + mo_video_call, + mo_sms, + mps_prio_access, + mcs_prio_access +}; + +} // namespace srsran diff --git a/include/srsran/ran/cause/e1ap_cause.h b/include/srsran/ran/cause/e1ap_cause.h new file mode 100644 index 0000000000..8bbd1e191f --- /dev/null +++ b/include/srsran/ran/cause/e1ap_cause.h @@ -0,0 +1,100 @@ +/* + * + * 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 "common.h" +#include "srsran/adt/variant.h" +#include "fmt/format.h" + +namespace srsran { + +enum class e1ap_cause_radio_network_t : uint8_t { + unspecified = 0, + unknown_or_already_allocated_gnb_cu_cp_ue_e1ap_id, + unknown_or_already_allocated_gnb_cu_up_ue_e1ap_id, + unknown_or_inconsistent_pair_of_ue_e1ap_id, + interaction_with_other_proc, + ppdcp_count_wrap_around, + not_supported_qci_value, + not_supported_5qi_value, + encryption_algorithms_not_supported, + integrity_protection_algorithms_not_supported, + up_integrity_protection_not_possible, + up_confidentiality_protection_not_possible, + multiple_pdu_session_id_instances, + unknown_pdu_session_id, + multiple_qos_flow_id_instances, + unknown_qos_flow_id, + multiple_drb_id_instances, + unknown_drb_id, + invalid_qos_combination, + proc_cancelled, + normal_release, + no_radio_res_available, + action_desirable_for_radio_reasons, + res_not_available_for_the_slice, + pdcp_cfg_not_supported, + ue_dl_max_ip_data_rate_reason, + up_integrity_protection_fail, + release_due_to_pre_emption, + rsn_not_available_for_the_up, + npn_not_supported, + report_characteristic_empty, + existing_meas_id, + meas_temporarily_not_available, + meas_not_supported_for_the_obj +}; + +enum class e1ap_cause_transport_t : uint8_t { unspecified = 0, transport_res_unavailable, unknown_tnl_address_for_iab }; + +using e1ap_cause_t = variant; + +} // namespace srsran + +namespace fmt { + +// e1ap_cause_t formatter +template <> +struct formatter { + template + auto parse(ParseContext& ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } + + template + auto format(srsran::e1ap_cause_t o, FormatContext& ctx) -> decltype(std::declval().out()) + { + if (srsran::variant_holds_alternative(o)) { + return format_to(ctx.out(), "radio_network-id{}", srsran::variant_get(o)); + } else if (srsran::variant_holds_alternative(o)) { + return format_to(ctx.out(), "transport-id{}", srsran::variant_get(o)); + } else if (srsran::variant_holds_alternative(o)) { + return format_to(ctx.out(), "protocol-id{}", srsran::variant_get(o)); + } else { + return format_to(ctx.out(), "misc-id{}", srsran::variant_get(o)); + } + } +}; + +} // namespace fmt diff --git a/include/srsran/ran/cause/e1ap_cause_converters.h b/include/srsran/ran/cause/e1ap_cause_converters.h new file mode 100644 index 0000000000..dd7997decf --- /dev/null +++ b/include/srsran/ran/cause/e1ap_cause_converters.h @@ -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/. + * + */ + +#pragma once + +#include "e1ap_cause.h" +#include "ngap_cause.h" + +namespace srsran { + +/// \brief Converts an E1AP cause to an NGAP cause. +ngap_cause_t e1ap_to_ngap_cause(e1ap_cause_t e1ap_cause); + +} // namespace srsran diff --git a/include/srsran/ran/cause/f1ap_cause.h b/include/srsran/ran/cause/f1ap_cause.h new file mode 100644 index 0000000000..8cd3bb028a --- /dev/null +++ b/include/srsran/ran/cause/f1ap_cause.h @@ -0,0 +1,112 @@ +/* + * + * 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 "common.h" +#include "srsran/adt/variant.h" +#include "fmt/format.h" + +namespace srsran { + +enum class f1ap_cause_radio_network_t : uint8_t { + unspecified = 0, + rl_fail_rlc, + unknown_or_already_allocated_gnb_cu_ue_f1ap_id, + unknown_or_already_allocated_gnb_du_ue_f1ap_id, + unknown_or_inconsistent_pair_of_ue_f1ap_id, + interaction_with_other_proc, + not_supported_qci_value, + action_desirable_for_radio_reasons, + no_radio_res_available, + proc_cancelled, + normal_release, + cell_not_available, + rl_fail_others, + ue_rejection, + res_not_available_for_the_slice, + amf_initiated_abnormal_release, + release_due_to_pre_emption, + plmn_not_served_by_the_gnb_cu, + multiple_drb_id_instances, + unknown_drb_id, + multiple_bh_rlc_ch_id_instances, + unknown_bh_rlc_ch_id, + cho_cpc_res_tobechanged, + npn_not_supported, + npn_access_denied, + gnb_cu_cell_capacity_exceeded, + report_characteristics_empty, + existing_meas_id, + meas_temporarily_not_available, + meas_not_supported_for_the_obj, + unknown_bh_address, + unknown_bap_routing_id, + insufficient_ue_cap, + scg_activation_deactivation_fail, + scg_deactivation_fail_due_to_data_tx, + requested_item_not_supported_on_time, + unknown_or_already_allocated_gnb_cu_mbs_f1ap_id, + unknown_or_already_allocated_gnb_du_mbs_f1ap_id, + unknown_or_inconsistent_pair_of_mbs_f1ap_id, + unknown_or_inconsistent_mrb_id, + tat_sdt_expiry +}; + +enum class f1ap_cause_transport_t : uint8_t { + unspecified = 0, + transport_res_unavailable, + unknown_tnl_address_for_iab, + unknown_up_tnl_info_for_iab +}; + +using f1ap_cause_t = variant; + +} // namespace srsran + +namespace fmt { + +// f1ap_cause_t formatter +template <> +struct formatter { + template + auto parse(ParseContext& ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } + + template + auto format(srsran::f1ap_cause_t o, FormatContext& ctx) -> decltype(std::declval().out()) + { + if (srsran::variant_holds_alternative(o)) { + return format_to(ctx.out(), "radio_network-id{}", srsran::variant_get(o)); + } else if (srsran::variant_holds_alternative(o)) { + return format_to(ctx.out(), "transport-id{}", srsran::variant_get(o)); + } else if (srsran::variant_holds_alternative(o)) { + return format_to(ctx.out(), "protocol-id{}", srsran::variant_get(o)); + } else { + return format_to(ctx.out(), "misc-id{}", srsran::variant_get(o)); + } + } +}; + +} // namespace fmt diff --git a/include/srsran/ran/cause/f1ap_cause_converters.h b/include/srsran/ran/cause/f1ap_cause_converters.h new file mode 100644 index 0000000000..3744cea70c --- /dev/null +++ b/include/srsran/ran/cause/f1ap_cause_converters.h @@ -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/. + * + */ + +#pragma once + +#include "f1ap_cause.h" +#include "ngap_cause.h" + +namespace srsran { + +/// \brief Converts an F1AP cause to an NGAP cause. +ngap_cause_t f1ap_to_ngap_cause(f1ap_cause_t f1ap_cause); + +} // namespace srsran diff --git a/include/srsran/ran/cause.h b/include/srsran/ran/cause/ngap_cause.h similarity index 70% rename from include/srsran/ran/cause.h rename to include/srsran/ran/cause/ngap_cause.h index 40b064e65c..7c5e6f9086 100644 --- a/include/srsran/ran/cause.h +++ b/include/srsran/ran/cause/ngap_cause.h @@ -22,11 +22,13 @@ #pragma once +#include "common.h" #include "srsran/adt/variant.h" +#include "fmt/format.h" namespace srsran { -enum class cause_radio_network_t : uint8_t { +enum class ngap_cause_radio_network_t : uint8_t { unspecified = 0, txnrelocoverall_expiry, successful_ho, @@ -71,24 +73,29 @@ enum class cause_radio_network_t : uint8_t { redirection, res_not_available_for_the_slice, ue_max_integrity_protected_data_rate_reason, - release_due_to_cn_detected_mob + release_due_to_cn_detected_mob, + n26_interface_not_available, + release_due_to_pre_emption, + multiple_location_report_ref_id_instances, + rsn_not_available_for_the_up, + npn_access_denied, + cag_only_access_denied, + insufficient_ue_cap, + redcap_ue_not_supported, + unknown_mbs_session_id, + indicated_mbs_session_area_info_not_served_by_the_gnb, + inconsistent_slice_info_for_the_session, + misaligned_assoc_for_multicast_unicast }; -enum class cause_transport_t : uint8_t { transport_res_unavailable = 0, unspecified }; - -enum class cause_nas_t : uint8_t { normal_release = 0, authentication_fail, deregister, unspecified }; - -enum class cause_protocol_t : uint8_t { - transfer_syntax_error = 0, - abstract_syntax_error_reject, - abstract_syntax_error_ignore_and_notify, - msg_not_compatible_with_receiver_state, - semantic_error, - abstract_syntax_error_falsely_constructed_msg, - unspecified +enum class ngap_cause_transport_t : uint8_t { + transport_res_unavailable = 0, + unspecified, }; -enum class cause_misc_t : uint8_t { +enum class cause_nas_t : uint8_t { normal_release = 0, authentication_fail, deregister, unspecified }; // only NGAP + +enum class ngap_cause_misc_t : uint8_t { ctrl_processing_overload = 0, not_enough_user_plane_processing_res, hardware_fail, @@ -97,30 +104,16 @@ enum class cause_misc_t : uint8_t { unspecified }; -using cause_t = variant; - -// Establishment cause - -enum class establishment_cause_t : uint8_t { - emergency = 0, - high_prio_access, - mt_access, - mo_sig, - mo_data, - mo_voice_call, - mo_video_call, - mo_sms, - mps_prio_access, - mcs_prio_access -}; +using ngap_cause_t = + variant; } // namespace srsran namespace fmt { -// cause_t formatter +// ngap_cause_t formatter template <> -struct formatter { +struct formatter { template auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { @@ -128,18 +121,18 @@ struct formatter { } template - auto format(srsran::cause_t o, FormatContext& ctx) -> decltype(std::declval().out()) + auto format(srsran::ngap_cause_t o, FormatContext& ctx) -> decltype(std::declval().out()) { - if (srsran::variant_holds_alternative(o)) { - return format_to(ctx.out(), "radio_network-id{}", srsran::variant_get(o)); - } else if (srsran::variant_holds_alternative(o)) { - return format_to(ctx.out(), "transport-id{}", srsran::variant_get(o)); + if (srsran::variant_holds_alternative(o)) { + return format_to(ctx.out(), "radio_network-id{}", srsran::variant_get(o)); + } else if (srsran::variant_holds_alternative(o)) { + return format_to(ctx.out(), "transport-id{}", srsran::variant_get(o)); } else if (srsran::variant_holds_alternative(o)) { return format_to(ctx.out(), "nas-id{}", srsran::variant_get(o)); } else if (srsran::variant_holds_alternative(o)) { return format_to(ctx.out(), "protocol-id{}", srsran::variant_get(o)); } else { - return format_to(ctx.out(), "misc-id{}", srsran::variant_get(o)); + return format_to(ctx.out(), "misc-id{}", srsran::variant_get(o)); } } }; diff --git a/include/srsran/ran/cause/ngap_cause_converters.h b/include/srsran/ran/cause/ngap_cause_converters.h new file mode 100644 index 0000000000..d6f7045c4e --- /dev/null +++ b/include/srsran/ran/cause/ngap_cause_converters.h @@ -0,0 +1,37 @@ +/* + * + * 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 "e1ap_cause.h" +#include "f1ap_cause.h" +#include "ngap_cause.h" + +namespace srsran { + +/// \brief Converts an NGAP cause to an F1AP cause. +f1ap_cause_t ngap_to_f1ap_cause(ngap_cause_t ngap_cause); + +/// \brief Converts an NGAP cause to an E1AP cause. +e1ap_cause_t ngap_to_e1ap_cause(ngap_cause_t ngap_cause); + +} // namespace srsran diff --git a/include/srsran/ran/csi_rs/csi_report_config.h b/include/srsran/ran/csi_rs/csi_report_config.h index f6dbbe8476..c47ac66905 100644 --- a/include/srsran/ran/csi_rs/csi_report_config.h +++ b/include/srsran/ran/csi_rs/csi_report_config.h @@ -41,6 +41,9 @@ enum csi_report_config_id_t : uint8_t { MAX_NOF_CSI_REPORT_CONFIGS = 48, }; +/// Maximum CSI report period in slots. +constexpr unsigned MAX_CSI_REPORT_PERIOD = 320; + /// \brief Periodicity and slot offset. /// \remark See TS 38.331, \c CSI-ReportPeriodicityAndOffset. enum class csi_report_periodicity { diff --git a/include/srsran/ran/direct_current_offset.h b/include/srsran/ran/direct_current_offset.h index ecdb242fbc..e22ab81a7b 100644 --- a/include/srsran/ran/direct_current_offset.h +++ b/include/srsran/ran/direct_current_offset.h @@ -24,6 +24,7 @@ #include "resource_block.h" #include "srsran/adt/strong_type.h" +#include "srsran/ran/resource_allocation/rb_interval.h" #include "srsran/support/srsran_assert.h" namespace srsran { @@ -68,6 +69,36 @@ inline uint16_t pack(dc_offset_t offset, unsigned carrier_nof_rbs) return 3300; } +/// \brief Checks if the DC position is contained in a CRB interval. +/// \param[in] offset DC offset parameter. +/// \param[in] carrier_nof_rbs Carrier bandwidth in resource blocks. +/// \param[in] rbs Resource block allocation. +/// \return \c true if the DC position is contained within the allocated CRBs. Otherwise \c false. +inline bool is_contained(dc_offset_t offset, unsigned carrier_nof_rbs, crb_interval rbs) +{ + if (offset == dc_offset_t::undetermined) { + return false; + } + + // Case where DC offset inside spectrum. + const int nof_subcarriers = static_cast(NOF_SUBCARRIERS_PER_RB * carrier_nof_rbs); + auto dc_position = static_cast>(offset); + if (dc_position >= -nof_subcarriers / 2 && dc_position < nof_subcarriers / 2) { + dc_position += nof_subcarriers / 2; + } + + return rbs.contains(dc_position / NOF_SUBCARRIERS_PER_RB); +} + +/// \brief Checks if the DC position is contained in a CRB interval. +/// \param[in] dc_position DC position in subcarriers within the grid relative to PointA. +/// \param[in] rbs Resource block allocation. +/// \return \c true if the DC position is contained within the allocated CRBs. Otherwise \c false. +inline bool is_contained(unsigned dc_position, crb_interval rbs) +{ + return rbs.contains(dc_position / NOF_SUBCARRIERS_PER_RB); +} + } // namespace dc_offset_helper } // namespace srsran diff --git a/include/srsran/ran/pdsch/dlsch_info.h b/include/srsran/ran/pdsch/dlsch_info.h index f29a58f8ce..34624dc89b 100644 --- a/include/srsran/ran/pdsch/dlsch_info.h +++ b/include/srsran/ran/pdsch/dlsch_info.h @@ -52,6 +52,8 @@ struct dlsch_configuration { unsigned nof_cdm_groups_without_data; /// Number of transmission layers. unsigned nof_layers; + /// Set to true if the transmission overlaps with the Direct Current (DC). + bool contains_dc; }; /// Collects Downlink Shared Channel (DL-SCH) derived parameters. @@ -60,6 +62,8 @@ struct dlsch_information { sch_information sch; /// Number of encoded and rate-matched DL-SCH data bits. units::bits nof_dl_sch_bits; + /// Number of bits that are affected by overlapping with the direct current. + units::bits nof_dc_overlap_bits; /// \brief Calculates the effective code rate normalized between 0 and 1. /// @@ -71,7 +75,7 @@ struct dlsch_information { srsran_assert(sch.nof_bits_per_cb.value() > sch.nof_filler_bits_per_cb.value(), "The number of bits per CB must be greater than the number of filler bits."); return static_cast((sch.nof_bits_per_cb.value() - sch.nof_filler_bits_per_cb.value()) * sch.nof_cb) / - static_cast(nof_dl_sch_bits.value()); + static_cast(nof_dl_sch_bits.value() - nof_dc_overlap_bits.value()); } }; diff --git a/include/srsran/ran/pusch/pusch_tpmi_select.h b/include/srsran/ran/pusch/pusch_tpmi_select.h index c79233211b..91e6f65b4e 100644 --- a/include/srsran/ran/pusch/pusch_tpmi_select.h +++ b/include/srsran/ran/pusch/pusch_tpmi_select.h @@ -62,6 +62,9 @@ class pusch_tpmi_select_info return info[nof_layers - 1]; } + /// Constructs a PUSCH TPMI information from a span. + pusch_tpmi_select_info(const span& info_) : info(info_.begin(), info_.end()) {} + /// Constructs a PUSCH TPMI information from an initializer list. pusch_tpmi_select_info(const std::initializer_list& info_) : info(info_.begin(), info_.end()) {} diff --git a/include/srsran/ran/pusch/ulsch_info.h b/include/srsran/ran/pusch/ulsch_info.h index af0a94141b..2b03d36bc6 100644 --- a/include/srsran/ran/pusch/ulsch_info.h +++ b/include/srsran/ran/pusch/ulsch_info.h @@ -71,6 +71,8 @@ struct ulsch_configuration { unsigned nof_cdm_groups_without_data; /// Number of transmission layers. unsigned nof_layers; + /// Set to true if the transmission overlaps with the Direct Current (DC). + bool contains_dc; }; /// \brief Collects Uplink Shared Channel (UL-SCH) derived parameters. @@ -95,6 +97,8 @@ struct ulsch_information { unsigned nof_csi_part1_re; /// Number of resource elements occupied by CSI Part 1 information. Parameter \f$Q'_\textup{CSI-2}\f$. unsigned nof_csi_part2_re; + /// Number of bits that are affected by overlapping with the direct current. + units::bits nof_dc_overlap_bits; /// \brief Calculates the effective code rate normalized between 0 and 1. /// @@ -134,6 +138,18 @@ struct ulsch_information { effective_ul_sch_bits -= nof_harq_ack_bits.value(); } + // Adjust the effective rate matched UL-SCH bits considering the bits overlapped with the DC position. + if (nof_dc_overlap_bits > 0_bits) { + // Ensure the subtraction of overlapped DC bits does not result in zero or a negative value. + srsran_assert(effective_ul_sch_bits > nof_dc_overlap_bits.value(), + "UL-SCH rate match length bits (i.e. {}) must be greater than the bits overlapped with DC position " + "(i.e. {}).", + effective_ul_sch_bits, + nof_dc_overlap_bits); + + effective_ul_sch_bits -= nof_dc_overlap_bits.value(); + } + // Calculate the exact number of bits to encode, including payload, transport block CRC, and codeblock CRC. unsigned nof_effective_payload_bits = (sch.value().nof_bits_per_cb.value() - sch.value().nof_filler_bits_per_cb.value()) * sch.value().nof_cb; diff --git a/include/srsran/ran/sr_configuration.h b/include/srsran/ran/sr_configuration.h index 95bc2feb47..f70a5be4ef 100644 --- a/include/srsran/ran/sr_configuration.h +++ b/include/srsran/ran/sr_configuration.h @@ -41,6 +41,9 @@ inline scheduling_request_id uint_to_sched_req_id(unsigned sr_id) return static_cast(sr_id); } +/// Maximum SR period in slots. +constexpr unsigned MAX_SR_PERIOD = 640; + /// Encodes the periodicity (only) of \c periodicityAndOffset for \c SchedulingRequestResourceConfig, TS 38.331. Note /// that the offset is encoded separately. enum class sr_periodicity : int { diff --git a/include/srsran/ran/up_transport_layer_info.h b/include/srsran/ran/up_transport_layer_info.h index 5e282a8a3f..78b7c7684e 100644 --- a/include/srsran/ran/up_transport_layer_info.h +++ b/include/srsran/ran/up_transport_layer_info.h @@ -29,14 +29,23 @@ namespace srsran { /// \brief Identifier for F1-U transport layer associated to a DRB. struct up_transport_layer_info { - transport_layer_address tp_address{"0.0.0.0"}; + transport_layer_address tp_address = transport_layer_address::create_from_string("0.0.0.0"); gtpu_teid_t gtp_teid; + up_transport_layer_info() = default; + + up_transport_layer_info(const transport_layer_address& tp_address_, gtpu_teid_t gtp_teid_) : + tp_address(tp_address_), gtp_teid(gtp_teid_) + { + } + bool operator==(const up_transport_layer_info& other) const { return tp_address == other.tp_address and gtp_teid == other.gtp_teid; } + bool operator!=(const up_transport_layer_info& other) const { return not(*this == other); } + bool operator<(const up_transport_layer_info& other) const { return gtp_teid < other.gtp_teid or (gtp_teid == other.gtp_teid and tp_address < other.tp_address); @@ -58,25 +67,27 @@ void up_transport_layer_info_to_asn1(Asn1Type& asn1obj, const up_transport_layer /// \param asn1obj ASN.1 object which is going to be converted. /// \return UP Transport Layer Info object where the result of the conversion is stored. template -up_transport_layer_info asn1_to_up_transport_layer_info(Asn1Type& asn1obj) +up_transport_layer_info asn1_to_up_transport_layer_info(const Asn1Type& asn1obj) { - up_transport_layer_info ret; - ret.gtp_teid = int_to_gtpu_teid(asn1obj.gtp_tunnel().gtp_teid.to_number()); - ret.tp_address.from_bitstring(asn1obj.gtp_tunnel().transport_layer_address.to_string()); - return ret; + return {transport_layer_address::create_from_bitstring(asn1obj.gtp_tunnel().transport_layer_address.to_string()), + int_to_gtpu_teid(asn1obj.gtp_tunnel().gtp_teid.to_number())}; } } // namespace srsran +namespace std { + template <> -struct std::hash { - std::size_t operator()(const srsran::up_transport_layer_info& s) const noexcept +struct hash { + size_t operator()(const srsran::up_transport_layer_info& s) const noexcept { - return (std::hash{}(s.tp_address) ^ - (std::hash{}(s.gtp_teid.value()) << 1U) >> 1U); + return (hash{}(s.tp_address) ^ + (hash{}(s.gtp_teid.value()) << 1U) >> 1U); } }; +} // namespace std + namespace fmt { template <> diff --git a/include/srsran/rrc/rrc_ue.h b/include/srsran/rrc/rrc_ue.h index 394eed090c..f0dca892c6 100644 --- a/include/srsran/rrc/rrc_ue.h +++ b/include/srsran/rrc/rrc_ue.h @@ -72,7 +72,7 @@ class rrc_ue_setup_proc_notifier virtual void on_new_dl_ccch(const asn1::rrc_nr::dl_ccch_msg_s& dl_ccch_msg) = 0; /// \brief Notify about the need to release a UE. - virtual void on_ue_release_required(const cause_t& cause) = 0; + virtual void on_ue_release_required(const ngap_cause_t& cause) = 0; }; struct srb_creation_message { @@ -111,7 +111,7 @@ class rrc_ue_reconfiguration_proc_notifier virtual void on_new_dl_dcch(srb_id_t srb_id, const asn1::rrc_nr::dl_dcch_msg_s& dl_dcch_msg) = 0; /// \brief Notify about the need to release a UE. - virtual void on_ue_release_required(const cause_t& cause) = 0; + virtual void on_ue_release_required(const ngap_cause_t& cause) = 0; }; /// Interface used by the RRC security mode procedure @@ -347,9 +347,11 @@ class rrc_ue_measurement_notifier virtual ~rrc_ue_measurement_notifier() = default; /// \brief Retrieve the measurement config (for any UE) connected to the given serving cell. + /// \param[in] ue_index The index of the UE to retrieve the measurement config for. /// \param[in] nci The cell id of the serving cell to update. /// \param[in] current_meas_config The current meas config of the UE (if applicable). - virtual optional on_measurement_config_request(nr_cell_id_t nci, + virtual optional on_measurement_config_request(ue_index_t ue_index, + nr_cell_id_t nci, optional current_meas_config = {}) = 0; /// \brief Submit measurement report for given UE to cell manager. diff --git a/include/srsran/ru/ru_controller.h b/include/srsran/ru/ru_controller.h index 55c18d7f25..1cc8a1520c 100644 --- a/include/srsran/ru/ru_controller.h +++ b/include/srsran/ru/ru_controller.h @@ -43,6 +43,18 @@ class ru_controller /// \brief Stops the Radio Unit operation. /// \note Caller will be blocked until the controller is fully stopped. virtual void stop() = 0; + + /// \brief Sets the transmission gain for the specified port. + /// \param[in] port_id Port identifier. + /// \param[in] gain_dB Transmission gain in dB. + /// \return \c true if the operation is successful, \c false otherwise. + virtual bool set_tx_gain(unsigned port_id, double gain_dB) = 0; + + /// \brief Sets the receive gain for the specified port. + /// \param[in] port_id Port identifier. + /// \param[in] gain_dB Receive gain in dB. + /// \return \c true if the operation is successful, \c false otherwise. + virtual bool set_rx_gain(unsigned port_id, double gain_dB) = 0; }; } // namespace srsran diff --git a/include/srsran/ru/ru_ofh_configuration.h b/include/srsran/ru/ru_ofh_configuration.h index 0f24dbff22..d272ebc1bc 100644 --- a/include/srsran/ru/ru_ofh_configuration.h +++ b/include/srsran/ru/ru_ofh_configuration.h @@ -47,9 +47,9 @@ struct ru_ofh_sector_configuration { optional ru_operating_bw; /// DU transmission window timing parameters. - ofh::du_tx_window_timing_parameters tx_window_timing_params; + ofh::tx_window_timing_parameters tx_window_timing_params; /// Reception window timing parameters. - ofh::du_rx_window_timing_parameters rx_window_timing_params; + ofh::rx_window_timing_parameters rx_window_timing_params; /// Enables the Control-Plane PRACH message signalling. bool is_prach_control_plane_enabled = false; diff --git a/include/srsran/scheduler/scheduler_slot_handler.h b/include/srsran/scheduler/scheduler_slot_handler.h index 7ccaddfcaf..74a775babb 100644 --- a/include/srsran/scheduler/scheduler_slot_handler.h +++ b/include/srsran/scheduler/scheduler_slot_handler.h @@ -68,8 +68,14 @@ struct pdsch_precoding_info { static_vector prg_infos; }; +/// Transmit power information associated with PDCCH PDU. struct tx_power_pdcch_information { - // TODO + /// Ratio of NZP CSI-RS EPRE to SSB/PBCH block EPRE. See 3GPP TS 38.214, clause 5.2.2.3.1. Values {-3, 0, 3, 6} dB. + /// \remark If the UE has not been provided dedicated higher layer parameters, the UE may assume that the ratio of + /// PDCCH DMRS EPRE to SSS EPRE is within -8 dB and 8 dB when the UE monitors PDCCHs for a DCI format 1_0 with CRC + /// scrambled by SI-RNTI, P-RNTI, or RA-RNTI. See TS 38.213, clause 4.1. + /// \remark [Implementation-defined] In case UE is not configured with powerControlOffsetSS we assume it to be 0dB. + int8_t pwr_ctrl_offset_ss{0}; }; struct dmrs_information { @@ -162,6 +168,17 @@ struct pdsch_codeword { bool new_data; }; +/// Transmit power information associated with PDSCH PDU. +struct tx_power_pdsch_information { + /// Ratio of PDSCH EPRE to NZP CSI-RS EPRE when UE derives CSI feedback. See 3GPP TS 38.214, clause 5.2.2.3.1. Values + /// {-8,...,15} dB with 1 dB step size. + /// \remark [Implementation-defined] In case UE is not configured with powerControlOffset we assume it to be 0dB. + int8_t pwr_ctrl_offset{0}; + /// Ratio of NZP CSI-RS EPRE to SSB/PBCH block EPRE. See 3GPP TS 38.214, clause 5.2.2.3.1. Values {-3, 0, 3, 6} dB. + /// \remark [Implementation-defined] In case UE is not configured with powerControlOffsetSS we assume it to be 0dB. + int8_t pwr_ctrl_offset_ss{0}; +}; + /// \brief Information relative to a PDSCH grant in a given slot. struct pdsch_information { rnti_t rnti; @@ -183,6 +200,8 @@ struct pdsch_information { harq_id_t harq_id; /// Precoding information for the PDSCH. This field is empty in case of 1-antenna port setups. optional precoding; + /// Transmit power information for the PDSCH. + tx_power_pdsch_information tx_pwr_info; }; /// Dummy MAC CE payload. @@ -397,9 +416,9 @@ struct csi_rs_info { /// \brief ScramblingID of the CSI-RS as per 3GPP TS 38.214, sec 5.2.2.3.1. Values: {0,...,1023}. uint16_t scrambling_id; /// Ratio of PDSCH EPRE to NZP CSI-RS EPRE as per 3GPP TS 38.214, clause 5.2.2.3.1. Values: {-8,...,15}. - int8_t power_ctrl_offset_profile_nr; + int8_t power_ctrl_offset; /// Ratio of NZP CSI-RS EPRE to SSB/PBCH block EPRE. Values: {-3,0,3,6}. - int8_t power_ctrl_offset_ss_profile_nr; + int8_t power_ctrl_offset_ss; }; struct dl_sched_result { diff --git a/include/srsran/sdap/sdap_factory.h b/include/srsran/sdap/sdap_factory.h index 8ccf2fb4b8..9b5a6b94fa 100644 --- a/include/srsran/sdap/sdap_factory.h +++ b/include/srsran/sdap/sdap_factory.h @@ -37,7 +37,6 @@ namespace srs_cu_up { struct sdap_entity_creation_message { uint32_t ue_index; pdu_session_id_t pdu_session_id; - unique_timer& ue_inactivity_timer; sdap_rx_sdu_notifier* rx_sdu_notifier; }; diff --git a/include/srsran/security/security.h b/include/srsran/security/security.h index 57a4565af2..a7b388ddde 100644 --- a/include/srsran/security/security.h +++ b/include/srsran/security/security.h @@ -428,4 +428,52 @@ struct formatter { } }; +// Key formatting +template <> +struct formatter { + template + auto parse(ParseContext& ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } + + template + auto format(const srsran::security::sec_128_key& key, FormatContext& ctx) + -> decltype(std::declval().out()) + { + return format_to(ctx.out(), "\n\t{:02x}", fmt::join(key, " ")); + } +}; + +template <> +struct formatter { + template + auto parse(ParseContext& ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } + + template + auto format(const srsran::security::sec_key& key, FormatContext& ctx) -> decltype(std::declval().out()) + { + format_to(ctx.out(), "\n\t{:02x}", fmt::join(key.begin(), key.begin() + 16, " ")); + return format_to(ctx.out(), "\n\t{:02x}", fmt::join(key.begin() + 16, key.end(), " ")); + } +}; + +template <> +struct formatter { + template + auto parse(ParseContext& ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } + + template + auto format(const srsran::security::sec_mac& mac, FormatContext& ctx) -> decltype(std::declval().out()) + { + return format_to(ctx.out(), "\n\t{:02x}", fmt::join(mac, " ")); + } +}; + } // namespace fmt diff --git a/include/srsran/srslog/detail/log_entry_metadata.h b/include/srsran/srslog/detail/log_entry_metadata.h index e5a7e78ff5..1d38d471ce 100644 --- a/include/srsran/srslog/detail/log_entry_metadata.h +++ b/include/srsran/srslog/detail/log_entry_metadata.h @@ -46,6 +46,7 @@ struct log_entry_metadata { fmt::dynamic_format_arg_store* store; std::string log_name; char log_tag; + std::shared_ptr log_label; std::vector hex_dump; }; diff --git a/include/srsran/srslog/log_channel.h b/include/srsran/srslog/log_channel.h index 057613b82e..79ba264372 100644 --- a/include/srsran/srslog/log_channel.h +++ b/include/srsran/srslog/log_channel.h @@ -148,6 +148,38 @@ class log_channel backend.push(std::move(entry)); } + /// Builds the provided log entry and passes it to the backend. When the + /// channel is disabled the log entry will be discarded. + template + void operator()(std::shared_ptr log_label, const char* fmtstr, Args&&... args) + { + if (!enabled()) { + return; + } + + // Populate the store with all incoming arguments. + auto* store = backend.alloc_arg_store(); + if (!store) { + return; + } + (void)std::initializer_list{(push_back(store, std::forward(args)), 0)...}; + + // Send the log entry to the backend. + log_formatter& formatter = log_sink.get_formatter(); + detail::log_entry entry = {&log_sink, + [&formatter](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) { + formatter.format(std::move(metadata), buffer); + }, + {std::chrono::high_resolution_clock::now(), + {ctx_value64, should_print_context}, + fmtstr, + store, + log_name, + log_tag, + std::move(log_label)}}; + backend.push(std::move(entry)); + } + /// Builds the provided log entry and passes it to the backend. When the /// channel is disabled the log entry will be discarded. template @@ -212,6 +244,49 @@ class log_channel store, log_name, log_tag, + {}, + std::vector(buffer, buffer + len)}}; + backend.push(std::move(entry)); + } + + /// Builds the provided log entry and passes it to the backend. When the + /// channel is disabled the log entry will be discarded. + template + void operator()(std::shared_ptr log_label, + const uint8_t* buffer, + size_t len, + const char* fmtstr, + Args&&... args) + { + if (!enabled()) { + return; + } + + // Populate the store with all incoming arguments. + auto* store = backend.alloc_arg_store(); + if (!store) { + return; + } + (void)std::initializer_list{(push_back(store, std::forward(args)), 0)...}; + + // Calculate the length to capture in the buffer. + if (hex_max_size >= 0) { + len = std::min(len, hex_max_size); + } + + // Send the log entry to the backend. + log_formatter& formatter = log_sink.get_formatter(); + detail::log_entry entry = {&log_sink, + [&formatter](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer_) { + formatter.format(std::move(metadata), buffer_); + }, + {std::chrono::high_resolution_clock::now(), + {ctx_value64, should_print_context}, + fmtstr, + store, + log_name, + log_tag, + std::move(log_label), std::vector(buffer, buffer + len)}}; backend.push(std::move(entry)); } @@ -249,6 +324,7 @@ class log_channel store, log_name, log_tag, + {}, std::vector(buffer, buffer + len)}}; backend.push(std::move(entry)); } @@ -286,6 +362,46 @@ class log_channel store, log_name, log_tag, + {}, + std::vector(it_begin, it_end)}}; + backend.push(std::move(entry)); + } + + /// Builds the provided log entry and passes it to the backend. When the + /// channel is disabled the log entry will be discarded. + template ::value, int>::type = 0> + void + operator()(std::shared_ptr log_label, It it_begin, It it_end, const char* fmtstr, Args&&... args) + { + if (!enabled()) { + return; + } + + // Populate the store with all incoming arguments. + auto* store = backend.alloc_arg_store(); + if (!store) { + return; + } + (void)std::initializer_list{(push_back(store, std::forward(args)), 0)...}; + + // Calculate the length to capture in the buffer. + if (hex_max_size >= 0 && hex_max_size < std::distance(it_begin, it_end)) { + it_end = it_begin + hex_max_size; + } + + // Send the log entry to the backend. + log_formatter& formatter = log_sink.get_formatter(); + detail::log_entry entry = {&log_sink, + [&formatter](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) { + formatter.format(std::move(metadata), buffer); + }, + {std::chrono::high_resolution_clock::now(), + {ctx_value64, should_print_context}, + fmtstr, + store, + log_name, + log_tag, + std::move(log_label), std::vector(it_begin, it_end)}}; backend.push(std::move(entry)); } diff --git a/include/srsran/srsvec/accumulate.h b/include/srsran/srsvec/accumulate.h index f070840c14..bc0f956ff8 100644 --- a/include/srsran/srsvec/accumulate.h +++ b/include/srsran/srsvec/accumulate.h @@ -22,10 +22,7 @@ #pragma once -#include "srsran/adt/complex.h" -#include "srsran/srsvec/detail/traits.h" -#include "srsran/support/srsran_assert.h" -#include +#include "srsran/adt/span.h" namespace srsran { namespace srsvec { diff --git a/include/srsran/srsvec/aligned_vec.h b/include/srsran/srsvec/aligned_vec.h index cd31d2765a..8dc63297ea 100644 --- a/include/srsran/srsvec/aligned_vec.h +++ b/include/srsran/srsvec/aligned_vec.h @@ -32,7 +32,7 @@ void* mem_alloc(std::size_t size); void mem_free(void* ptr); } // namespace detail -/// Type to store a dynamic amount of aligned contiguous elements +/// Type to store a dynamic amount of aligned contiguous elements. template class aligned_vec : public span { @@ -44,22 +44,21 @@ class aligned_vec : public span aligned_vec(const aligned_vec& other) = delete; aligned_vec(aligned_vec&& other) noexcept = delete; - aligned_vec() : span(nullptr, 0UL){}; + aligned_vec() = default; explicit aligned_vec(std::size_t size) { resize(size); } + ~aligned_vec() { dealloc(); } - void resize(unsigned new_size) + void resize(std::size_t new_size) { if (new_size == this->size()) { return; } + dealloc(); - T* ptr_ = (T*)detail::mem_alloc(sizeof(T) * new_size); - span o = span(ptr_, new_size); - *(span*)this = o; + T* p = reinterpret_cast(detail::mem_alloc(sizeof(T) * new_size)); + span::operator=(span(p, new_size)); } - - ~aligned_vec() { dealloc(); } }; } // namespace srsvec diff --git a/include/srsran/srsvec/clip.h b/include/srsran/srsvec/clip.h index 4d5e11af90..c38f1a1a6c 100644 --- a/include/srsran/srsvec/clip.h +++ b/include/srsran/srsvec/clip.h @@ -45,7 +45,7 @@ namespace srsvec { /// \param [in] x Input Span. /// \param [in] threshold Clipping threshold. /// \return The number of clipped samples. -unsigned clip(span y, span x, const float threshold); +unsigned clip(span y, span x, float threshold); /// \brief Clips the real and imaginary components of a complex span. /// @@ -68,7 +68,7 @@ unsigned clip(span y, span x, const float threshold); /// \param [in] x Input Span. /// \param [in] threshold Clipping threshold. /// \return The number of clipped samples. -unsigned clip_iq(span y, span x, const float threshold); +unsigned clip_iq(span y, span x, float threshold); /// \brief Clips the magnitude of a complex span. /// @@ -85,7 +85,7 @@ unsigned clip_iq(span y, span x, const float threshold); /// \param [in] x Input Span. /// \param [in] threshold Clipping threshold. /// \return The number of clipped samples. -unsigned clip_magnitude(span y, span x, const float threshold); +unsigned clip_magnitude(span y, span x, float threshold); } // namespace srsvec } // namespace srsran diff --git a/include/srsran/srsvec/compare.h b/include/srsran/srsvec/compare.h index 6e62dd4ff4..af55e4d40a 100644 --- a/include/srsran/srsvec/compare.h +++ b/include/srsran/srsvec/compare.h @@ -29,7 +29,7 @@ namespace srsran { namespace srsvec { namespace detail { -const char* find(span input, char value); +const char* find(span input, const char* value); } template @@ -59,7 +59,9 @@ template const T* find(span input, T value) { static_assert(sizeof(T) == 1, "The datatype must be one byte wide."); - return (const T*)detail::find(span((const char*)input.data(), input.size()), *((char*)&value)); + return reinterpret_cast( + detail::find(span(reinterpret_cast(input.data()), input.size()), + reinterpret_cast(&value))); } /// \brief Finds the maximum absolute value in a complex span. diff --git a/include/srsran/srsvec/division.h b/include/srsran/srsvec/division.h index 8435b9dccb..f2446368fa 100644 --- a/include/srsran/srsvec/division.h +++ b/include/srsran/srsvec/division.h @@ -25,7 +25,6 @@ #include "srsran/srsvec/types.h" namespace srsran { - namespace srsvec { /// \brief Element-wise division between two sequences. diff --git a/include/srsran/srsvec/dot_prod.h b/include/srsran/srsvec/dot_prod.h index 5a0df3749a..43212e6772 100644 --- a/include/srsran/srsvec/dot_prod.h +++ b/include/srsran/srsvec/dot_prod.h @@ -29,7 +29,6 @@ #include "srsran/srsvec/detail/traits.h" #include "srsran/srsvec/types.h" #include "srsran/support/srsran_assert.h" - #include namespace srsran { diff --git a/include/srsran/srsvec/modulus_square.h b/include/srsran/srsvec/modulus_square.h index e119b496a7..656e46b046 100644 --- a/include/srsran/srsvec/modulus_square.h +++ b/include/srsran/srsvec/modulus_square.h @@ -25,7 +25,6 @@ #include "srsran/srsvec/types.h" namespace srsran { - namespace srsvec { /// \brief Calculates the element-wise modulus square of a sequence of complex values. diff --git a/include/srsran/srsvec/types.h b/include/srsran/srsvec/types.h index 71a765818a..1ebeef59a2 100644 --- a/include/srsran/srsvec/types.h +++ b/include/srsran/srsvec/types.h @@ -27,15 +27,18 @@ #include "srsran/support/srsran_assert.h" #include -// The supported vector data types are: -// - srsran::span: For complex float vectors -// - srsran::span: For float vectors -// - srsran::span: For signed 16 bit integer vectors -// - srsran::span: For signed 8 bit integer vectors -// - srsran::span: For unsigned 8 bit integer vectors -// -// To make the span constant use srsran::span. +/// \brief The srsvec library provides optimized SIMD commonly used operations. +/// +/// The supported vector data types are: +/// - srsran::span: For complex float vectors +/// - srsran::span: For float vectors +/// - srsran::span: For signed 16 bit integer vectors +/// - srsran::span: For signed 8 bit integer vectors +/// - srsran::span: For unsigned 8 bit integer vectors +/// +/// To make the span constant use srsran::span. +/// Asserts that both objects have the same size. #define srsran_srsvec_assert_size(X, Y) \ srsran_assert( \ X.size() == Y.size(), "The size " #X " (i.e., {}) and " #Y " (i.e., {}) must be equal.", X.size(), Y.size()) diff --git a/include/srsran/support/async/execute_on.h b/include/srsran/support/async/execute_on.h index 3de1e39e68..5262b88497 100644 --- a/include/srsran/support/async/execute_on.h +++ b/include/srsran/support/async/execute_on.h @@ -55,7 +55,7 @@ auto execute_on(TaskExecutor& exec) private: TaskExecutor& exec; - bool success; + bool success = false; }; return task_executor_awaiter{exec}; @@ -89,7 +89,7 @@ auto defer_to(TaskExecutor& exec) private: TaskExecutor& exec; - bool success; + bool success = false; }; return task_executor_awaiter{exec}; diff --git a/include/srsran/support/cpu_architecture_info.h b/include/srsran/support/cpu_architecture_info.h new file mode 100644 index 0000000000..0f38223c5b --- /dev/null +++ b/include/srsran/support/cpu_architecture_info.h @@ -0,0 +1,94 @@ +/* + * + * 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/bounded_bitset.h" + +namespace srsran { + +/// Class discovering the CPU architecture of the underlying hardware. +class cpu_architecture_info +{ + /// Aggregates CPU information. + struct cpu_description { + /// Set of CPUs as returned by the OS. + cpu_set_t cpuset; + /// Total number of CPUs. + size_t nof_cpus; + /// Number of available CPUs. + size_t nof_available_cpus; + /// Highest index of the available CPU. + size_t max_cpu_id; + /// List of CPUs that the application is allowed to use. + bounded_bitset<1024> allowed_cpus; + /// Number of NUMA nodes. + size_t nof_numa_nodes; + /// List of CPUs pertaining to each NUMA node. + std::vector> node_cpus; + /// Lists of logical CPUs belonging to each physical CPU. + std::vector> logical_cpu_lists; + }; + + /// Discovers CPU architecture at the application start-up. + static cpu_description discover_cpu_architecture(); + + /// Stores the CPU description. + static const cpu_description cpu_desc; + + /// Default constructor. + cpu_architecture_info() = default; + +public: + cpu_architecture_info(const cpu_architecture_info& other) = delete; + cpu_architecture_info(cpu_architecture_info&& other) = delete; + void operator=(const cpu_architecture_info& other) = delete; + void operator=(cpu_architecture_info&& other) = delete; + + /// Returns reference to a static instance of this class. + static cpu_architecture_info& get() + { + /// Singleton object. + static cpu_architecture_info cpu_info; + return cpu_info; + } + + /// Get total number of CPUs discovered in the given host. + size_t get_host_total_nof_cpus() const { return cpu_desc.nof_cpus; } + + /// Get the number of CPUs available to the application. + size_t get_host_nof_available_cpus() const { return cpu_desc.nof_available_cpus; } + + /// Get available CPUs as a cpu_set_t structure. + cpu_set_t get_available_cpuset() const { return cpu_desc.cpuset; } + + /// Prints discovered CPU information. + void print_cpu_info(srslog::basic_logger& logger) const; + + /// Returns CPUs of the given NUMA node. + bounded_bitset<1024> get_node_cpu_mask(unsigned node_id) const; + + /// Run calling thread and its children and allocate memory on the given NUMA node. + bool run_on_numa_node(unsigned node_id) const; +}; + +} // namespace srsran diff --git a/include/srsran/support/executors/strand_executor.h b/include/srsran/support/executors/strand_executor.h index b2560de73f..968c621acd 100644 --- a/include/srsran/support/executors/strand_executor.h +++ b/include/srsran/support/executors/strand_executor.h @@ -247,7 +247,8 @@ class task_strand_with_queue : public base_task_strand } if (run_count != queue_size) { // Unexpected failure to pop enqueued tasks. It might be due to queue shutdown. - srslog::fetch_basic_logger("ALL").info("Couldn't run all pending tasks in strand"); + srslog::fetch_basic_logger("ALL").info( + "Couldn't run all pending tasks in strand. run_count={} queue_size={}", run_count, queue_size); } // We have run all the tasks that were enqueued since when we computed queue_size. @@ -454,4 +455,4 @@ std::vector> make_strand_executor_ptrs(OutExec&& return detail::make_strand_executors_iter_helper(std::forward(out_exec), strand_queues); } -} // namespace srsran \ No newline at end of file +} // namespace srsran diff --git a/include/srsran/support/io/transport_layer_address.h b/include/srsran/support/io/transport_layer_address.h index f786283e9c..cca6d45335 100644 --- a/include/srsran/support/io/transport_layer_address.h +++ b/include/srsran/support/io/transport_layer_address.h @@ -33,30 +33,32 @@ namespace srsran { class transport_layer_address { public: + /// Underlying native type used to store a transport layer address. + using native_type = ::sockaddr_storage; + transport_layer_address() = default; - transport_layer_address(const std::string& ip_str) { from_string(ip_str); } - /// \brief Converts a string with an IPv4 address with format X.X.X.X or with an IPv6 address with - /// format X:X:X:X:X:X:X:X to a transport_layer_address. - transport_layer_address& from_string(const std::string& ip_str); + /// Creates a transport_layer_address object from a string with an IPv4 address with format X.X.X.X or with an IPv6 + /// address with format X:X:X:X:X:X:X:X. + static transport_layer_address create_from_string(const std::string& ip_str); + + /// Creates a transport_layer_address object from a string of bits (each character is base 2). + static transport_layer_address create_from_bitstring(const std::string& bit_str); /// Converts the transport_layer_address to an IPv4 or IPv6 string. std::string to_string() const { return fmt::format("{}", *this); } - /// Converts a string of bits (each character is base 2) to a transport_layer_address. - transport_layer_address& from_bitstring(std::string bit_str); - /// Converts the transport layer address to a string of bits (each character is base 2). std::string to_bitstring() const; - /// Extracts the posix representation of the transport layer address. - const struct sockaddr_storage& native() const { return addr; } - struct sockaddr_storage& native() { return addr; } + /// Extracts the POSIX representation of the transport layer address. + const native_type& native() const { return addr; } + native_type& native() { return addr; } /// Compares two transport_layer_addresses. bool operator==(const transport_layer_address& other) const; bool operator!=(const transport_layer_address& other) const { return not(*this == other); } - bool operator==(const std::string& ip_str) const { return *this == transport_layer_address(ip_str); } + bool operator==(const std::string& ip_str) const { return *this == create_from_string(ip_str); } bool operator!=(const std::string& ip_str) const { return not(*this == ip_str); } bool operator<(const transport_layer_address& other) const; @@ -65,18 +67,20 @@ class transport_layer_address bool operator>(const transport_layer_address& other) const { return not(*this <= other); } private: - struct sockaddr_storage addr; + explicit transport_layer_address(const native_type& addr_) : addr(addr_) {} + +private: + native_type addr; }; } // namespace srsran +namespace std { template <> -struct std::hash { - std::size_t operator()(const srsran::transport_layer_address& s) const noexcept - { - return std::hash{}(s.to_string()); - } +struct hash { + size_t operator()(const srsran::transport_layer_address& s) const noexcept { return hash{}(s.to_string()); } }; +} // namespace std namespace fmt { @@ -87,7 +91,13 @@ struct formatter : public formatter decltype(std::declval().out()) { char ip_addr[NI_MAXHOST]; - if (getnameinfo((sockaddr*)&s.native(), sizeof(s), ip_addr, NI_MAXHOST, nullptr, 0, NI_NUMERICHOST) != 0) { + if (::getnameinfo(reinterpret_cast(&s.native()), + sizeof(srsran::transport_layer_address::native_type), + ip_addr, + sizeof(ip_addr), + nullptr, + 0, + NI_NUMERICHOST) != 0) { return format_to(ctx.out(), "invalid_addr"); } diff --git a/include/srsran/support/memory_pool/fixed_size_memory_block_pool.h b/include/srsran/support/memory_pool/fixed_size_memory_block_pool.h index 6bc3d4e415..799ad29ab5 100644 --- a/include/srsran/support/memory_pool/fixed_size_memory_block_pool.h +++ b/include/srsran/support/memory_pool/fixed_size_memory_block_pool.h @@ -221,6 +221,13 @@ class fixed_size_memory_block_pool return (w->local_cache.size() - 1) * block_batch_size + w->local_cache.back().size(); } + /// Check if the memory pool owns the given memory segment. + bool owns_segment(void* segment) const + { + uint8_t* ptr = static_cast(segment); + return ptr >= allocated_memory.data() and ptr < allocated_memory.data() + allocated_memory.size(); + } + private: struct worker_ctxt { /// Thread ID of the worker. diff --git a/include/srsran/support/memory_pool/linear_memory_allocator.h b/include/srsran/support/memory_pool/linear_memory_allocator.h new file mode 100644 index 0000000000..a3b896fd49 --- /dev/null +++ b/include/srsran/support/memory_pool/linear_memory_allocator.h @@ -0,0 +1,63 @@ +/* + * + * 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/support/memory_pool/memory_pool_utils.h" +#include "srsran/support/srsran_assert.h" +#include + +namespace srsran { + +/// \brief Linear allocator for a contiguous memory block. +/// +/// This type of allocator doesn't provide a deallocation mechanism. +class linear_memory_allocator +{ +public: + linear_memory_allocator(void* memory_resource, std::size_t memory_resource_size) : + mem_start(static_cast(memory_resource)), mem_res_size(memory_resource_size) + { + srsran_sanity_check(mem_start != nullptr and mem_res_size != 0, "Invalid memory resource"); + } + + std::size_t size() const { return mem_res_size; } + std::size_t nof_bytes_left() const { return mem_res_size - mem_offset; } + std::size_t nof_bytes_allocated() const { return mem_offset; } + + void* memory_resource_start() const { return mem_start; } + + void* allocate(std::size_t sz, std::size_t al) noexcept + { + void* p = align_next(mem_start + mem_offset, al); + mem_offset = (static_cast(p) - mem_start) + sz; + srsran_sanity_check(mem_offset <= mem_res_size, "Out of memory"); + return p; + } + +private: + uint8_t* mem_start; + std::size_t mem_res_size; + std::size_t mem_offset = 0; +}; + +} // namespace srsran diff --git a/include/srsran/support/prefixed_logger.h b/include/srsran/support/prefixed_logger.h index d5a896426f..8a073cd83f 100644 --- a/include/srsran/support/prefixed_logger.h +++ b/include/srsran/support/prefixed_logger.h @@ -37,13 +37,18 @@ template class prefixed_logger { public: - prefixed_logger(const std::string& log_name, Prefix prefix_, const char* prefix_separator_ = "") : - logger(srslog::fetch_basic_logger(log_name, false)), prefix(prefix_), prefix_separator(prefix_separator_) + prefixed_logger(const std::string& log_name, Prefix prefix, const char* prefix_separator = "") : + logger(srslog::fetch_basic_logger(log_name, false)) { + set_prefix(prefix, prefix_separator); } - void set_prefix(Prefix prefix_) { prefix = prefix_; } - Prefix get_prefix() const { return prefix; } + void set_prefix(Prefix prefix, const char* prefix_separator = "") + { + fmt::memory_buffer buffer; + fmt::format_to(buffer, "{}{}", prefix, prefix_separator); + log_label = std::make_shared(fmt::to_string(buffer)); + } template void log_debug(const char* fmt, Args&&... args) const @@ -206,9 +211,8 @@ class prefixed_logger srslog::basic_logger& get_basic_logger() { return logger; } private: - srslog::basic_logger& logger; - Prefix prefix; - const char* prefix_separator; + srslog::basic_logger& logger; + std::shared_ptr log_label; template void log_helper(srslog::log_channel& channel, const char* fmt, Args&&... args) const @@ -216,9 +220,7 @@ class prefixed_logger if (!channel.enabled()) { return; } - fmt::memory_buffer buffer; - fmt::format_to(buffer, fmt, std::forward(args)...); - channel("{}{}{}", prefix, prefix_separator, to_c_str(buffer)); + channel(log_label, fmt, std::forward(args)...); } template @@ -227,9 +229,7 @@ class prefixed_logger if (!channel.enabled()) { return; } - fmt::memory_buffer buffer; - fmt::format_to(buffer, fmt, std::forward(args)...); - channel(it_begin, it_end, "{}{}{}", prefix, prefix_separator, to_c_str(buffer)); + channel(log_label, it_begin, it_end, fmt, std::forward(args)...); } template @@ -238,9 +238,7 @@ class prefixed_logger if (!channel.enabled()) { return; } - fmt::memory_buffer buffer; - fmt::format_to(buffer, fmt, std::forward(args)...); - channel(msg, len, "{}{}{}", prefix, prefix_separator, to_c_str(buffer)); + channel(log_label, msg, len, fmt, std::forward(args)...); } }; diff --git a/include/srsran/support/unique_thread.h b/include/srsran/support/unique_thread.h index 031d25dd6e..5b3f02f3e3 100644 --- a/include/srsran/support/unique_thread.h +++ b/include/srsran/support/unique_thread.h @@ -22,6 +22,7 @@ #pragma once +#include "cpu_architecture_info.h" #include "srsran/adt/bounded_bitset.h" #include "srsran/adt/strong_type.h" #include "srsran/adt/unique_function.h" @@ -30,12 +31,6 @@ namespace srsran { -/// Computes the number of threads that are usable in the given host. -size_t compute_host_nof_hardware_threads(); - -/// Get maximum CPU ID available to the application. -size_t get_host_max_cpu_id(); - /// OS thread RT scheduling priority. /// Note: posix defines a minimum spread between sched_get_priority_max() and sched_get_priority_min() of 32. struct os_thread_realtime_priority_tag {}; @@ -112,9 +107,13 @@ struct os_sched_affinity_bitmask { public: static constexpr size_t MAX_CPUS = 1024; - os_sched_affinity_bitmask() : cpu_bitset(get_host_max_cpu_id() + 1) {} + os_sched_affinity_bitmask() : cpu_bitset(cpu_architecture_info::get().get_host_total_nof_cpus()) {} - explicit os_sched_affinity_bitmask(size_t cpu_idx) : cpu_bitset(get_host_max_cpu_id() + 1) { set(cpu_idx); } + explicit os_sched_affinity_bitmask(size_t cpu_idx) : + cpu_bitset(cpu_architecture_info::get().get_host_total_nof_cpus()) + { + set(cpu_idx); + } os_sched_affinity_bitmask(size_t bitset_size, size_t cpu_idx) : cpu_bitset(bitset_size) { set(cpu_idx); } diff --git a/lib/asn1/asn1_utils.cpp b/lib/asn1/asn1_utils.cpp index 4108441e02..53d85eede6 100644 --- a/lib/asn1/asn1_utils.cpp +++ b/lib/asn1/asn1_utils.cpp @@ -941,7 +941,7 @@ uint64_t octet_string_helper::to_uint(const byte_buffer& buf) void octet_string_helper::to_octet_string(srsran::span buf, uint64_t number) { uint64_t nbytes = buf.size(); - if ((static_cast(1U) << (8U * nbytes)) <= number) { + if (nbytes < 8 and (static_cast(1U) << (8U * nbytes)) <= number) { log_error("Integer={} does not fit in an OCTET STRING of size={}", number, nbytes); return; } @@ -952,7 +952,7 @@ void octet_string_helper::to_octet_string(srsran::span buf, uint64_t nu void octet_string_helper::to_octet_string(srsran::byte_buffer& buf, uint64_t number) { - buf.clear(); + buf = byte_buffer{byte_buffer::fallback_allocation_tag{}}; size_t nbytes = sizeof(number); for (uint32_t i = 0; i < nbytes; ++i) { if (not buf.append((number >> (uint64_t)((nbytes - 1 - i) * 8U)) & 0xffu)) { @@ -997,7 +997,7 @@ unsigned octet_string_helper::hex_string_to_octets(srsran::span buf, co { srsran_assert(buf.size() >= ceil_frac(str.size(), (size_t)2U), "out-of-bounds access"); if (str.size() % 2 != 0) { - log_warning("The provided hex string size={} is not a multiple of 2.", str.size()); + log_error("The provided hex string size={} is not a multiple of 2.", str.size()); } char cstr[] = "\0\0\0"; for (unsigned i = 0; i < str.size(); i += 2) { @@ -1010,7 +1010,7 @@ unsigned octet_string_helper::hex_string_to_octets(srsran::span buf, co void octet_string_helper::append_hex_string(byte_buffer& buf, const std::string& str) { if (str.size() % 2 != 0) { - log_warning("The provided hex string size={} is not a multiple of 2.", str.size()); + log_error("The provided hex string size={} is not a multiple of 2.", str.size()); } char cstr[] = "\0\0\0"; for (unsigned i = 0; i < str.size(); i += 2) { @@ -1025,11 +1025,19 @@ void octet_string_helper::append_hex_string(byte_buffer& buf, const std::string& dyn_octstring ************************/ +template +unbounded_octstring::unbounded_octstring(const unbounded_octstring& other) noexcept : + // Use fallback allocator, because operator= should never fail. + srsran::byte_buffer(fallback_allocation_tag{}, other) +{ +} + template unbounded_octstring& unbounded_octstring::operator=(const unbounded_octstring& other) noexcept { if (this != &other) { - *static_cast(this) = other.deep_copy(); + // Use fallback allocator, because operator= should never fail. + *this = byte_buffer{fallback_allocation_tag{}, other}; } return *this; } @@ -1046,7 +1054,7 @@ SRSASN_CODE unbounded_octstring::pack(bit_ref& bref) const { HANDLE_CODE(pack_length(bref, length(), aligned)); for (uint8_t b : *this) { - bref.pack(b, 8); + HANDLE_CODE(bref.pack(b, 8)); } return SRSASN_SUCCESS; } @@ -1074,8 +1082,12 @@ std::string unbounded_octstring::to_string() const template unbounded_octstring& unbounded_octstring::from_string(const std::string& hexstr) { - this->clear(); + // clears previous buffer. + *this = byte_buffer{byte_buffer::fallback_allocation_tag{}}; + + // appends hex string to buffer. octet_string_helper::append_hex_string(*this, hexstr); + return *this; } diff --git a/lib/cu_cp/CMakeLists.txt b/lib/cu_cp/CMakeLists.txt index b7640dad71..57665665f8 100644 --- a/lib/cu_cp/CMakeLists.txt +++ b/lib/cu_cp/CMakeLists.txt @@ -57,4 +57,4 @@ set(SOURCES add_library(srsran_cu_cp STATIC ${SOURCES}) -target_link_libraries(srsran_cu_cp srsran_cu_cp_cell_meas_manager srsran_cu_cp_mobility_manager srslog srsran_support srsran_e1ap srsran_f1ap_cu srsran_ngap srsran_rrc srsran_security) +target_link_libraries(srsran_cu_cp srsran_cu_cp_cell_meas_manager srsran_cu_cp_mobility_manager srsran_e1ap srsran_f1ap_cu srsran_ngap srsran_rrc srsran_ran srslog srsran_support srsran_security) diff --git a/lib/cu_cp/adapters/rrc_ue_adapters.h b/lib/cu_cp/adapters/rrc_ue_adapters.h index c41a6a472b..485fc42265 100644 --- a/lib/cu_cp/adapters/rrc_ue_adapters.h +++ b/lib/cu_cp/adapters/rrc_ue_adapters.h @@ -204,11 +204,12 @@ class rrc_ue_cu_cp_adapter : public rrc_ue_context_update_notifier, public rrc_u return ue_removal_handler->handle_ue_removal_request(ue_index); } - optional on_measurement_config_request(nr_cell_id_t nci, + optional on_measurement_config_request(ue_index_t ue_index, + nr_cell_id_t nci, optional current_meas_config = {}) override { srsran_assert(meas_handler != nullptr, "Measurement handler must not be nullptr"); - return meas_handler->handle_measurement_config_request(nci, current_meas_config); + return meas_handler->handle_measurement_config_request(ue_index, nci, current_meas_config); } void on_measurement_report(const ue_index_t ue_index, const rrc_meas_results& meas_results) override diff --git a/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.cpp b/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.cpp index 85642166b3..6c3ef7d9bd 100644 --- a/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.cpp +++ b/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.cpp @@ -32,13 +32,10 @@ cell_meas_manager::cell_meas_manager(const cell_meas_manager_cfg& cfg_, { srsran_assert(is_valid_configuration(cfg), "Invalid cell measurement configuration"); log_cells(logger, cfg); - - // Mark zero index as occupied as the first valid meas(-obj) ID is 1. - meas_ids.emplace(0); - meas_obj_ids.emplace(0); } -optional cell_meas_manager::get_measurement_config(nr_cell_id_t serving_nci, +optional cell_meas_manager::get_measurement_config(ue_index_t ue_index, + nr_cell_id_t serving_nci, optional current_meas_config) { optional meas_cfg; @@ -52,10 +49,19 @@ optional cell_meas_manager::get_measurement_config(nr_cell_id_t // Measurement config is only generated if neighbor cells are configured or serving cell measurements are enabled. if (cell_config.ncells.empty() and !cell_config.periodic_report_cfg_id.has_value()) { - logger.debug("No neighbor cells configured and periodic serving cell reports disabled for nci={}", serving_nci); + logger.debug("ue={}: No neighbor cells configured and periodic serving cell reports disabled for nci={}", + ue_index, + serving_nci); return meas_cfg; } + // If no ue context for the current UE exists, create a new one. + if (ue_contexts.find(ue_index) == ue_contexts.end()) { + logger.debug("ue={}: Creating new measurement context", ue_index); + ue_contexts.emplace(ue_index, cell_meas_manager_ue_context{}); + } + auto& ue_context = ue_contexts.at(ue_index); + // Create fresh config. meas_cfg.emplace(); auto& new_cfg = meas_cfg.value(); @@ -66,15 +72,17 @@ optional cell_meas_manager::get_measurement_config(nr_cell_id_t // Remove measurement objects. for (const auto& meas_obj : old_cfg.meas_obj_to_add_mod_list) { - logger.debug("Removing meas_obj_id={}", meas_obj.meas_obj_id); - meas_obj_ids.erase(meas_obj_id_to_uint(meas_obj.meas_obj_id)); + logger.debug("ue={}: Removing meas_obj_id={}", ue_index, meas_obj.meas_obj_id); + ue_context.remove_meas_obj_id(meas_obj.meas_obj_id); + ue_context.meas_obj_id_to_nci.erase(meas_obj.meas_obj_id); new_cfg.meas_obj_to_rem_list.push_back(meas_obj.meas_obj_id); } // Remove measurement IDs. for (const auto& meas_id : old_cfg.meas_id_to_add_mod_list) { - logger.debug("Removing meas_id={}", meas_id.meas_id); - meas_ids.erase(meas_id_to_uint(meas_id.meas_id)); + logger.debug("ue={}: Removing meas_id={}", ue_index, meas_id.meas_id); + ue_context.remove_meas_id(meas_id.meas_id); + ue_context.meas_id_to_meas_context.erase(meas_id.meas_id); new_cfg.meas_id_to_rem_list.push_back(meas_id.meas_id); } @@ -84,12 +92,63 @@ optional cell_meas_manager::get_measurement_config(nr_cell_id_t } } + // Add periodic report configuration for the serving cell. + // Create meas object for serving cell if periodic report cfg is set + if (cell_config.periodic_report_cfg_id.has_value()) { + if (ue_context.meas_obj_ids.size() == MAX_NOF_MEAS_OBJ) { + logger.warning("ue={}: Can't add periodical report config for serving cell. Maximum ({}) reached", + ue_index, + MAX_NOF_MEAS_OBJ); + } else { + // add meas obj to add mod + rrc_meas_obj_to_add_mod meas_obj; + meas_obj.meas_obj_id = ue_context.allocate_meas_obj_id(); + auto& meas_obj_nr = meas_obj.meas_obj_nr.emplace(); + meas_obj_nr.ssb_freq = cell_config.serving_cell_cfg.ssb_arfcn; + meas_obj_nr.ssb_subcarrier_spacing = cell_config.serving_cell_cfg.ssb_scs; + meas_obj_nr.smtc1 = cell_config.serving_cell_cfg.ssb_mtc; + + // Mandatory fields. + meas_obj_nr.ref_sig_cfg.ssb_cfg_mob.emplace().derive_ssb_idx_from_cell = true; + meas_obj_nr.nrof_ss_blocks_to_average.emplace() = 8; + meas_obj_nr.quant_cfg_idx = 1; + meas_obj_nr.freq_band_ind_nr.emplace() = nr_band_to_uint(cell_config.serving_cell_cfg.band.value()); + + new_cfg.meas_obj_to_add_mod_list.push_back(meas_obj); + + // add meas obj id to lookup + ue_context.meas_obj_id_to_nci.emplace(meas_obj.meas_obj_id, serving_nci); + + // add report cfg to add mod + rrc_report_cfg_to_add_mod report_cfg_to_add_mod; + report_cfg_to_add_mod.report_cfg_id = cell_config.periodic_report_cfg_id.value(); + report_cfg_to_add_mod.report_cfg = cfg.report_config_ids.at(cell_config.periodic_report_cfg_id.value()); + + new_cfg.report_cfg_to_add_mod_list.push_back(report_cfg_to_add_mod); + + // Add meas id to link the neighbor cell and the report together. + rrc_meas_id_to_add_mod meas_id_to_add_mod; + + meas_id_to_add_mod.meas_id = ue_context.allocate_meas_id(); + meas_id_to_add_mod.meas_obj_id = meas_obj.meas_obj_id; + meas_id_to_add_mod.report_cfg_id = cell_config.periodic_report_cfg_id.value(); + + new_cfg.meas_id_to_add_mod_list.push_back(meas_id_to_add_mod); + + // add meas id to lookup + ue_context.meas_id_to_meas_context.emplace( + meas_id_to_add_mod.meas_id, meas_context_t{meas_id_to_add_mod.meas_obj_id, meas_id_to_add_mod.report_cfg_id}); + } + } + // Create meas object for each neighbor cell for (const auto& ncell : cell_config.ncells) { // Verify we have a complete config for this cell. if (!is_complete(cfg.cells.at(ncell.nci).serving_cell_cfg)) { - logger.debug( - "Cell {} is neighbor of {} but skipping due to missing measurement parameters", ncell.nci, serving_nci); + logger.debug("ue={}: Cell {} is neighbor of {} but skipping due to missing measurement parameters", + ue_index, + ncell.nci, + serving_nci); continue; } srsran_assert(cfg.cells.find(ncell.nci) != cfg.cells.end(), "Cell id {} not found in list", ncell.nci); @@ -97,7 +156,7 @@ optional cell_meas_manager::get_measurement_config(nr_cell_id_t // add meas obj to add mod rrc_meas_obj_to_add_mod meas_obj; - meas_obj.meas_obj_id = get_next_meas_obj_id(); + meas_obj.meas_obj_id = ue_context.allocate_meas_obj_id(); meas_obj.meas_obj_nr.emplace(); auto& meas_obj_nr = meas_obj.meas_obj_nr.value(); meas_obj_nr.ssb_freq = meas_cell_config.serving_cell_cfg.ssb_arfcn; @@ -113,7 +172,7 @@ optional cell_meas_manager::get_measurement_config(nr_cell_id_t new_cfg.meas_obj_to_add_mod_list.push_back(meas_obj); // add meas obj id to lookup - meas_obj_id_to_nci.emplace(meas_obj.meas_obj_id, ncell.nci); + ue_context.meas_obj_id_to_nci.emplace(meas_obj.meas_obj_id, ncell.nci); // add report cfg to add mod for (const auto& report_cfg_id : ncell.report_cfg_ids) { @@ -126,62 +185,15 @@ optional cell_meas_manager::get_measurement_config(nr_cell_id_t // Add meas id to link the neighbor cell and the report together. rrc_meas_id_to_add_mod meas_id_to_add_mod; - meas_id_to_add_mod.meas_id = get_next_meas_id(); + meas_id_to_add_mod.meas_id = ue_context.allocate_meas_id(); meas_id_to_add_mod.meas_obj_id = meas_obj.meas_obj_id; meas_id_to_add_mod.report_cfg_id = report_cfg_id; new_cfg.meas_id_to_add_mod_list.push_back(meas_id_to_add_mod); // add meas id to lookup - meas_id_to_meas_context.emplace(meas_id_to_add_mod.meas_id, - meas_context_t{meas_id_to_add_mod.meas_obj_id, meas_id_to_add_mod.report_cfg_id}); - } - } - - // Add periodic report configuration for the serving cell. - // Create meas object for serving cell if periodic report cfg is set - if (cell_config.periodic_report_cfg_id.has_value()) { - if (meas_obj_id_to_nci.size() == MAX_NOF_MEAS_OBJ) { - logger.warning("Can't add periodical report config for serving cell. Maximum ({}) reached", MAX_NOF_MEAS_OBJ); - } else { - // add meas obj to add mod - rrc_meas_obj_to_add_mod meas_obj; - meas_obj.meas_obj_id = get_next_meas_obj_id(); - auto& meas_obj_nr = meas_obj.meas_obj_nr.emplace(); - meas_obj_nr.ssb_freq = cell_config.serving_cell_cfg.ssb_arfcn; - meas_obj_nr.ssb_subcarrier_spacing = cell_config.serving_cell_cfg.ssb_scs; - meas_obj_nr.smtc1 = cell_config.serving_cell_cfg.ssb_mtc; - - // Mandatory fields. - meas_obj_nr.ref_sig_cfg.ssb_cfg_mob.emplace().derive_ssb_idx_from_cell = true; - meas_obj_nr.nrof_ss_blocks_to_average.emplace() = 8; - meas_obj_nr.quant_cfg_idx = 1; - meas_obj_nr.freq_band_ind_nr.emplace() = nr_band_to_uint(cell_config.serving_cell_cfg.band.value()); - - new_cfg.meas_obj_to_add_mod_list.push_back(meas_obj); - - // add meas obj id to lookup - meas_obj_id_to_nci.emplace(meas_obj.meas_obj_id, serving_nci); - - // add report cfg to add mod - rrc_report_cfg_to_add_mod report_cfg_to_add_mod; - report_cfg_to_add_mod.report_cfg_id = cell_config.periodic_report_cfg_id.value(); - report_cfg_to_add_mod.report_cfg = cfg.report_config_ids.at(cell_config.periodic_report_cfg_id.value()); - - new_cfg.report_cfg_to_add_mod_list.push_back(report_cfg_to_add_mod); - - // Add meas id to link the neighbor cell and the report together. - rrc_meas_id_to_add_mod meas_id_to_add_mod; - - meas_id_to_add_mod.meas_id = get_next_meas_id(); - meas_id_to_add_mod.meas_obj_id = meas_obj.meas_obj_id; - meas_id_to_add_mod.report_cfg_id = cell_config.periodic_report_cfg_id.value(); - - new_cfg.meas_id_to_add_mod_list.push_back(meas_id_to_add_mod); - - // add meas id to lookup - meas_id_to_meas_context.emplace(meas_id_to_add_mod.meas_id, - meas_context_t{meas_id_to_add_mod.meas_obj_id, meas_id_to_add_mod.report_cfg_id}); + ue_context.meas_id_to_meas_context.emplace( + meas_id_to_add_mod.meas_id, meas_context_t{meas_id_to_add_mod.meas_obj_id, meas_id_to_add_mod.report_cfg_id}); } } @@ -206,16 +218,16 @@ optional cell_meas_manager::get_cell_config(nr_cell_id_t nci) return cell_cfg; } -void cell_meas_manager::update_cell_config(nr_cell_id_t nci, - const serving_cell_meas_config& serv_cell_cfg_, - std::vector ncells_) +void cell_meas_manager::update_cell_config(nr_cell_id_t nci, + const serving_cell_meas_config& serv_cell_cfg_, + const std::vector& ncells_) { if (!is_complete(serv_cell_cfg_)) { logger.debug("Updating incomplete cell measurement configuration for nci={}", nci); } if (cfg.cells.find(nci) == cfg.cells.end()) { - logger.debug("No configuration to update for nci={}. Adding configuration.", nci); + logger.debug("No configuration to update for nci={}. Adding configuration", nci); cell_meas_config meas_cfg; meas_cfg.serving_cell_cfg = serv_cell_cfg_; @@ -223,7 +235,7 @@ void cell_meas_manager::update_cell_config(nr_cell_id_t cfg.cells.emplace(nci, meas_cfg); } else { - logger.debug("Updating measurement configuration for nci={}.", nci); + logger.debug("Updating measurement configuration for nci={}", nci); // Update serving cell config cfg.cells.at(nci).serving_cell_cfg = serv_cell_cfg_; @@ -248,13 +260,20 @@ optional get_ssb_rsrp(const rrc_meas_result_nr& meas_result) return rsrp; } -void cell_meas_manager::report_measurement(const ue_index_t ue_index, const rrc_meas_results& meas_results) +void cell_meas_manager::report_measurement(ue_index_t ue_index, const rrc_meas_results& meas_results) { logger.debug("ue={} Received measurement result with meas_id={}", ue_index, meas_results.meas_id); + // Verify measurement context exists for this UE. + if (ue_contexts.find(ue_index) == ue_contexts.end()) { + logger.debug("ue={}: No measurement context found", ue_index); + return; + } + auto& ue_context = ue_contexts.at(ue_index); + // Verify meas_id is valid. - if (meas_id_to_meas_context.find(meas_results.meas_id) == meas_id_to_meas_context.end()) { - logger.debug("ue={} Measurement result for unknown meas_id={} received.", ue_index, meas_results.meas_id); + if (ue_context.meas_id_to_meas_context.find(meas_results.meas_id) == ue_context.meas_id_to_meas_context.end()) { + logger.debug("ue={} Measurement result for unknown meas_id={} received", ue_index, meas_results.meas_id); return; } @@ -301,7 +320,8 @@ void cell_meas_manager::report_measurement(const ue_index_t ue_index, const rrc_ if (strongest_neighbor.has_value()) { // Report cell. - logger.info("Neighbor PCI={} (ssb_rsrp={}) stronger than current serving cell (ssb_rsrp={})", + logger.info("ue={}: Neighbor PCI={} (ssb_rsrp={}) stronger than current serving cell (ssb_rsrp={})", + ue_index, strongest_neighbor.value(), max_rsrp, serv_cell_rsrp.value()); @@ -312,34 +332,13 @@ void cell_meas_manager::report_measurement(const ue_index_t ue_index, const rrc_ } } -/// \brief Get the next available meas id. -meas_id_t cell_meas_manager::get_next_meas_id() +void cell_meas_manager::remove_ue_context(ue_index_t ue_index) { - // return invalid when no meas id is available - if (meas_id_to_meas_context.size() == MAX_NOF_MEAS) { - return meas_id_t::invalid; - } - - meas_id_t new_meas_id = (meas_id_t)meas_ids.find_first_empty(); - if (new_meas_id == meas_id_t::max) { - return meas_id_t::invalid; - } - meas_ids.emplace(meas_id_to_uint(new_meas_id)); - return new_meas_id; -} - -/// \brief Get the next available meas obj id. -meas_obj_id_t cell_meas_manager::get_next_meas_obj_id() -{ - // return invalid when no meas obj id is available - if (meas_obj_id_to_nci.size() == MAX_NOF_MEAS_OBJ) { - return meas_obj_id_t::invalid; + if (ue_contexts.find(ue_index) == ue_contexts.end()) { + logger.debug("ue={}: Can't remove UE measurement context. Cause: Context not found", ue_index); + return; } - meas_obj_id_t new_meas_obj_id = (meas_obj_id_t)meas_obj_ids.find_first_empty(); - if (new_meas_obj_id == meas_obj_id_t::max) { - return meas_obj_id_t::invalid; - } - meas_obj_ids.emplace(meas_obj_id_to_uint(new_meas_obj_id)); - return new_meas_obj_id; + logger.debug("ue={}: Measurement context removed", ue_index); + ue_contexts.erase(ue_index); } diff --git a/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.h b/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.h index 6130d05292..3f519f9875 100644 --- a/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.h +++ b/lib/cu_cp/cell_meas_manager/cell_meas_manager_impl.h @@ -39,35 +39,81 @@ class cell_meas_mobility_manager_notifier virtual void on_neighbor_better_than_spcell(ue_index_t ue_index, pci_t neighbor_pci) = 0; }; +class cell_meas_manager_ue_context +{ +public: + /// \brief Get the next available meas id. + meas_id_t allocate_meas_id() + { + // return invalid when no meas id is available + if (meas_ids.size() == MAX_NOF_MEAS) { + return meas_id_t::invalid; + } + + auto new_meas_id = (meas_id_t)meas_ids.find_first_empty(); + if (new_meas_id == meas_id_t::max) { + return meas_id_t::invalid; + } + meas_ids.emplace(meas_id_to_uint(new_meas_id)); + return new_meas_id; + } + + void remove_meas_id(meas_id_t meas_id) { meas_ids.erase(meas_id_to_uint(meas_id)); } + + /// \brief Get the next available meas obj id. + meas_obj_id_t allocate_meas_obj_id() + { + // return invalid when no meas obj id is available + if (meas_obj_ids.size() == MAX_NOF_MEAS_OBJ) { + return meas_obj_id_t::invalid; + } + + auto new_meas_obj_id = (meas_obj_id_t)meas_obj_ids.find_first_empty(); + if (new_meas_obj_id == meas_obj_id_t::max) { + return meas_obj_id_t::invalid; + } + meas_obj_ids.emplace(meas_obj_id_to_uint(new_meas_obj_id)); + return new_meas_obj_id; + } + + void remove_meas_obj_id(meas_obj_id_t meas_obj_id) { meas_obj_ids.erase(meas_obj_id_to_uint(meas_obj_id)); } + + slotted_array meas_ids; // 0 is reserved for invalid meas_id + slotted_array meas_obj_ids; // 0 is reserved for invalid meas_obj_id + + std::map meas_id_to_meas_context; + std::map meas_obj_id_to_nci; + + cell_meas_manager_ue_context() + { + // Mark zero index as occupied as the first valid meas(-obj) ID is 1. + meas_ids.emplace(0); + meas_obj_ids.emplace(0); + } +}; + /// Basic cell manager implementation class cell_meas_manager { public: - cell_meas_manager(const cell_meas_manager_cfg& cfg, cell_meas_mobility_manager_notifier& mobility_mng_notfier_); + cell_meas_manager(const cell_meas_manager_cfg& cfg_, cell_meas_mobility_manager_notifier& mobility_mng_notifier_); ~cell_meas_manager() = default; - optional get_measurement_config(nr_cell_id_t nci, optional current_meas_config = {}); + optional + get_measurement_config(ue_index_t ue_index, nr_cell_id_t nci, optional current_meas_config = {}); optional get_cell_config(nr_cell_id_t nci); - void update_cell_config(nr_cell_id_t nci, - const serving_cell_meas_config& serv_cell_cfg_, - std::vector ncells_ = {}); - void report_measurement(const ue_index_t ue_index, const rrc_meas_results& meas_results); + void update_cell_config(nr_cell_id_t nci, + const serving_cell_meas_config& serv_cell_cfg_, + const std::vector& ncells_ = {}); + void report_measurement(ue_index_t ue_index, const rrc_meas_results& meas_results); - /// \brief Get the next available meas_id. - meas_id_t get_next_meas_id(); - - /// \brief Get the next available meas_obj_id. - meas_obj_id_t get_next_meas_obj_id(); + void remove_ue_context(ue_index_t ue_index); private: cell_meas_manager_cfg cfg; cell_meas_mobility_manager_notifier& mobility_mng_notifier; - slotted_array meas_ids; - slotted_array meas_obj_ids; - - std::map meas_id_to_meas_context; - std::map meas_obj_id_to_nci; + std::map ue_contexts; srslog::basic_logger& logger; }; diff --git a/lib/cu_cp/cu_cp_impl.cpp b/lib/cu_cp/cu_cp_impl.cpp index 6ace1bdfc5..e9ddd25c54 100644 --- a/lib/cu_cp/cu_cp_impl.cpp +++ b/lib/cu_cp/cu_cp_impl.cpp @@ -279,10 +279,11 @@ void cu_cp_impl::handle_handover_ue_context_push(ue_index_t source_ue_index, ue_ cu_up_db.get_cu_up(uint_to_cu_up_index(0)).update_ue_index(target_ue_index, source_ue_index); } -optional cu_cp_impl::handle_measurement_config_request(nr_cell_id_t nci, +optional cu_cp_impl::handle_measurement_config_request(ue_index_t ue_index, + nr_cell_id_t nci, optional current_meas_config) { - return cell_meas_mng.get_measurement_config(nci, current_meas_config); + return cell_meas_mng.get_measurement_config(ue_index, nci, current_meas_config); } void cu_cp_impl::handle_measurement_report(const ue_index_t ue_index, const rrc_meas_results& meas_results) @@ -307,6 +308,7 @@ void cu_cp_impl::handle_ue_removal_request(ue_index_t ue_index) e1_adapter != e1ap_adapters.end() ? &e1_adapter->second : nullptr, f1ap_adapters.at(du_index), ngap_adapter, + cell_meas_mng, ue_mng, logger); } diff --git a/lib/cu_cp/cu_cp_impl.h b/lib/cu_cp/cu_cp_impl.h index d21cb0785f..895ce8c3da 100644 --- a/lib/cu_cp/cu_cp_impl.h +++ b/lib/cu_cp/cu_cp_impl.h @@ -74,7 +74,8 @@ class cu_cp_impl final : public cu_cp, public cu_cp_impl_interface, public cu_cp void handle_handover_ue_context_push(ue_index_t source_ue_index, ue_index_t target_ue_index) override; // cu_cp_measurement_handler - optional handle_measurement_config_request(nr_cell_id_t nci, + optional handle_measurement_config_request(ue_index_t ue_index, + nr_cell_id_t nci, optional current_meas_config = {}) override; void handle_measurement_report(const ue_index_t ue_index, const rrc_meas_results& meas_results) override; diff --git a/lib/cu_cp/cu_cp_impl_interface.h b/lib/cu_cp/cu_cp_impl_interface.h index eacd844ed9..6690f12b21 100644 --- a/lib/cu_cp/cu_cp_impl_interface.h +++ b/lib/cu_cp/cu_cp_impl_interface.h @@ -238,9 +238,11 @@ class cu_cp_measurement_handler virtual ~cu_cp_measurement_handler() = default; /// \brief Handle a measurement config request (for any UE) connected to the given serving cell. + /// \param[in] ue_index The index of the UE to update the measurement config for. /// \param[in] nci The cell id of the serving cell to update. /// \param[in] current_meas_config The current meas config of the UE (if applicable). - virtual optional handle_measurement_config_request(nr_cell_id_t nci, + virtual optional handle_measurement_config_request(ue_index_t ue_index, + nr_cell_id_t nci, optional current_meas_config = {}) = 0; /// \brief Handle a measurement report for given UE. diff --git a/lib/cu_cp/cu_up_processor/cu_up_processor_impl.cpp b/lib/cu_cp/cu_up_processor/cu_up_processor_impl.cpp index f15279abd1..96bc786ff1 100644 --- a/lib/cu_cp/cu_up_processor/cu_up_processor_impl.cpp +++ b/lib/cu_cp/cu_up_processor/cu_up_processor_impl.cpp @@ -73,7 +73,7 @@ void cu_up_processor_impl::send_cu_up_e1_setup_response() e1ap->handle_cu_up_e1_setup_response(response); } -void cu_up_processor_impl::send_cu_up_e1_setup_failure(cause_t cause) +void cu_up_processor_impl::send_cu_up_e1_setup_failure(e1ap_cause_t cause) { cu_up_e1_setup_response response; response.success = false; diff --git a/lib/cu_cp/cu_up_processor/cu_up_processor_impl.h b/lib/cu_cp/cu_up_processor/cu_up_processor_impl.h index bb0f4cde18..34e344e12a 100644 --- a/lib/cu_cp/cu_up_processor/cu_up_processor_impl.h +++ b/lib/cu_cp/cu_up_processor/cu_up_processor_impl.h @@ -67,7 +67,7 @@ class cu_up_processor_impl : public cu_up_processor_impl_interface /// \brief Create and transmit the GNB-CU-UP E1 Setup failure message. /// \param[in] cause The cause of the failure. - void send_cu_up_e1_setup_failure(cause_t cause); + void send_cu_up_e1_setup_failure(e1ap_cause_t cause); srslog::basic_logger& logger = srslog::fetch_basic_logger("CU-CP"); cu_up_processor_config_t cfg; diff --git a/lib/cu_cp/du_processor/du_processor_impl.cpp b/lib/cu_cp/du_processor/du_processor_impl.cpp index 29802a6e66..377cfa40a6 100644 --- a/lib/cu_cp/du_processor/du_processor_impl.cpp +++ b/lib/cu_cp/du_processor/du_processor_impl.cpp @@ -23,6 +23,8 @@ #include "du_processor_impl.h" #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/f1ap/cu_cp/f1ap_cu_factory.h" +#include "srsran/ran/cause/f1ap_cause_converters.h" +#include "srsran/ran/cause/ngap_cause.h" #include "srsran/ran/nr_cgi_helpers.h" #include "srsran/rrc/rrc_du_factory.h" @@ -105,7 +107,7 @@ du_setup_result du_processor_impl::handle_du_setup_request(const du_setup_reques // TODO: How to handle missing optional freq and timing in meas timing config? if (!rrc_du_adapter.on_new_served_cell_list(request.gnb_du_served_cells_list)) { res.result = - du_setup_result::rejected{cause_transport_t::unspecified, "Could not establish served cell list in RRC"}; + du_setup_result::rejected{f1ap_cause_transport_t::unspecified, "Could not establish served cell list in RRC"}; return res; } @@ -330,12 +332,12 @@ void du_processor_impl::handle_du_initiated_ue_context_release_request(const f1a ngap_ctrl_notifier.on_ue_context_release_request( cu_cp_ue_context_release_request{request.ue_index, ue->get_up_resource_manager().get_pdu_sessions(), - cause_radio_network_t::radio_conn_with_ue_lost})); + f1ap_to_ngap_cause(request.cause)})); if (!ngap_release_successful) { // Release UE from DU, if it doesn't exist in the NGAP logger.debug("ue={}: Releasing UE from DU. ReleaseRequest not sent to AMF", request.ue_index); CORO_AWAIT(handle_ue_context_release_command( - cu_cp_ue_context_release_command{request.ue_index, cause_nas_t::unspecified})); + cu_cp_ue_context_release_command{request.ue_index, ngap_cause_radio_network_t::user_inactivity})); } CORO_RETURN(); })); @@ -484,14 +486,14 @@ void du_processor_impl::handle_paging_message(cu_cp_paging_message& msg) f1ap_paging_notifier.on_paging_message(msg); } -void du_processor_impl::send_ngap_ue_context_release_request(ue_index_t ue_index, cause_t cause) +void du_processor_impl::send_ngap_ue_context_release_request(ue_index_t ue_index, ngap_cause_t cause) { du_ue* ue = ue_manager.find_du_ue(ue_index); srsran_assert(ue != nullptr, "ue={}: Could not find DU UE", ue_index); cu_cp_ue_context_release_request req; req.ue_index = ue_index; - req.cause = cause_radio_network_t::user_inactivity; + req.cause = cause; // Add PDU Session IDs auto& up_resource_manager = ue->get_up_resource_manager(); @@ -511,7 +513,7 @@ void du_processor_impl::send_ngap_ue_context_release_request(ue_index_t ue_index void du_processor_impl::handle_inactivity_notification(const cu_cp_inactivity_notification& msg) { if (msg.ue_inactive) { - send_ngap_ue_context_release_request(msg.ue_index, cause_radio_network_t::user_inactivity); + send_ngap_ue_context_release_request(msg.ue_index, ngap_cause_radio_network_t::user_inactivity); } else { logger.debug("Inactivity notification level not supported"); } diff --git a/lib/cu_cp/du_processor/du_processor_impl.h b/lib/cu_cp/du_processor/du_processor_impl.h index 7d6d09b17d..e144c0112a 100644 --- a/lib/cu_cp/du_processor/du_processor_impl.h +++ b/lib/cu_cp/du_processor/du_processor_impl.h @@ -164,7 +164,7 @@ class du_processor_impl : public du_processor_impl_interface, public du_setup_ha /// \brief Request UE context release over NGAP. /// \param[in] ue_index The UE. /// \param[in] cause The cause of the failure. - void send_ngap_ue_context_release_request(ue_index_t ue_index, cause_t cause); + void send_ngap_ue_context_release_request(ue_index_t ue_index, ngap_cause_t cause); srslog::basic_logger& logger = srslog::fetch_basic_logger("CU-CP"); du_processor_config_t cfg; 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 3575a33f70..2af3f2a43f 100644 --- a/lib/cu_cp/routine_managers/cu_cp_routine_manager.cpp +++ b/lib/cu_cp/routine_managers/cu_cp_routine_manager.cpp @@ -43,11 +43,12 @@ void cu_cp_routine_manager::start_ue_removal_routine(ue_index_t cu_cp_e1ap_ue_removal_notifier* e1ap_notifier, cu_cp_f1ap_ue_removal_notifier& f1ap_notifier, cu_cp_ngap_control_notifier& ngap_notifier, + cell_meas_manager& cell_meas_mng, ue_manager& ue_mng, srslog::basic_logger& logger) { ue_task_sched.handle_ue_async_task( ue_index, launch_async( - ue_index, rrc_du_notifier, e1ap_notifier, f1ap_notifier, ngap_notifier, ue_mng, logger)); + ue_index, rrc_du_notifier, e1ap_notifier, f1ap_notifier, ngap_notifier, cell_meas_mng, ue_mng, logger)); } 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 4c57802ccf..bed02962b8 100644 --- a/lib/cu_cp/routine_managers/cu_cp_routine_manager.h +++ b/lib/cu_cp/routine_managers/cu_cp_routine_manager.h @@ -24,6 +24,7 @@ #include "../adapters/cu_cp_adapters.h" #include "../adapters/ngap_adapters.h" +#include "../cell_meas_manager/cell_meas_manager_impl.h" #include "../cu_cp_impl_interface.h" #include "../ue_manager/ue_manager_impl.h" #include "../ue_manager/ue_task_scheduler.h" @@ -52,6 +53,7 @@ class cu_cp_routine_manager cu_cp_e1ap_ue_removal_notifier* e1ap_notifier, cu_cp_f1ap_ue_removal_notifier& f1ap_notifier, cu_cp_ngap_control_notifier& ngap_notifier, + cell_meas_manager& cell_meas_mng, ue_manager& ue_mng, srslog::basic_logger& logger); diff --git a/lib/cu_cp/routines/mobility/inter_cu_handover_target_routine.cpp b/lib/cu_cp/routines/mobility/inter_cu_handover_target_routine.cpp index b4d4486d76..b799e32091 100644 --- a/lib/cu_cp/routines/mobility/inter_cu_handover_target_routine.cpp +++ b/lib/cu_cp/routines/mobility/inter_cu_handover_target_routine.cpp @@ -23,7 +23,7 @@ #include "inter_cu_handover_target_routine.h" #include "../pdu_session_routine_helpers.h" #include "srsran/ngap/ngap_handover.h" -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/e1ap_cause_converters.h" using namespace srsran; using namespace srs_cu_cp; @@ -334,7 +334,7 @@ inter_cu_handover_target_routine::generate_handover_resource_allocation_response // qos flow id qos_flow_item.qos_flow_id = flow_failed_item.qos_flow_id; // cause - qos_flow_item.cause = flow_failed_item.cause; + qos_flow_item.cause = e1ap_to_ngap_cause(flow_failed_item.cause); admitted_item.ho_request_ack_transfer.qos_flow_failed_to_setup_list.push_back(qos_flow_item); } diff --git a/lib/cu_cp/routines/mobility/inter_du_handover_routine.cpp b/lib/cu_cp/routines/mobility/inter_du_handover_routine.cpp index 261d6e4877..4d9223b810 100644 --- a/lib/cu_cp/routines/mobility/inter_du_handover_routine.cpp +++ b/lib/cu_cp/routines/mobility/inter_du_handover_routine.cpp @@ -157,7 +157,7 @@ void inter_du_handover_routine::operator()(coro_contextget_ue_index(); - ue_context_release_command.cause = cause_radio_network_t::unspecified; + ue_context_release_command.cause = ngap_cause_radio_network_t::unspecified; CORO_AWAIT(source_du_processor_notifier.handle_ue_context_release_command(ue_context_release_command)); logger.debug("ue={}: \"{}\" removed source UE context", ue_context_release_command.ue_index, name()); } 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 44cec87622..32dd019de6 100644 --- a/lib/cu_cp/routines/pdu_session_resource_modification_routine.cpp +++ b/lib/cu_cp/routines/pdu_session_resource_modification_routine.cpp @@ -290,7 +290,7 @@ void fill_modify_failed_list(cu_cp_pdu_session_resource_modify_response& re for (const auto& item : modify_request.pdu_session_res_modify_items) { cu_cp_pdu_session_resource_failed_to_modify_item failed_item; failed_item.pdu_session_id = item.pdu_session_id; - failed_item.unsuccessful_transfer.cause = cause_misc_t::unspecified; + failed_item.unsuccessful_transfer.cause = ngap_cause_misc_t::unspecified; response_msg.pdu_session_res_failed_to_modify_list.emplace(failed_item.pdu_session_id, failed_item); } } @@ -316,7 +316,7 @@ void mark_all_sessions_as_failed(cu_cp_pdu_session_resource_modify_response& for (const auto& modify_item : modify_request.pdu_session_res_modify_items) { cu_cp_pdu_session_resource_failed_to_modify_item failed_item; failed_item.pdu_session_id = modify_item.pdu_session_id; - failed_item.unsuccessful_transfer.cause = cause_radio_network_t::unspecified; + failed_item.unsuccessful_transfer.cause = ngap_cause_radio_network_t::unspecified; response_msg.pdu_session_res_failed_to_modify_list.emplace(failed_item.pdu_session_id, failed_item); } // No PDU session modified can be successful at the same time. @@ -339,7 +339,7 @@ pdu_session_resource_modification_routine::generate_pdu_session_resource_modify_ for (const auto& psi : next_config.pdu_sessions_failed_to_modify_list) { cu_cp_pdu_session_resource_failed_to_modify_item failed_item; failed_item.pdu_session_id = psi; - failed_item.unsuccessful_transfer.cause = cause_radio_network_t::unspecified; + failed_item.unsuccessful_transfer.cause = ngap_cause_radio_network_t::unspecified; response_msg.pdu_session_res_failed_to_modify_list.emplace(failed_item.pdu_session_id, failed_item); } } else { 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 bd4f0cbb60..17ac0f9296 100644 --- a/lib/cu_cp/routines/pdu_session_resource_release_routine.cpp +++ b/lib/cu_cp/routines/pdu_session_resource_release_routine.cpp @@ -67,14 +67,14 @@ void pdu_session_resource_release_routine::operator()( if (next_config.context_removal_required) { // Remove bearer context. bearer_context_release_command.ue_index = release_cmd.ue_index; - bearer_context_release_command.cause = cause_radio_network_t::unspecified; + bearer_context_release_command.cause = e1ap_cause_radio_network_t::unspecified; CORO_AWAIT(e1ap_ctrl_notifier.on_bearer_context_release_command(bearer_context_release_command)); // Request UE context removal. logger.info("ue={}: \"{}\" Requesting UE context release", release_cmd.ue_index, name()); ue_context_release_request.ue_index = release_cmd.ue_index; - ue_context_release_request.cause = cause_radio_network_t::unknown_pdu_session_id; + ue_context_release_request.cause = ngap_cause_radio_network_t::unknown_pdu_session_id; CORO_AWAIT(ngap_ctrl_notifier.on_ue_context_release_request(ue_context_release_request)); } else { // prepare BearerContextModificationRequest and call e1 notifier 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 e6c2620e97..7d42c4df6e 100644 --- a/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp +++ b/lib/cu_cp/routines/pdu_session_resource_setup_routine.cpp @@ -22,6 +22,7 @@ #include "pdu_session_resource_setup_routine.h" #include "pdu_session_routine_helpers.h" +#include "srsran/ran/cause/ngap_cause.h" using namespace srsran; using namespace srsran::srs_cu_cp; @@ -321,7 +322,7 @@ void fill_setup_failed_list(cu_cp_pdu_session_resource_setup_response& resp for (const auto& item : setup_msg.pdu_session_res_setup_items) { cu_cp_pdu_session_res_setup_failed_item failed_item; failed_item.pdu_session_id = item.pdu_session_id; - failed_item.unsuccessful_transfer.cause = cause_misc_t::unspecified; + failed_item.unsuccessful_transfer.cause = ngap_cause_misc_t::unspecified; response_msg.pdu_session_res_failed_to_setup_items.emplace(failed_item.pdu_session_id, failed_item); } } @@ -374,7 +375,7 @@ bool handle_procedure_response(cu_cp_pdu_session_resource_setup_response& r // Helper to mark all PDU sessions that were requested to be set up as failed. void mark_all_sessions_as_failed(cu_cp_pdu_session_resource_setup_response& response_msg, const cu_cp_pdu_session_resource_setup_request& setup_msg, - cause_t cause) + e1ap_cause_t cause) { slotted_id_vector failed_list; for (const auto& setup_item : setup_msg.pdu_session_res_setup_items) { @@ -403,7 +404,7 @@ pdu_session_resource_setup_routine::handle_pdu_session_resource_setup_result(boo } else { logger.info("ue={}: \"{}\" failed", setup_msg.ue_index, name()); - mark_all_sessions_as_failed(response_msg, setup_msg, cause_radio_network_t::unspecified); + mark_all_sessions_as_failed(response_msg, setup_msg, e1ap_cause_t{e1ap_cause_radio_network_t::unspecified}); } return response_msg; diff --git a/lib/cu_cp/routines/pdu_session_routine_helpers.cpp b/lib/cu_cp/routines/pdu_session_routine_helpers.cpp index 69484f8246..1e17e09a4a 100644 --- a/lib/cu_cp/routines/pdu_session_routine_helpers.cpp +++ b/lib/cu_cp/routines/pdu_session_routine_helpers.cpp @@ -22,6 +22,7 @@ #include "pdu_session_routine_helpers.h" #include "srsran/asn1/rrc_nr/cell_group_config.h" +#include "srsran/ran/cause/e1ap_cause_converters.h" using namespace srsran; using namespace srsran::srs_cu_cp; @@ -499,7 +500,7 @@ void srsran::srs_cu_cp::update_failed_list( // Add to list taking cause received from CU-UP. cu_cp_pdu_session_res_setup_failed_item failed_item; failed_item.pdu_session_id = e1ap_item.pdu_session_id; - failed_item.unsuccessful_transfer.cause = e1ap_item.cause; + failed_item.unsuccessful_transfer.cause = e1ap_to_ngap_cause(e1ap_item.cause); ngap_failed_list.emplace(failed_item.pdu_session_id, failed_item); } } diff --git a/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp b/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp index bc6f106a78..403375238f 100644 --- a/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp +++ b/lib/cu_cp/routines/reestablishment_context_modification_routine.cpp @@ -50,10 +50,6 @@ void reestablishment_context_modification_routine::operator()(coro_context>& ctx) // Remove UE from UE manager ue_mng.remove_ue(ue_index); + cell_meas_mng.remove_ue_context(ue_index); + logger.info("ue={}: \"{}\" finalized", ue_index, name()); CORO_RETURN(); diff --git a/lib/cu_cp/routines/ue_removal_routine.h b/lib/cu_cp/routines/ue_removal_routine.h index 45bb1ec305..13ec77d4db 100644 --- a/lib/cu_cp/routines/ue_removal_routine.h +++ b/lib/cu_cp/routines/ue_removal_routine.h @@ -24,6 +24,7 @@ #include "../adapters/cu_cp_adapters.h" #include "../adapters/ngap_adapters.h" +#include "../cell_meas_manager/cell_meas_manager_impl.h" #include "../cu_cp_impl_interface.h" #include "../ue_manager/ue_manager_impl.h" #include "../ue_manager/ue_task_scheduler.h" @@ -42,6 +43,7 @@ class ue_removal_routine cu_cp_e1ap_ue_removal_notifier* e1ap_notifier_, cu_cp_f1ap_ue_removal_notifier& f1ap_notifier_, cu_cp_ngap_control_notifier& ngap_notifier_, + cell_meas_manager& cell_meas_mng_, ue_manager& ue_mng_, srslog::basic_logger& logger_); @@ -55,6 +57,7 @@ class ue_removal_routine cu_cp_e1ap_ue_removal_notifier* e1ap_notifier; // to trigger removal of the UE at the E1AP cu_cp_f1ap_ue_removal_notifier& f1ap_notifier; // to trigger removal of the UE at the F1AP cu_cp_ngap_control_notifier& ngap_notifier; // to trigger removal of the UE at the NGAP + cell_meas_manager& cell_meas_mng; // to remove UE context from cell measurement manager ue_manager& ue_mng; // to remove UE context from DU processor srslog::basic_logger& logger; }; diff --git a/lib/cu_cp/up_resource_manager/up_resource_manager_helpers.cpp b/lib/cu_cp/up_resource_manager/up_resource_manager_helpers.cpp index c582b00e01..e0cd2b7d6b 100644 --- a/lib/cu_cp/up_resource_manager/up_resource_manager_helpers.cpp +++ b/lib/cu_cp/up_resource_manager/up_resource_manager_helpers.cpp @@ -116,7 +116,7 @@ bool srsran::srs_cu_cp::is_valid( // Reject request if PDU session with same ID already exists. for (const auto& pdu_session : setup_items) { if (context.pdu_sessions.find(pdu_session.pdu_session_id) != context.pdu_sessions.end()) { - logger.debug("PDU session ID {} already exists", pdu_session.pdu_session_id); + logger.info("PDU session ID {} already exists", pdu_session.pdu_session_id); return false; } diff --git a/lib/cu_up/cu_up_impl.cpp b/lib/cu_up/cu_up_impl.cpp index 7d76566681..57bf6f0112 100644 --- a/lib/cu_up/cu_up_impl.cpp +++ b/lib/cu_up/cu_up_impl.cpp @@ -310,7 +310,7 @@ void process_successful_pdu_resource_modification_outcome( } else { e1ap_pdu_session_resource_failed_item failed_item; failed_item.pdu_session_id = result.pdu_session_id; - failed_item.cause = cause_radio_network_t::unspecified; + failed_item.cause = e1ap_cause_radio_network_t::unspecified; pdu_session_resource_failed_to_modify_list.emplace(failed_item.pdu_session_id, failed_item); } } diff --git a/lib/cu_up/pdu_session_manager.h b/lib/cu_up/pdu_session_manager.h index 8a7b06000a..1ab40361ea 100644 --- a/lib/cu_up/pdu_session_manager.h +++ b/lib/cu_up/pdu_session_manager.h @@ -23,7 +23,7 @@ #pragma once #include "srsran/e1ap/common/e1ap_types.h" -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/e1ap_cause.h" #include "srsran/ran/cu_types.h" #include "srsran/ran/up_transport_layer_info.h" @@ -35,14 +35,14 @@ namespace srs_cu_up { struct qos_flow_setup_result { bool success = false; qos_flow_id_t qos_flow_id = qos_flow_id_t::invalid; - cause_t cause; // Cause if setup was unsuccessful. + e1ap_cause_t cause; // Cause if setup was unsuccessful. }; // Result when creating a new DRB containing QoS flow results struct drb_setup_result { bool success = false; drb_id_t drb_id = drb_id_t::invalid; - cause_t cause; // Cause if setup was unsuccessful. + e1ap_cause_t cause; // Cause if setup was unsuccessful. up_transport_layer_info gtp_tunnel; std::vector qos_flow_results; }; @@ -51,7 +51,7 @@ struct drb_setup_result { struct pdu_session_setup_result { bool success = false; // True if PDU session could be set up. pdu_session_id_t pdu_session_id = pdu_session_id_t::invalid; // The PDU session ID. - cause_t cause; // Cause if setup was unsuccessful. + e1ap_cause_t cause; // Cause if setup was unsuccessful. up_transport_layer_info gtp_tunnel; optional security_result; std::vector drb_setup_results; @@ -61,7 +61,7 @@ struct pdu_session_setup_result { struct pdu_session_modification_result { bool success = false; // True if PDU session could be set up. pdu_session_id_t pdu_session_id = pdu_session_id_t::invalid; // The PDU session ID. - cause_t cause; // Cause if modification was unsuccessful. + e1ap_cause_t cause; // Cause if modification was unsuccessful. std::vector drb_setup_results; std::vector drb_modification_results; }; diff --git a/lib/cu_up/pdu_session_manager_impl.cpp b/lib/cu_up/pdu_session_manager_impl.cpp index a8e861466b..27d3606edc 100644 --- a/lib/cu_up/pdu_session_manager_impl.cpp +++ b/lib/cu_up/pdu_session_manager_impl.cpp @@ -40,12 +40,16 @@ pdu_session_manager_impl::pdu_session_manager_impl(ue_index_t n3_interface_config& n3_config_, cu_up_ue_logger& logger_, unique_timer& ue_inactivity_timer_, - timer_factory timers_, + timer_factory ue_dl_timer_factory_, + timer_factory ue_ul_timer_factory_, + timer_factory ue_ctrl_timer_factory_, f1u_cu_up_gateway& f1u_gw_, gtpu_teid_pool& f1u_teid_allocator_, gtpu_tunnel_tx_upper_layer_notifier& gtpu_tx_notifier_, gtpu_demux_ctrl& gtpu_rx_demux_, task_executor& ue_dl_exec_, + task_executor& ue_ul_exec_, + task_executor& ue_ctrl_exec_, dlt_pcap& gtpu_pcap_) : ue_index(ue_index_), qos_cfg(std::move(qos_cfg_)), @@ -54,14 +58,19 @@ pdu_session_manager_impl::pdu_session_manager_impl(ue_index_t n3_config(n3_config_), logger(logger_), ue_inactivity_timer(ue_inactivity_timer_), - timers(timers_), + ue_dl_timer_factory(ue_dl_timer_factory_), + ue_ul_timer_factory(ue_ul_timer_factory_), + ue_ctrl_timer_factory(ue_ctrl_timer_factory_), gtpu_tx_notifier(gtpu_tx_notifier_), f1u_teid_allocator(f1u_teid_allocator_), gtpu_rx_demux(gtpu_rx_demux_), ue_dl_exec(ue_dl_exec_), + ue_ul_exec(ue_ul_exec_), + ue_ctrl_exec(ue_ctrl_exec_), gtpu_pcap(gtpu_pcap_), f1u_gw(f1u_gw_) { + (void)ue_ctrl_exec; } drb_setup_result pdu_session_manager_impl::handle_drb_to_setup_item(pdu_session& new_session, @@ -70,7 +79,7 @@ drb_setup_result pdu_session_manager_impl::handle_drb_to_setup_item(pdu_session& // prepare DRB creation result drb_setup_result drb_result = {}; drb_result.success = false; - drb_result.cause = cause_radio_network_t::unspecified; + drb_result.cause = e1ap_cause_radio_network_t::unspecified; drb_result.drb_id = drb_to_setup.drb_id; // get DRB from list and create context @@ -84,7 +93,7 @@ drb_setup_result pdu_session_manager_impl::handle_drb_to_setup_item(pdu_session& five_qi_t five_qi = drb_to_setup.qos_flow_info_to_be_setup.begin()->qos_flow_level_qos_params.qos_characteristics.get_five_qi(); if (qos_cfg.find(five_qi) == qos_cfg.end()) { - drb_result.cause = cause_radio_network_t::not_supported_5qi_value; + drb_result.cause = e1ap_cause_radio_network_t::not_supported_5qi_value; return drb_result; } @@ -93,7 +102,7 @@ drb_setup_result pdu_session_manager_impl::handle_drb_to_setup_item(pdu_session& // prepare QoS flow creation result qos_flow_setup_result flow_result = {}; flow_result.success = false; - flow_result.cause = cause_radio_network_t::unspecified; + flow_result.cause = e1ap_cause_radio_network_t::unspecified; flow_result.qos_flow_id = qos_flow_info.qos_flow_id; if (!new_session.sdap->is_mapped(qos_flow_info.qos_flow_id) && @@ -112,8 +121,8 @@ drb_setup_result pdu_session_manager_impl::handle_drb_to_setup_item(pdu_session& // fail if mapping already exists flow_result.success = false; flow_result.cause = new_session.sdap->is_mapped(qos_flow_info.qos_flow_id) - ? cause_radio_network_t::multiple_qos_flow_id_instances - : cause_radio_network_t::not_supported_5qi_value; + ? e1ap_cause_radio_network_t::multiple_qos_flow_id_instances + : e1ap_cause_radio_network_t::not_supported_5qi_value; logger.log_error("Cannot overwrite existing mapping for {}", qos_flow_info.qos_flow_id); } @@ -126,7 +135,7 @@ drb_setup_result pdu_session_manager_impl::handle_drb_to_setup_item(pdu_session& logger.log_error( "Failed to create {} for {}: Could not map any QoS flow", drb_to_setup.drb_id, new_session.pdu_session_id); new_session.drbs.erase(drb_to_setup.drb_id); - drb_result.cause = cause_radio_network_t::unspecified; + drb_result.cause = e1ap_cause_radio_network_t::unspecified; drb_result.success = false; return drb_result; } @@ -136,7 +145,7 @@ drb_setup_result pdu_session_manager_impl::handle_drb_to_setup_item(pdu_session& logger.log_error( "Failed to create {} for {}: Could not find 5QI. {}", drb_to_setup.drb_id, new_session.pdu_session_id, five_qi); new_session.drbs.erase(drb_to_setup.drb_id); - drb_result.cause = cause_radio_network_t::not_supported_5qi_value; + drb_result.cause = e1ap_cause_radio_network_t::not_supported_5qi_value; drb_result.success = false; return drb_result; } @@ -151,7 +160,9 @@ drb_setup_result pdu_session_manager_impl::handle_drb_to_setup_item(pdu_session& pdcp_msg.tx_upper_cn = &new_drb->pdcp_tx_to_e1ap_adapter; pdcp_msg.rx_upper_dn = &new_drb->pdcp_to_sdap_adapter; pdcp_msg.rx_upper_cn = &new_drb->pdcp_rx_to_e1ap_adapter; - pdcp_msg.timers = timers; + pdcp_msg.ue_dl_timer_factory = ue_dl_timer_factory; + pdcp_msg.ue_ul_timer_factory = ue_ul_timer_factory; + pdcp_msg.ue_ctrl_timer_factory = ue_ctrl_timer_factory; new_drb->pdcp = srsran::create_pdcp_entity(pdcp_msg); security::sec_128_as_config sec_128 = security::truncate_config(security_info); @@ -190,17 +201,19 @@ drb_setup_result pdu_session_manager_impl::handle_drb_to_setup_item(pdu_session& } gtpu_teid_t f1u_ul_teid = ret.value(); - up_transport_layer_info f1u_ul_tunnel_addr; - f1u_ul_tunnel_addr.tp_address.from_string(net_config.f1u_bind_addr); - f1u_ul_tunnel_addr.gtp_teid = f1u_ul_teid; - new_drb->f1u = f1u_gw.create_cu_bearer(ue_index, + up_transport_layer_info f1u_ul_tunnel_addr(transport_layer_address::create_from_string(net_config.f1u_bind_addr), + f1u_ul_teid); + + new_drb->f1u = f1u_gw.create_cu_bearer(ue_index, drb_to_setup.drb_id, f1u_ul_tunnel_addr, new_drb->f1u_to_pdcp_adapter, new_drb->f1u_to_pdcp_adapter, - timers); - new_drb->f1u_ul_teid = f1u_ul_teid; - drb_result.gtp_tunnel = f1u_ul_tunnel_addr; + ue_ul_exec, + ue_dl_timer_factory, + ue_inactivity_timer); + new_drb->f1u_ul_teid = f1u_ul_teid; + drb_result.gtp_tunnel = f1u_ul_tunnel_addr; // Connect F1-U's "F1-U->PDCP adapter" directly to PDCP new_drb->f1u_to_pdcp_adapter.connect_pdcp(new_drb->pdcp->get_rx_lower_interface(), @@ -224,7 +237,7 @@ pdu_session_setup_result pdu_session_manager_impl::setup_pdu_session(const e1ap_ pdu_session_setup_result pdu_session_result = {}; pdu_session_result.success = false; pdu_session_result.pdu_session_id = session.pdu_session_id; - pdu_session_result.cause = cause_radio_network_t::unspecified; + pdu_session_result.cause = e1ap_cause_radio_network_t::unspecified; if (pdu_sessions.find(session.pdu_session_id) != pdu_sessions.end()) { logger.log_error("PDU Session with {} already exists", session.pdu_session_id); @@ -250,15 +263,12 @@ pdu_session_setup_result pdu_session_manager_impl::setup_pdu_session(const e1ap_ // Allocate local TEID new_session->local_teid = allocate_local_teid(new_session->pdu_session_id); - up_transport_layer_info n3_dl_tunnel_addr; - n3_dl_tunnel_addr.tp_address.from_string(net_config.n3_bind_addr); - n3_dl_tunnel_addr.gtp_teid = new_session->local_teid; - pdu_session_result.gtp_tunnel = n3_dl_tunnel_addr; + pdu_session_result.gtp_tunnel = up_transport_layer_info( + transport_layer_address::create_from_string(net_config.n3_bind_addr), new_session->local_teid); // Create SDAP entity - sdap_entity_creation_message sdap_msg = { - ue_index, session.pdu_session_id, ue_inactivity_timer, &new_session->sdap_to_gtpu_adapter}; - new_session->sdap = create_sdap(sdap_msg); + sdap_entity_creation_message sdap_msg = {ue_index, session.pdu_session_id, &new_session->sdap_to_gtpu_adapter}; + new_session->sdap = create_sdap(sdap_msg); // Create GTPU entity gtpu_tunnel_ngu_creation_message msg = {}; @@ -271,7 +281,7 @@ pdu_session_setup_result pdu_session_manager_impl::setup_pdu_session(const e1ap_ msg.rx_lower = &new_session->gtpu_to_sdap_adapter; msg.tx_upper = >pu_tx_notifier; msg.gtpu_pcap = >pu_pcap; - msg.timers = timers; + msg.ue_dl_timer_factory = ue_dl_timer_factory; new_session->gtpu = create_gtpu_tunnel_ngu(msg); // Connect adapters @@ -293,13 +303,13 @@ pdu_session_setup_result pdu_session_manager_impl::setup_pdu_session(const e1ap_ } // Ref: TS 38.463 Sec. 8.3.1.2: - // For each PDU session for which the Security Indication IE is included in the PDU Session Resource To Setup List IE - // of the BEARER CONTEXT SETUP REQUEST message, and the Integrity Protection Indication IE or Confidentiality + // For each PDU session for which the Security Indication IE is included in the PDU Session Resource To Setup List + // IE of the BEARER CONTEXT SETUP REQUEST message, and the Integrity Protection Indication IE or Confidentiality // Protection Indication IE is set to "preferred", then the gNB-CU-UP should, if supported, perform user plane // integrity protection or ciphering, respectively, for the concerned PDU session and shall notify whether it // performed the user plane integrity protection or ciphering by including the Integrity Protection Result IE or - // Confidentiality Protection Result IE, respectively, in the PDU Session Resource Setup List IE of the BEARER CONTEXT - // SETUP RESPONSE message. + // Confidentiality Protection Result IE, respectively, in the PDU Session Resource Setup List IE of the BEARER + // CONTEXT SETUP RESPONSE message. if (security_result_required(session.security_ind)) { pdu_session_result.security_result = security_result_t{}; auto& sec_res = pdu_session_result.security_result.value(); @@ -323,7 +333,7 @@ pdu_session_manager_impl::modify_pdu_session(const e1ap_pdu_session_res_to_modif pdu_session_modification_result pdu_session_result; pdu_session_result.success = false; pdu_session_result.pdu_session_id = session.pdu_session_id; - pdu_session_result.cause = cause_radio_network_t::unspecified; + pdu_session_result.cause = e1ap_cause_radio_network_t::unspecified; if (pdu_sessions.find(session.pdu_session_id) == pdu_sessions.end()) { logger.log_error("PDU Session {} doesn't exists", session.pdu_session_id); @@ -343,7 +353,7 @@ pdu_session_manager_impl::modify_pdu_session(const e1ap_pdu_session_res_to_modif // prepare DRB modification result drb_setup_result drb_result = {}; drb_result.success = false; - drb_result.cause = cause_radio_network_t::unspecified; + drb_result.cause = e1ap_cause_radio_network_t::unspecified; drb_result.drb_id = drb_to_mod.drb_id; // find DRB in PDU session @@ -374,13 +384,18 @@ pdu_session_manager_impl::modify_pdu_session(const e1ap_pdu_session_res_to_modif drb->f1u_ul_teid = ret.value(); // Create UL UP TNL address. - up_transport_layer_info f1u_ul_tunnel_addr; - f1u_ul_tunnel_addr.tp_address.from_string(net_config.f1u_bind_addr); - f1u_ul_tunnel_addr.gtp_teid = drb->f1u_ul_teid; + up_transport_layer_info f1u_ul_tunnel_addr(transport_layer_address::create_from_string(net_config.f1u_bind_addr), + drb->f1u_ul_teid); // create new F1-U and connect it. This will automatically disconnect the old F1-U. - drb->f1u = f1u_gw.create_cu_bearer( - ue_index, drb->drb_id, f1u_ul_tunnel_addr, drb->f1u_to_pdcp_adapter, drb->f1u_to_pdcp_adapter, timers); + drb->f1u = f1u_gw.create_cu_bearer(ue_index, + drb->drb_id, + f1u_ul_tunnel_addr, + drb->f1u_to_pdcp_adapter, + drb->f1u_to_pdcp_adapter, + ue_dl_exec, + ue_dl_timer_factory, + ue_inactivity_timer); drb_iter->second->pdcp_to_f1u_adapter.disconnect_f1u(); drb_result.gtp_tunnel = f1u_ul_tunnel_addr; @@ -388,10 +403,10 @@ pdu_session_manager_impl::modify_pdu_session(const e1ap_pdu_session_res_to_modif // F1-U apply modification if (!drb_to_mod.dl_up_params.empty()) { - up_transport_layer_info f1u_ul_tunnel_addr; - f1u_ul_tunnel_addr.tp_address.from_string(net_config.f1u_bind_addr); - f1u_ul_tunnel_addr.gtp_teid = drb_iter->second->f1u_ul_teid; - f1u_gw.attach_dl_teid(f1u_ul_tunnel_addr, drb_to_mod.dl_up_params[0].up_tnl_info); + f1u_gw.attach_dl_teid( + up_transport_layer_info(transport_layer_address::create_from_string(net_config.f1u_bind_addr), + drb_iter->second->f1u_ul_teid), + drb_to_mod.dl_up_params[0].up_tnl_info); drb_iter->second->pdcp_to_f1u_adapter.connect_f1u(drb_iter->second->f1u->get_tx_sdu_handler()); } @@ -478,10 +493,8 @@ void pdu_session_manager_impl::remove_pdu_session(pdu_session_id_t pdu_session_i auto& pdu_session = pdu_sessions.at(pdu_session_id); for (const auto& drb : pdu_session->drbs) { logger.log_debug("Disconnecting CU bearer with UL-TEID={}", drb.second->f1u_ul_teid); - up_transport_layer_info f1u_ul_tunnel_addr; - f1u_ul_tunnel_addr.tp_address.from_string(net_config.f1u_bind_addr); - f1u_ul_tunnel_addr.gtp_teid = drb.second->f1u_ul_teid; - f1u_gw.disconnect_cu_bearer(f1u_ul_tunnel_addr); + f1u_gw.disconnect_cu_bearer(up_transport_layer_info( + transport_layer_address::create_from_string(net_config.f1u_bind_addr), drb.second->f1u_ul_teid)); if (f1u_teid_allocator.release_teid(drb.second->f1u_ul_teid)) { logger.log_error( "{} could not remove ul_teid at session termination. ul_teid={}", pdu_session_id, drb.second->f1u_ul_teid); diff --git a/lib/cu_up/pdu_session_manager_impl.h b/lib/cu_up/pdu_session_manager_impl.h index 768144ebca..5c05049042 100644 --- a/lib/cu_up/pdu_session_manager_impl.h +++ b/lib/cu_up/pdu_session_manager_impl.h @@ -48,13 +48,17 @@ class pdu_session_manager_impl final : public pdu_session_manager_ctrl network_interface_config& net_config_, n3_interface_config& n3_config_, cu_up_ue_logger& logger_, - unique_timer& ue_inactivity_timer, - timer_factory timers_, + unique_timer& ue_inactivity_timer_, + timer_factory ue_dl_timer_factory_, + timer_factory ue_ul_timer_factory_, + timer_factory ue_ctrl_timer_factory_, f1u_cu_up_gateway& f1u_gw_, gtpu_teid_pool& f1u_teid_allocator_, gtpu_tunnel_tx_upper_layer_notifier& gtpu_tx_notifier_, gtpu_demux_ctrl& gtpu_rx_demux_, task_executor& ue_dl_exec_, + task_executor& ue_ul_exec_, + task_executor& ue_ctrl_exec_, dlt_pcap& gtpu_pcap_); pdu_session_setup_result setup_pdu_session(const e1ap_pdu_session_res_to_setup_item& session) override; @@ -82,11 +86,15 @@ class pdu_session_manager_impl final : public pdu_session_manager_ctrl n3_interface_config& n3_config; cu_up_ue_logger& logger; unique_timer& ue_inactivity_timer; - timer_factory timers; + timer_factory ue_dl_timer_factory; + timer_factory ue_ul_timer_factory; + timer_factory ue_ctrl_timer_factory; gtpu_tunnel_tx_upper_layer_notifier& gtpu_tx_notifier; gtpu_teid_pool& f1u_teid_allocator; gtpu_demux_ctrl& gtpu_rx_demux; task_executor& ue_dl_exec; + task_executor& ue_ul_exec; + task_executor& ue_ctrl_exec; dlt_pcap& gtpu_pcap; f1u_cu_up_gateway& f1u_gw; std::map> pdu_sessions; // key is pdu_session_id diff --git a/lib/cu_up/ue_context.h b/lib/cu_up/ue_context.h index d6ef0c448c..3f46ec0f18 100644 --- a/lib/cu_up/ue_context.h +++ b/lib/cu_up/ue_context.h @@ -52,8 +52,12 @@ class ue_context : public pdu_session_manager_ctrl e1ap_control_message_handler& e1ap_, network_interface_config& net_config_, n3_interface_config& n3_config_, - std::unique_ptr> ue_exec_, - timer_factory timers_, + std::unique_ptr> ue_dl_exec_, + std::unique_ptr> ue_ul_exec_, + std::unique_ptr> ue_ctrl_exec_, + timer_factory ue_dl_timer_factory_, + timer_factory ue_ul_timer_factory_, + timer_factory ue_ctrl_timer_factory_, f1u_cu_up_gateway& f1u_gw_, gtpu_teid_pool& f1u_teid_allocator_, gtpu_tunnel_tx_upper_layer_notifier& gtpu_tx_notifier_, @@ -70,22 +74,30 @@ class ue_context : public pdu_session_manager_ctrl n3_config_, logger, ue_inactivity_timer, - timers_, + ue_dl_timer_factory_, + ue_ul_timer_factory_, + ue_ctrl_timer_factory_, f1u_gw_, f1u_teid_allocator_, gtpu_tx_notifier_, gtpu_rx_demux_, - *ue_exec_, + *ue_dl_exec_, + *ue_ul_exec_, + *ue_ctrl_exec_, gtpu_pcap), - ue_exec(std::move(ue_exec_)), - timers(timers_) + ue_dl_exec(std::move(ue_dl_exec_)), + ue_ul_exec(std::move(ue_ul_exec_)), + ue_ctrl_exec(std::move(ue_ctrl_exec_)), + ue_dl_timer_factory(ue_dl_timer_factory_), + ue_ul_timer_factory(ue_ul_timer_factory_), + ue_ctrl_timer_factory(ue_ctrl_timer_factory_) { if (cfg.activity_level == activity_notification_level_t::ue) { if (not cfg.ue_inactivity_timeout.has_value()) { report_error( "Failed to create UE context. Activity notification level is UE, but no UE inactivity timer configured\n"); } - ue_inactivity_timer = timers.create_timer(); + ue_inactivity_timer = ue_ul_timer_factory.create_timer(); ue_inactivity_timer.set(*cfg.ue_inactivity_timeout, [this](timer_id_t /*tid*/) { on_ue_inactivity_timer_expired(); }); ue_inactivity_timer.run(); @@ -122,15 +134,29 @@ class ue_context : public pdu_session_manager_ctrl e1ap_control_message_handler& e1ap; pdu_session_manager_impl pdu_session_manager; - std::unique_ptr> ue_exec; + std::unique_ptr> ue_dl_exec; + std::unique_ptr> ue_ul_exec; + std::unique_ptr> ue_ctrl_exec; - timer_factory timers; - unique_timer ue_inactivity_timer; - void on_ue_inactivity_timer_expired() + timer_factory ue_dl_timer_factory; + timer_factory ue_ul_timer_factory; + timer_factory ue_ctrl_timer_factory; + + unique_timer ue_inactivity_timer; + + /// Handle expired UE inactivity timer. This function is called from a timer that is run in UE executor, + /// therefore it handovers the handling to control executor. + void on_ue_inactivity_timer_expired() { - e1ap_bearer_context_inactivity_notification msg = {}; - msg.ue_index = index; - e1ap.handle_bearer_context_inactivity_notification(msg); + auto fn = [this]() mutable { + e1ap_bearer_context_inactivity_notification msg = {}; + msg.ue_index = index; + e1ap.handle_bearer_context_inactivity_notification(msg); + }; + + if (!ue_ctrl_exec->execute(std::move(fn))) { + logger.log_warning("Could not handle expired UE inactivity handler, queue is full. ue={}", index); + } } }; diff --git a/lib/cu_up/ue_manager.cpp b/lib/cu_up/ue_manager.cpp index 1ccf99202a..a0523881bf 100644 --- a/lib/cu_up/ue_manager.cpp +++ b/lib/cu_up/ue_manager.cpp @@ -69,8 +69,17 @@ ue_context* ue_manager::add_ue(const ue_context_cfg& ue_cfg) return nullptr; } - // Create UE executor - std::unique_ptr> ue_exec = exec_pool.create_dl_pdu_executor(); + // Create UE executors + // TODO, these should be created within the same function, so that UL, DL and CTRL executors + // can point to the same executor. + std::unique_ptr> ue_dl_exec = exec_pool.create_dl_pdu_executor(); + std::unique_ptr> ue_ul_exec = exec_pool.create_ul_pdu_executor(); + std::unique_ptr> ue_ctrl_exec = exec_pool.create_ctrl_executor(); + + // Create executor-specific timer factories + timer_factory ue_dl_timer_factory = {timers, *ue_dl_exec}; + timer_factory ue_ul_timer_factory = {timers, *ue_ul_exec}; + timer_factory ue_ctrl_timer_factory = {timers, *ue_ctrl_exec}; // Create UE object std::unique_ptr new_ctx = std::make_unique(new_idx, @@ -78,8 +87,12 @@ ue_context* ue_manager::add_ue(const ue_context_cfg& ue_cfg) e1ap, net_config, n3_config, - std::move(ue_exec), - timer_factory{timers, *ue_exec}, + std::move(ue_dl_exec), + std::move(ue_ul_exec), + std::move(ue_ctrl_exec), + ue_dl_timer_factory, + ue_ul_timer_factory, + ue_ctrl_timer_factory, f1u_gw, f1u_teid_allocator, gtpu_tx_notifier, @@ -102,7 +115,6 @@ void ue_manager::remove_ue(ue_index_t ue_index) ue_db.erase(ue_index); logger.info("Removed ue_index={}", ue_index); - return; } ue_index_t ue_manager::get_next_ue_index() diff --git a/lib/du_high/adapters/f1ap_adapters.h b/lib/du_high/adapters/f1ap_adapters.h index 44ec9bf0a6..d0ab77f650 100644 --- a/lib/du_high/adapters/f1ap_adapters.h +++ b/lib/du_high/adapters/f1ap_adapters.h @@ -96,6 +96,11 @@ class f1ap_du_configurator_adapter : public f1ap_du_configurator return du_mng->handle_ue_delete_request(request); } + async_task request_ue_drb_deactivation(du_ue_index_t ue_index) override + { + return du_mng->handle_ue_deactivation_request(ue_index); + } + void notify_reestablishment_of_old_ue(du_ue_index_t new_ue_index, du_ue_index_t old_ue_index) override { du_mng->handle_ue_reestablishment(new_ue_index, old_ue_index); diff --git a/lib/du_high/adapters/mac_test_mode_adapter.cpp b/lib/du_high/adapters/mac_test_mode_adapter.cpp index 6eeb179f55..a2f007b1a9 100644 --- a/lib/du_high/adapters/mac_test_mode_adapter.cpp +++ b/lib/du_high/adapters/mac_test_mode_adapter.cpp @@ -371,39 +371,39 @@ void mac_test_mode_cell_adapter::handle_uci(const mac_uci_indication_message& ms if (not ue_info_mgr.is_test_ue(pdu.rnti)) { // non-test mode UE. Forward the original UCI indication PDU. msg_copy.ucis.push_back(pdu); - } - - // test mode UE. - // Intercept the UCI indication and force HARQ-ACK=ACK and CSI. - msg_copy.ucis.push_back(pdu); - mac_uci_pdu& test_uci = msg_copy.ucis.back(); - - bool entry_found = false; - if (variant_holds_alternative(test_uci.pdu)) { - for (const ul_sched_info& pusch : entry.puschs) { - if (pusch.pusch_cfg.rnti == pdu.rnti and pusch.uci.has_value()) { - fill_uci_pdu(variant_get(test_uci.pdu), pusch); - entry_found = true; - } - } } else { - // PUCCH case. - for (const pucch_info& pucch : entry.pucchs) { - if (pucch_info_and_uci_ind_match(pucch, test_uci)) { - // Intercept the UCI indication and force HARQ-ACK=ACK and UCI. - if (pucch.format == pucch_format::FORMAT_1) { - fill_uci_pdu(variant_get(test_uci.pdu), pucch); - } else { - fill_uci_pdu(variant_get(test_uci.pdu), pucch); + // test mode UE. + // Intercept the UCI indication and force HARQ-ACK=ACK and CSI. + msg_copy.ucis.push_back(pdu); + mac_uci_pdu& test_uci = msg_copy.ucis.back(); + + bool entry_found = false; + if (variant_holds_alternative(test_uci.pdu)) { + for (const ul_sched_info& pusch : entry.puschs) { + if (pusch.pusch_cfg.rnti == pdu.rnti and pusch.uci.has_value()) { + fill_uci_pdu(variant_get(test_uci.pdu), pusch); + entry_found = true; + } + } + } else { + // PUCCH case. + for (const pucch_info& pucch : entry.pucchs) { + if (pucch_info_and_uci_ind_match(pucch, test_uci)) { + // Intercept the UCI indication and force HARQ-ACK=ACK and UCI. + if (pucch.format == pucch_format::FORMAT_1) { + fill_uci_pdu(variant_get(test_uci.pdu), pucch); + } else { + fill_uci_pdu(variant_get(test_uci.pdu), pucch); + } + entry_found = true; } - entry_found = true; } } - } - if (not entry_found) { - msg_copy.ucis.pop_back(); - logger.warning("c-rnti={}: Mismatch between provided UCI and expected UCI for slot_rx={}", pdu.rnti, msg.sl_rx); + if (not entry_found) { + msg_copy.ucis.pop_back(); + logger.warning("c-rnti={}: Mismatch between provided UCI and expected UCI for slot_rx={}", pdu.rnti, msg.sl_rx); + } } } diff --git a/lib/du_high/du_high_executor_strategies.h b/lib/du_high/du_high_executor_strategies.h index a152ee0cb7..bcdca2b0b4 100644 --- a/lib/du_high/du_high_executor_strategies.h +++ b/lib/du_high/du_high_executor_strategies.h @@ -167,19 +167,13 @@ class cell_executor_mapper final : public du_high_cell_executor_mapper /// \param cell_execs_ List of task executors that will be used by the MAC DL and scheduler. /// \param blocking_slot_ind sets whether slot indication tasks are processed synchronously or asynchronously explicit cell_executor_mapper(const std::initializer_list& cell_execs_, - const std::initializer_list& slot_execs_ = {}, - const std::initializer_list& err_ind_execs_ = {}) : - cell_execs(cell_execs_.begin(), cell_execs_.end()), - slot_execs(slot_execs_.begin(), slot_execs_.end()), - err_ind_execs(cell_execs_.begin(), cell_execs_.end()) + const std::initializer_list& slot_execs_ = {}) : + cell_execs(cell_execs_.begin(), cell_execs_.end()), slot_execs(slot_execs_.begin(), slot_execs_.end()) { srsran_assert(not cell_execs.empty(), "The number of DL executors must be higher than 1"); if (slot_execs.empty()) { slot_execs = cell_execs; } - if (err_ind_execs.empty()) { - err_ind_execs = cell_execs; - } } task_executor& executor(du_cell_index_t cell_index) override { return *cell_execs[cell_index % cell_execs.size()]; } @@ -189,11 +183,6 @@ class cell_executor_mapper final : public du_high_cell_executor_mapper return *slot_execs[cell_index % slot_execs.size()]; } - task_executor& error_ind_executor(du_cell_index_t cell_index) override - { - return *err_ind_execs[cell_index % err_ind_execs.size()]; - } - private: /// Executors used to process tasks related to the MAC DL and scheduler other than the slot indications. std::vector cell_execs; @@ -202,9 +191,6 @@ class cell_executor_mapper final : public du_high_cell_executor_mapper /// \c cell_execs or point to a sync_task_executor adapter stored in \c blocking_slot_execs, depending on whether /// slot indication tasks are processed synchronously or asynchronously. std::vector slot_execs; - - /// \brief Executors to handle error indications triggered from the FAPI. - std::vector err_ind_execs; }; /// \brief Task Executor Mapper for DU-high. diff --git a/lib/du_manager/du_manager_impl.cpp b/lib/du_manager/du_manager_impl.cpp index 7e2a8c6464..e9ea2c08fe 100644 --- a/lib/du_manager/du_manager_impl.cpp +++ b/lib/du_manager/du_manager_impl.cpp @@ -155,6 +155,11 @@ async_task du_manager_impl::handle_ue_delete_request(const f1ap_ue_delete_ return ue_mng.handle_ue_delete_request(request); } +async_task du_manager_impl::handle_ue_deactivation_request(du_ue_index_t ue_index) +{ + return ue_mng.handle_ue_deactivation_request(ue_index); +} + void du_manager_impl::handle_ue_reestablishment(du_ue_index_t new_ue_index, du_ue_index_t old_ue_index) { ue_mng.handle_reestablishment_request(new_ue_index, old_ue_index); diff --git a/lib/du_manager/du_manager_impl.h b/lib/du_manager/du_manager_impl.h index 15882043f3..c004b301f7 100644 --- a/lib/du_manager/du_manager_impl.h +++ b/lib/du_manager/du_manager_impl.h @@ -61,6 +61,8 @@ class du_manager_impl final : public du_manager_interface async_task handle_ue_delete_request(const f1ap_ue_delete_request& request) override; + async_task handle_ue_deactivation_request(du_ue_index_t ue_index) override; + void handle_ue_reestablishment(du_ue_index_t new_ue_index, du_ue_index_t old_ue_index) override; size_t nof_ues() override; diff --git a/lib/du_manager/du_ue/du_bearer.cpp b/lib/du_manager/du_ue/du_bearer.cpp index 9e26e0394d..4b6282abd0 100644 --- a/lib/du_manager/du_ue/du_bearer.cpp +++ b/lib/du_manager/du_ue/du_bearer.cpp @@ -22,7 +22,6 @@ #include "du_bearer.h" #include "../converters/rlc_config_helpers.h" -#include "srsran/adt/static_vector.h" #include "srsran/du_manager/du_manager_params.h" #include "srsran/gtpu/gtpu_teid_pool.h" @@ -162,7 +161,8 @@ std::unique_ptr srsran::srs_du::create_drb(du_ue_index_t drb->dluptnl_info_list[0], drb->uluptnl_info_list[0], drb->connector.f1u_rx_sdu_notif, - timer_factory{du_params.services.timers, du_params.services.ue_execs.ctrl_executor(ue_index)}); + timer_factory{du_params.services.timers, du_params.services.ue_execs.ctrl_executor(ue_index)}, + du_params.services.ue_execs.f1u_dl_pdu_executor(ue_index)); if (f1u_drb == nullptr) { srslog::fetch_basic_logger("DU-MNG").warning("ue={}: Failed to connect F1-U bearer to CU-UP.", ue_index); return nullptr; diff --git a/lib/du_manager/du_ue/du_ue.h b/lib/du_manager/du_ue/du_ue.h index 9a8421d3f2..204c35f48b 100644 --- a/lib/du_manager/du_ue/du_ue.h +++ b/lib/du_manager/du_ue/du_ue.h @@ -56,6 +56,12 @@ class du_ue_controller /// its bearers during the layer by layer UE context removal. virtual async_task disconnect_notifiers() = 0; + /// \brief Stop DRB activity and the detection of RLF for this UE. + /// + /// This method can be called when the DU receives a request to delete a UE context, but the UE is not yet in a + /// state where it is ready to be deleted (e.g. pending SRB PDUs). + virtual async_task handle_activity_stop_request() = 0; + /// \brief Schedule task for a given UE. virtual void schedule_async_task(async_task task) = 0; diff --git a/lib/du_manager/du_ue/du_ue_controller_impl.cpp b/lib/du_manager/du_ue/du_ue_controller_impl.cpp index 1b82881c70..783f68d192 100644 --- a/lib/du_manager/du_ue/du_ue_controller_impl.cpp +++ b/lib/du_manager/du_ue/du_ue_controller_impl.cpp @@ -262,6 +262,21 @@ async_task du_ue_controller_impl::disconnect_notifiers() }); } +async_task du_ue_controller_impl::handle_activity_stop_request() +{ + // > Disconnect RLF notifiers. + rlf_handler->disconnect(); + + // > Disconnect bearers from within the UE execution context. + return dispatch_and_resume_on(cfg.services.ue_execs.ctrl_executor(ue_index), cfg.services.du_mng_exec, [this]() { + // > Disconnect DRBs. + for (auto& drb_pair : bearers.drbs()) { + du_ue_drb& drb = *drb_pair.second; + drb.stop(); + } + }); +} + void du_ue_controller_impl::handle_rlf_detection(rlf_cause cause) { rlf_handler->handle_rlf_detection(cause); @@ -275,6 +290,8 @@ void du_ue_controller_impl::handle_crnti_ce_detection() void du_ue_controller_impl::stop_drb_traffic() { // > Disconnect DRBs. + logger.debug("ue={}: Stopping DRB traffic...", ue_index); + // Note: We use an async task rather than just an execute call, to ensure that this task is not dispatched after // the UE has already been deleted. schedule_async_task( diff --git a/lib/du_manager/du_ue/du_ue_controller_impl.h b/lib/du_manager/du_ue/du_ue_controller_impl.h index 5c67e6a588..e925a4313a 100644 --- a/lib/du_manager/du_ue/du_ue_controller_impl.h +++ b/lib/du_manager/du_ue/du_ue_controller_impl.h @@ -38,6 +38,8 @@ class du_ue_controller_impl final : public du_ue async_task disconnect_notifiers() override; + async_task handle_activity_stop_request() override; + void schedule_async_task(async_task task) override { ue_db.schedule_async_task(ue_index, std::move(task)); } void handle_rlf_detection(rlf_cause cause) override; diff --git a/lib/du_manager/du_ue/du_ue_manager.cpp b/lib/du_manager/du_ue/du_ue_manager.cpp index f42c6e2105..af8549b980 100644 --- a/lib/du_manager/du_ue/du_ue_manager.cpp +++ b/lib/du_manager/du_ue/du_ue_manager.cpp @@ -103,6 +103,15 @@ async_task du_ue_manager::handle_ue_delete_request(const f1ap_ue_delete_re return launch_async(msg, *this, cfg); } +async_task du_ue_manager::handle_ue_deactivation_request(du_ue_index_t ue_index) +{ + if (not ue_db.contains(ue_index)) { + logger.warning("ue={}: UE deactivation request for inexistent UE index", ue_index); + return launch_no_op_task(); + } + return ue_db[ue_index].handle_activity_stop_request(); +} + void du_ue_manager::handle_reestablishment_request(du_ue_index_t new_ue_index, du_ue_index_t old_ue_index) { srsran_assert(ue_db.contains(new_ue_index), "Invalid UE index={}", new_ue_index); diff --git a/lib/du_manager/du_ue/du_ue_manager.h b/lib/du_manager/du_ue/du_ue_manager.h index 50ed88b70f..9220a5a64c 100644 --- a/lib/du_manager/du_ue/du_ue_manager.h +++ b/lib/du_manager/du_ue/du_ue_manager.h @@ -53,6 +53,9 @@ class du_ue_manager final : public du_ue_manager_repository /// \brief Handle the removal of an existing UE context by F1AP request. async_task handle_ue_delete_request(const f1ap_ue_delete_request& msg); + /// \brief Handle the deactivation of an existing UE context by F1AP request. + async_task handle_ue_deactivation_request(du_ue_index_t ue_index); + void handle_reestablishment_request(du_ue_index_t new_ue_index, du_ue_index_t old_ue_index); /// \brief Handle the configuration of an existing UE context by RIC request. diff --git a/lib/e1ap/common/e1ap_asn1_converters.h b/lib/e1ap/common/e1ap_asn1_converters.h index 43eb9bda60..42fa084e0d 100644 --- a/lib/e1ap/common/e1ap_asn1_converters.h +++ b/lib/e1ap/common/e1ap_asn1_converters.h @@ -22,16 +22,14 @@ #pragma once -#include "srsran/adt/byte_buffer.h" #include "srsran/adt/optional.h" -#include "srsran/adt/static_vector.h" #include "srsran/asn1/asn1_utils.h" -#include "srsran/asn1/e1ap/e1ap.h" #include "srsran/asn1/e1ap/e1ap_ies.h" #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/e1ap/cu_cp/e1ap_cu_cp.h" #include "srsran/e1ap/cu_cp/e1ap_cu_cp_bearer_context_update.h" #include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/cause/e1ap_cause.h" #include "srsran/ran/qos_prio_level.h" #include "srsran/support/error_handling.h" #include @@ -980,19 +978,19 @@ inline e1ap_pdcp_config e1ap_asn1_to_pdcp_config(asn1::e1ap::pdcp_cfg_s asn1_pdc return pdcp_cfg; } -/// \brief Convert E1AP Cause to \c cause_t type. +/// \brief Convert E1AP Cause to \c e1ap_cause_t type. /// \param e1ap_cause The E1AP Cause. /// \return The cause type. -inline cause_t e1ap_cause_to_cause(asn1::e1ap::cause_c e1ap_cause) +inline e1ap_cause_t asn1_to_cause(asn1::e1ap::cause_c e1ap_cause) { - cause_t cause; + e1ap_cause_t cause; switch (e1ap_cause.type()) { case asn1::e1ap::cause_c::types_opts::radio_network: - cause = static_cast(e1ap_cause.radio_network().value); + cause = static_cast(e1ap_cause.radio_network().value); break; case asn1::e1ap::cause_c::types_opts::transport: - cause = static_cast(e1ap_cause.transport().value); + cause = static_cast(e1ap_cause.transport().value); break; case asn1::e1ap::cause_c::types_opts::protocol: cause = static_cast(e1ap_cause.protocol().value); @@ -1007,29 +1005,29 @@ inline cause_t e1ap_cause_to_cause(asn1::e1ap::cause_c e1ap_cause) return cause; } -/// \brief Convert \c cause_t type to E1AP ASN.1 cause. -/// \param cause The cause_t type. +/// \brief Convert \c e1ap_cause_t type to E1AP ASN.1 cause. +/// \param cause The e1ap_cause_t type. /// \return The E1AP ASN.1 cause. -inline asn1::e1ap::cause_c cause_to_asn1_cause(cause_t cause) +inline asn1::e1ap::cause_c cause_to_asn1(e1ap_cause_t cause) { - asn1::e1ap::cause_c e1ap_cause; - - if (variant_holds_alternative(cause)) { - e1ap_cause.set_radio_network() = - static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - e1ap_cause.set_transport() = - static_cast(variant_get(cause)); + asn1::e1ap::cause_c asn1_cause; + + if (variant_holds_alternative(cause)) { + asn1_cause.set_radio_network() = + static_cast(variant_get(cause)); + } else if (variant_holds_alternative(cause)) { + asn1_cause.set_transport() = + static_cast(variant_get(cause)); } else if (variant_holds_alternative(cause)) { - e1ap_cause.set_protocol() = + asn1_cause.set_protocol() = static_cast(variant_get(cause)); } else if (variant_holds_alternative(cause)) { - e1ap_cause.set_misc() = static_cast(variant_get(cause)); + asn1_cause.set_misc() = static_cast(variant_get(cause)); } else { - report_fatal_error("Cannot convert cause to E1AP type"); + report_fatal_error("Cannot convert cause to E1AP type: {}", cause); } - return e1ap_cause; + return asn1_cause; } /// \brief Convert E1AP NG DL UP Unchanged to its boolean representation @@ -1332,7 +1330,7 @@ inline void e1ap_drb_failed_item_list_to_asn1( // Add DRB ID asn1_drb_failed_item.drb_id = drb_id_to_uint(drb_failed_item.drb_id); // Add Cause - asn1_drb_failed_item.cause = cause_to_asn1_cause(drb_failed_item.cause); + asn1_drb_failed_item.cause = cause_to_asn1(drb_failed_item.cause); asn1_drb_item_list.push_back(asn1_drb_failed_item); } } diff --git a/lib/e1ap/cu_cp/e1ap_cu_cp_asn1_helpers.h b/lib/e1ap/cu_cp/e1ap_cu_cp_asn1_helpers.h index 00497975bc..9e71a08e38 100644 --- a/lib/e1ap/cu_cp/e1ap_cu_cp_asn1_helpers.h +++ b/lib/e1ap/cu_cp/e1ap_cu_cp_asn1_helpers.h @@ -435,7 +435,7 @@ fill_e1ap_bearer_context_setup_response(e1ap_bearer_context_setup_response& e1ap_qos_flow_failed_item failed_qos_flow_item; failed_qos_flow_item.qos_flow_id = uint_to_qos_flow_id(asn1_failed_qos_flow_item.qos_flow_id); - failed_qos_flow_item.cause = e1ap_cause_to_cause(asn1_failed_qos_flow_item.cause); + failed_qos_flow_item.cause = asn1_to_cause(asn1_failed_qos_flow_item.cause); drb_setup_item.flow_failed_list.emplace(failed_qos_flow_item.qos_flow_id, failed_qos_flow_item); } @@ -459,7 +459,7 @@ fill_e1ap_bearer_context_setup_response(e1ap_bearer_context_setup_response& for (const auto& asn1_drb_failed_item : asn1_res_setup_item.drb_failed_list_ng_ran) { e1ap_drb_failed_item_ng_ran drb_failed_item; drb_failed_item.drb_id = uint_to_drb_id(asn1_drb_failed_item.drb_id); - drb_failed_item.cause = e1ap_cause_to_cause(asn1_drb_failed_item.cause); + drb_failed_item.cause = asn1_to_cause(asn1_drb_failed_item.cause); res_setup_item.drb_failed_list_ng_ran.emplace(drb_failed_item.drb_id, drb_failed_item); } @@ -499,7 +499,7 @@ fill_e1ap_bearer_context_setup_response(e1ap_bearer_context_setup_response& e1ap_pdu_session_resource_failed_item failed_item; failed_item.pdu_session_id = uint_to_pdu_session_id(asn1_failed_item.pdu_session_id); - failed_item.cause = e1ap_cause_to_cause(asn1_failed_item.cause); + failed_item.cause = asn1_to_cause(asn1_failed_item.cause); res.pdu_session_resource_failed_list.emplace(failed_item.pdu_session_id, failed_item); } @@ -512,7 +512,7 @@ fill_e1ap_bearer_context_setup_response(e1ap_bearer_context_setup_response& const asn1::e1ap::bearer_context_setup_fail_s& e1ap_bearer_context_setup_fail) { res.success = false; - res.cause = e1ap_cause_to_cause(e1ap_bearer_context_setup_fail->cause); + res.cause = asn1_to_cause(e1ap_bearer_context_setup_fail->cause); if (e1ap_bearer_context_setup_fail->crit_diagnostics_present) { // TODO: Add crit diagnostics } @@ -707,7 +707,7 @@ inline void fill_e1ap_bearer_context_modification_response( e1ap_qos_flow_failed_item failed_qos_flow_item; failed_qos_flow_item.qos_flow_id = uint_to_qos_flow_id(asn1_failed_qos_flow_item.qos_flow_id); - failed_qos_flow_item.cause = e1ap_cause_to_cause(asn1_failed_qos_flow_item.cause); + failed_qos_flow_item.cause = asn1_to_cause(asn1_failed_qos_flow_item.cause); drb_setup_item.flow_failed_list.emplace(failed_qos_flow_item.qos_flow_id, failed_qos_flow_item); } @@ -733,7 +733,7 @@ inline void fill_e1ap_bearer_context_modification_response( for (const auto& asn1_drb_failed_item : asn1_res_mod_item.drb_failed_mod_list_ng_ran) { e1ap_drb_failed_item_ng_ran drb_failed_item; drb_failed_item.drb_id = uint_to_drb_id(asn1_drb_failed_item.drb_id); - drb_failed_item.cause = e1ap_cause_to_cause(asn1_drb_failed_item.cause); + drb_failed_item.cause = asn1_to_cause(asn1_drb_failed_item.cause); res_mod_item.drb_failed_list_ng_ran.emplace(drb_failed_item.drb_id, drb_failed_item); } @@ -767,7 +767,7 @@ inline void fill_e1ap_bearer_context_modification_response( e1ap_pdu_session_resource_failed_item failed_item; failed_item.pdu_session_id = uint_to_pdu_session_id(asn1_failed_item.pdu_session_id); - failed_item.cause = e1ap_cause_to_cause(asn1_failed_item.cause); + failed_item.cause = asn1_to_cause(asn1_failed_item.cause); res.pdu_session_resource_failed_list.emplace(failed_item.pdu_session_id, failed_item); } @@ -812,7 +812,7 @@ inline void fill_e1ap_bearer_context_modification_response( e1ap_qos_flow_failed_item failed_qos_flow_item; failed_qos_flow_item.qos_flow_id = uint_to_qos_flow_id(asn1_failed_qos_flow_item.qos_flow_id); - failed_qos_flow_item.cause = e1ap_cause_to_cause(asn1_failed_qos_flow_item.cause); + failed_qos_flow_item.cause = asn1_to_cause(asn1_failed_qos_flow_item.cause); drb_setup_item.flow_failed_list.emplace(failed_qos_flow_item.qos_flow_id, failed_qos_flow_item); } @@ -838,7 +838,7 @@ inline void fill_e1ap_bearer_context_modification_response( for (const auto& asn1_drb_failed_item : asn1_res_mod_item.drb_failed_list_ng_ran) { e1ap_drb_failed_item_ng_ran drb_failed_item; drb_failed_item.drb_id = uint_to_drb_id(asn1_drb_failed_item.drb_id); - drb_failed_item.cause = e1ap_cause_to_cause(asn1_drb_failed_item.cause); + drb_failed_item.cause = asn1_to_cause(asn1_drb_failed_item.cause); res_mod_item.drb_failed_list_ng_ran.emplace(drb_failed_item.drb_id, drb_failed_item); } @@ -870,7 +870,7 @@ inline void fill_e1ap_bearer_context_modification_response( e1ap_qos_flow_failed_item failed_qos_flow_item; failed_qos_flow_item.qos_flow_id = uint_to_qos_flow_id(asn1_failed_qos_flow_item.qos_flow_id); - failed_qos_flow_item.cause = e1ap_cause_to_cause(asn1_failed_qos_flow_item.cause); + failed_qos_flow_item.cause = asn1_to_cause(asn1_failed_qos_flow_item.cause); drb_mod_item.flow_failed_list.emplace(failed_qos_flow_item.qos_flow_id, failed_qos_flow_item); } @@ -898,7 +898,7 @@ inline void fill_e1ap_bearer_context_modification_response( for (const auto& asn1_drb_failed_item : asn1_res_mod_item.drb_failed_to_modify_list_ng_ran) { e1ap_drb_failed_item_ng_ran drb_failed_item; drb_failed_item.drb_id = uint_to_drb_id(asn1_drb_failed_item.drb_id); - drb_failed_item.cause = e1ap_cause_to_cause(asn1_drb_failed_item.cause); + drb_failed_item.cause = asn1_to_cause(asn1_drb_failed_item.cause); res_mod_item.drb_failed_to_modify_list_ng_ran.emplace(drb_failed_item.drb_id, drb_failed_item); } @@ -932,7 +932,7 @@ inline void fill_e1ap_bearer_context_modification_response( e1ap_pdu_session_resource_failed_item failed_item; failed_item.pdu_session_id = uint_to_pdu_session_id(asn1_failed_item.pdu_session_id); - failed_item.cause = e1ap_cause_to_cause(asn1_failed_item.cause); + failed_item.cause = asn1_to_cause(asn1_failed_item.cause); res.pdu_session_resource_failed_to_modify_list.emplace(failed_item.pdu_session_id, failed_item); } @@ -946,7 +946,7 @@ inline void fill_e1ap_bearer_context_modification_response( const asn1::e1ap::bearer_context_mod_fail_s& asn1_bearer_context_modification_fail) { res.success = false; - res.cause = e1ap_cause_to_cause(asn1_bearer_context_modification_fail->cause); + res.cause = asn1_to_cause(asn1_bearer_context_modification_fail->cause); if (asn1_bearer_context_modification_fail->crit_diagnostics_present) { // TODO: Add crit diagnostics } @@ -955,7 +955,7 @@ inline void fill_e1ap_bearer_context_modification_response( inline void fill_asn1_bearer_context_release_command(asn1::e1ap::bearer_context_release_cmd_s& asn1_command, const e1ap_bearer_context_release_command& command) { - asn1_command->cause = cause_to_asn1_cause(command.cause); + asn1_command->cause = cause_to_asn1(command.cause); } } // namespace srs_cu_cp diff --git a/lib/e1ap/cu_cp/e1ap_cu_cp_impl.cpp b/lib/e1ap/cu_cp/e1ap_cu_cp_impl.cpp index 613973592b..8842ed7e61 100644 --- a/lib/e1ap/cu_cp/e1ap_cu_cp_impl.cpp +++ b/lib/e1ap/cu_cp/e1ap_cu_cp_impl.cpp @@ -24,7 +24,7 @@ #include "../common/e1ap_asn1_helpers.h" #include "e1ap_cu_cp_asn1_helpers.h" #include "srsran/asn1/e1ap/e1ap.h" -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/e1ap_cause.h" using namespace srsran; using namespace asn1::e1ap; @@ -76,7 +76,7 @@ void e1ap_cu_cp_impl::handle_cu_up_e1_setup_response(const cu_up_e1_setup_respon e1ap_msg.pdu.unsuccessful_outcome().load_info_obj(ASN1_E1AP_ID_GNB_CU_UP_E1_SETUP); e1ap_msg.pdu.unsuccessful_outcome().value.gnb_cu_up_e1_setup_fail(); auto& setup_fail = e1ap_msg.pdu.unsuccessful_outcome().value.gnb_cu_up_e1_setup_fail(); - setup_fail->cause = cause_to_asn1_cause(msg.cause.value()); + setup_fail->cause = cause_to_asn1(msg.cause.value()); // set values handled by E1 setup_fail->transaction_id = current_transaction_id; diff --git a/lib/e1ap/cu_up/e1ap_cu_up_asn1_helpers.h b/lib/e1ap/cu_up/e1ap_cu_up_asn1_helpers.h index cc7c67f288..38e9b77f5d 100644 --- a/lib/e1ap/cu_up/e1ap_cu_up_asn1_helpers.h +++ b/lib/e1ap/cu_up/e1ap_cu_up_asn1_helpers.h @@ -373,7 +373,7 @@ inline void fill_asn1_bearer_context_setup_response(asn1::e1ap::sys_bearer_conte for (const auto& failed_item : response.pdu_session_resource_failed_list) { asn1::e1ap::pdu_session_res_failed_item_s asn1_failed_item; asn1_failed_item.pdu_session_id = pdu_session_id_to_uint(failed_item.pdu_session_id); - asn1_failed_item.cause = cause_to_asn1_cause(failed_item.cause); + asn1_failed_item.cause = cause_to_asn1(failed_item.cause); asn1_bearer_context_setup_response.pdu_session_res_failed_list.push_back(asn1_failed_item); } @@ -736,7 +736,7 @@ inline void fill_asn1_bearer_context_modification_response(asn1::e1ap::sys_beare for (const auto& res_failed_mod_item : response.pdu_session_resource_failed_list) { asn1::e1ap::pdu_session_res_failed_mod_item_s asn1_res_failed_mod_item; asn1_res_failed_mod_item.pdu_session_id = pdu_session_id_to_uint(res_failed_mod_item.pdu_session_id); - asn1_res_failed_mod_item.cause = cause_to_asn1_cause(res_failed_mod_item.cause); + asn1_res_failed_mod_item.cause = cause_to_asn1(res_failed_mod_item.cause); asn1_bearer_context_modification_response.pdu_session_res_failed_mod_list.push_back(asn1_res_failed_mod_item); } @@ -859,7 +859,7 @@ inline void fill_asn1_bearer_context_modification_response(asn1::e1ap::sys_beare for (const auto& res_failed_to_modify_item : response.pdu_session_resource_failed_to_modify_list) { asn1::e1ap::pdu_session_res_failed_to_modify_item_s asn1_res_failed_to_modify_item; asn1_res_failed_to_modify_item.pdu_session_id = pdu_session_id_to_uint(res_failed_to_modify_item.pdu_session_id); - asn1_res_failed_to_modify_item.cause = cause_to_asn1_cause(res_failed_to_modify_item.cause); + asn1_res_failed_to_modify_item.cause = cause_to_asn1(res_failed_to_modify_item.cause); asn1_bearer_context_modification_response.pdu_session_res_failed_to_modify_list.push_back( asn1_res_failed_to_modify_item); diff --git a/lib/e1ap/cu_up/e1ap_cu_up_impl.cpp b/lib/e1ap/cu_up/e1ap_cu_up_impl.cpp index cd63db31d2..20de9c4f28 100644 --- a/lib/e1ap/cu_up/e1ap_cu_up_impl.cpp +++ b/lib/e1ap/cu_up/e1ap_cu_up_impl.cpp @@ -252,7 +252,7 @@ void e1ap_cu_up_impl::handle_bearer_context_setup_request(const asn1::e1ap::bear connection_handler.on_new_message(e1ap_msg); } else { e1ap_msg.pdu.unsuccessful_outcome().value.bearer_context_setup_fail()->cause = - cause_to_asn1_cause(bearer_context_setup_response_msg.cause.value()); + cause_to_asn1(bearer_context_setup_response_msg.cause.value()); // send response ue_ctxt.logger.log_debug("Sending BearerContextSetupFailure"); @@ -323,7 +323,7 @@ void e1ap_cu_up_impl::handle_bearer_context_modification_request(const asn1::e1a connection_handler.on_new_message(e1ap_msg); } else { e1ap_msg.pdu.unsuccessful_outcome().value.bearer_context_mod_fail()->cause = - cause_to_asn1_cause(bearer_context_mod_response_msg.cause.value()); + cause_to_asn1(bearer_context_mod_response_msg.cause.value()); // send response ue_ctxt.logger.log_debug("Sending BearerContextModificationFailure"); @@ -343,7 +343,7 @@ void e1ap_cu_up_impl::handle_bearer_context_release_command(const asn1::e1ap::be e1ap_ue_context& ue_ctxt = ue_ctxt_list[int_to_gnb_cu_up_ue_e1ap_id(msg->gnb_cu_up_ue_e1ap_id)]; bearer_context_release_cmd.ue_index = ue_ctxt.ue_ids.ue_index; - bearer_context_release_cmd.cause = e1ap_cause_to_cause(msg->cause); + bearer_context_release_cmd.cause = asn1_to_cause(msg->cause); // Forward message to CU-UP cu_up_notifier.on_bearer_context_release_command_received(bearer_context_release_cmd); diff --git a/lib/e2/e2sm/e2sm_rc/e2sm_rc_asn1_packer.cpp b/lib/e2/e2sm/e2sm_rc/e2sm_rc_asn1_packer.cpp index 8f34fe8532..31e86ef0f3 100644 --- a/lib/e2/e2sm/e2sm_rc/e2sm_rc_asn1_packer.cpp +++ b/lib/e2/e2sm/e2sm_rc/e2sm_rc_asn1_packer.cpp @@ -99,7 +99,10 @@ e2_ric_control_response e2sm_rc_asn1_packer::pack_ric_control_response(const e2s if (variant_get(e2sm_response.ric_ctrl_outcome).pack(bref) != asn1::SRSASN_SUCCESS) { printf("Failed to pack E2SM RC RIC Control Outcome (Ack)\n"); } - e2_control_response.ack->ri_cctrl_outcome->resize(buf.length()); + if (!e2_control_response.ack->ri_cctrl_outcome->resize(buf.length())) { + printf("Failed to resize E2SM RC RIC Control Outcome (Ack)\n"); + return {}; + } std::copy(buf.begin(), buf.end(), e2_control_response.ack->ri_cctrl_outcome->begin()); } } else { @@ -110,7 +113,10 @@ e2_ric_control_response e2sm_rc_asn1_packer::pack_ric_control_response(const e2s if (variant_get(e2sm_response.ric_ctrl_outcome).pack(bref) != asn1::SRSASN_SUCCESS) { printf("Failed to pack E2SM RC RIC Control Outcome (Failure)\n"); } - e2_control_response.failure->ri_cctrl_outcome->resize(buf.length()); + if (!e2_control_response.failure->ri_cctrl_outcome->resize(buf.length())) { + printf("Failed to resize E2SM RC RIC Control Outcome (Failure)\n"); + return {}; + } std::copy(buf.begin(), buf.end(), e2_control_response.failure->ri_cctrl_outcome->begin()); } e2_control_response.failure->cause.value = e2sm_response.cause; @@ -166,7 +172,10 @@ asn1::unbounded_octstring e2sm_rc_asn1_packer::pack_ran_function_descripti return ran_function_description; } - ran_function_description.resize(buf.length()); + if (!ran_function_description.resize(buf.length())) { + printf("Failed to resize E2SM RC RAN Function Description\n"); + return ran_function_description; + } std::copy(buf.begin(), buf.end(), ran_function_description.begin()); return ran_function_description; } diff --git a/lib/e2/procedures/e2_indication_procedure.cpp b/lib/e2/procedures/e2_indication_procedure.cpp index e05f5a8b92..d740613b63 100644 --- a/lib/e2/procedures/e2_indication_procedure.cpp +++ b/lib/e2/procedures/e2_indication_procedure.cpp @@ -87,9 +87,15 @@ void e2_indication_procedure::operator()(coro_context>& c } // Put RIC indication content into message. - e2_ind.indication->ri_cind_msg.value.resize(ind_msg_bytes.length()); + if (!e2_ind.indication->ri_cind_msg.value.resize(ind_msg_bytes.length())) { + logger.error("Unable to resize byte_buffer, dropping indication"); + continue; + } std::copy(ind_msg_bytes.begin(), ind_msg_bytes.end(), e2_ind.indication->ri_cind_msg.value.begin()); - e2_ind.indication->ri_cind_hdr.value.resize(ind_hdr_bytes.length()); + if (!e2_ind.indication->ri_cind_hdr.value.resize(ind_hdr_bytes.length())) { + logger.error("Unable to resize byte_buffer, dropping indication"); + continue; + } std::copy(ind_hdr_bytes.begin(), ind_hdr_bytes.end(), e2_ind.indication->ri_cind_hdr.value.begin()); logger.info("Sending E2 indication"); send_e2_indication(e2_ind); diff --git a/lib/f1ap/cu_cp/f1ap_asn1_converters.h b/lib/f1ap/cu_cp/f1ap_asn1_converters.h index 358b921a00..019259ce3b 100644 --- a/lib/f1ap/cu_cp/f1ap_asn1_converters.h +++ b/lib/f1ap/cu_cp/f1ap_asn1_converters.h @@ -23,11 +23,12 @@ #pragma once #include "srsran/adt/optional.h" +#include "srsran/adt/variant.h" #include "srsran/asn1/f1ap/common.h" #include "srsran/asn1/f1ap/f1ap_ies.h" #include "srsran/cu_cp/cu_cp_types.h" -#include "srsran/f1ap/cu_cp/du_setup_notifier.h" #include "srsran/f1ap/cu_cp/f1ap_cu_ue_context_update.h" +#include "srsran/ran/cause/f1ap_cause.h" #include "srsran/ran/nr_cgi.h" #include #include @@ -35,56 +36,56 @@ namespace srsran { namespace srs_cu_cp { -/// \brief Convert F1AP ASN.1 Cause to \c cause_t type. -/// \param f1ap_cause The F1AP Cause. -/// \return The cause_t type. -inline cause_t f1ap_asn1_to_cause(asn1::f1ap::cause_c f1ap_cause) +/// \brief Convert F1AP ASN.1 Cause to \c f1ap_cause_t type. +/// \param asn1_cause The F1AP Cause. +/// \return The f1ap_cause_t type. +inline f1ap_cause_t asn1_to_cause(asn1::f1ap::cause_c asn1_cause) { - cause_t cause; + f1ap_cause_t cause; - switch (f1ap_cause.type()) { + switch (asn1_cause.type()) { case asn1::f1ap::cause_c::types_opts::radio_network: - cause = static_cast(f1ap_cause.radio_network().value); + cause = static_cast(asn1_cause.radio_network().value); break; case asn1::f1ap::cause_c::types_opts::transport: - cause = static_cast(f1ap_cause.transport().value); + cause = static_cast(asn1_cause.transport().value); break; case asn1::f1ap::cause_c::types_opts::protocol: - cause = static_cast(f1ap_cause.protocol().value); + cause = static_cast(asn1_cause.protocol().value); break; case asn1::f1ap::cause_c::types_opts::misc: - cause = static_cast(f1ap_cause.misc().value); + cause = static_cast(asn1_cause.misc().value); break; default: - report_fatal_error("Cannot convert F1AP ASN.1 cause {} to common type", f1ap_cause.type()); + report_fatal_error("Cannot convert F1AP ASN.1 cause {} to common type", asn1_cause.type()); } return cause; } -/// \brief Convert \c cause_t type to F1AP cause. -/// \param cause The cause_t type. +/// \brief Convert \c f1ap_cause_t type to F1AP cause. +/// \param cause The f1ap_cause_t type. /// \return The F1AP cause. -inline asn1::f1ap::cause_c cause_to_f1ap_asn1(cause_t cause) +inline asn1::f1ap::cause_c cause_to_asn1(f1ap_cause_t cause) { - asn1::f1ap::cause_c f1ap_cause; - - if (variant_holds_alternative(cause)) { - f1ap_cause.set_radio_network() = - static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - f1ap_cause.set_transport() = - static_cast(variant_get(cause)); + asn1::f1ap::cause_c asn1_cause; + + if (variant_holds_alternative(cause)) { + asn1_cause.set_radio_network() = + static_cast(variant_get(cause)); + } else if (variant_holds_alternative(cause)) { + asn1_cause.set_transport() = + static_cast(variant_get(cause)); } else if (variant_holds_alternative(cause)) { - f1ap_cause.set_protocol() = + asn1_cause.set_protocol() = static_cast(variant_get(cause)); } else if (variant_holds_alternative(cause)) { - f1ap_cause.set_misc() = static_cast(variant_get(cause)); + asn1_cause.set_misc() = static_cast(variant_get(cause)); } else { - report_fatal_error("Cannot convert cause to F1AP type"); + report_fatal_error("Cannot convert cause to F1AP type: {}", cause); } - return f1ap_cause; + return asn1_cause; } /// \brief Convert F1AP NRCGI to NR Cell Identity. @@ -736,7 +737,7 @@ asn1_to_f1ap_srbs_failed_to_be_setup_mod_item(const template_asn1_item& asn1_srb // srb id srbs_failed_to_be_setup_mod_item.srb_id = int_to_srb_id(asn1_srbs_failed_to_be_setup_mod_item.srb_id); if (asn1_srbs_failed_to_be_setup_mod_item.cause_present) { - srbs_failed_to_be_setup_mod_item.cause = f1ap_asn1_to_cause(asn1_srbs_failed_to_be_setup_mod_item.cause); + srbs_failed_to_be_setup_mod_item.cause = asn1_to_cause(asn1_srbs_failed_to_be_setup_mod_item.cause); } return srbs_failed_to_be_setup_mod_item; @@ -754,7 +755,7 @@ asn1_to_f1ap_drbs_failed_to_be_setup_mod_item(const template_asn1_item& asn1_drb // drb id drbs_failed_to_be_setup_mod_item.drb_id = uint_to_drb_id(asn1_drbs_failed_to_be_setup_mod_item.drb_id); if (asn1_drbs_failed_to_be_setup_mod_item.cause_present) { - drbs_failed_to_be_setup_mod_item.cause = f1ap_asn1_to_cause(asn1_drbs_failed_to_be_setup_mod_item.cause); + drbs_failed_to_be_setup_mod_item.cause = asn1_to_cause(asn1_drbs_failed_to_be_setup_mod_item.cause); } return drbs_failed_to_be_setup_mod_item; diff --git a/lib/f1ap/cu_cp/f1ap_asn1_helpers.h b/lib/f1ap/cu_cp/f1ap_asn1_helpers.h index e3846a03b6..c3d293ca87 100644 --- a/lib/f1ap/cu_cp/f1ap_asn1_helpers.h +++ b/lib/f1ap/cu_cp/f1ap_asn1_helpers.h @@ -393,7 +393,7 @@ inline void fill_f1ap_ue_context_modification_response(f1ap_ue_context_modificat f1ap_scell_failed_to_setup_mod_item scell_failed_item; scell_failed_item.scell_id = cgi_from_asn1(asn1_scell_failed_item.scell_id); if (asn1_scell_failed_item.cause_present) { - scell_failed_item.cause = f1ap_asn1_to_cause(asn1_scell_failed_item.cause); + scell_failed_item.cause = asn1_to_cause(asn1_scell_failed_item.cause); } res.scell_failed_to_setup_mod_list.push_back(scell_failed_item); } @@ -468,7 +468,7 @@ inline void fill_f1ap_ue_context_modification_response(f1ap_ue_context_modificat const asn1::f1ap::ue_context_mod_fail_s& asn1_fail) { res.success = false; - res.cause = f1ap_asn1_to_cause(asn1_fail->cause); + res.cause = asn1_to_cause(asn1_fail->cause); if (asn1_fail->crit_diagnostics_present) { // TODO: Add crit diagnostics } diff --git a/lib/f1ap/cu_cp/f1ap_cu_impl.cpp b/lib/f1ap/cu_cp/f1ap_cu_impl.cpp index 162580258b..88d86365ff 100644 --- a/lib/f1ap/cu_cp/f1ap_cu_impl.cpp +++ b/lib/f1ap/cu_cp/f1ap_cu_impl.cpp @@ -363,7 +363,7 @@ void f1ap_cu_impl::handle_ue_context_release_request(const asn1::f1ap::ue_contex f1ap_ue_context_release_request req; req.ue_index = ue_ctxt.ue_ids.ue_index; - req.cause = f1ap_asn1_to_cause(msg->cause); + req.cause = asn1_to_cause(msg->cause); du_processor_notifier.on_du_initiated_ue_context_release_request(req); } diff --git a/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp b/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp index 3a83ce84be..1f7c1ccb79 100644 --- a/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp +++ b/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp @@ -27,7 +27,7 @@ #include "srsran/asn1/f1ap/f1ap_pdu_contents.h" #include "srsran/f1ap/common/f1ap_message.h" #include "srsran/f1ap/cu_cp/du_setup_notifier.h" -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/f1ap_cause.h" using namespace srsran; using namespace srs_cu_cp; @@ -198,7 +198,7 @@ void srsran::srs_cu_cp::handle_f1_setup_procedure(const asn1::f1ap::f1_setup_req // Failed to setup DU case. auto& fail_resp = variant_get(request_outcome.result); logger.warning("Rejecting F1 Setup Request. Cause: {}", fail_resp.cause_str); - f1ap_msg = create_f1_setup_reject(request, cause_to_f1ap_asn1(fail_resp.cause)); + f1ap_msg = create_f1_setup_reject(request, cause_to_asn1(fail_resp.cause)); } else { // DU has been accepted. f1ap_msg = create_f1_setup_response(request, variant_get(request_outcome.result)); diff --git a/lib/f1ap/cu_cp/procedures/ue_context_modification_procedure.cpp b/lib/f1ap/cu_cp/procedures/ue_context_modification_procedure.cpp index cdc848228b..5e6bea1655 100644 --- a/lib/f1ap/cu_cp/procedures/ue_context_modification_procedure.cpp +++ b/lib/f1ap/cu_cp/procedures/ue_context_modification_procedure.cpp @@ -24,7 +24,7 @@ #include "../../common/asn1_helpers.h" #include "../f1ap_asn1_helpers.h" #include "srsran/f1ap/common/f1ap_message.h" -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/ngap_cause.h" using namespace srsran; using namespace srsran::srs_cu_cp; diff --git a/lib/f1ap/cu_cp/procedures/ue_context_release_procedure.cpp b/lib/f1ap/cu_cp/procedures/ue_context_release_procedure.cpp index 36271a8194..b38ee19f79 100644 --- a/lib/f1ap/cu_cp/procedures/ue_context_release_procedure.cpp +++ b/lib/f1ap/cu_cp/procedures/ue_context_release_procedure.cpp @@ -37,7 +37,7 @@ ue_context_release_procedure::ue_context_release_procedure(const f1ap_ue_context { command->gnb_cu_ue_f1ap_id = gnb_cu_ue_f1ap_id_to_uint(ue_ctxt.ue_ids.cu_ue_f1ap_id); command->gnb_du_ue_f1ap_id = gnb_du_ue_f1ap_id_to_uint(ue_ctxt.ue_ids.du_ue_f1ap_id); - command->cause = cause_to_f1ap_asn1(cmd_.cause); + command->cause = cause_to_asn1(cmd_.cause); if (!cmd_.rrc_release_pdu.empty()) { command->rrc_container_present = true; command->rrc_container = cmd_.rrc_release_pdu.copy(); diff --git a/lib/f1ap/cu_cp/procedures/ue_context_setup_procedure.cpp b/lib/f1ap/cu_cp/procedures/ue_context_setup_procedure.cpp index 34f34fe053..507e5141db 100644 --- a/lib/f1ap/cu_cp/procedures/ue_context_setup_procedure.cpp +++ b/lib/f1ap/cu_cp/procedures/ue_context_setup_procedure.cpp @@ -404,7 +404,7 @@ static void fill_f1ap_ue_context_setup_response(f1ap_ue_context_setup_response& response.ue_index = ue_index; // cause - response.cause = f1ap_asn1_to_cause(asn1_failure->cause); + response.cause = asn1_to_cause(asn1_failure->cause); // potential sp cell list if (asn1_failure->potential_sp_cell_list_present) { @@ -493,7 +493,7 @@ static void fill_f1ap_ue_context_setup_response(f1ap_ue_context_setup_response& // cause if (asn1_scell_failed_to_setup_item->scell_failedto_setup_item().cause_present) { scell_failed_to_setup_item.cause = - f1ap_asn1_to_cause(asn1_scell_failed_to_setup_item->scell_failedto_setup_item().cause); + asn1_to_cause(asn1_scell_failed_to_setup_item->scell_failedto_setup_item().cause); } response.scell_failed_to_setup_list.push_back(scell_failed_to_setup_item); diff --git a/lib/f1ap/du/procedures/f1ap_du_ue_context_common.h b/lib/f1ap/du/procedures/f1ap_du_ue_context_common.h index 065272a990..5b6982adbc 100644 --- a/lib/f1ap/du/procedures/f1ap_du_ue_context_common.h +++ b/lib/f1ap/du/procedures/f1ap_du_ue_context_common.h @@ -28,55 +28,56 @@ namespace srsran { namespace srs_du { -/// \brief Creates a \c srb_id_t from ASN.1 type +/// \brief Creates a \c srb_id_t from ASN.1 type. /// -/// This function is used by the following procedures +/// This function is used by the following procedures: /// - f1ap_du_ue_context_setup_procedure /// - f1ap_du_ue_context_modification_procedure /// -/// \param srb_item ASN.1 item with SRB-specific parameters to be setup -/// \return srb_id_t object +/// \param srb_item ASN.1 item with SRB-specific parameters to be setup. +/// \return srb_id_t object. template srb_id_t make_srb_id(const Asn1Type& srb_item) { - return (srb_id_t)srb_item.srb_id; + return static_cast(srb_item.srb_id); } -/// \brief Creates a \c f1ap_drb_to_setup from ASN.1 type +/// \brief Creates a \c f1ap_drb_to_setup from ASN.1 type. /// -/// This function is used by the following procedures +/// This function is used by the following procedures: /// - f1ap_du_ue_context_setup_procedure /// - f1ap_du_ue_context_modification_procedure /// -/// \param drb_item ASN.1 item with DRB-specific parameters to be setup -/// \return f1ap_drb_to_setup object +/// \param drb_item ASN.1 item with DRB-specific parameters to be setup. +/// \return f1ap_drb_to_setup object. template f1ap_drb_to_setup make_drb_to_setup(const Asn1Type& drb_item) { f1ap_drb_to_setup drb_obj; - drb_obj.drb_id = (drb_id_t)drb_item.drb_id; - drb_obj.mode = (drb_rlc_mode)(unsigned)drb_item.rlc_mode; - drb_obj.uluptnl_info_list.resize(drb_item.ul_up_tnl_info_to_be_setup_list.size()); - for (unsigned j = 0; j != drb_obj.uluptnl_info_list.size(); ++j) { - drb_obj.uluptnl_info_list[j] = - asn1_to_up_transport_layer_info(drb_item.ul_up_tnl_info_to_be_setup_list[j].ul_up_tnl_info); - } + + drb_obj.drb_id = static_cast(drb_item.drb_id); + drb_obj.mode = static_cast(static_cast(drb_item.rlc_mode)); drb_obj.five_qi = uint_to_five_qi( drb_item.qos_info.choice_ext().value().drb_info().drb_qos.qos_characteristics.non_dyn_5qi().five_qi); + drb_obj.uluptnl_info_list.reserve(drb_item.ul_up_tnl_info_to_be_setup_list.size()); + for (const auto& tnl_info : drb_item.ul_up_tnl_info_to_be_setup_list) { + drb_obj.uluptnl_info_list.push_back(asn1_to_up_transport_layer_info(tnl_info.ul_up_tnl_info)); + } + return drb_obj; } -/// \brief Creates a \c drb_id_t from ASN.1 type +/// \brief Creates a \c drb_id_t from ASN.1 type. /// -/// This function is used by the following procedures +/// This function is used by the following procedures: /// - f1ap_du_ue_context_modification_procedure /// -/// \param drb_item ASN.1 item with DRB-specific parameters to be removed -/// \return drb_id_t object +/// \param drb_item ASN.1 item with DRB-specific parameters to be removed. +/// \return drb_id_t object. template drb_id_t make_drb_id(const Asn1Type& drb_item) { - return (drb_id_t)drb_item.drb_id; + return static_cast(drb_item.drb_id); } } // namespace srs_du diff --git a/lib/f1ap/du/procedures/f1ap_du_ue_context_release_procedure.cpp b/lib/f1ap/du/procedures/f1ap_du_ue_context_release_procedure.cpp index 8e92935fe2..02ae738c7d 100644 --- a/lib/f1ap/du/procedures/f1ap_du_ue_context_release_procedure.cpp +++ b/lib/f1ap/du/procedures/f1ap_du_ue_context_release_procedure.cpp @@ -51,6 +51,9 @@ void f1ap_du_ue_context_release_procedure::operator()(coro_contextrrc_container_present) { // If the UE CONTEXT RELEASE COMMAND message contains the RRC-Container IE, the gNB-DU shall send the RRC // container to the UE via the SRB indicated by the SRB ID IE. diff --git a/lib/f1u/cu_up/f1u_bearer_factory.cpp b/lib/f1u/cu_up/f1u_bearer_factory.cpp index 718b6d6fef..b660aa9141 100644 --- a/lib/f1u/cu_up/f1u_bearer_factory.cpp +++ b/lib/f1u/cu_up/f1u_bearer_factory.cpp @@ -32,10 +32,20 @@ std::unique_ptr srsran::srs_cu_up::create_f1u_bearer(uint32_t f1u_tx_pdu_notifier& tx_pdu_notifier, f1u_rx_delivery_notifier& rx_delivery_notifier, f1u_rx_sdu_notifier& rx_sdu_notifier, - timer_factory timers, + timer_factory ue_dl_timer_factory, + unique_timer& ue_inactivity_timer, + task_executor& ul_exec, f1u_bearer_disconnector& disconnector) { - auto bearer = std::make_unique( - ue_index, drb_id, ul_up_tnl_info, tx_pdu_notifier, rx_delivery_notifier, rx_sdu_notifier, timers, disconnector); + auto bearer = std::make_unique(ue_index, + drb_id, + ul_up_tnl_info, + tx_pdu_notifier, + rx_delivery_notifier, + rx_sdu_notifier, + ue_dl_timer_factory, + ue_inactivity_timer, + ul_exec, + disconnector); return bearer; } diff --git a/lib/f1u/cu_up/f1u_bearer_impl.cpp b/lib/f1u/cu_up/f1u_bearer_impl.cpp index 8db3eb652c..66d7cc7274 100644 --- a/lib/f1u/cu_up/f1u_bearer_impl.cpp +++ b/lib/f1u/cu_up/f1u_bearer_impl.cpp @@ -31,7 +31,9 @@ f1u_bearer_impl::f1u_bearer_impl(uint32_t ue_index, f1u_tx_pdu_notifier& tx_pdu_notifier_, f1u_rx_delivery_notifier& rx_delivery_notifier_, f1u_rx_sdu_notifier& rx_sdu_notifier_, - timer_factory timers, + timer_factory ue_dl_timer_factory, + unique_timer& ue_inactivity_timer_, + task_executor& ul_exec_, f1u_bearer_disconnector& disconnector_) : logger("CU-F1-U", {ue_index, drb_id_, ul_tnl_info_}), tx_pdu_notifier(tx_pdu_notifier_), @@ -39,17 +41,29 @@ f1u_bearer_impl::f1u_bearer_impl(uint32_t ue_index, rx_sdu_notifier(rx_sdu_notifier_), disconnector(disconnector_), ul_tnl_info(ul_tnl_info_), - dl_notif_timer(timers.create_timer()) + ul_exec(ul_exec_), + dl_notif_timer(ue_dl_timer_factory.create_timer()), + ue_inactivity_timer(ue_inactivity_timer_) { dl_notif_timer.set(std::chrono::milliseconds(f1u_dl_notif_time_ms), [this](timer_id_t tid) { on_expired_dl_notif_timer(); }); } void f1u_bearer_impl::handle_pdu(nru_ul_message msg) +{ + auto fn = [this, m = std::move(msg)]() mutable { handle_pdu_impl(std::move(m)); }; + + if (not ul_exec.execute(std::move(fn))) { + logger.log_warning("Dropped F1-U PDU, queue is full."); + } +} + +void f1u_bearer_impl::handle_pdu_impl(nru_ul_message msg) { logger.log_debug("F1-U bearer received PDU"); // handle T-PDU if (!msg.t_pdu.empty()) { + ue_inactivity_timer.run(); // restart inactivity timer due to UL PDU logger.log_debug("Delivering T-PDU of size={}", msg.t_pdu.length()); rx_sdu_notifier.on_new_sdu(std::move(msg.t_pdu)); } @@ -58,6 +72,7 @@ void f1u_bearer_impl::handle_pdu(nru_ul_message msg) nru_dl_data_delivery_status& status = msg.data_delivery_status.value(); // Highest transmitted PDCP SN if (status.highest_transmitted_pdcp_sn.has_value()) { + ue_inactivity_timer.run(); // restart inactivity timer due to confirmed transmission of DL PDU uint32_t pdcp_sn = status.highest_transmitted_pdcp_sn.value(); logger.log_debug("Notifying highest transmitted pdcp_sn={}", pdcp_sn); rx_delivery_notifier.on_transmit_notification(pdcp_sn); diff --git a/lib/f1u/cu_up/f1u_bearer_impl.h b/lib/f1u/cu_up/f1u_bearer_impl.h index bcca94821f..ca69614967 100644 --- a/lib/f1u/cu_up/f1u_bearer_impl.h +++ b/lib/f1u/cu_up/f1u_bearer_impl.h @@ -42,7 +42,9 @@ class f1u_bearer_impl final : public f1u_bearer, public f1u_rx_pdu_handler, publ f1u_tx_pdu_notifier& tx_pdu_notifier_, f1u_rx_delivery_notifier& rx_delivery_notifier_, f1u_rx_sdu_notifier& rx_sdu_notifier_, - timer_factory timers, + timer_factory ue_dl_timer_factory, + unique_timer& ue_inactivity_timer_, + task_executor& ul_exec_, f1u_bearer_disconnector& diconnector_); f1u_bearer_impl(const f1u_bearer_impl&) = delete; f1u_bearer_impl& operator=(const f1u_bearer_impl&) = delete; @@ -74,15 +76,22 @@ class f1u_bearer_impl final : public f1u_bearer, public f1u_rx_pdu_handler, publ f1u_rx_sdu_notifier& rx_sdu_notifier; f1u_bearer_disconnector& disconnector; up_transport_layer_info ul_tnl_info; + task_executor& ul_exec; /// Downlink notification timer that triggers periodic transmission of discard blocks towards lower layers. The /// purpose of this timer is to avoid excessive downlink notifications for every PDCP SN that is discarded by upper /// layers. unique_timer dl_notif_timer; + /// UE inactivity timer that is injected from parent entities. The timer must run in the UL executor! + /// The timer shall be restarted on each UL PDU (= UL activity) and on each transmit notification (= DL activity). + unique_timer& ue_inactivity_timer; + /// Collection of pending \c nru_pdcp_sn_discard_block objects. nru_pdcp_sn_discard_blocks discard_blocks; + void handle_pdu_impl(nru_ul_message msg); + /// \brief Fills the provided \c nru_dl_message with all SDU discard blocks that have been aggregated since the last /// call of this function (or since creation of this object). /// \param msg The message to be filled with SDU discard blocks. diff --git a/lib/f1u/du/f1u_bearer_factory.cpp b/lib/f1u/du/f1u_bearer_factory.cpp index 0b6a13d0b3..37074dff76 100644 --- a/lib/f1u/du/f1u_bearer_factory.cpp +++ b/lib/f1u/du/f1u_bearer_factory.cpp @@ -28,7 +28,16 @@ using namespace srs_du; std::unique_ptr srsran::srs_du::create_f1u_bearer(const f1u_bearer_creation_message& msg) { - auto bearer = std::make_unique( - msg.ue_index, msg.drb_id, msg.dl_tnl_info, msg.config, *msg.rx_sdu_notifier, *msg.tx_pdu_notifier, msg.timers); + srsran_assert(msg.rx_sdu_notifier != nullptr, "Cannot create F1-U bearer: RX SDU notifier is not configured."); + srsran_assert(msg.tx_pdu_notifier != nullptr, "Cannot create F1-U bearer: TX PDU notifier is not configured."); + srsran_assert(msg.ue_executor != nullptr, "Cannot create F1-U bearer: UE executor is not configured."); + auto bearer = std::make_unique(msg.ue_index, + msg.drb_id, + msg.dl_tnl_info, + msg.config, + *msg.rx_sdu_notifier, + *msg.tx_pdu_notifier, + msg.timers, + *msg.ue_executor); return bearer; } diff --git a/lib/f1u/du/f1u_bearer_impl.cpp b/lib/f1u/du/f1u_bearer_impl.cpp index 012bb381cc..cbfe0009c2 100644 --- a/lib/f1u/du/f1u_bearer_impl.cpp +++ b/lib/f1u/du/f1u_bearer_impl.cpp @@ -31,11 +31,13 @@ f1u_bearer_impl::f1u_bearer_impl(uint32_t ue_index, const f1u_config& config, f1u_rx_sdu_notifier& rx_sdu_notifier_, f1u_tx_pdu_notifier& tx_pdu_notifier_, - timer_factory timers) : + timer_factory timers, + task_executor& ue_executor_) : logger("DU-F1-U", {ue_index, drb_id_, dl_tnl_info_}), cfg(config), rx_sdu_notifier(rx_sdu_notifier_), tx_pdu_notifier(tx_pdu_notifier_), + ue_executor(ue_executor_), ul_notif_timer(timers.create_timer()) { ul_notif_timer.set(std::chrono::milliseconds(cfg.t_notify), [this](timer_id_t tid) { on_expired_ul_notif_timer(); }); @@ -59,11 +61,19 @@ void f1u_bearer_impl::handle_sdu(byte_buffer_chain sdu) } void f1u_bearer_impl::handle_pdu(nru_dl_message msg) +{ + auto fn = [this, m = std::move(msg)]() mutable { handle_pdu_impl(std::move(m)); }; + if (!ue_executor.execute(std::move(fn))) { + logger.log_warning("Dropped F1-U PDU, queue is full"); + } +} + +void f1u_bearer_impl::handle_pdu_impl(nru_dl_message msg) { logger.log_debug("F1-U bearer received PDU"); // handle T-PDU if (!msg.t_pdu.empty()) { - logger.log_debug("Delivering T-PDU of size={}, pdcp_sn={}", msg.t_pdu.length(), msg.pdcp_sn); + logger.log_debug("Delivering T-PDU. size={} pdcp_sn={}", msg.t_pdu.length(), msg.pdcp_sn); pdcp_tx_pdu tx_sdu = {}; tx_sdu.buf = std::move(msg.t_pdu); tx_sdu.pdcp_sn = msg.pdcp_sn; diff --git a/lib/f1u/du/f1u_bearer_impl.h b/lib/f1u/du/f1u_bearer_impl.h index c1a9cdc612..9fa7260579 100644 --- a/lib/f1u/du/f1u_bearer_impl.h +++ b/lib/f1u/du/f1u_bearer_impl.h @@ -46,7 +46,8 @@ class f1u_bearer_impl final : public f1u_bearer, const f1u_config& config, f1u_rx_sdu_notifier& rx_sdu_notifier_, f1u_tx_pdu_notifier& tx_pdu_notifier_, - timer_factory timers); + timer_factory timers, + task_executor& ue_executor_); f1u_tx_sdu_handler& get_tx_sdu_handler() override { return *this; } f1u_tx_delivery_handler& get_tx_delivery_handler() override { return *this; } @@ -68,6 +69,8 @@ class f1u_bearer_impl final : public f1u_bearer, f1u_rx_sdu_notifier& rx_sdu_notifier; f1u_tx_pdu_notifier& tx_pdu_notifier; + task_executor& ue_executor; + /// Sentinel value representing a not-yet set PDCP SN static constexpr uint32_t unset_pdcp_sn = UINT32_MAX; @@ -87,6 +90,8 @@ class f1u_bearer_impl final : public f1u_bearer, bool fill_highest_transmitted_pdcp_sn(nru_dl_data_delivery_status& status); bool fill_highest_delivered_pdcp_sn(nru_dl_data_delivery_status& status); void fill_data_delivery_status(nru_ul_message& msg); + + void handle_pdu_impl(nru_dl_message msg); }; } // namespace srs_du diff --git a/lib/f1u/local_connector/f1u_local_connector.cpp b/lib/f1u/local_connector/f1u_local_connector.cpp index 183af754f0..7f1654d389 100644 --- a/lib/f1u/local_connector/f1u_local_connector.cpp +++ b/lib/f1u/local_connector/f1u_local_connector.cpp @@ -33,7 +33,9 @@ f1u_local_connector::create_cu_bearer(uint32_t ue_in const up_transport_layer_info& ul_up_tnl_info, srs_cu_up::f1u_rx_delivery_notifier& rx_delivery_notifier, srs_cu_up::f1u_rx_sdu_notifier& rx_sdu_notifier, - timer_factory timers) + task_executor& ul_exec, + timer_factory ue_dl_timer_factory, + unique_timer& ue_inactivity_timer) { logger_cu.info("Creating CU F1-U bearer with UL GTP Tunnel={}", ul_up_tnl_info); std::unique_lock lock(map_mutex); @@ -42,9 +44,17 @@ f1u_local_connector::create_cu_bearer(uint32_t ue_in ul_up_tnl_info); std::unique_ptr cu_tx = std::make_unique(ue_index, drb_id, ul_up_tnl_info); - std::unique_ptr f1u_bearer = srs_cu_up::create_f1u_bearer( - ue_index, drb_id, ul_up_tnl_info, *cu_tx, rx_delivery_notifier, rx_sdu_notifier, timers, *this); - f1u_cu_bearer cu_bearer(std::move(cu_tx), f1u_bearer.get()); + std::unique_ptr f1u_bearer = srs_cu_up::create_f1u_bearer(ue_index, + drb_id, + ul_up_tnl_info, + *cu_tx, + rx_delivery_notifier, + rx_sdu_notifier, + ue_dl_timer_factory, + ue_inactivity_timer, + ul_exec, + *this); + f1u_cu_bearer cu_bearer(std::move(cu_tx), f1u_bearer.get()); cu_map.insert({ul_up_tnl_info, std::move(cu_bearer)}); return f1u_bearer; } @@ -116,7 +126,8 @@ srs_du::f1u_bearer* f1u_local_connector::create_du_bearer(uint32_t const up_transport_layer_info& dl_up_tnl_info, const up_transport_layer_info& ul_up_tnl_info, srs_du::f1u_rx_sdu_notifier& du_rx, - timer_factory timers) + timer_factory timers, + task_executor& ue_executor) { std::unique_lock lock(map_mutex); if (cu_map.find(ul_up_tnl_info) == cu_map.end()) { @@ -137,6 +148,7 @@ srs_du::f1u_bearer* f1u_local_connector::create_du_bearer(uint32_t f1u_msg.rx_sdu_notifier = &du_rx; f1u_msg.tx_pdu_notifier = du_tx.get(); f1u_msg.timers = timers; + f1u_msg.ue_executor = &ue_executor; std::unique_ptr f1u_bearer = srs_du::create_f1u_bearer(f1u_msg); srs_du::f1u_bearer* ptr = f1u_bearer.get(); diff --git a/lib/fapi_adaptor/mac/mac_to_fapi_translator.cpp b/lib/fapi_adaptor/mac/mac_to_fapi_translator.cpp index b90ee9b488..5331bc042d 100644 --- a/lib/fapi_adaptor/mac/mac_to_fapi_translator.cpp +++ b/lib/fapi_adaptor/mac/mac_to_fapi_translator.cpp @@ -138,8 +138,8 @@ static void add_csi_rs_pdus_to_dl_request(fapi::dl_tti_request_message_builder& csi_builder.set_bwp_parameters(pdu.bwp_cfg->scs, pdu.bwp_cfg->cp); if (is_nzp_csi) { - csi_builder.set_tx_power_info_parameters(pdu.power_ctrl_offset_profile_nr, - to_nzp_csi_rs_epre_to_ssb(pdu.power_ctrl_offset_ss_profile_nr)); + csi_builder.set_tx_power_info_parameters(pdu.power_ctrl_offset, + to_nzp_csi_rs_epre_to_ssb(pdu.power_ctrl_offset_ss)); } else { // ZP-CSI type does not use these values. csi_builder.set_tx_power_info_parameters(0, fapi::nzp_csi_rs_epre_to_ssb::dB0); diff --git a/lib/gtpu/gtpu_echo_rx_impl.h b/lib/gtpu/gtpu_echo_rx_impl.h index 18d3dbd7b8..ded7a78aa6 100644 --- a/lib/gtpu/gtpu_echo_rx_impl.h +++ b/lib/gtpu/gtpu_echo_rx_impl.h @@ -75,7 +75,7 @@ class gtpu_echo_rx : public gtpu_tunnel_base_rx return; case GTPU_MSG_ERROR_INDICATION: // TODO: unpack and print information elements; add handling - logger.log_error(pdu.buf.begin(), pdu.buf.end(), "Received error indication from peer"); + logger.log_info(pdu.buf.begin(), pdu.buf.end(), "Received error indication from peer"); // TS 29.281 Sec. 7.3.1: Error Indication // When a GTP-U node receives a G-PDU for which no EPS Bearer context, PDP context, PDU Session, MBMS Bearer // context, or RAB exists, the GTP-U node shall discard the G - PDU.If the TEID of the incoming G-PDU is diff --git a/lib/gtpu/gtpu_tunnel_impl_ngu.h b/lib/gtpu/gtpu_tunnel_impl_ngu.h index 33265aee26..02966b07a3 100644 --- a/lib/gtpu/gtpu_tunnel_impl_ngu.h +++ b/lib/gtpu/gtpu_tunnel_impl_ngu.h @@ -39,10 +39,10 @@ class gtpu_tunnel_ngu_impl : public gtpu_tunnel_ngu dlt_pcap& gtpu_pcap, gtpu_tunnel_ngu_rx_lower_layer_notifier& rx_lower, gtpu_tunnel_tx_upper_layer_notifier& tx_upper, - timer_factory timers) : + timer_factory ue_dl_timer_factory) : logger(srslog::fetch_basic_logger("GTPU")) { - rx = std::make_unique(ue_index, cfg.rx, rx_lower, timers); + rx = std::make_unique(ue_index, cfg.rx, rx_lower, ue_dl_timer_factory); tx = std::make_unique(ue_index, cfg.tx, gtpu_pcap, tx_upper); } ~gtpu_tunnel_ngu_impl() override = default; diff --git a/lib/gtpu/gtpu_tunnel_ngu_factory.cpp b/lib/gtpu/gtpu_tunnel_ngu_factory.cpp index e65f018e27..784e49fa8f 100644 --- a/lib/gtpu/gtpu_tunnel_ngu_factory.cpp +++ b/lib/gtpu/gtpu_tunnel_ngu_factory.cpp @@ -30,5 +30,5 @@ using namespace srsran; std::unique_ptr srsran::create_gtpu_tunnel_ngu(gtpu_tunnel_ngu_creation_message& msg) { return std::make_unique( - msg.ue_index, msg.cfg, *msg.gtpu_pcap, *msg.rx_lower, *msg.tx_upper, msg.timers); + msg.ue_index, msg.cfg, *msg.gtpu_pcap, *msg.rx_lower, *msg.tx_upper, msg.ue_dl_timer_factory); } diff --git a/lib/gtpu/gtpu_tunnel_ngu_rx.h b/lib/gtpu/gtpu_tunnel_ngu_rx.h index e41c9c22b2..da7b7df27b 100644 --- a/lib/gtpu/gtpu_tunnel_ngu_rx.h +++ b/lib/gtpu/gtpu_tunnel_ngu_rx.h @@ -52,23 +52,23 @@ struct gtpu_rx_sdu_info { optional sn = {}; }; -/// Class used for receiving GTP-U bearers. +/// Class used for receiving GTP-U NGU bearers, e.g. on N3 interface. class gtpu_tunnel_ngu_rx : public gtpu_tunnel_base_rx { public: gtpu_tunnel_ngu_rx(srs_cu_up::ue_index_t ue_index, gtpu_config::gtpu_rx_config cfg, gtpu_tunnel_ngu_rx_lower_layer_notifier& rx_lower_, - timer_factory timers_) : + timer_factory ue_dl_timer_factory_) : gtpu_tunnel_base_rx(gtpu_tunnel_log_prefix{ue_index, cfg.local_teid, "DL"}), psup_packer(logger.get_basic_logger()), lower_dn(rx_lower_), config(cfg), rx_window(std::make_unique>(logger)), - timers(timers_) + ue_dl_timer_factory(ue_dl_timer_factory_) { if (config.t_reordering.count() != 0) { - reordering_timer = timers.create_timer(); + reordering_timer = ue_dl_timer_factory.create_timer(); reordering_timer.set(config.t_reordering, reordering_callback{this}); } logger.log_info( @@ -259,7 +259,7 @@ class gtpu_tunnel_ngu_rx : public gtpu_tunnel_base_rx unique_timer reordering_timer; /// Timer factory - timer_factory timers; + timer_factory ue_dl_timer_factory; /// Reordering callback (t-Reordering) class reordering_callback diff --git a/lib/gtpu/gtpu_tunnel_ngu_tx.h b/lib/gtpu/gtpu_tunnel_ngu_tx.h index ea269cd413..71736fb93d 100644 --- a/lib/gtpu/gtpu_tunnel_ngu_tx.h +++ b/lib/gtpu/gtpu_tunnel_ngu_tx.h @@ -34,7 +34,7 @@ namespace srsran { -/// Class used for transmitting GTP-U bearers. +/// Class used for transmitting GTP-U NGU bearers, e.g. on N3 interface. class gtpu_tunnel_ngu_tx : public gtpu_tunnel_base_tx, public gtpu_tunnel_tx_lower_layer_interface { public: diff --git a/lib/hal/dpdk/bbdev/ldpc/bbdev_ldpc_decoder.cpp b/lib/hal/dpdk/bbdev/ldpc/bbdev_ldpc_decoder.cpp index 28f2713aec..8aed4923b7 100644 --- a/lib/hal/dpdk/bbdev/ldpc/bbdev_ldpc_decoder.cpp +++ b/lib/hal/dpdk/bbdev/ldpc/bbdev_ldpc_decoder.cpp @@ -143,7 +143,7 @@ bool dpdk::set_ldpc_dec_bbdev_data(::rte_bbdev_dec_op& dec_op, dec_op.ldpc_dec.input.length += cw_len; // Harq offset for the current CB (based on its unique absolute ID). - unsigned harq_offset = srsran::hal::HARQ_INCR * absolute_cb_id; + unsigned harq_offset = srsran::hal::HARQ_INCR.value() * absolute_cb_id; // Input HARQ combining data is only needed in case of a retransmission. if (!new_data) { diff --git a/lib/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository_factory.cpp b/lib/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository_factory.cpp index b83840f5af..78415e187c 100644 --- a/lib/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository_factory.cpp +++ b/lib/hal/phy/upper/channel_processors/pusch/ext_harq_buffer_context_repository_factory.cpp @@ -26,7 +26,9 @@ using namespace srsran; using namespace hal; std::shared_ptr -srsran::hal::create_ext_harq_buffer_context_repository(unsigned nof_rnti, unsigned nof_harq_id, bool debug_mode) +srsran::hal::create_ext_harq_buffer_context_repository(unsigned nof_codeblocks, + uint64_t ext_harq_buff_size, + bool debug_mode) { - return std::make_shared(nof_rnti, nof_harq_id, debug_mode); + return std::make_shared(nof_codeblocks, ext_harq_buff_size, debug_mode); } diff --git a/lib/mac/CMakeLists.txt b/lib/mac/CMakeLists.txt index 421ddc3f8d..8507044f48 100644 --- a/lib/mac/CMakeLists.txt +++ b/lib/mac/CMakeLists.txt @@ -34,6 +34,7 @@ set(SOURCES mac_factory.cpp mac_dl/paging_pdu_assembler.cpp mac_dl/ssb_assembler.cpp mac_dl/mac_dl_ue_manager.cpp + mac_dl/cell_dl_harq_buffer_pool.cpp mac_ul/mac_ul_sch_pdu.cpp mac_ul/ul_bsr.cpp mac_ul/pdu_rx_handler.cpp diff --git a/lib/mac/mac_dl/cell_dl_harq_buffer_pool.cpp b/lib/mac/mac_dl/cell_dl_harq_buffer_pool.cpp new file mode 100644 index 0000000000..35bf36662e --- /dev/null +++ b/lib/mac/mac_dl/cell_dl_harq_buffer_pool.cpp @@ -0,0 +1,119 @@ +/* + * + * 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 "cell_dl_harq_buffer_pool.h" +#include "srsran/ran/pdsch/pdsch_constants.h" + +using namespace srsran; + +/// Derive maximum TB/MAC PDU length given a cell parameters. +static units::bytes derive_max_pdu_length(unsigned cell_nof_prbs, unsigned nof_ports) +{ + srsran_assert(nof_ports >= 1 and nof_ports <= pdsch_constants::CODEWORD_MAX_NOF_LAYERS, "Invalid number of ports"); + units::bits cw_max_size{pdsch_constants::MAX_NRE_PER_RB * cell_nof_prbs * nof_ports * + pdsch_constants::MAX_MODULATION_ORDER}; + return cw_max_size.round_up_to_bytes(); +} + +// (Implementation-defined) Batch of DL HARQ buffers to allocate every time the pool is getting depleted. +const unsigned DL_HARQ_ALLOC_BATCH = MAX_NOF_HARQS * 2; + +// (Implementation-defined) Number of DL HARQ buffers to allocate in the control executor in each executor call. This +// value should be small to avoid blocking the control executor for too long. +const unsigned DL_HARQ_ALLOC_MINIBATCH = 2; + +cell_dl_harq_buffer_pool::cell_dl_harq_buffer_pool(unsigned cell_nof_prbs, + unsigned nof_ports, + task_executor& ctrl_exec_) : + max_pdu_len(derive_max_pdu_length(cell_nof_prbs, nof_ports).value()), + ctrl_exec(ctrl_exec_), + logger(srslog::fetch_basic_logger("MAC")), + cell_buffers(MAX_NOF_DU_UES) +{ + buffer_cache.reserve(MAX_NOF_DU_UES * MAX_NOF_HARQS); + + // Preallocate DL HARQ buffers for any UEs that may be added. + for (unsigned i = 0; i < DL_HARQ_ALLOC_BATCH; i++) { + buffer_cache.emplace_back(max_pdu_len); + } +} + +void cell_dl_harq_buffer_pool::allocate_ue_buffers(du_ue_index_t ue_index, unsigned nof_harqs) +{ + srsran_sanity_check(is_du_ue_index_valid(ue_index), "Invalid UE index"); + srsran_assert(nof_harqs <= MAX_NOF_HARQS, "Invalid maximum number of HARQs"); + + ue_dl_harq_buffer_list& ue_harqs = cell_buffers[ue_index]; + + if (not ue_harqs.empty()) { + logger.error("ue={}: HARQ buffers already allocated for new UE", ue_index); + } + + // Grow the list of HARQ buffers associated with this UE. + while (ue_harqs.size() < nof_harqs) { + if (buffer_cache.empty()) { + // Allocate a new HARQ buffer as there are not enough buffers in the cache. + // In general, we should avoid allocating a DL HARQ at this point, to avoid delaying the UE creation. + ue_harqs.emplace_back(max_pdu_len); + } else { + // Reuse a HARQ buffer from the cache. + ue_harqs.push_back(std::move(buffer_cache.back())); + buffer_cache.pop_back(); + } + } + + // Defer the growth of the DL HARQ buffer pool. + // We do not want to perform this operation at this point to avoid affecting the UE creation latency. + grow_pool_in_background(); +} + +void cell_dl_harq_buffer_pool::deallocate_ue_buffers(du_ue_index_t ue_idx) +{ + srsran_assert(is_du_ue_index_valid(ue_idx), "Invalid UE index"); + ue_dl_harq_buffer_list& ue_harqs = cell_buffers[ue_idx]; + + // Move allocated HARQs for this UE into the cache. + for (std::vector& harq : ue_harqs) { + buffer_cache.push_back(std::move(harq)); + } + ue_harqs.clear(); +} + +void cell_dl_harq_buffer_pool::grow_pool_in_background() +{ + if (buffer_cache.size() >= DL_HARQ_ALLOC_BATCH) { + // Stop growing the pool if it has enough DL HARQ buffers in cache. + return; + } + + if (not ctrl_exec.defer([this]() { + // Allocate minibatch of DL HARQ buffers and save them in cache. + for (unsigned i = 0; i != DL_HARQ_ALLOC_MINIBATCH; ++i) { + buffer_cache.emplace_back(max_pdu_len); + } + + // Dispatch new task to grow pool if it hasn't yet achieved the desired size. + grow_pool_in_background(); + })) { + logger.warning("Failed to dispatch task to allocate DL HARQ buffers"); + } +} diff --git a/lib/mac/mac_dl/cell_dl_harq_buffer_pool.h b/lib/mac/mac_dl/cell_dl_harq_buffer_pool.h new file mode 100644 index 0000000000..c2793b2b52 --- /dev/null +++ b/lib/mac/mac_dl/cell_dl_harq_buffer_pool.h @@ -0,0 +1,80 @@ +/* + * + * 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/span.h" +#include "srsran/ran/du_types.h" +#include "srsran/scheduler/harq_id.h" +#include "srsran/support/executors/task_executor.h" + +namespace srsran { + +/// Class that manages the allocation, deallocation and fetching of DL HARQ buffers for a given cell. +class cell_dl_harq_buffer_pool +{ + /// Type representing a DL HARQ buffer. + using dl_harq_buffer_storage = std::vector; + + /// Type representing the list of DL HARQ buffers allocated for a given UE in a given cell. + using ue_dl_harq_buffer_list = static_vector; + +public: + /// \brief Construction of a DL HARQ buffer pool for a given cell. + /// \param cell_nof_prbs Number of PRBs of the cell. + /// \param nof_ports Number of ports of the cell. + /// \param ctrl_exec Executor to which DL HARQ buffer allocation tasks is dispatched. + explicit cell_dl_harq_buffer_pool(unsigned cell_nof_prbs, unsigned nof_ports, task_executor& ctrl_exec); + + /// \brief Allocate DL HARQ buffers for a newly created UE. + void allocate_ue_buffers(du_ue_index_t ue_index, unsigned nof_harqs); + + /// \brief Deallocate DL HARQ buffers for a removed UE. + void deallocate_ue_buffers(du_ue_index_t ue_index); + + /// Get DL HARQ buffer for a given UE. + span dl_harq_buffer(du_ue_index_t ue_index, harq_id_t h_id) + { + srsran_sanity_check(is_du_ue_index_valid(ue_index), "Invalid UE index"); + srsran_assert(cell_buffers[ue_index].size() >= h_id, "Invalid HARQ ID={}", h_id); + return cell_buffers[ue_index][h_id]; + } + +private: + // Maximum MAC PDU length, derived based on the cell properties. + const unsigned max_pdu_len; + // Executor to which DL HARQ buffer allocation tasks is dispatched in the background. + task_executor& ctrl_exec; + srslog::basic_logger& logger; + + // This function dispatches a task to grow the pool of DL HARQ buffers, using the \c ctrl_exec, in case the pool + // size decreased below a specific threshold. + void grow_pool_in_background(); + + // List of DL HARQ buffers currently allocated to UEs in the cell. + std::vector cell_buffers; + + // DL HARQ buffers that are not associated with any UE and can be allocated to newly created UEs. + std::vector> buffer_cache; +}; + +} // namespace srsran diff --git a/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp b/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp index 6eaf32dde7..ebda572276 100644 --- a/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp +++ b/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp @@ -21,6 +21,7 @@ */ #include "dl_sch_pdu_assembler.h" +#include "cell_dl_harq_buffer_pool.h" #include "srsran/adt/byte_buffer_chain.h" #include "srsran/ran/pdsch/pdsch_constants.h" #include "srsran/support/error_handling.h" @@ -262,8 +263,8 @@ class dl_sch_pdu_assembler::pdu_log_builder // ///////////////////////// -dl_sch_pdu_assembler::dl_sch_pdu_assembler(mac_dl_ue_manager& ue_mng_) : - ue_mng(ue_mng_), logger(srslog::fetch_basic_logger("MAC")) +dl_sch_pdu_assembler::dl_sch_pdu_assembler(mac_dl_ue_manager& ue_mng_, cell_dl_harq_buffer_pool& cell_dl_harq_buffers) : + ue_mng(ue_mng_), harq_buffers(cell_dl_harq_buffers), logger(srslog::fetch_basic_logger("MAC")) { } @@ -276,17 +277,21 @@ span dl_sch_pdu_assembler::assemble_newtx_pdu(rnti_t const dl_msg_tb_info& tb_info, unsigned tb_size_bytes) { - span buffer = ue_mng.get_dl_harq_buffer(rnti, h_id, tb_idx); + du_ue_index_t ue_idx = ue_mng.get_ue_index(rnti); + if (ue_idx == INVALID_DU_UE_INDEX) { + logger.error("DL rnti={} h_id={}: Failed to assemble MAC PDU. Cause: C-RNTI has no associated UE id.", rnti, h_id); + return span(zero_buffer).first(tb_size_bytes); + } + + span buffer = harq_buffers.dl_harq_buffer(ue_idx, h_id); if (buffer.size() < tb_size_bytes) { - logger.error("DL ue={} rnti={} h_id={}: Failed to assemble MAC PDU. Cause: No HARQ buffers available", - ue_mng.get_ue_index(rnti), - rnti, - h_id); + logger.error( + "DL ue={} rnti={} h_id={}: Failed to assemble MAC PDU. Cause: No HARQ buffers available", ue_idx, rnti, h_id); return span(zero_buffer).first(tb_size_bytes); } dl_sch_pdu ue_pdu(buffer.first(tb_size_bytes)); - pdu_log_builder pdu_logger{ue_mng.get_ue_index(rnti), rnti, units::bytes{tb_size_bytes}, fmtbuf, logger}; + pdu_log_builder pdu_logger{ue_idx, rnti, units::bytes{tb_size_bytes}, fmtbuf, logger}; // Encode added subPDUs. for (const dl_msg_lc_info& sched_lch : tb_info.lc_chs_to_sched) { @@ -303,7 +308,7 @@ span dl_sch_pdu_assembler::assemble_newtx_pdu(rnti_t ue_pdu.add_padding(tb_size_bytes - current_size); } else if (current_size > tb_size_bytes) { logger.error("ERROR: Allocated subPDUs exceed TB size ({} > {})", current_size, tb_size_bytes); - return {}; + return span(zero_buffer).first(tb_size_bytes); } pdu_logger.log(); @@ -418,7 +423,12 @@ void dl_sch_pdu_assembler::assemble_ce(dl_sch_pdu& ue_pdu, span dl_sch_pdu_assembler::assemble_retx_pdu(rnti_t rnti, harq_id_t h_id, unsigned tb_idx, unsigned tbs_bytes) { - span buffer = ue_mng.get_dl_harq_buffer(rnti, h_id, tb_idx); + du_ue_index_t ue_idx = ue_mng.get_ue_index(rnti); + if (ue_idx == INVALID_DU_UE_INDEX) { + logger.error("DL rnti={} h_id={}: Failed to assemble MAC PDU. Cause: C-RNTI has no associated UE id.", rnti, h_id); + return span(zero_buffer).first(tbs_bytes); + } + span buffer = harq_buffers.dl_harq_buffer(ue_idx, h_id); if (buffer.size() < tbs_bytes) { logger.error("DL ue={} rnti={} h_id={}: Failed to assemble MAC PDU. Cause: No HARQ buffers available", ue_mng.get_ue_index(rnti), diff --git a/lib/mac/mac_dl/dl_sch_pdu_assembler.h b/lib/mac/mac_dl/dl_sch_pdu_assembler.h index 318c64475d..1ca390ff48 100644 --- a/lib/mac/mac_dl/dl_sch_pdu_assembler.h +++ b/lib/mac/mac_dl/dl_sch_pdu_assembler.h @@ -31,6 +31,7 @@ namespace srsran { class byte_buffer_chain; +class cell_dl_harq_buffer_pool; /// \brief This class represents and encodes a MAC DL-SCH PDU that may contain multiple subPDUs. /// Each subPDU is composed of a MAC subheader and MAC CE or MAC SDU payload. @@ -97,7 +98,7 @@ class dl_sch_pdu class dl_sch_pdu_assembler { public: - explicit dl_sch_pdu_assembler(mac_dl_ue_manager& ue_mng_); + explicit dl_sch_pdu_assembler(mac_dl_ue_manager& ue_mng_, cell_dl_harq_buffer_pool& cell_dl_harq_buffers); /// \brief Encodes a MAC DL-SCH PDU with the provided scheduler information. /// \param rnti RNTI for which the MAC PDU was allocated. @@ -130,7 +131,8 @@ class dl_sch_pdu_assembler /// Assemble MAC subPDU with a CE. void assemble_ce(dl_sch_pdu& ue_pdu, rnti_t rnti, const dl_msg_lc_info& subpdu, pdu_log_builder& pdu_logger); - mac_dl_ue_manager& ue_mng; + mac_dl_ue_manager& ue_mng; + cell_dl_harq_buffer_pool& harq_buffers; srslog::basic_logger& logger; // memory buffer to avoid allocations during formatting of pdus diff --git a/lib/mac/mac_dl/mac_cell_processor.cpp b/lib/mac/mac_dl/mac_cell_processor.cpp index 46f8ed56c6..6d10d583a9 100644 --- a/lib/mac/mac_dl/mac_cell_processor.cpp +++ b/lib/mac/mac_dl/mac_cell_processor.cpp @@ -37,16 +37,20 @@ mac_cell_processor::mac_cell_processor(const mac_cell_creation_request& cell_cfg mac_cell_result_notifier& phy_notifier_, task_executor& cell_exec_, task_executor& slot_exec_, - task_executor& err_ind_exec_, task_executor& ctrl_exec_, mac_pcap& pcap_) : logger(srslog::fetch_basic_logger("MAC")), cell_cfg(cell_cfg_req_), cell_exec(cell_exec_), slot_exec(slot_exec_), - err_ind_exec(err_ind_exec_), ctrl_exec(ctrl_exec_), phy_cell(phy_notifier_), + dl_harq_buffers(band_helper::get_n_rbs_from_bw( + MHz_to_bs_channel_bandwidth(cell_cfg.dl_carrier.carrier_bw_mhz), + cell_cfg.scs_common, + band_helper::get_freq_range(band_helper::get_band_from_dl_arfcn(cell_cfg.dl_carrier.arfcn))), + cell_cfg.dl_carrier.nof_ant, + ctrl_exec_), // The PDU pool has to be large enough to fit the maximum number of RARs and Paging PDUs per slot for all possible K0 // values. pdu_pool(MAX_DL_PDU_LENGTH, @@ -55,7 +59,7 @@ mac_cell_processor::mac_cell_processor(const mac_cell_creation_request& cell_cfg ssb_helper(cell_cfg_req_), sib_assembler(cell_cfg_req_.bcch_dl_sch_payloads), rar_assembler(pdu_pool), - dlsch_assembler(ue_mng_), + dlsch_assembler(ue_mng_, dl_harq_buffers), paging_assembler(pdu_pool), sched(sched_), ue_mng(ue_mng_), @@ -87,11 +91,8 @@ void mac_cell_processor::handle_slot_indication(slot_point sl_tx) void mac_cell_processor::handle_error_indication(slot_point sl_tx, error_event event) { - // Change execution context to slot indication executor. - if (not err_ind_exec.execute( - [this, sl_tx, event]() { sched.handle_error_indication(sl_tx, cell_cfg.cell_index, event); })) { - logger.warning("slot={}: Skipped error indication. Cause: DL task queue is full.", sl_tx); - } + // Forward error indication to the scheduler to be processed asynchronously. + sched.handle_error_indication(sl_tx, cell_cfg.cell_index, event); } void mac_cell_processor::handle_slot_indication_impl(slot_point sl_tx) diff --git a/lib/mac/mac_dl/mac_cell_processor.h b/lib/mac/mac_dl/mac_cell_processor.h index dc16330a22..dbf6c235dd 100644 --- a/lib/mac/mac_dl/mac_cell_processor.h +++ b/lib/mac/mac_dl/mac_cell_processor.h @@ -23,6 +23,7 @@ #pragma once #include "../mac_ctrl/mac_config.h" +#include "cell_dl_harq_buffer_pool.h" #include "dl_sch_pdu_assembler.h" #include "mac_dl_ue_manager.h" #include "mac_scheduler_cell_info_handler.h" @@ -46,7 +47,6 @@ class mac_cell_processor final : public mac_cell_slot_handler, public mac_cell_c mac_cell_result_notifier& phy_notifier, task_executor& cell_exec, task_executor& slot_exec, - task_executor& err_ind_exec, task_executor& ctrl_exec, mac_pcap& pcap); @@ -59,6 +59,9 @@ class mac_cell_processor final : public mac_cell_slot_handler, public mac_cell_c void handle_slot_indication(slot_point sl_tx) override; void handle_error_indication(slot_point sl_tx, error_event event) override; + /// Gets DL HARQ buffer pool that is specific to this cell. + cell_dl_harq_buffer_pool& get_dl_harq_pool() { return dl_harq_buffers; } + private: void handle_slot_indication_impl(slot_point sl_tx); @@ -85,10 +88,12 @@ class mac_cell_processor final : public mac_cell_slot_handler, public mac_cell_c const mac_cell_creation_request cell_cfg; task_executor& cell_exec; task_executor& slot_exec; - task_executor& err_ind_exec; task_executor& ctrl_exec; mac_cell_result_notifier& phy_cell; + // Pool of DL HARQ buffers used for UE PDSCH. + cell_dl_harq_buffer_pool dl_harq_buffers; + ticking_ring_buffer_pool pdu_pool; /// ssb_helper: contains the SSB-specific parameters that are derived from those passed by the DU interface. These diff --git a/lib/mac/mac_dl/mac_dl_processor.cpp b/lib/mac/mac_dl/mac_dl_processor.cpp index 9d79ad12b4..588a040e40 100644 --- a/lib/mac/mac_dl/mac_dl_processor.cpp +++ b/lib/mac/mac_dl/mac_dl_processor.cpp @@ -49,7 +49,6 @@ void mac_dl_processor::add_cell(const mac_cell_creation_request& cell_cfg_req) cfg.phy_notifier.get_cell(cell_cfg_req.cell_index), cfg.cell_exec_mapper.executor(cell_cfg_req.cell_index), cfg.cell_exec_mapper.slot_ind_executor(cell_cfg_req.cell_index), - cfg.cell_exec_mapper.error_ind_executor(cell_cfg_req.cell_index), cfg.ctrl_exec, cfg.pcap); } @@ -64,8 +63,12 @@ void mac_dl_processor::remove_cell(du_cell_index_t cell_index) async_task mac_dl_processor::add_ue(const mac_ue_create_request& request) { - // > Allocate UE DL HARQ buffers. - // Note: This is a large allocation, and therefore, should be done outside of the cell thread to avoid causing lates. + // > Allocate DL HARQ resources for the new UE. + // Note: This may call a large allocation, and therefore, should be done out of the cell thread to avoid causing + // lates. + cells[request.cell_index]->get_dl_harq_pool().allocate_ue_buffers(request.ue_index, MAX_NOF_HARQS); + + // > Create a MAC UE DL context. mac_dl_ue_context ue_inst(request); return launch_async([this, request, ue_inst = std::move(ue_inst)](coro_context>& ctx) mutable { @@ -86,18 +89,21 @@ async_task mac_dl_processor::add_ue(const mac_ue_create_request& request) async_task mac_dl_processor::remove_ue(const mac_ue_delete_request& request) { - return launch_async([this, request](coro_context>& ctx) { + return launch_async([this, request](coro_context>& ctx) mutable { CORO_BEGIN(ctx); - // 1. Change to respective DL executor + // Change to respective DL executor CORO_AWAIT(execute_on(cfg.cell_exec_mapper.executor(request.cell_index))); - // 2. Remove UE associated DL channels + // Remove UE associated DL channels ue_mng.remove_ue(request.ue_index); - // 3. Change back to CTRL executor before returning + // Change back to CTRL executor before returning CORO_AWAIT(execute_on(cfg.ctrl_exec)); + // Deallocate DL HARQ buffers back in the CTRL executor. + cells[request.cell_index]->get_dl_harq_pool().deallocate_ue_buffers(request.ue_index); + CORO_RETURN(); }); } diff --git a/lib/mac/mac_dl/mac_dl_ue_manager.cpp b/lib/mac/mac_dl/mac_dl_ue_manager.cpp index 1df83f91f6..527e4c25fa 100644 --- a/lib/mac/mac_dl/mac_dl_ue_manager.cpp +++ b/lib/mac/mac_dl/mac_dl_ue_manager.cpp @@ -26,15 +26,8 @@ using namespace srsran; -mac_dl_ue_context::mac_dl_ue_context(const mac_ue_create_request& req) : - ue_index(req.ue_index), harq_buffers(MAX_NOF_HARQS) +mac_dl_ue_context::mac_dl_ue_context(const mac_ue_create_request& req) : ue_index(req.ue_index) { - // Resize each HARQ buffer to maximum DL PDU size. - // TODO: Account the maximum PDU length, given cell BW. - for (std::vector& harq : harq_buffers) { - harq.resize(MAX_DL_PDU_LENGTH); - } - // Store DL logical channel notifiers. addmod_logical_channels(req.bearers); @@ -86,12 +79,7 @@ bool mac_dl_ue_manager::remove_ue(du_ue_index_t ue_index) { // Erase UE from the repository. const std::lock_guard lock(ue_mutex[ue_index]); - if (not ue_db.contains(ue_index)) { - return false; - } - ue_db.erase(ue_index); - - return true; + return ue_db.erase(ue_index); } bool mac_dl_ue_manager::addmod_bearers(du_ue_index_t ue_index, diff --git a/lib/mac/mac_dl/mac_dl_ue_manager.h b/lib/mac/mac_dl/mac_dl_ue_manager.h index b2c9f88765..b61390ccf8 100644 --- a/lib/mac/mac_dl/mac_dl_ue_manager.h +++ b/lib/mac/mac_dl/mac_dl_ue_manager.h @@ -22,6 +22,7 @@ #pragma once +#include "cell_dl_harq_buffer_pool.h" #include "srsran/du_high/rnti_value_table.h" #include "srsran/mac/mac.h" #include "srsran/mac/mac_config.h" @@ -52,10 +53,6 @@ class mac_dl_ue_context du_ue_index_t get_ue_index() const { return ue_index; } - // HARQ buffer methods. - span dl_harq_buffer(harq_id_t h_id) { return harq_buffers[h_id]; } - span dl_harq_buffer(harq_id_t h_id) const { return harq_buffers[h_id]; } - // DL Logical Channel methods. const slotted_id_vector& logical_channels() const { return dl_bearers; } void addmod_logical_channels(span dl_logical_channels); @@ -65,7 +62,6 @@ class mac_dl_ue_context private: du_ue_index_t ue_index; - std::vector> harq_buffers; slotted_id_vector dl_bearers; ue_con_res_id_t msg3_subpdu = {}; }; @@ -134,19 +130,6 @@ class mac_dl_ue_manager /// \brief Returns UE Contention Resolution Id, which is derived from Msg3 bytes. ue_con_res_id_t get_con_res_id(rnti_t rnti); - span get_dl_harq_buffer(rnti_t rnti, harq_id_t h_id, unsigned tb_idx) - { - du_ue_index_t ue_index = rnti_table[rnti]; - if (ue_index == INVALID_DU_UE_INDEX) { - return {}; - } - std::lock_guard lock(ue_mutex[ue_index]); - if (not ue_db.contains(ue_index)) { - return {}; - } - return ue_db[ue_index].dl_harq_buffer(h_id); - } - private: du_rnti_table& rnti_table; diff --git a/lib/mac/mac_sched/rlf_detector.h b/lib/mac/mac_sched/rlf_detector.h index 97b4136c2d..44003aeadc 100644 --- a/lib/mac/mac_sched/rlf_detector.h +++ b/lib/mac/mac_sched/rlf_detector.h @@ -78,12 +78,12 @@ class rlf_detector void handle_ack(du_ue_index_t ue_index, du_cell_index_t cell_index, bool ack) { - handle_ack_common(ue_index, cell_index, ack, 0); + handle_ack_common(ue_index, cell_index, ack, /* is_dl=*/true); } void handle_crc(du_ue_index_t ue_index, du_cell_index_t cell_index, bool crc) { - handle_ack_common(ue_index, cell_index, crc, 1); + handle_ack_common(ue_index, cell_index, crc, /* is_dl=*/false); } void handle_csi(du_ue_index_t ue_index, du_cell_index_t cell_index, bool csi_decoded) @@ -101,7 +101,7 @@ class rlf_detector if (current_count == max_consecutive_kos[cell_index].max_consecutive_csi_dtx) { std::lock_guard lock(u.notifier_mutex); if (u.notifier != nullptr) { - logger.info("ue={}: RLF detected. Cause: {} consecutive undecoded CSIs", ue_index, current_count); + logger.warning("ue={}: RLF detected. Cause: {} consecutive undecoded CSIs", ue_index, current_count); // Notify upper layers. u.notifier->on_rlf_detected(); @@ -122,25 +122,26 @@ class rlf_detector } private: - void handle_ack_common(du_ue_index_t ue_index, du_cell_index_t cell_index, bool ack, unsigned count_index) + void handle_ack_common(du_ue_index_t ue_index, du_cell_index_t cell_index, bool ack, bool is_dl) { srsran_assert(ue_index < MAX_NOF_DU_UES, "Invalid ue_index={}", ue_index); - auto& u = ues[ue_index]; + auto& u = ues[ue_index]; + const unsigned count_index = is_dl ? 0 : 1; if (ack) { u.ko_counters[count_index].store(0, std::memory_order::memory_order_relaxed); } else { unsigned current_count = u.ko_counters[count_index].fetch_add(1, std::memory_order::memory_order_relaxed) + 1; // Note: We use == instead of <= to ensure only one notification is sent. - const unsigned max_counter = count_index == 0 ? max_consecutive_kos[cell_index].max_consecutive_dl_kos - : max_consecutive_kos[cell_index].max_consecutive_csi_dtx; + const unsigned max_counter = is_dl ? max_consecutive_kos[cell_index].max_consecutive_dl_kos + : max_consecutive_kos[cell_index].max_consecutive_ul_kos; if (current_count == max_counter) { std::lock_guard lock(u.notifier_mutex); if (u.notifier != nullptr) { - logger.info("ue={}: RLF detected. Cause: {} consecutive {} KOs.", - ue_index, - current_count, - count_index == 0 ? "HARQ-ACK" : "CRC"); + logger.warning("ue={}: RLF detected. Cause: {} consecutive {} KOs.", + ue_index, + current_count, + is_dl ? "HARQ-ACK" : "CRC"); // Notify upper layers. u.notifier->on_rlf_detected(); diff --git a/lib/mac/mac_ul/pdu_rx_handler.cpp b/lib/mac/mac_ul/pdu_rx_handler.cpp index 78fef523f4..edd42da0a8 100644 --- a/lib/mac/mac_ul/pdu_rx_handler.cpp +++ b/lib/mac/mac_ul/pdu_rx_handler.cpp @@ -130,7 +130,7 @@ bool pdu_rx_handler::push_ul_ccch_msg(du_ue_index_t ue_index, byte_buffer ul_ccc { mac_ul_ue_context* ue = ue_manager.find_ue(ue_index); if (ue == nullptr) { - logger.warning("UL subPDU ue={}, lcid={} UL-CCCH: Received UL-CCCH for inexistent UE", ue_index, LCID_SRB0); + logger.warning("UL subPDU ue={}, lcid={} UL-CCCH: Received UL-CCCH for non-existent UE", ue_index, LCID_SRB0); return false; } @@ -171,13 +171,13 @@ bool pdu_rx_handler::handle_rx_subpdus(const decoded_mac_rx_pdu& ctx) bool pdu_rx_handler::handle_sdu(const decoded_mac_rx_pdu& ctx, const mac_ul_sch_subpdu& sdu, mac_ul_ue_context* ue) { if (ue == nullptr) { - logger.warning("{}: Discarding SDU. Cause: Inexistent C-RNTI", create_prefix(ctx, sdu)); + logger.warning("{}: Discarding SDU. Cause: Non-existent C-RNTI", create_prefix(ctx, sdu)); return false; } lcid_t lcid = (lcid_t)sdu.lcid().value(); if (not ue->ul_bearers.contains(lcid)) { - logger.warning("{}: Discarding SDU. Cause: Inexistent LCID", create_prefix(ctx, sdu)); + logger.warning("{}: Discarding SDU. Cause: Non-existent LCID", create_prefix(ctx, sdu)); return false; } @@ -290,7 +290,11 @@ bool pdu_rx_handler::handle_ccch_msg(const decoded_mac_rx_pdu& ctx, const mac_ul msg.tc_rnti = ctx.pdu_rx.rnti; msg.cell_index = ctx.cell_index_rx; msg.slot_rx = ctx.slot_rx; - msg.subpdu.append(sdu.payload()); + + if (!msg.subpdu.append(sdu.payload())) { + logger.warning("{}: Unable to append SDU into sub-PDU", create_prefix(ctx, sdu)); + return false; + } ccch_notifier.on_ul_ccch_msg_received(msg); // TODO: Do not discard remaining CEs. diff --git a/lib/ngap/ngap_asn1_converters.h b/lib/ngap/ngap_asn1_converters.h index bb7f55d138..49664de91a 100644 --- a/lib/ngap/ngap_asn1_converters.h +++ b/lib/ngap/ngap_asn1_converters.h @@ -23,10 +23,12 @@ #pragma once #include "ngap_asn1_utils.h" +#include "srsran/adt/variant.h" #include "srsran/asn1/ngap/ngap_ies.h" #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/ngap/ngap_handover.h" #include "srsran/ran/bcd_helpers.h" +#include "srsran/ran/cause/ngap_cause.h" #include "srsran/ran/cu_types.h" #include "srsran/ran/lcid.h" #include "srsran/ran/up_transport_layer_info.h" @@ -103,7 +105,7 @@ cu_cp_assoc_qos_flow_to_ngap_assoc_qos_flow_item(cu_cp_associated_qos_flow cu_cp /// \param cu_cp_qos_flow_info The CU-CP QoS Flow Per TNL Info. /// \return The NGAP QoS Flow Per TNL Info. inline asn1::ngap::qos_flow_per_tnl_info_s -cu_cp_qos_flow_per_tnl_info_to_ngap_qos_flow_per_tnl_info(cu_cp_qos_flow_per_tnl_information cu_cp_qos_flow_info) +cu_cp_qos_flow_per_tnl_info_to_ngap_qos_flow_per_tnl_info(const cu_cp_qos_flow_per_tnl_information& cu_cp_qos_flow_info) { asn1::ngap::qos_flow_per_tnl_info_s ngap_qos_flow_info; @@ -119,58 +121,58 @@ cu_cp_qos_flow_per_tnl_info_to_ngap_qos_flow_per_tnl_info(cu_cp_qos_flow_per_tnl return ngap_qos_flow_info; } -/// \brief Convert \c cause_t type to NGAP cause. -/// \param cause The cause_t type. +/// \brief Convert \c ngap_cause_t type to NGAP cause. +/// \param cause The ngap_cause_t type. /// \return The NGAP cause. -inline asn1::ngap::cause_c cause_to_asn1(cause_t cause) +inline asn1::ngap::cause_c cause_to_asn1(ngap_cause_t cause) { - asn1::ngap::cause_c ngap_cause; - - if (variant_holds_alternative(cause)) { - ngap_cause.set_radio_network() = - static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - ngap_cause.set_transport() = - static_cast(variant_get(cause)); + asn1::ngap::cause_c asn1_cause; + + if (variant_holds_alternative(cause)) { + asn1_cause.set_radio_network() = + static_cast(variant_get(cause)); + } else if (variant_holds_alternative(cause)) { + asn1_cause.set_transport() = + static_cast(variant_get(cause)); } else if (variant_holds_alternative(cause)) { - ngap_cause.set_nas() = static_cast(variant_get(cause)); + asn1_cause.set_nas() = static_cast(variant_get(cause)); } else if (variant_holds_alternative(cause)) { - ngap_cause.set_protocol() = + asn1_cause.set_protocol() = static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - ngap_cause.set_misc() = static_cast(variant_get(cause)); + } else if (variant_holds_alternative(cause)) { + asn1_cause.set_misc() = static_cast(variant_get(cause)); } else { - report_fatal_error("Cannot convert cause to NGAP type"); + report_fatal_error("Cannot convert cause to NGAP type:{}", cause); } - return ngap_cause; + return asn1_cause; } -/// \brief Convert NGAP ASN1 cause to \c cause_t type. -/// \param ngap_cause The ASN1 NGAP cause. -/// \return The cause_t type. -inline cause_t asn1_to_cause(asn1::ngap::cause_c ngap_cause) +/// \brief Convert NGAP ASN1 cause to \c ngap_cause_t type. +/// \param asn1_cause The ASN1 NGAP cause. +/// \return The ngap_cause_t type. +inline ngap_cause_t asn1_to_cause(asn1::ngap::cause_c asn1_cause) { - cause_t cause; + ngap_cause_t cause; - switch (ngap_cause.type()) { + switch (asn1_cause.type()) { case asn1::ngap::cause_c::types_opts::radio_network: - cause = static_cast(ngap_cause.radio_network().value); + cause = static_cast(asn1_cause.radio_network().value); break; case asn1::ngap::cause_c::types_opts::transport: - cause = static_cast(ngap_cause.transport().value); + cause = static_cast(asn1_cause.transport().value); break; case asn1::ngap::cause_c::types_opts::nas: - cause = static_cast(ngap_cause.nas().value); + cause = static_cast(asn1_cause.nas().value); break; case asn1::ngap::cause_c::types_opts::protocol: - cause = static_cast(ngap_cause.protocol().value); + cause = static_cast(asn1_cause.protocol().value); break; case asn1::ngap::cause_c::types_opts::misc: - cause = static_cast(ngap_cause.misc().value); + cause = static_cast(asn1_cause.misc().value); break; default: - report_fatal_error("Cannot convert NGAP ASN.1 cause {} to common type", ngap_cause.type()); + report_fatal_error("Cannot convert NGAP ASN.1 cause {} to common type", asn1_cause.type()); } return cause; @@ -225,7 +227,7 @@ cu_cp_user_location_info_to_asn1(const cu_cp_user_location_info_nr& cu_cp_user_l /// \return The humand-readable string. inline std::string asn1_cause_to_string(const asn1::ngap::cause_c& cause) { - std::string cause_str = ""; + std::string cause_str; switch (cause.type()) { case asn1::ngap::cause_c::types_opts::radio_network: @@ -251,13 +253,13 @@ inline std::string asn1_cause_to_string(const asn1::ngap::cause_c& cause) return cause_str; } -/// \brief Convert common type Initial Context Setup Response message to NGAP Initial Context Setup Response -/// message. +/// \brief Convert common type Initial Context Setup Response message to NGAP Initial Context Setup Response message. /// \param[out] resp The ASN1 NGAP Initial Context Setup Response message. /// \param[in] cu_cp_resp The CU-CP Initial Context Setup Response message. +/// \return True on success, otherwise false. template -inline void pdu_session_res_setup_response_item_to_asn1(template_asn1_item& asn1_resp, - const cu_cp_pdu_session_res_setup_response_item resp) +inline bool pdu_session_res_setup_response_item_to_asn1(template_asn1_item& asn1_resp, + const cu_cp_pdu_session_res_setup_response_item& resp) { asn1_resp.pdu_session_id = pdu_session_id_to_uint(resp.pdu_session_id); @@ -293,16 +295,21 @@ inline void pdu_session_res_setup_response_item_to_asn1(template_asn1_item& // Pack pdu_session_res_setup_resp_transfer_s byte_buffer pdu = pack_into_pdu(response_transfer); - asn1_resp.pdu_session_res_setup_resp_transfer.resize(pdu.length()); + if (!asn1_resp.pdu_session_res_setup_resp_transfer.resize(pdu.length())) { + return false; + } std::copy(pdu.begin(), pdu.end(), asn1_resp.pdu_session_res_setup_resp_transfer.begin()); + + return true; } /// \brief Convert common type modify response item to ASN1 type message. /// \param[out] asn1_resp The ASN1 NGAP struct. /// \param[in] resp The common type struct. +/// \return True on success, otherwise false. template -inline void pdu_session_res_modify_response_item_to_asn1(template_asn1_item& asn1_resp, - const cu_cp_pdu_session_resource_modify_response_item resp) +inline bool pdu_session_res_modify_response_item_to_asn1(template_asn1_item& asn1_resp, + const cu_cp_pdu_session_resource_modify_response_item& resp) { asn1_resp.pdu_session_id = pdu_session_id_to_uint(resp.pdu_session_id); @@ -327,16 +334,21 @@ inline void pdu_session_res_modify_response_item_to_asn1(template_asn1_item& asn // Pack pdu_session_res_modify_resp_transfer_s byte_buffer pdu = pack_into_pdu(response_transfer); - asn1_resp.pdu_session_res_modify_resp_transfer.resize(pdu.length()); + if (!asn1_resp.pdu_session_res_modify_resp_transfer.resize(pdu.length())) { + return false; + } std::copy(pdu.begin(), pdu.end(), asn1_resp.pdu_session_res_modify_resp_transfer.begin()); + + return true; } /// \brief Convert common type modify response item to ASN1 type message. /// \param[out] asn1_resp The ASN1 NGAP struct. /// \param[in] resp The common type struct. +/// \return True on success, otherwise false. template -inline void pdu_session_res_failed_to_modify_item_to_asn1(template_asn1_item& asn1_resp, - const cu_cp_pdu_session_resource_failed_to_modify_item resp) +inline bool pdu_session_res_failed_to_modify_item_to_asn1(template_asn1_item& asn1_resp, + const cu_cp_pdu_session_resource_failed_to_modify_item& resp) { asn1_resp.pdu_session_id = pdu_session_id_to_uint(resp.pdu_session_id); @@ -346,17 +358,21 @@ inline void pdu_session_res_failed_to_modify_item_to_asn1(template_asn1_item& as // Pack transfer byte_buffer pdu = pack_into_pdu(response_transfer); - asn1_resp.pdu_session_res_modify_unsuccessful_transfer.resize(pdu.length()); + if (!asn1_resp.pdu_session_res_modify_unsuccessful_transfer.resize(pdu.length())) { + return false; + } std::copy(pdu.begin(), pdu.end(), asn1_resp.pdu_session_res_modify_unsuccessful_transfer.begin()); + + return true; } -/// \brief Convert common type Initial Context Setup Response message to NGAP Initial Context Setup Response -/// message. +/// \brief Convert common type Initial Context Setup Response message to NGAP Initial Context Setup Response message. /// \param[out] resp The ASN1 NGAP Initial Context Setup Response message. /// \param[in] cu_cp_resp The CU-CP Initial Context Setup Response message. +/// \return True on success, otherwise false. template -inline void pdu_session_res_setup_failed_item_to_asn1(template_asn1_item& asn1_resp, - const cu_cp_pdu_session_res_setup_failed_item resp) +inline bool pdu_session_res_setup_failed_item_to_asn1(template_asn1_item& asn1_resp, + const cu_cp_pdu_session_res_setup_failed_item& resp) { asn1_resp.pdu_session_id = pdu_session_id_to_uint(resp.pdu_session_id); @@ -368,8 +384,12 @@ inline void pdu_session_res_setup_failed_item_to_asn1(template_asn1_item& // Pack pdu_session_res_setup_unsuccessful_transfer_s byte_buffer pdu = pack_into_pdu(setup_unsuccessful_transfer); - asn1_resp.pdu_session_res_setup_unsuccessful_transfer.resize(pdu.length()); + if (!asn1_resp.pdu_session_res_setup_unsuccessful_transfer.resize(pdu.length())) { + return false; + } std::copy(pdu.begin(), pdu.end(), asn1_resp.pdu_session_res_setup_unsuccessful_transfer.begin()); + + return true; } /// \brief Convert ASN.1 GUAMI to a common type. diff --git a/lib/ngap/ngap_asn1_helpers.h b/lib/ngap/ngap_asn1_helpers.h index f78ae5c80c..ec73fc1650 100644 --- a/lib/ngap/ngap_asn1_helpers.h +++ b/lib/ngap/ngap_asn1_helpers.h @@ -198,7 +198,7 @@ inline void fill_asn1_ul_nas_transport(asn1::ngap::ul_nas_transport_s& asn1_msg, template inline bool fill_cu_cp_pdu_session_resource_setup_item_base(cu_cp_pdu_session_res_setup_item& setup_item, const template_asn1_item& asn1_session_item, - const byte_buffer asn1_request_transfer) + byte_buffer asn1_request_transfer) { // pDUSessionID setup_item.pdu_session_id = uint_to_pdu_session_id(asn1_session_item.pdu_session_id); @@ -327,7 +327,9 @@ inline bool fill_cu_cp_pdu_session_resource_setup_request( // pDUSessionNAS-PDU if (!asn1_session_item.pdu_session_nas_pdu.empty()) { - setup_item.pdu_session_nas_pdu.resize(asn1_session_item.pdu_session_nas_pdu.size()); + if (!setup_item.pdu_session_nas_pdu.resize(asn1_session_item.pdu_session_nas_pdu.size())) { + return false; + } std::copy(asn1_session_item.pdu_session_nas_pdu.begin(), asn1_session_item.pdu_session_nas_pdu.end(), setup_item.pdu_session_nas_pdu.begin()); @@ -359,7 +361,9 @@ inline bool fill_cu_cp_pdu_session_resource_setup_request( // NAS-PDU if (!asn1_session_item.nas_pdu.empty()) { - setup_item.pdu_session_nas_pdu.resize(asn1_session_item.nas_pdu.size()); + if (!setup_item.pdu_session_nas_pdu.resize(asn1_session_item.nas_pdu.size())) { + return false; + } std::copy( asn1_session_item.nas_pdu.begin(), asn1_session_item.nas_pdu.end(), setup_item.pdu_session_nas_pdu.begin()); } @@ -448,11 +452,11 @@ inline bool fill_ngap_initial_context_setup_request(ngap_init_context_setup_requ return true; } -/// \brief Convert common type Initial Context Setup Response message to NGAP Initial Context Setup Response -/// message. +/// \brief Convert common type Initial Context Setup Response message to NGAP Initial Context Setup Response message. /// \param[out] asn1_resp The ASN1 NGAP Initial Context Setup Response message. /// \param[in] resp The CU-CP Initial Context Setup Response message. -inline void fill_asn1_initial_context_setup_response(asn1::ngap::init_context_setup_resp_s& asn1_resp, +/// \return True on success, otherwise false. +inline bool fill_asn1_initial_context_setup_response(asn1::ngap::init_context_setup_resp_s& asn1_resp, const ngap_init_context_setup_response& resp) { // Fill PDU Session Resource Setup Response List @@ -462,7 +466,9 @@ inline void fill_asn1_initial_context_setup_response(asn1::ngap::init_context_se for (const auto& resp_item : resp.pdu_session_res_setup_response_items) { asn1::ngap::pdu_session_res_setup_item_cxt_res_s asn1_resp_item; - pdu_session_res_setup_response_item_to_asn1(asn1_resp_item, resp_item); + if (!pdu_session_res_setup_response_item_to_asn1(asn1_resp_item, resp_item)) { + return false; + } asn1_resp->pdu_session_res_setup_list_cxt_res.push_back(asn1_resp_item); } @@ -474,7 +480,9 @@ inline void fill_asn1_initial_context_setup_response(asn1::ngap::init_context_se for (const auto& setup_failed_item : resp.pdu_session_res_failed_to_setup_items) { asn1::ngap::pdu_session_res_failed_to_setup_item_cxt_res_s asn1_setup_failed_item; - pdu_session_res_setup_failed_item_to_asn1(asn1_setup_failed_item, setup_failed_item); + if (!pdu_session_res_setup_failed_item_to_asn1(asn1_setup_failed_item, setup_failed_item)) { + return false; + } asn1_resp->pdu_session_res_failed_to_setup_list_cxt_res.push_back(asn1_setup_failed_item); } @@ -484,6 +492,8 @@ inline void fill_asn1_initial_context_setup_response(asn1::ngap::init_context_se if (resp.crit_diagnostics.has_value()) { // TODO: Add crit diagnostics } + + return true; } /// \brief Convert common type Initial Context Setup Failure message to NGAP Initial Context Setup Failure @@ -514,10 +524,11 @@ inline void fill_asn1_initial_context_setup_failure(asn1::ngap::init_context_set } } -/// \brief Convert a NGAP ASN1 modify item to commong type. +/// \brief Convert a NGAP ASN1 modify item to common type. /// \param[out] modify_item The flat/common version /// \param[in] asn1_session_item The ASN1 struct to be converted. -inline void fill_cu_cp_pdu_session_resource_modify_item_base( +/// \return True on success, otherwise false. +inline bool fill_cu_cp_pdu_session_resource_modify_item_base( cu_cp_pdu_session_res_modify_item_mod_req& modify_item, const asn1::ngap::pdu_session_res_modify_item_mod_req_s& asn1_session_item) { @@ -527,7 +538,7 @@ inline void fill_cu_cp_pdu_session_resource_modify_item_base( asn1::cbit_ref bref(asn1_session_item.pdu_session_res_modify_request_transfer); if (asn1_modify_req_transfer.unpack(bref) != asn1::SRSASN_SUCCESS) { srslog::fetch_basic_logger("NGAP").error("Couldn't unpack PDU Session Resource Modify Request Transfer PDU."); - return; + return false; } if (asn1_modify_req_transfer->qos_flow_add_or_modify_request_list_present) { @@ -586,33 +597,42 @@ inline void fill_cu_cp_pdu_session_resource_modify_item_base( } if (!asn1_session_item.nas_pdu.empty()) { - modify_item.nas_pdu.resize(asn1_session_item.nas_pdu.size()); + if (!modify_item.nas_pdu.resize(asn1_session_item.nas_pdu.size())) { + return false; + } std::copy(asn1_session_item.nas_pdu.begin(), asn1_session_item.nas_pdu.end(), modify_item.nas_pdu.begin()); } + + return true; } /// \brief Convert NGAP ASN1 PDU Session Resource Modify List ASN1 struct to common type. /// \param[out] cu_cp_pdu_session_resource_modify_msg The cu_cp_pdu_session_res_modify_msg struct to fill. /// \param[in] asn1_pdu_session_res_modify_list The pdu_session_res_modify_list ASN1 struct. -inline void fill_cu_cp_pdu_session_resource_modify_request( +/// \return True on success, otherwise false. +inline bool fill_cu_cp_pdu_session_resource_modify_request( cu_cp_pdu_session_resource_modify_request& cu_cp_pdu_session_resource_modify_msg, const asn1::dyn_seq_of& asn1_pdu_session_res_modify_list) { for (const auto& asn1_session_item : asn1_pdu_session_res_modify_list) { cu_cp_pdu_session_res_modify_item_mod_req modify_item; - fill_cu_cp_pdu_session_resource_modify_item_base(modify_item, asn1_session_item); + if (!fill_cu_cp_pdu_session_resource_modify_item_base(modify_item, asn1_session_item)) { + return false; + } cu_cp_pdu_session_resource_modify_msg.pdu_session_res_modify_items.emplace(modify_item.pdu_session_id, std::move(modify_item)); } + + return true; } /// \brief Convert common type PDU Session Resource Setup Response message to NGAP PDU Session Resource Setup Response /// message. /// \param[out] resp The ASN1 NGAP PDU Session Resource Setup Response message. /// \param[in] cu_cp_resp The CU-CP PDU Session Resource Setup Response message. -inline void fill_asn1_pdu_session_res_setup_response(asn1::ngap::pdu_session_res_setup_resp_s& resp, - const cu_cp_pdu_session_resource_setup_response cu_cp_resp) +inline void fill_asn1_pdu_session_res_setup_response(asn1::ngap::pdu_session_res_setup_resp_s& resp, + const cu_cp_pdu_session_resource_setup_response& cu_cp_resp) { // Fill PDU Session Resource Setup Response List if (!cu_cp_resp.pdu_session_res_setup_response_items.empty()) { @@ -643,10 +663,12 @@ inline void fill_asn1_pdu_session_res_setup_response(asn1::ngap::pdu_session_res } /// \brief Convert common type PDU Session Resource Modify Response message to NGAP PDU Session Resource Modify -/// Response message. \param[out] resp The ASN1 NGAP PDU Session Resource Modify Response message. \param[in] -/// cu_cp_resp The CU-CP PDU Session Resource Modify Response message. -inline void fill_asn1_pdu_session_res_modify_response(asn1::ngap::pdu_session_res_modify_resp_s& resp, - const cu_cp_pdu_session_resource_modify_response cu_cp_resp) +/// Response message. +/// \param[out] resp The ASN1 NGAP PDU Session Resource Modify Response message. +/// \param[in] cu_cp_resp The CU-CP PDU Session Resource Modify Response message. +/// \return True on success, otherwise false. +inline bool fill_asn1_pdu_session_res_modify_response(asn1::ngap::pdu_session_res_modify_resp_s& resp, + const cu_cp_pdu_session_resource_modify_response& cu_cp_resp) { // Fill PDU Session Resource Modify Response List if (!cu_cp_resp.pdu_session_res_modify_list.empty()) { @@ -654,7 +676,9 @@ inline void fill_asn1_pdu_session_res_modify_response(asn1::ngap::pdu_session_re for (const auto& cu_cp_resp_item : cu_cp_resp.pdu_session_res_modify_list) { asn1::ngap::pdu_session_res_modify_item_mod_res_s resp_item; - pdu_session_res_modify_response_item_to_asn1(resp_item, cu_cp_resp_item); + if (!pdu_session_res_modify_response_item_to_asn1(resp_item, cu_cp_resp_item)) { + return false; + } resp->pdu_session_res_modify_list_mod_res.push_back(resp_item); } } @@ -664,10 +688,14 @@ inline void fill_asn1_pdu_session_res_modify_response(asn1::ngap::pdu_session_re resp->pdu_session_res_failed_to_modify_list_mod_res_present = true; for (const auto& cu_cp_resp_item : cu_cp_resp.pdu_session_res_failed_to_modify_list) { asn1::ngap::pdu_session_res_failed_to_modify_item_mod_res_s resp_item; - pdu_session_res_failed_to_modify_item_to_asn1(resp_item, cu_cp_resp_item); + if (!pdu_session_res_failed_to_modify_item_to_asn1(resp_item, cu_cp_resp_item)) { + return false; + } resp->pdu_session_res_failed_to_modify_list_mod_res.push_back(resp_item); } } + + return true; } /// \brief Convert common type UE Context Release Request to NGAP UE Context Release Request. @@ -689,7 +717,7 @@ inline void fill_asn1_ue_context_release_request(asn1::ngap::ue_context_release_ asn1_msg->cause = cause_to_asn1(msg.cause); } -/// \brief Convert NGAP ASN1 PDU Session Resource Release Comman ASN1 struct to common type. +/// \brief Convert NGAP ASN1 PDU Session Resource Release Command ASN1 struct to common type. /// \param[out] pdu_session_resource_release_cmd The cu_cp_pdu_session_resource_release_command struct to fill. /// \param[in] asn1_pdu_session_resource_release_cmd The pdu_session_res_release_cmd ASN1 struct. inline void fill_cu_cp_pdu_session_resource_release_command( @@ -727,9 +755,11 @@ inline void fill_cu_cp_pdu_session_resource_release_command( } /// \brief Convert common type PDU Session Resource Release Response message to NGAP PDU Session Resource Release -/// Response \param[out] resp The ASN1 NGAP PDU Session Resource Release Response message. +/// Response. +/// \param[out] resp The ASN1 NGAP PDU Session Resource Release Response message. /// \param[in] cu_cp_resp The CU-CP PDU Session Resource Release Response message. -inline void +/// \return True on success, otherwise false. +inline bool fill_asn1_pdu_session_resource_release_response(asn1::ngap::pdu_session_res_release_resp_s& resp, const cu_cp_pdu_session_resource_release_response& cu_cp_resp) { @@ -802,7 +832,9 @@ fill_asn1_pdu_session_resource_release_response(asn1::ngap::pdu_session_res_rele // Pack pdu_session_res_release_resp_transfer_s byte_buffer pdu = pack_into_pdu(res_release_resp_transfer); - asn1_pdu_session_res_released_item.pdu_session_res_release_resp_transfer.resize(pdu.length()); + if (!asn1_pdu_session_res_released_item.pdu_session_res_release_resp_transfer.resize(pdu.length())) { + return false; + } std::copy(pdu.begin(), pdu.end(), asn1_pdu_session_res_released_item.pdu_session_res_release_resp_transfer.begin()); resp->pdu_session_res_released_list_rel_res.push_back(asn1_pdu_session_res_released_item); @@ -813,6 +845,8 @@ fill_asn1_pdu_session_resource_release_response(asn1::ngap::pdu_session_res_rele resp->user_location_info.set_user_location_info_nr() = cu_cp_user_location_info_to_asn1(cu_cp_resp.user_location_info.value()); } + + return true; } /// \brief Convert NGAP ASN1 UE Context Release Command ASN1 struct to common type. @@ -830,7 +864,7 @@ fill_cu_cp_ue_context_release_command(cu_cp_ue_context_release_command& /// \param[out] asn1_resp The ASN1 NGAP UE Context Release Complete message. /// \param[in] cu_cp_resp The CU-CP UE Context Release Complete message. inline void fill_asn1_ue_context_release_complete(asn1::ngap::ue_context_release_complete_s& asn1_resp, - const cu_cp_ue_context_release_complete cu_cp_resp) + const cu_cp_ue_context_release_complete& cu_cp_resp) { // add user location info if (cu_cp_resp.user_location_info.has_value()) { @@ -1096,7 +1130,7 @@ inline bool fill_asn1_handover_resource_allocation_response(asn1::ngap::ho_request_ack_s& asn1_ho_request_ack, const ngap_handover_resource_allocation_response& ho_response) { - if (ho_response.success == true) { + if (ho_response.success) { // pdu session res admitted list for (const auto& admitted_item : ho_response.pdu_session_res_admitted_list) { asn1::ngap::pdu_session_res_admitted_item_s asn1_admitted_item; diff --git a/lib/ngap/ngap_context.h b/lib/ngap/ngap_context.h index 3fa062c5f3..8279c5b976 100644 --- a/lib/ngap/ngap_context.h +++ b/lib/ngap/ngap_context.h @@ -37,7 +37,7 @@ struct ngap_context_t { unsigned tac; std::vector served_guami_list; guami_t current_guami; - std::chrono::seconds ue_context_setup_timeout_s; // timeout for ue context setup in seconds + std::chrono::seconds pdu_session_setup_timeout; // timeout for PDU context setup in seconds }; } // namespace srs_cu_cp diff --git a/lib/ngap/ngap_error_indication_helper.h b/lib/ngap/ngap_error_indication_helper.h index 613d95430b..2164d2bc7d 100644 --- a/lib/ngap/ngap_error_indication_helper.h +++ b/lib/ngap/ngap_error_indication_helper.h @@ -42,7 +42,7 @@ inline void send_error_indication(ngap_message_notifier& ngap_notifier, srslog::basic_logger& logger, optional ran_ue_id = {}, optional amf_ue_id = {}, - optional cause = {}) + optional cause = {}) { ngap_message ngap_msg = {}; ngap_msg.pdu.set_init_msg(); diff --git a/lib/ngap/ngap_impl.cpp b/lib/ngap/ngap_impl.cpp index 7d430e454a..e9067ae691 100644 --- a/lib/ngap/ngap_impl.cpp +++ b/lib/ngap/ngap_impl.cpp @@ -35,7 +35,7 @@ #include "procedures/ngap_ue_context_release_procedure.h" #include "srsran/asn1/ngap/common.h" #include "srsran/ngap/ngap_types.h" -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/ngap_cause.h" #include "srsran/support/srsran_assert.h" using namespace srsran; @@ -59,11 +59,11 @@ ngap_impl::ngap_impl(ngap_configuration& ngap_cfg_, ctrl_exec(ctrl_exec_), ev_mng(timer_factory{task_sched.get_timer_manager(), ctrl_exec}) { - context.gnb_id = ngap_cfg_.gnb_id; - context.ran_node_name = ngap_cfg_.ran_node_name; - context.plmn = ngap_cfg_.plmn; - context.tac = ngap_cfg_.tac; - context.ue_context_setup_timeout_s = ngap_cfg_.ue_context_setup_timeout; + context.gnb_id = ngap_cfg_.gnb_id; + context.ran_node_name = ngap_cfg_.ran_node_name; + context.plmn = ngap_cfg_.plmn; + context.tac = ngap_cfg_.tac; + context.pdu_session_setup_timeout = ngap_cfg_.pdu_session_setup_timeout; } // Note: For fwd declaration of member types, dtor cannot be trivial. @@ -150,14 +150,14 @@ void ngap_impl::handle_initial_ue_message(const cu_cp_initial_ue_message& msg) fill_asn1_initial_ue_message(init_ue_msg, msg, context); - // Start UE context setup timer - ue_ctxt.ue_context_setup_timer.set(context.ue_context_setup_timeout_s, [this, msg](timer_id_t /*tid*/) { - on_ue_context_setup_timer_expired(msg.ue_index); + // Start PDU session setup timer + ue_ctxt.pdu_session_setup_timer.set(context.pdu_session_setup_timeout, [this, msg](timer_id_t /*tid*/) { + on_pdu_session_setup_timer_expired(msg.ue_index); }); - ue_ctxt.ue_context_setup_timer.run(); + ue_ctxt.pdu_session_setup_timer.run(); - ue_ctxt.logger.log_debug("Sending InitialUeMessage (timeout={}ms)", - ue_ctxt.ue_context_setup_timer.duration().count()); + ue_ctxt.logger.log_debug("Sending InitialUeMessage (PDU session timeout={}ms)", + ue_ctxt.pdu_session_setup_timer.duration().count()); // Forward message to AMF ngap_notifier.on_new_message(ngap_msg); @@ -267,7 +267,7 @@ void ngap_impl::handle_dl_nas_transport_message(const asn1::ngap::dl_nas_transpo logger.warning("ran_ue_id={} amf_ue_id={}: Dropping DlNasTransportMessage. UE context does not exist", msg->ran_ue_ngap_id, msg->amf_ue_ngap_id); - send_error_indication(ngap_notifier, logger, {}, {}, cause_radio_network_t::unknown_local_ue_ngap_id); + send_error_indication(ngap_notifier, logger, {}, {}, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); return; } @@ -275,7 +275,7 @@ void ngap_impl::handle_dl_nas_transport_message(const asn1::ngap::dl_nas_transpo if (ue_ctxt.release_scheduled) { ue_ctxt.logger.log_info("Dropping DlNasTransportMessage. UE is already scheduled for release"); - schedule_error_indication(ue_ctxt.ue_ids.ue_index, cause_radio_network_t::unknown_local_ue_ngap_id); + schedule_error_indication(ue_ctxt.ue_ids.ue_index, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); return; } @@ -289,7 +289,7 @@ void ngap_impl::handle_dl_nas_transport_message(const asn1::ngap::dl_nas_transpo // Add AMF UE ID to ue ngap context if it is not set (this is the first DL NAS Transport message) if (ue_ctxt.ue_ids.amf_ue_id == amf_ue_id_t::invalid) { // Set AMF UE ID in the UE context and also in the lookup - ue_ctxt_list.add_amf_ue_id(ue_ctxt.ue_ids.ran_ue_id, uint_to_amf_ue_id(msg->amf_ue_ngap_id)); + ue_ctxt_list.update_amf_ue_id(ue_ctxt.ue_ids.ran_ue_id, uint_to_amf_ue_id(msg->amf_ue_ngap_id)); } ue_ctxt.logger.log_info("Received DlNasTransportMessage"); @@ -306,7 +306,7 @@ void ngap_impl::handle_initial_context_setup_request(const asn1::ngap::init_cont logger.warning("ran_ue_id={} amf_ue_id={}: Dropping InitialContextSetupRequest. UE context does not exist", request->ran_ue_ngap_id, request->amf_ue_ngap_id); - send_error_indication(ngap_notifier, logger, {}, {}, cause_radio_network_t::unknown_local_ue_ngap_id); + send_error_indication(ngap_notifier, logger, {}, {}, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); return; } @@ -314,7 +314,7 @@ void ngap_impl::handle_initial_context_setup_request(const asn1::ngap::init_cont if (ue_ctxt.release_scheduled) { ue_ctxt.logger.log_info("Dropping InitialContextSetup. UE is already scheduled for release"); - schedule_error_indication(ue_ctxt.ue_ids.ue_index, cause_radio_network_t::unknown_local_ue_ngap_id); + schedule_error_indication(ue_ctxt.ue_ids.ue_index, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); return; } @@ -325,13 +325,17 @@ void ngap_impl::handle_initial_context_setup_request(const asn1::ngap::init_cont ue_ctxt.ue_ids.ran_ue_id, ue_ctxt.ue_ids.amf_ue_id); - // Stop UE context setup timer - ue_ctxt.ue_context_setup_timer.stop(); + // If InitialContextSetupRequest contains PDU Session Setup list, stop pdu session setup timer + if (request->pdu_session_res_setup_list_cxt_req_present) { + ue_ctxt.pdu_session_setup_timer.stop(); + } ue_ctxt.logger.log_info("Received InitialContextSetupRequest"); // Update AMF ID and use the one from this Context Setup as per TS 38.413 v16.2 page 38 - ue_ctxt_list.add_amf_ue_id(ue_ctxt.ue_ids.ran_ue_id, uint_to_amf_ue_id(request->amf_ue_ngap_id)); + if (ue_ctxt.ue_ids.amf_ue_id != uint_to_amf_ue_id(request->amf_ue_ngap_id)) { + ue_ctxt_list.update_amf_ue_id(ue_ctxt.ue_ids.ran_ue_id, uint_to_amf_ue_id(request->amf_ue_ngap_id)); + } // Convert to common type ngap_init_context_setup_request init_ctxt_setup_req; @@ -375,7 +379,7 @@ void ngap_impl::handle_pdu_session_resource_setup_request(const asn1::ngap::pdu_ logger.warning("ran_ue_id={} amf_ue_id={}: Dropping PduSessionResourceSetupRequest. UE context does not exist", request->ran_ue_ngap_id, request->amf_ue_ngap_id); - send_error_indication(ngap_notifier, logger, {}, {}, cause_radio_network_t::unknown_local_ue_ngap_id); + send_error_indication(ngap_notifier, logger, {}, {}, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); return; } @@ -383,7 +387,7 @@ void ngap_impl::handle_pdu_session_resource_setup_request(const asn1::ngap::pdu_ if (ue_ctxt.release_scheduled) { ue_ctxt.logger.log_info("Dropping PduSessionResourceSetupRequest. UE is already scheduled for release"); - schedule_error_indication(ue_ctxt.ue_ids.ue_index, cause_radio_network_t::unknown_local_ue_ngap_id); + schedule_error_indication(ue_ctxt.ue_ids.ue_index, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); return; } @@ -394,6 +398,9 @@ void ngap_impl::handle_pdu_session_resource_setup_request(const asn1::ngap::pdu_ ue_ctxt.ue_ids.ran_ue_id, ue_ctxt.ue_ids.amf_ue_id); + // Stop PDU session setup timer + ue_ctxt.pdu_session_setup_timer.stop(); + if (!ue->get_rrc_ue_control_notifier().on_security_enabled()) { ue_ctxt.logger.log_warning("Dropping PduSessionResourceSetupRequest. Security context does not exist"); send_error_indication(ngap_notifier, logger, ue_ctxt.ue_ids.ran_ue_id, ue_ctxt.ue_ids.amf_ue_id, {}); @@ -437,14 +444,14 @@ void ngap_impl::handle_pdu_session_resource_modify_request(const asn1::ngap::pdu logger.warning("ran_ue_id={} amf_ue_id={}: Dropping PduSessionResourceModifyRequest. UE context does not exist", request->ran_ue_ngap_id, request->amf_ue_ngap_id); - send_error_indication(ngap_notifier, logger, {}, {}, cause_radio_network_t::unknown_local_ue_ngap_id); + send_error_indication(ngap_notifier, logger, {}, {}, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); return; } ngap_ue_context& ue_ctxt = ue_ctxt_list[uint_to_ran_ue_id(request->ran_ue_ngap_id)]; if (ue_ctxt.release_scheduled) { ue_ctxt.logger.log_info("Dropping PduSessionResourceModifyRequest. UE is already scheduled for release"); - schedule_error_indication(ue_ctxt.ue_ids.ue_index, cause_radio_network_t::unknown_local_ue_ngap_id); + schedule_error_indication(ue_ctxt.ue_ids.ue_index, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); return; } @@ -452,13 +459,13 @@ void ngap_impl::handle_pdu_session_resource_modify_request(const asn1::ngap::pdu byte_buffer asn1_request_pdu = pack_into_pdu(request); if (asn1_request_pdu == ue_ctxt.last_pdu_session_resource_modify_request) { ue_ctxt.logger.log_warning("Received duplicate PduSessionResourceModifyRequest"); - schedule_error_indication(ue_ctxt.ue_ids.ue_index, cause_radio_network_t::unspecified); + schedule_error_indication(ue_ctxt.ue_ids.ue_index, ngap_cause_radio_network_t::unspecified); return; - } else { - // Store last PDU session resource modify request - ue_ctxt.last_pdu_session_resource_modify_request = asn1_request_pdu.copy(); } + // Store last PDU session resource modify request + ue_ctxt.last_pdu_session_resource_modify_request = asn1_request_pdu.copy(); + ngap_ue* ue = ue_manager.find_ngap_ue(ue_ctxt.ue_ids.ue_index); srsran_assert(ue != nullptr, "ue={} ran_ue_id={} amf_ue_id={}: UE for UE context doesn't exist", @@ -475,7 +482,11 @@ void ngap_impl::handle_pdu_session_resource_modify_request(const asn1::ngap::pdu // Convert to common type cu_cp_pdu_session_resource_modify_request msg; msg.ue_index = ue_ctxt.ue_ids.ue_index; - fill_cu_cp_pdu_session_resource_modify_request(msg, request->pdu_session_res_modify_list_mod_req); + if (!fill_cu_cp_pdu_session_resource_modify_request(msg, request->pdu_session_res_modify_list_mod_req)) { + ue_ctxt.logger.log_warning("Unable to fill ASN1 contents for PduSessionResourceModifyRequest"); + schedule_error_indication(ue_ctxt.ue_ids.ue_index, ngap_cause_radio_network_t::unspecified); + return; + } // start routine task_sched.schedule_async_task( @@ -495,7 +506,7 @@ void ngap_impl::handle_pdu_session_resource_release_command(const asn1::ngap::pd logger.warning("ran_ue_id={} amf_ue_id={}: Dropping PduSessionResourceReleaseCommand. UE context does not exist", command->ran_ue_ngap_id, command->amf_ue_ngap_id); - send_error_indication(ngap_notifier, logger, {}, {}, cause_radio_network_t::unknown_local_ue_ngap_id); + send_error_indication(ngap_notifier, logger, {}, {}, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); return; } @@ -503,7 +514,7 @@ void ngap_impl::handle_pdu_session_resource_release_command(const asn1::ngap::pd if (ue_ctxt.release_scheduled) { ue_ctxt.logger.log_info("Dropping PduSessionResourceReleaseCommand. UE is already scheduled for release"); - schedule_error_indication(ue_ctxt.ue_ids.ue_index, cause_radio_network_t::unknown_local_ue_ngap_id); + schedule_error_indication(ue_ctxt.ue_ids.ue_index, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); return; } @@ -519,7 +530,11 @@ void ngap_impl::handle_pdu_session_resource_release_command(const asn1::ngap::pd // Handle optional NAS PDU if (command->nas_pdu_present) { byte_buffer nas_pdu; - nas_pdu.resize(command->nas_pdu.size()); + if (!nas_pdu.resize(command->nas_pdu.size())) { + ue_ctxt.logger.log_warning("Unable to resize the storage for the PduSessionResourceReleaseCommand PDU"); + schedule_error_indication(ue_ctxt.ue_ids.ue_index, ngap_cause_radio_network_t::unspecified); + return; + } std::copy(command->nas_pdu.begin(), command->nas_pdu.end(), nas_pdu.begin()); ue_ctxt.logger.log_debug(nas_pdu.begin(), nas_pdu.end(), "DlNasTransport PDU ({} B)", nas_pdu.length()); @@ -544,38 +559,53 @@ void ngap_impl::handle_ue_context_release_command(const asn1::ngap::ue_context_r ran_ue_id_t ran_ue_id = ran_ue_id_t::invalid; if (cmd->ue_ngap_ids.type() == asn1::ngap::ue_ngap_ids_c::types_opts::amf_ue_ngap_id) { amf_ue_id = uint_to_amf_ue_id(cmd->ue_ngap_ids.amf_ue_ngap_id()); + + if (!ue_ctxt_list.contains(amf_ue_id)) { + // TS 38.413 section 8.3.3 doesn't specify abnormal conditions, so we just drop the message and send an error + // indication + logger.warning("{}amf_ue_id={}: Dropping UeContextReleaseCommand. UE does not exist", + ran_ue_id == ran_ue_id_t::invalid ? "" : fmt::format("ran_ue_id={} ", ran_ue_id), + amf_ue_id); + send_error_indication(ngap_notifier, logger, {}, amf_ue_id, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); + return; + } } else if (cmd->ue_ngap_ids.type() == asn1::ngap::ue_ngap_ids_c::types_opts::ue_ngap_id_pair) { amf_ue_id = uint_to_amf_ue_id(cmd->ue_ngap_ids.ue_ngap_id_pair().amf_ue_ngap_id); ran_ue_id = uint_to_ran_ue_id(cmd->ue_ngap_ids.ue_ngap_id_pair().ran_ue_ngap_id); - } - if (!ue_ctxt_list.contains(amf_ue_id)) { - // TS 38.413 section 8.3.3 doesn't specify abnormal conditions, so we just drop the message and send an error - // indication - logger.warning("{}amf_ue_id={}: Dropping UeContextReleaseCommand. UE does not exist", - ran_ue_id == ran_ue_id_t::invalid ? "" : fmt::format("ran_ue_id={} ", ran_ue_id), - amf_ue_id); - send_error_indication(ngap_notifier, logger, {}, amf_ue_id, cause_radio_network_t::unknown_local_ue_ngap_id); - return; + if (!ue_ctxt_list.contains(ran_ue_id)) { + // TS 38.413 section 8.3.3 doesn't specify abnormal conditions, so we just drop the message and send an error + // indication + logger.warning( + "ran_ue_id={} amf_ue_id={}: Dropping UeContextReleaseCommand. UE does not exist", ran_ue_id, amf_ue_id); + send_error_indication(ngap_notifier, logger, {}, amf_ue_id, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); + return; + } + + // Update AMF UE ID + if (ue_ctxt_list[ran_ue_id].ue_ids.amf_ue_id == amf_ue_id_t::invalid or + ue_ctxt_list[ran_ue_id].ue_ids.amf_ue_id != amf_ue_id) { + ue_ctxt_list.update_amf_ue_id(ran_ue_id, amf_ue_id); + } } ngap_ue_context& ue_ctxt = ue_ctxt_list[amf_ue_id]; if (ue_ctxt.release_scheduled) { ue_ctxt.logger.log_info("Dropping UeContextReleaseCommand. UE is already scheduled for release"); - schedule_error_indication(ue_ctxt.ue_ids.ue_index, cause_radio_network_t::unknown_local_ue_ngap_id, amf_ue_id); + schedule_error_indication(ue_ctxt.ue_ids.ue_index, ngap_cause_radio_network_t::unknown_local_ue_ngap_id, amf_ue_id); return; - } else { - ue_ctxt.release_scheduled = true; } + ue_ctxt.release_scheduled = true; + if (ran_ue_id == ran_ue_id_t::invalid) { ran_ue_id = ue_ctxt.ue_ids.ran_ue_id; } // Add AMF UE ID to UE, if its not set if (ue_ctxt.ue_ids.amf_ue_id == amf_ue_id_t::invalid) { - ue_ctxt_list.add_amf_ue_id(ran_ue_id, amf_ue_id); + ue_ctxt_list.update_amf_ue_id(ran_ue_id, amf_ue_id); } ngap_ue* ue = ue_manager.find_ngap_ue(ue_ctxt.ue_ids.ue_index); @@ -604,7 +634,7 @@ void ngap_impl::handle_paging(const asn1::ngap::paging_s& msg) logger.info("Received Paging"); if (msg->ue_paging_id.type() != asn1::ngap::ue_paging_id_c::types::five_g_s_tmsi) { - logger.warning("Dropping PDU. Unsupportet UE Paging ID"); + logger.warning("Dropping PDU. Unsupported UE Paging ID"); send_error_indication(ngap_notifier, logger); return; } @@ -673,7 +703,7 @@ void ngap_impl::handle_error_indication(const asn1::ngap::error_ind_s& msg) amf_ue_id_t amf_ue_id = amf_ue_id_t::invalid; ran_ue_id_t ran_ue_id = ran_ue_id_t::invalid; ue_index_t ue_index = ue_index_t::invalid; - std::string msg_cause = ""; + std::string msg_cause; if (msg->cause_present) { msg_cause = asn1_cause_to_string(msg->cause); @@ -683,7 +713,7 @@ void ngap_impl::handle_error_indication(const asn1::ngap::error_ind_s& msg) amf_ue_id = uint_to_amf_ue_id(msg->amf_ue_ngap_id); if (!ue_ctxt_list.contains(uint_to_amf_ue_id(msg->amf_ue_ngap_id))) { logger.warning("amf_ue_id={}: Dropping ErrorIndication. UE context does not exist", msg->amf_ue_ngap_id); - send_error_indication(ngap_notifier, logger, {}, {}, cause_radio_network_t::inconsistent_remote_ue_ngap_id); + send_error_indication(ngap_notifier, logger, {}, {}, ngap_cause_radio_network_t::inconsistent_remote_ue_ngap_id); return; } ue_index = ue_ctxt_list[amf_ue_id].ue_ids.ue_index; @@ -691,7 +721,7 @@ void ngap_impl::handle_error_indication(const asn1::ngap::error_ind_s& msg) ran_ue_id = uint_to_ran_ue_id(msg->ran_ue_ngap_id); if (!ue_ctxt_list.contains(uint_to_ran_ue_id(msg->ran_ue_ngap_id))) { logger.warning("ran_ue_id={}: Dropping ErrorIndication. UE context does not exist", msg->ran_ue_ngap_id); - send_error_indication(ngap_notifier, logger, {}, {}, cause_radio_network_t::unknown_local_ue_ngap_id); + send_error_indication(ngap_notifier, logger, {}, {}, ngap_cause_radio_network_t::unknown_local_ue_ngap_id); return; } ue_index = ue_ctxt_list[ran_ue_id].ue_ids.ue_index; @@ -705,8 +735,8 @@ void ngap_impl::handle_error_indication(const asn1::ngap::error_ind_s& msg) // Request UE release task_sched.schedule_async_task(ue_index, launch_async([this, ue_index](coro_context>& ctx) { CORO_BEGIN(ctx); - CORO_AWAIT(handle_ue_context_release_request( - cu_cp_ue_context_release_request{ue_index, {}, cause_nas_t::unspecified})); + CORO_AWAIT(handle_ue_context_release_request(cu_cp_ue_context_release_request{ + ue_index, {}, ngap_cause_radio_network_t::unspecified})); CORO_RETURN(); })); @@ -753,6 +783,9 @@ async_task ngap_impl::handle_ue_context_release_request(const cu_cp_ue_con ngap_ue_context& ue_ctxt = ue_ctxt_list[msg.ue_index]; + // Stop PDU session setup timer + ue_ctxt.pdu_session_setup_timer.stop(); + if (ue_ctxt.ue_ids.amf_ue_id == amf_ue_id_t::invalid) { ue_ctxt.logger.log_debug("Ignoring UeContextReleaseRequest. UE does not have an AMF UE ID"); return launch_async([](coro_context>& ctx) { @@ -869,7 +902,7 @@ void ngap_impl::remove_ue_context(ue_index_t ue_index) ue_ctxt_list.remove_ue_context(ue_index); } -void ngap_impl::schedule_error_indication(ue_index_t ue_index, cause_t cause, optional amf_ue_id) +void ngap_impl::schedule_error_indication(ue_index_t ue_index, ngap_cause_t cause, optional amf_ue_id) { logger.info("{}{}: Scheduling ErrorIndication", ue_index != ue_index_t::invalid ? fmt::format("ue={}", ue_index) : "", @@ -882,31 +915,44 @@ void ngap_impl::schedule_error_indication(ue_index_t ue_index, cause_t cause, op })); } -void ngap_impl::on_ue_context_setup_timer_expired(ue_index_t ue_index) +void ngap_impl::on_pdu_session_setup_timer_expired(ue_index_t ue_index) { if (ue_ctxt_list.contains(ue_index)) { ngap_ue_context& ue_ctxt = ue_ctxt_list[ue_index]; - ue_ctxt.logger.log_warning("UE context setup timer expired after {}ms. Releasing UE from DU", - ue_ctxt.ue_context_setup_timer.duration().count()); - - auto* ue = ue_manager.find_ngap_ue(ue_ctxt.ue_ids.ue_index); - srsran_assert(ue != nullptr, - "ue={} ran_ue_id={} amf_ue_id={}: UE for UE context doesn't exist", - ue_ctxt.ue_ids.ue_index, - ue_ctxt.ue_ids.ran_ue_id, - ue_ctxt.ue_ids.amf_ue_id); - - task_sched.schedule_async_task(ue_index, launch_async([ue, ue_index](coro_context>& ctx) { - CORO_BEGIN(ctx); - CORO_AWAIT( - ue->get_du_processor_control_notifier().on_new_ue_context_release_command( - {ue_index, cause_nas_t::unspecified})); - CORO_RETURN(); - })); - + if (ue_ctxt.ue_ids.amf_ue_id == amf_ue_id_t::invalid) { + // AMF never responded to InitialUEMessage, so we only remove the UE from the DU + ue_ctxt.logger.log_warning("PDU session setup timer expired after {}ms. Releasing UE from DU", + ue_ctxt.pdu_session_setup_timer.duration().count()); + + auto* ue = ue_manager.find_ngap_ue(ue_ctxt.ue_ids.ue_index); + srsran_assert(ue != nullptr, + "ue={} ran_ue_id={} amf_ue_id={}: UE for UE context doesn't exist", + ue_ctxt.ue_ids.ue_index, + ue_ctxt.ue_ids.ran_ue_id, + ue_ctxt.ue_ids.amf_ue_id); + + task_sched.schedule_async_task(ue_index, launch_async([ue, ue_index](coro_context>& ctx) { + CORO_BEGIN(ctx); + CORO_AWAIT( + ue->get_du_processor_control_notifier().on_new_ue_context_release_command( + {ue_index, ngap_cause_radio_network_t::unspecified})); + CORO_RETURN(); + })); + } else { + ue_ctxt.logger.log_warning("PDU session setup timer expired after {}ms. Requesting UE release", + ue_ctxt.pdu_session_setup_timer.duration().count()); + + // Request UE release + task_sched.schedule_async_task(ue_index, launch_async([this, ue_index](coro_context>& ctx) { + CORO_BEGIN(ctx); + CORO_AWAIT(handle_ue_context_release_request(cu_cp_ue_context_release_request{ + ue_index, {}, ngap_cause_radio_network_t::unspecified})); + CORO_RETURN(); + })); + } } else { - logger.debug("ue={}: Ignoring expired UE context setup timer. UE context not found", ue_index); + logger.debug("ue={}: Ignoring expired PDU session setup timer. UE context not found", ue_index); return; } } diff --git a/lib/ngap/ngap_impl.h b/lib/ngap/ngap_impl.h index dcde007123..6d4bcff4fd 100644 --- a/lib/ngap/ngap_impl.h +++ b/lib/ngap/ngap_impl.h @@ -137,9 +137,10 @@ class ngap_impl final : public ngap_interface /// \param[in] ue_index The index of the related UE. /// \param[in] cause The cause of the Error Indication. /// \param[in] amf_ue_id The AMF UE ID. - void schedule_error_indication(ue_index_t ue_index, cause_t cause, optional amf_ue_id = {}); + void schedule_error_indication(ue_index_t ue_index, ngap_cause_t cause, optional amf_ue_id = {}); - void on_ue_context_setup_timer_expired(ue_index_t ue_index); + /// \brief Callback for the PDU Session Setup Timer expiration. Triggers the release of the UE. + void on_pdu_session_setup_timer_expired(ue_index_t ue_index); ngap_context_t context; diff --git a/lib/ngap/ngap_validators/ngap_validators.cpp b/lib/ngap/ngap_validators/ngap_validators.cpp index d5584b3736..102ccda646 100644 --- a/lib/ngap/ngap_validators/ngap_validators.cpp +++ b/lib/ngap/ngap_validators/ngap_validators.cpp @@ -45,7 +45,7 @@ pdu_session_resource_setup_validation_outcome srsran::srs_cu_cp::verify_pdu_sess // Add failed psi to response cu_cp_pdu_session_res_setup_failed_item failed_item; failed_item.pdu_session_id = psi; - failed_item.unsuccessful_transfer.cause = cause_radio_network_t::multiple_pdu_session_id_instances; + failed_item.unsuccessful_transfer.cause = ngap_cause_radio_network_t::multiple_pdu_session_id_instances; verification_outcome.response.pdu_session_res_failed_to_setup_items.emplace(psi, failed_item); } } @@ -72,7 +72,7 @@ pdu_session_resource_setup_validation_outcome srsran::srs_cu_cp::verify_pdu_sess // Add failed psi to response cu_cp_pdu_session_res_setup_failed_item failed_item; failed_item.pdu_session_id = psi; - failed_item.unsuccessful_transfer.cause = cause_radio_network_t::invalid_qos_combination; + failed_item.unsuccessful_transfer.cause = ngap_cause_radio_network_t::invalid_qos_combination; verification_outcome.response.pdu_session_res_failed_to_setup_items.emplace(psi, failed_item); // If single QoS flow fails, then the whole PDU session fails break; @@ -116,7 +116,7 @@ pdu_session_resource_modify_validation_outcome srsran::srs_cu_cp::verify_pdu_ses // Add failed psi to response cu_cp_pdu_session_res_setup_failed_item failed_item; failed_item.pdu_session_id = psi; - failed_item.unsuccessful_transfer.cause = cause_radio_network_t::multiple_pdu_session_id_instances; + failed_item.unsuccessful_transfer.cause = ngap_cause_radio_network_t::multiple_pdu_session_id_instances; verification_outcome.response.pdu_session_res_failed_to_modify_list.emplace(psi, failed_item); } } diff --git a/lib/ngap/procedures/ngap_handover_resource_allocation_procedure.cpp b/lib/ngap/procedures/ngap_handover_resource_allocation_procedure.cpp index 967cc0032d..5184798abe 100644 --- a/lib/ngap/procedures/ngap_handover_resource_allocation_procedure.cpp +++ b/lib/ngap/procedures/ngap_handover_resource_allocation_procedure.cpp @@ -65,7 +65,7 @@ void ngap_handover_resource_allocation_procedure::operator()(coro_contextamf_ue_ngap_id = amf_ue_id_to_uint(amf_ue_id); init_ctxt_setup_resp->ran_ue_ngap_id = ran_ue_id_to_uint(ran_ue_id); - fill_asn1_initial_context_setup_response(init_ctxt_setup_resp, msg); + if (!fill_asn1_initial_context_setup_response(init_ctxt_setup_resp, msg)) { + logger.log_warning("Unable to fill ASN1 contents for InitialContextSetupResponse"); + return; + } logger.log_info("Sending InitialContextSetupResponse"); amf_notifier.on_new_message(ngap_msg); diff --git a/lib/ngap/procedures/ngap_pdu_session_resource_modify_procedure.cpp b/lib/ngap/procedures/ngap_pdu_session_resource_modify_procedure.cpp index 244722382f..a05b1c09e0 100644 --- a/lib/ngap/procedures/ngap_pdu_session_resource_modify_procedure.cpp +++ b/lib/ngap/procedures/ngap_pdu_session_resource_modify_procedure.cpp @@ -79,7 +79,8 @@ void ngap_pdu_session_resource_modify_procedure::operator()(coro_contextamf_ue_ngap_id = amf_ue_id_to_uint(ue_ids.amf_ue_id); pdu_session_res_modify_resp->ran_ue_ngap_id = ran_ue_id_to_uint(ue_ids.ran_ue_id); - fill_asn1_pdu_session_res_modify_response(pdu_session_res_modify_resp, response); + // TODO: needs more handling in the coro above? + if (!fill_asn1_pdu_session_res_modify_response(pdu_session_res_modify_resp, response)) { + logger.log_warning("Unable to fill ASN1 contents of PDUSessionResourceModifyResponse", name()); + return; + } logger.log_info("Sending PduSessionResourceModifyResponse"); amf_notifier.on_new_message(ngap_msg); diff --git a/lib/ngap/procedures/ngap_pdu_session_resource_release_procedure.cpp b/lib/ngap/procedures/ngap_pdu_session_resource_release_procedure.cpp index 99a8ac1061..0240375284 100644 --- a/lib/ngap/procedures/ngap_pdu_session_resource_release_procedure.cpp +++ b/lib/ngap/procedures/ngap_pdu_session_resource_release_procedure.cpp @@ -68,8 +68,12 @@ void ngap_pdu_session_resource_release_procedure::send_pdu_session_resource_rele ngap_msg.pdu.set_successful_outcome(); ngap_msg.pdu.successful_outcome().load_info_obj(ASN1_NGAP_ID_PDU_SESSION_RES_RELEASE); - fill_asn1_pdu_session_resource_release_response( - ngap_msg.pdu.successful_outcome().value.pdu_session_res_release_resp(), response); + // TODO: needs more handling in the coro above? + if (!fill_asn1_pdu_session_resource_release_response( + ngap_msg.pdu.successful_outcome().value.pdu_session_res_release_resp(), response)) { + logger.log_warning("Cannot fill ASN1 PDU Session Resource Release Response"); + return; + } auto& pdu_session_res_release_resp = ngap_msg.pdu.successful_outcome().value.pdu_session_res_release_resp(); pdu_session_res_release_resp->amf_ue_ngap_id = amf_ue_id_to_uint(ue_ids.amf_ue_id); diff --git a/lib/ngap/procedures/ngap_pdu_session_resource_setup_procedure.cpp b/lib/ngap/procedures/ngap_pdu_session_resource_setup_procedure.cpp index 26476a8ffc..f3b86d02c2 100644 --- a/lib/ngap/procedures/ngap_pdu_session_resource_setup_procedure.cpp +++ b/lib/ngap/procedures/ngap_pdu_session_resource_setup_procedure.cpp @@ -85,7 +85,8 @@ void ngap_pdu_session_resource_setup_procedure::operator()(coro_context c_prbs, __m256i r0, __m256i r1, __m256i r2) -{ - srsran_assert(c_prbs.size() == 2, "Output span must contain 2 resource blocks"); - - // Returns first 18 packed bytes out of 27 for the first RB (9 bytes in each 128bit lane - same applies below). - __m256i reg0_packed_epi8 = pack_avx2_register_9b_be(r0); - // Returns last 9 packed bytes out of 27 for the first RB and first 9 packed bytes for the second RB. - __m256i reg1_packed_epi8 = pack_avx2_register_9b_be(r1); - // Returns last 18 packed bytes out of 27 for the second RB. - __m256i reg2_packed_epi8 = pack_avx2_register_9b_be(r2); - - // AVX2 doesn't provide instruction to store specific bytes of the lanes using a mask, so we create new vectors, - // and use a regular store intrinsic. - // - // Next example explains the bytes permutations performed below for each RB to group its packed IQs. - // r0: {0xe959c2834776bd64, 0x8b, 0xdc0c7ca5b0ab3a56, 0x31} - // r1: {0xb1e93964f248fe46, 0xaf, 0x4f0f368e76fc7a9e, 0x12} - // r0_tmp0: {0xe959c2834776bd64, 0x000000000000008b, 0x0c7ca5b0ab3a5600, 0x00000000000031dc} - // r1_tmp0: {0x3964f248fe460000, 0x0000000000afb1e9, 0x0000000000000000, 0x0000000000000000} - // r0_tmp1: {0xe959c2834776bd64, 0x0c7ca5b0ab3a5600, 0x00000000000031dc, 0x0000000000000000} - // r1_tmp1: {0x0000000000000000, 0x0000000000000000, 0x3964f248fe460000, 0x0000000000afb1e9} - // rb0_tmp0: {0xe959c2834776bd64, 0x0c7ca5b0ab3a568b, XXXXXXXXXXXXXXXXXX, XXXXXXXXXXXXXXXXXX} - // rb0_tmp1: {XXXXXXXXXXXXXXXXXX, XXXXXXXXXXXXXXXXXX, 0x3964f248fe4631dc, 0x0000000000afb1e9} - // select 64bit words between rb0_tmp0 and rb0_tmp1: - // rb0: {0xe959c2834776bd64, 0x0c7ca5b0ab3a568b, 0x3964f248fe4631dc, 0x0000000000afb1e9} - - // Pack the first input RB. - const __m256i shuffle_mask_0 = - _mm256_setr_epi64x(0x0706050403020100, 0x0f0e0d0c0b0a0908, 0x06050403020100ff, 0xffffffffffff0807); - __m256i reg0_tmp0_epi8 = _mm256_shuffle_epi8(reg0_packed_epi8, shuffle_mask_0); - - const __m256i shuffle_mask_1 = - _mm256_setr_epi64x(0x050403020100ffff, 0xffffffffff080706, 0xffffffffffffffff, 0xffffffffffffffff); - __m256i reg1_tmp0_epi8 = _mm256_shuffle_epi8(reg1_packed_epi8, shuffle_mask_1); - - __m256i reg0_tmp1_epi8 = _mm256_permutevar8x32_epi32(reg0_tmp0_epi8, _mm256_setr_epi32(0, 1, 4, 5, 6, 7, 3, 3)); - __m256i reg1_tmp1_epi8 = _mm256_permute4x64_epi64(reg1_tmp0_epi8, 0b01001110); - - const __m256i sel_mask_0_epi8 = - _mm256_setr_epi64x(0x0000000000000000, 0x0000000000000000, 0xffffffffffffffff, 0xffffffffffffffff); - __m256i prb0_tmp0_epi8 = _mm256_or_si256(reg0_tmp0_epi8, reg0_tmp1_epi8); - __m256i prb0_tmp1_epi8 = _mm256_or_si256(reg0_tmp1_epi8, reg1_tmp1_epi8); - __m256i prb0_epi8 = _mm256_blendv_epi8(prb0_tmp0_epi8, prb0_tmp1_epi8, sel_mask_0_epi8); - - // Pack the second input RB. - const __m256i shuffle_mask_2 = - _mm256_setr_epi64x(0xffffffffffff0807, 0x06050403020100ff, 0x050403020100ffff, 0xffffffffff080706); - __m256i reg1_tmp2_epi8 = _mm256_permutevar8x32_epi32(reg1_packed_epi8, _mm256_setr_epi32(4, 5, 6, 7, 3, 3, 3, 3)); - __m256i reg2_tmp0_epi8 = _mm256_shuffle_epi8(reg2_packed_epi8, shuffle_mask_2); - __m256i reg2_tmp1_epi8 = _mm256_permutevar8x32_epi32(reg2_tmp0_epi8, _mm256_setr_epi32(1, 1, 2, 3, 0, 1, 6, 7)); - - const __m256i sel_mask_1_epi8 = - _mm256_setr_epi64x(0x0000000000000000, 0x0000000000000000, 0xffffffffffffffff, 0xffffffffffffffff); - __m256i prb1_tmp0_epi8 = _mm256_or_si256(reg1_tmp2_epi8, reg2_tmp1_epi8); - __m256i prb1_tmp1_epi8 = _mm256_or_si256(reg2_tmp0_epi8, reg2_tmp1_epi8); - __m256i prb1_epi8 = _mm256_blendv_epi8(prb1_tmp0_epi8, prb1_tmp1_epi8, sel_mask_1_epi8); - - // Write resource blocks to the memory. - _mm256_storeu_si256(reinterpret_cast<__m256i*>(c_prbs[0].get_byte_buffer().data()), prb0_epi8); - c_prbs[0].set_stored_size(BYTES_PER_PRB_9BIT_COMPRESSION); - - _mm256_storeu_si256(reinterpret_cast<__m256i*>(c_prbs[1].get_byte_buffer().data()), prb1_epi8); - c_prbs[1].set_stored_size(BYTES_PER_PRB_9BIT_COMPRESSION); -} - -void mm256::pack_prbs_big_endian(span c_prbs, __m256i r0, __m256i r1, __m256i r2, unsigned iq_width) -{ - switch (iq_width) { - case 9: - avx2_pack_prbs_9b_big_endian(c_prbs, r0, r1, r2); - break; - default: - report_fatal_error("Unsupported bit width"); - } -} - -/// \brief Unpacks packed 9bit IQ samples stored as bytes in big-endian format to an array of 16bit signed values. -/// -/// \param[out] unpacked_iq_data A span of 16bit integers, corresponding to \c NOF_CARRIERS_PER_RB unpacked IQ pairs. -/// \param[in] packed_data A span of 27 packed bytes. -/// -/// \note The \c unpacked_iq_data parameter should be sized to store 32 output IQ samples: it is 24 IQ samples of one RB -/// rounded up to 32-byte boundary required by AVX2 intrinsics. -static void unpack_prb_9b_be(span unpacked_iq_data, span packed_data) -{ - constexpr size_t avx2_size_short_words = 16; - srsran_assert(unpacked_iq_data.size() >= avx2_size_short_words * 2, "Wrong unpacked data span size"); - - // Load input, 27 bytes (fits in one AVX2 register). - __m256i packed_data_epi8 = _mm256_loadu_si256(reinterpret_cast(packed_data.data())); - - // Duplicate input words (it is required since below in the code every byte will be used twice: - // to provide MSB bits of the current IQ sample and LSB bits of the previous IQ sample). - __m256i packed_data_0_epi8 = _mm256_permute4x64_epi64(packed_data_epi8, 0b10010100); - __m256i packed_data_1_epi8 = _mm256_permute4x64_epi64(packed_data_epi8, 0b00001110); - - // Swap bytes for little-endian representation. - // As a result each pair of bytes will contain all bits of a single I or Q sample. - const __m256i shuffle_mask_0_epi8 = - _mm256_setr_epi64x(0x0304020301020001, 0x0708060705060405, 0x0405030402030102, 0x0809070806070506); - const __m256i shuffle_mask_1_epi8 = - _mm256_setr_epi64x(0x0506040503040203, 0x090a080907080607, 0xffffffffffffffff, 0xffffffffffffffff); - __m256i packed_data_0_le_epi8 = _mm256_shuffle_epi8(packed_data_0_epi8, shuffle_mask_0_epi8); - __m256i packed_data_1_le_epi8 = _mm256_shuffle_epi8(packed_data_1_epi8, shuffle_mask_1_epi8); - - // Do logical shift left, then arithmetical shift right in order to sign extend values. - // Note, AVX2 only allows to shift 32bit words using vector of indexes, so we do it twice. - const __m256i shl_0_epi32 = _mm256_setr_epi32(0, 2, 4, 6, 0, 2, 4, 6); - const __m256i shl_1_epi32 = _mm256_setr_epi32(1, 3, 5, 7, 1, 3, 5, 7); - // Mask 16bit words to use only shifted bits. - const __m256i mask_even_epi16 = _mm256_set1_epi32(0x0000ffff); - const __m256i mask_odd_epi16 = _mm256_set1_epi32(0xffff0000); - - __m256i unpacked_data_00_epi16 = _mm256_sllv_epi32(packed_data_0_le_epi8, shl_0_epi32); - __m256i unpacked_data_01_epi16 = _mm256_sllv_epi32(packed_data_0_le_epi8, shl_1_epi32); - __m256i unpacked_data_10_epi16 = _mm256_sllv_epi32(packed_data_1_le_epi8, shl_0_epi32); - __m256i unpacked_data_11_epi16 = _mm256_sllv_epi32(packed_data_1_le_epi8, shl_1_epi32); - unpacked_data_00_epi16 = _mm256_and_si256(unpacked_data_00_epi16, mask_even_epi16); - unpacked_data_01_epi16 = _mm256_and_si256(unpacked_data_01_epi16, mask_odd_epi16); - unpacked_data_10_epi16 = _mm256_and_si256(unpacked_data_10_epi16, mask_even_epi16); - unpacked_data_11_epi16 = _mm256_and_si256(unpacked_data_11_epi16, mask_odd_epi16); - - // Merge two vectors to get vector of shifted 16bit IQ samples, right-shift it to sign-extend values. - __m256i unpacked_data_0_epi16 = _mm256_srai_epi16(_mm256_or_si256(unpacked_data_00_epi16, unpacked_data_01_epi16), 7); - __m256i unpacked_data_1_epi16 = _mm256_srai_epi16(_mm256_or_si256(unpacked_data_10_epi16, unpacked_data_11_epi16), 7); - - // Write results. - _mm256_storeu_si256(reinterpret_cast<__m256i*>(unpacked_iq_data.data()), unpacked_data_0_epi16); - _mm256_storeu_si256(reinterpret_cast<__m256i*>(unpacked_iq_data.subspan(16, 8).data()), unpacked_data_1_epi16); -} - -void mm256::unpack_prb_big_endian(span unpacked_iq_data, span packed_data, unsigned iq_width) -{ - switch (iq_width) { - case 9: - unpack_prb_9b_be(unpacked_iq_data, packed_data); - break; - default: - report_fatal_error("Unsupported bit width"); - } -} diff --git a/lib/ofh/compression/packing_utils_avx2.h b/lib/ofh/compression/packing_utils_avx2.h index a6e0d3b8b9..7e5976f125 100644 --- a/lib/ofh/compression/packing_utils_avx2.h +++ b/lib/ofh/compression/packing_utils_avx2.h @@ -22,13 +22,146 @@ #pragma once +#include "srsran/adt/span.h" #include "srsran/ofh/compression/compressed_prb.h" +#include "srsran/support/error_handling.h" #include namespace srsran { namespace ofh { namespace mm256 { +/// \brief Packs 16bit IQ samples in one AVX2 register using 9bit big-endian format. +/// +/// \param[in] cmp_data_epi16 AVX2 register storing 16bit IQ samples. +/// \return An AVX2 register containing 18 packed bytes. +/// +/// \note Each 128bit lane of the input contains eight I and Q values, with each value consisting of 9 bits of data (the +/// LSB ones) and 7 bits of padding. The function packs the eight IQ samples (only the data bits, discarding the +/// padding) into the first 9 bytes of the respective 128bit lane of the output register (the remaining 7 bytes are +/// padding). Thus, the return value contains 18 packed data bytes in total. +inline __m256i pack_avx2_register_9b_be(__m256i cmp_data_epi16) +{ + // Input IQ samples need to be shifted in order to align bits before final packing. + // 0: i0 0 0 0 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 <- rotate right by 1 (shift left by 7, swap bytes later) + // 1: 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 0 0 0 0 <- shift left by 6 + // 2: 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 0 0 0 0 <- shift left by 5 + // 3: 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 0 0 <- shift left by 4 + // 4: 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 0 0 <- shift left by 3 + // 5: 0 0 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 <- shift left by 2 + // 6: 0 0 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 <- shift left by 1 + // 7: 0 0 0 0 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 <- no shift + // ... the same pattern ... + // + // Note, AVX2 only allows shifting 32bit words using vector of indexes, so we do it twice. + const __m256i shift_0_epi32 = _mm256_setr_epi32(7, 5, 3, 1, 7, 5, 3, 1); + const __m256i shift_1_epi32 = _mm256_setr_epi32(6, 4, 2, 0, 6, 4, 2, 0); + __m256i cmp_data_sh0_epi16 = _mm256_sllv_epi32(cmp_data_epi16, shift_0_epi32); + __m256i cmp_data_sh1_epi16 = _mm256_sllv_epi32(cmp_data_epi16, shift_1_epi32); + // Mask 16bit words to keep only 9 shifted bits. + const __m256i mask_even_epi16 = + _mm256_setr_epi32(0x0000ff80, 0x00003fe0, 0x00000ff8, 0x000003fe, 0x0000ff80, 0x00003fe0, 0x00000ff8, 0x000003fe); + const __m256i mask_odd_epi16 = + _mm256_setr_epi32(0x7fc00000, 0x1ff00000, 0x07fc0000, 0x01ff0000, 0x7fc00000, 0x1ff00000, 0x07fc0000, 0x01ff0000); + cmp_data_sh0_epi16 = _mm256_and_si256(cmp_data_sh0_epi16, mask_even_epi16); + cmp_data_sh1_epi16 = _mm256_and_si256(cmp_data_sh1_epi16, mask_odd_epi16); + + // Merge two vectors to get vector of shifted IQ samples as explained above. + __m256i cmp_data_shifted_epi16 = _mm256_or_si256(cmp_data_sh0_epi16, cmp_data_sh1_epi16); + + // Shuffle it and create two new vectors that can be OR'ed to produce the final result. + __m256i cmp_data_v0_epi16 = _mm256_shuffle_epi8( + cmp_data_shifted_epi16, + _mm256_setr_epi64x(0x0c0a0806040200ff, 0xffffffffffffff0e, 0x0c0a0806040200ff, 0xffffffffffffff0e)); + __m256i cmp_data_v1_epi16 = _mm256_shuffle_epi8( + cmp_data_shifted_epi16, + _mm256_setr_epi64x(0x0f0d0b0907050301, 0xffffffffffffffff, 0x0f0d0b0907050301, 0xffffffffffffffff)); + // Perform 'bitwise OR' and return the result. + return _mm256_or_si256(cmp_data_v0_epi16, cmp_data_v1_epi16); +} + +/// \brief Packs 16bit IQ values of the two input RBs using 9bit big-endian format. +/// +/// The following diagram shows the input format. Here RBx stands for one unique RE (pair of IQ samples, 32 bits long) +/// pertaining to a respective RB): +/// | | | | | | +/// | ----- | ------- | ------- | ------- | ------- | +/// | \c r0: | RB0 RB0 | RB0 RB0 | RB0 RB0 | RB0 RB0 | +/// | \c r1: | RB0 RB0 | RB0 RB0 | RB1 RB1 | RB1 RB1 | +/// | \c r2: | RB1 RB1 | RB1 RB1 | RB1 RB1 | RB1 RB1 | +/// +/// \param[out] c_prbs Span of two compressed PRBs. +/// \param[in] r0 AVX2 register storing 16bit IQ samples of the first RB. +/// \param[in] r1 AVX2 register storing 16bit IQ samples of the first and second RB. +/// \param[in] r2 AVX2 register storing 16bit IQ samples of the second RB. +inline void avx2_pack_prbs_9b_big_endian(span c_prbs, __m256i r0, __m256i r1, __m256i r2) +{ + /// Number of bytes used by 1 packed resource block with IQ samples compressed to 9 bits. + static constexpr unsigned BYTES_PER_PRB_9BIT_COMPRESSION = 27; + + srsran_assert(c_prbs.size() == 2, "Output span must contain 2 resource blocks"); + + // Returns first 18 packed bytes out of 27 for the first RB (9 bytes in each 128bit lane - same applies below). + __m256i reg0_packed_epi8 = pack_avx2_register_9b_be(r0); + // Returns last 9 packed bytes out of 27 for the first RB and first 9 packed bytes for the second RB. + __m256i reg1_packed_epi8 = pack_avx2_register_9b_be(r1); + // Returns last 18 packed bytes out of 27 for the second RB. + __m256i reg2_packed_epi8 = pack_avx2_register_9b_be(r2); + + // AVX2 doesn't provide instruction to store specific bytes of the lanes using a mask, so we create new vectors, + // and use a regular store intrinsic. + // + // Next example explains the bytes permutations performed below for each RB to group its packed IQs. + // r0: {0xe959c2834776bd64, 0x8b, 0xdc0c7ca5b0ab3a56, 0x31} + // r1: {0xb1e93964f248fe46, 0xaf, 0x4f0f368e76fc7a9e, 0x12} + // r0_tmp0: {0xe959c2834776bd64, 0x000000000000008b, 0x0c7ca5b0ab3a5600, 0x00000000000031dc} + // r1_tmp0: {0x3964f248fe460000, 0x0000000000afb1e9, 0x0000000000000000, 0x0000000000000000} + // r0_tmp1: {0xe959c2834776bd64, 0x0c7ca5b0ab3a5600, 0x00000000000031dc, 0x0000000000000000} + // r1_tmp1: {0x0000000000000000, 0x0000000000000000, 0x3964f248fe460000, 0x0000000000afb1e9} + // rb0_tmp0: {0xe959c2834776bd64, 0x0c7ca5b0ab3a568b, XXXXXXXXXXXXXXXXXX, XXXXXXXXXXXXXXXXXX} + // rb0_tmp1: {XXXXXXXXXXXXXXXXXX, XXXXXXXXXXXXXXXXXX, 0x3964f248fe4631dc, 0x0000000000afb1e9} + // select 64bit words between rb0_tmp0 and rb0_tmp1: + // rb0: {0xe959c2834776bd64, 0x0c7ca5b0ab3a568b, 0x3964f248fe4631dc, 0x0000000000afb1e9} + + // Pack the first input RB. + const __m256i shuffle_mask_0 = + _mm256_setr_epi64x(0x0706050403020100, 0x0f0e0d0c0b0a0908, 0x06050403020100ff, 0xffffffffffff0807); + __m256i reg0_tmp0_epi8 = _mm256_shuffle_epi8(reg0_packed_epi8, shuffle_mask_0); + + const __m256i shuffle_mask_1 = + _mm256_setr_epi64x(0x050403020100ffff, 0xffffffffff080706, 0xffffffffffffffff, 0xffffffffffffffff); + __m256i reg1_tmp0_epi8 = _mm256_shuffle_epi8(reg1_packed_epi8, shuffle_mask_1); + + __m256i reg0_tmp1_epi8 = _mm256_permutevar8x32_epi32(reg0_tmp0_epi8, _mm256_setr_epi32(0, 1, 4, 5, 6, 7, 3, 3)); + __m256i reg1_tmp1_epi8 = _mm256_permute4x64_epi64(reg1_tmp0_epi8, 0b01001110); + + const __m256i sel_mask_0_epi8 = + _mm256_setr_epi64x(0x0000000000000000, 0x0000000000000000, 0xffffffffffffffff, 0xffffffffffffffff); + __m256i prb0_tmp0_epi8 = _mm256_or_si256(reg0_tmp0_epi8, reg0_tmp1_epi8); + __m256i prb0_tmp1_epi8 = _mm256_or_si256(reg0_tmp1_epi8, reg1_tmp1_epi8); + __m256i prb0_epi8 = _mm256_blendv_epi8(prb0_tmp0_epi8, prb0_tmp1_epi8, sel_mask_0_epi8); + + // Pack the second input RB. + const __m256i shuffle_mask_2 = + _mm256_setr_epi64x(0xffffffffffff0807, 0x06050403020100ff, 0x050403020100ffff, 0xffffffffff080706); + __m256i reg1_tmp2_epi8 = _mm256_permutevar8x32_epi32(reg1_packed_epi8, _mm256_setr_epi32(4, 5, 6, 7, 3, 3, 3, 3)); + __m256i reg2_tmp0_epi8 = _mm256_shuffle_epi8(reg2_packed_epi8, shuffle_mask_2); + __m256i reg2_tmp1_epi8 = _mm256_permutevar8x32_epi32(reg2_tmp0_epi8, _mm256_setr_epi32(1, 1, 2, 3, 0, 1, 6, 7)); + + const __m256i sel_mask_1_epi8 = + _mm256_setr_epi64x(0x0000000000000000, 0x0000000000000000, 0xffffffffffffffff, 0xffffffffffffffff); + __m256i prb1_tmp0_epi8 = _mm256_or_si256(reg1_tmp2_epi8, reg2_tmp1_epi8); + __m256i prb1_tmp1_epi8 = _mm256_or_si256(reg2_tmp0_epi8, reg2_tmp1_epi8); + __m256i prb1_epi8 = _mm256_blendv_epi8(prb1_tmp0_epi8, prb1_tmp1_epi8, sel_mask_1_epi8); + + // Write resource blocks to the memory. + _mm256_storeu_si256(reinterpret_cast<__m256i*>(c_prbs[0].get_byte_buffer().data()), prb0_epi8); + c_prbs[0].set_stored_size(BYTES_PER_PRB_9BIT_COMPRESSION); + + _mm256_storeu_si256(reinterpret_cast<__m256i*>(c_prbs[1].get_byte_buffer().data()), prb1_epi8); + c_prbs[1].set_stored_size(BYTES_PER_PRB_9BIT_COMPRESSION); +} + /// \brief Packs 16bit IQ values of the two RBs using the specified width and big-endian format. /// /// The following diagram shows the input format. Here RBx stands for one unique RE (pair of IQ samples, 32 bits long) @@ -44,7 +177,72 @@ namespace mm256 { /// \param[in] r1 AVX2 register storing 16bit IQ pairs of the first and second PRB. /// \param[in] r2 AVX2 register storing 16bit IQ pairs of the second PRB. /// \param[in] iq_width Bit width of the resulting packed IQ samples. -void pack_prbs_big_endian(span c_prbs, __m256i r0, __m256i r1, __m256i r2, unsigned iq_width); +inline void +pack_prbs_big_endian(span c_prbs, __m256i r0, __m256i r1, __m256i r2, unsigned iq_width) +{ + switch (iq_width) { + case 9: + avx2_pack_prbs_9b_big_endian(c_prbs, r0, r1, r2); + break; + default: + report_fatal_error("Unsupported bit width"); + } +} + +/// \brief Unpacks packed 9bit IQ samples stored as bytes in big-endian format to an array of 16bit signed values. +/// +/// \param[out] unpacked_iq_data A span of 16bit integers, corresponding to \c NOF_CARRIERS_PER_RB unpacked IQ pairs. +/// \param[in] packed_data A span of 27 packed bytes. +/// +/// \note The \c unpacked_iq_data parameter should be sized to store 32 output IQ samples: it is 24 IQ samples of one RB +/// rounded up to 32-byte boundary required by AVX2 intrinsics. +inline void unpack_prb_9b_be(span unpacked_iq_data, span packed_data) +{ + constexpr size_t avx2_size_short_words = 16; + srsran_assert(unpacked_iq_data.size() >= avx2_size_short_words * 2, "Wrong unpacked data span size"); + + // Load input, 27 bytes (fits in one AVX2 register). + __m256i packed_data_epi8 = _mm256_loadu_si256(reinterpret_cast(packed_data.data())); + + // Duplicate input words (it is required since below in the code every byte will be used twice: + // to provide MSB bits of the current IQ sample and LSB bits of the previous IQ sample). + __m256i packed_data_0_epi8 = _mm256_permute4x64_epi64(packed_data_epi8, 0b10010100); + __m256i packed_data_1_epi8 = _mm256_permute4x64_epi64(packed_data_epi8, 0b00001110); + + // Swap bytes for little-endian representation. + // As a result each pair of bytes will contain all bits of a single I or Q sample. + const __m256i shuffle_mask_0_epi8 = + _mm256_setr_epi64x(0x0304020301020001, 0x0708060705060405, 0x0405030402030102, 0x0809070806070506); + const __m256i shuffle_mask_1_epi8 = + _mm256_setr_epi64x(0x0506040503040203, 0x090a080907080607, 0xffffffffffffffff, 0xffffffffffffffff); + __m256i packed_data_0_le_epi8 = _mm256_shuffle_epi8(packed_data_0_epi8, shuffle_mask_0_epi8); + __m256i packed_data_1_le_epi8 = _mm256_shuffle_epi8(packed_data_1_epi8, shuffle_mask_1_epi8); + + // Do logical shift left, then arithmetical shift right in order to sign extend values. + // Note, AVX2 only allows to shift 32bit words using vector of indexes, so we do it twice. + const __m256i shl_0_epi32 = _mm256_setr_epi32(0, 2, 4, 6, 0, 2, 4, 6); + const __m256i shl_1_epi32 = _mm256_setr_epi32(1, 3, 5, 7, 1, 3, 5, 7); + // Mask 16bit words to use only shifted bits. + const __m256i mask_even_epi16 = _mm256_set1_epi32(0x0000ffff); + const __m256i mask_odd_epi16 = _mm256_set1_epi32(0xffff0000); + + __m256i unpacked_data_00_epi16 = _mm256_sllv_epi32(packed_data_0_le_epi8, shl_0_epi32); + __m256i unpacked_data_01_epi16 = _mm256_sllv_epi32(packed_data_0_le_epi8, shl_1_epi32); + __m256i unpacked_data_10_epi16 = _mm256_sllv_epi32(packed_data_1_le_epi8, shl_0_epi32); + __m256i unpacked_data_11_epi16 = _mm256_sllv_epi32(packed_data_1_le_epi8, shl_1_epi32); + unpacked_data_00_epi16 = _mm256_and_si256(unpacked_data_00_epi16, mask_even_epi16); + unpacked_data_01_epi16 = _mm256_and_si256(unpacked_data_01_epi16, mask_odd_epi16); + unpacked_data_10_epi16 = _mm256_and_si256(unpacked_data_10_epi16, mask_even_epi16); + unpacked_data_11_epi16 = _mm256_and_si256(unpacked_data_11_epi16, mask_odd_epi16); + + // Merge two vectors to get vector of shifted 16bit IQ samples, right-shift it to sign-extend values. + __m256i unpacked_data_0_epi16 = _mm256_srai_epi16(_mm256_or_si256(unpacked_data_00_epi16, unpacked_data_01_epi16), 7); + __m256i unpacked_data_1_epi16 = _mm256_srai_epi16(_mm256_or_si256(unpacked_data_10_epi16, unpacked_data_11_epi16), 7); + + // Write results. + _mm256_storeu_si256(reinterpret_cast<__m256i*>(unpacked_iq_data.data()), unpacked_data_0_epi16); + _mm256_storeu_si256(reinterpret_cast<__m256i*>(unpacked_iq_data.subspan(16, 8).data()), unpacked_data_1_epi16); +} /// \brief Unpacks packed IQ samples stored as bytes in big-endian format to an array of 16bit signed values. /// @@ -54,13 +252,25 @@ void pack_prbs_big_endian(span c_prbs, __m256i r0, __m256i /// /// \note The \c unpacked_iq_data parameter should be sized to store 32 output IQ samples: it is 24 IQ samples of one RB /// rounded up to 32-byte boundary required by AVX2 intrinsics. -void unpack_prb_big_endian(span unpacked_iq_data, span packed_data, unsigned iq_width); +void unpack_prb_big_endian(span unpacked_iq_data, span packed_data, unsigned iq_width) +{ + switch (iq_width) { + case 9: + unpack_prb_9b_be(unpacked_iq_data, packed_data); + break; + default: + report_fatal_error("Unsupported bit width"); + } +} /// \brief Checks whether the requested bit width is supported by the AVX2 implementation. /// \param[in] iq_width Requested bit width. /// /// \return True in case packing/unpacking with the requested bit width is supported -bool iq_width_packing_supported(unsigned iq_width); +inline bool iq_width_packing_supported(unsigned iq_width) +{ + return iq_width == 9; +} } // namespace mm256 } // namespace ofh diff --git a/lib/ofh/compression/packing_utils_avx512.cpp b/lib/ofh/compression/packing_utils_avx512.cpp deleted file mode 100644 index 59435f1036..0000000000 --- a/lib/ofh/compression/packing_utils_avx512.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - * - * 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 "packing_utils_avx512.h" -#include "srsran/support/error_handling.h" - -using namespace srsran; -using namespace ofh; - -/// Number of bytes used by 1 packed PRB with IQ samples compressed to 9 bits. -static constexpr unsigned BYTES_PER_PRB_9BIT_COMPRESSION = 27; - -/// Number of bytes used by 1 packed PRB with non-compressed 16 bits IQ samples. -static constexpr unsigned BYTES_PER_PRB_NO_COMPRESSION = 48; - -bool mm512::iq_width_packing_supported(unsigned iq_width) -{ - if ((iq_width == 9) || (iq_width == 16)) { - return true; - } - return false; -} - -/// \brief Packs 16bit IQ values of the PRB using given bit width and big-endian format. -/// -/// \param[out] c_prb Compressed PRB object storing packed bytes. -/// \param[in] reg AVX512 register storing 16bit IQ samples of the PRB. -static void avx512_pack_prb_9b_big_endian(compressed_prb& c_prb, __m512i reg) -{ - static constexpr unsigned bytes_per_lane = BYTES_PER_PRB_9BIT_COMPRESSION / 3; - static constexpr unsigned lane_write_mask = 0x01ff; - - // Input IQ samples need to be shifted in order to align bits before final packing. - // 0: i0 0 0 0 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 <- rotate right by 1 (shift left by 7, swap bytes later) - // 1: 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 0 0 0 0 <- shift left by 6 - // 2: 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 0 0 0 0 <- shift left by 5 - // 3: 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 0 0 <- shift left by 4 - // 4: 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 0 0 <- shift left by 3 - // 5: 0 0 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 <- shift left by 2 - // 6: 0 0 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 <- shift left by 1 - // 7: 0 0 0 0 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 <- no shift - // ... the same pattern in every 128bit lane ... - - const __m512i shiftv_epi16 = - _mm512_set_epi16(0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7); - __m512i iq_shl_epi16 = _mm512_sllv_epi16(reg, shiftv_epi16); - - // Mask 16bit words to keep only 9 shifted bits. - const __m512i mask_epi16 = _mm512_set_epi64(0x01ff03fe07fc0ff8, - 0x1ff03fe07fc0ff80, - 0x01ff03fe07fc0ff8, - 0x1ff03fe07fc0ff80, - 0x01ff03fe07fc0ff8, - 0x1ff03fe07fc0ff80, - 0x01ff03fe07fc0ff8, - 0x1ff03fe07fc0ff80); - iq_shl_epi16 = _mm512_and_si512(iq_shl_epi16, mask_epi16); - - // Shuffle it and create two new vectors that can be OR'ed to produce final result. Temporal vectors look as follows: - // 0 0 0 0 0 0 0 0 | i0 0 0 0 0 0 0 0 | q1 q0 0 0 0 0 0 0 | i2 i1 i0 0 0 0 0 0 | ... - // i8 i7 i6 i5 i4 i3 i2 i1 | 0 q8 q7 q6 q5 q4 q3 q2 | 0 0 i8 i7 i6 i5 i4 i3 | 0 0 0 q8 q7 q6 q5 q4 | ... - __m512i tmp_iq_0_epi8 = _mm512_shuffle_epi8(iq_shl_epi16, - _mm512_setr_epi64(0x0c0a0806040200ff, - 0xffffffffffffff0e, - 0x0c0a0806040200ff, - 0xffffffffffffff0e, - 0x0c0a0806040200ff, - 0xffffffffffffff0e, - 0xffffffffffffffff, - 0xffffffffffffffff)); - __m512i tmp_iq_1_epi8 = _mm512_shuffle_epi8(iq_shl_epi16, - _mm512_setr_epi64(0x0f0d0b0907050301, - 0xffffffffffffffff, - 0x0f0d0b0907050301, - 0xffffffffffffffff, - 0x0f0d0b0907050301, - 0xffffffffffffffff, - 0xffffffffffffffff, - 0xffffffffffffffff)); - - // Perform 'bitwise OR'. - __m512i iq_packed_epi8 = _mm512_or_si512(tmp_iq_0_epi8, tmp_iq_1_epi8); - - // Store first 9 bytes of the first three 128bit lanes of the AVX512 register. - uint8_t* data = c_prb.get_byte_buffer().data(); - _mm_mask_storeu_epi8(data, lane_write_mask, _mm512_extracti64x2_epi64(iq_packed_epi8, 0)); - _mm_mask_storeu_epi8(data + bytes_per_lane, lane_write_mask, _mm512_extracti64x2_epi64(iq_packed_epi8, 1)); - _mm_mask_storeu_epi8(data + bytes_per_lane * 2, lane_write_mask, _mm512_extracti64x2_epi64(iq_packed_epi8, 2)); - - c_prb.set_stored_size(BYTES_PER_PRB_9BIT_COMPRESSION); -} - -/// \brief Packs 16bit IQ values of the PRB using given bit width and big-endian format. -/// -/// \param[out] c_prb Compressed PRB object storing packed bytes. -/// \param[in] reg AVX512 register storing 16bit IQ samples of the PRB. -static void avx512_pack_prb_16b_big_endian(compressed_prb& c_prb, __m512i reg) -{ - static constexpr unsigned write_mask = 0xffffff; - - // Input contains 24 16 bit Iand Q samples. - uint8_t* data = c_prb.get_byte_buffer().data(); - - // Swap bytes to convert from big-endian format and write them directly to the output memory. - const __m512i shuffle_mask_epi8 = _mm512_setr_epi64(0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x1617141512131011, - 0x1e1f1c1d1a1b1819, - 0x2627242522232021, - 0x2e2f2c2d2a2b2829, - 0x3637343532333031, - 0x3e3f3c3d3a3b3839); - - __m512i reg_swp_epi16 = _mm512_shuffle_epi8(reg, shuffle_mask_epi8); - _mm512_mask_storeu_epi16(data, write_mask, reg_swp_epi16); - c_prb.set_stored_size(BYTES_PER_PRB_NO_COMPRESSION); -} - -void mm512::pack_prb_big_endian(compressed_prb& c_prb, __m512i reg, unsigned iq_width) -{ - if (iq_width == 9) { - return avx512_pack_prb_9b_big_endian(c_prb, reg); - } - if (iq_width == 16) { - return avx512_pack_prb_16b_big_endian(c_prb, reg); - } - report_fatal_error("Unsupported bit width"); -} - -/// \brief Unpacks packed 9bit IQ samples stored as bytes in big-endian format to an array of 16bit signed values. -/// -/// \param[out] unpacked_iq_data A span of 16bit integers, corresponding to \c NOF_CARRIERS_PER_RB unpacked IQ pairs. -/// \param[in] packed_data A span of 27 packed bytes. -/// -/// \note The \c unpacked_iq_data parameter should be sized to store 32 output IQ samples: it is 24 IQ samples of one RB -/// rounded up to 64-byte boundary required by AVX512 intrinsics. -static void avx512_unpack_prb_9b_be(span unpacked_iq_data, span packed_data) -{ - // Load input, 27 bytes (fits in one AVX512 register). - __m512i packed_data_epi8 = _mm512_mask_loadu_epi8(_mm512_set1_epi64(0), 0x1fffffff, packed_data.data()); - - // Duplicate input words (it is required since below in the code every byte will be used twice: - // to provide MSB bits of the current IQ sample and LSB bits of the previous IQ sample). - __m512i packed_data_0_epi8 = _mm512_mask_permutexvar_epi64( - _mm512_set1_epi64(0), 0xff, _mm512_setr_epi64(0, 1, 1, 2, 2, 3, 0, 0), packed_data_epi8); - - // Swap bytes for little-endian representation. Each pair of bytes contains all bits of a single I or Q sample. - const __m512i shuffle_mask_epi8 = _mm512_setr_epi64(0x0304020301020001, - 0x0708060705060405, - 0x0405030402030102, - 0x0809070806070506, - 0x0506040503040203, - 0x090a080907080607, - 0xffffffffffffffff, - 0xffffffffffffffff); - - __m512i packed_data_0_le_epi16 = _mm512_shuffle_epi8(packed_data_0_epi8, shuffle_mask_epi8); - - // Shift left to align to 16bit boundary, then shift right to sign-extend values. - const __m512i shl_mask_epi16 = - _mm512_set_epi16(7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0); - __m512i unpacked_data_epi16 = _mm512_srai_epi16(_mm512_sllv_epi16(packed_data_0_le_epi16, shl_mask_epi16), 7); - - // Write results to the output buffer. - _mm512_mask_storeu_epi16(unpacked_iq_data.data(), 0x00ffffff, unpacked_data_epi16); -} - -/// \brief Unpacks packed 16bit IQ samples stored as bytes in big-endian format to an array of 16bit signed values. -/// -/// \param[out] unpacked_iq_data A span of 16bit integers, corresponding to \c NOF_CARRIERS_PER_RB unpacked IQ pairs. -/// \param[in] packed_data A span of 48 packed bytes. -static void avx512_unpack_prb_16b_be(span unpacked_iq_data, span packed_data) -{ - // Load input, 48 bytes (fits in one AVX512 register). - const __mmask32 rw_mask = 0x00ffffff; - __m512i packed_data_epi16 = _mm512_maskz_loadu_epi16(rw_mask, packed_data.data()); - // Swap bytes for Little-endian representation. - const __m512i shuffle_mask_epi8 = _mm512_setr_epi64(0x0607040502030001, - 0x0e0f0c0d0a0b0809, - 0x1617141512131011, - 0x1e1f1c1d1a1b1819, - 0x2627242522232021, - 0x2e2f2c2d2a2b2829, - 0x3637343532333031, - 0x3e3f3c3d3a3b3839); - - __m512i unpacked_data_epi16 = _mm512_shuffle_epi8(packed_data_epi16, shuffle_mask_epi8); - _mm512_mask_storeu_epi16(unpacked_iq_data.data(), rw_mask, unpacked_data_epi16); -} - -void mm512::unpack_prb_big_endian(span unpacked_iq_data, span packed_data, unsigned iq_width) -{ - if (iq_width == 9) { - return avx512_unpack_prb_9b_be(unpacked_iq_data, packed_data); - } - if (iq_width == 16) { - return avx512_unpack_prb_16b_be(unpacked_iq_data, packed_data); - } - report_fatal_error("Unsupported bit width"); -} diff --git a/lib/ofh/compression/packing_utils_avx512.h b/lib/ofh/compression/packing_utils_avx512.h index 619cfa3857..d71229fd57 100644 --- a/lib/ofh/compression/packing_utils_avx512.h +++ b/lib/ofh/compression/packing_utils_avx512.h @@ -24,17 +24,185 @@ #include "avx512_helpers.h" #include "srsran/ofh/compression/compressed_prb.h" +#include "srsran/support/error_handling.h" namespace srsran { namespace ofh { namespace mm512 { +/// \brief Packs 16bit IQ values of the PRB as 9 bit values in big-endian format. +/// +/// \param[out] c_prb Compressed PRB object storing packed bytes. +/// \param[in] reg AVX512 register storing 16bit IQ samples of the PRB. +inline void avx512_pack_prb_9b_big_endian(compressed_prb& c_prb, __m512i reg) +{ + static constexpr unsigned BYTES_PER_PRB_9BIT_COMPRESSION = 27; + static constexpr unsigned bytes_per_lane = BYTES_PER_PRB_9BIT_COMPRESSION / 3; + static constexpr unsigned lane_write_mask = 0x01ff; + + // Input IQ samples need to be shifted in order to align bits before final packing. + // 0: i0 0 0 0 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 <- rotate right by 1 (shift left by 7, swap bytes later) + // 1: 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 0 0 0 0 <- shift left by 6 + // 2: 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 0 0 0 0 <- shift left by 5 + // 3: 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 0 0 <- shift left by 4 + // 4: 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 0 0 <- shift left by 3 + // 5: 0 0 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 <- shift left by 2 + // 6: 0 0 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 <- shift left by 1 + // 7: 0 0 0 0 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 <- no shift + // ... the same pattern in every 128bit lane ... + + const __m512i shiftv_epi16 = + _mm512_set_epi16(0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7); + __m512i iq_shl_epi16 = _mm512_sllv_epi16(reg, shiftv_epi16); + + // Mask 16bit words to keep only 9 shifted bits. + const __m512i mask_epi16 = _mm512_set_epi64(0x01ff03fe07fc0ff8, + 0x1ff03fe07fc0ff80, + 0x01ff03fe07fc0ff8, + 0x1ff03fe07fc0ff80, + 0x01ff03fe07fc0ff8, + 0x1ff03fe07fc0ff80, + 0x01ff03fe07fc0ff8, + 0x1ff03fe07fc0ff80); + iq_shl_epi16 = _mm512_and_si512(iq_shl_epi16, mask_epi16); + + // Shuffle it and create two new vectors that can be OR'ed to produce final result. Temporal vectors look as follows: + // 0 0 0 0 0 0 0 0 | i0 0 0 0 0 0 0 0 | q1 q0 0 0 0 0 0 0 | i2 i1 i0 0 0 0 0 0 | ... + // i8 i7 i6 i5 i4 i3 i2 i1 | 0 q8 q7 q6 q5 q4 q3 q2 | 0 0 i8 i7 i6 i5 i4 i3 | 0 0 0 q8 q7 q6 q5 q4 | ... + __m512i tmp_iq_0_epi8 = _mm512_shuffle_epi8(iq_shl_epi16, + _mm512_setr_epi64(0x0c0a0806040200ff, + 0xffffffffffffff0e, + 0x0c0a0806040200ff, + 0xffffffffffffff0e, + 0x0c0a0806040200ff, + 0xffffffffffffff0e, + 0xffffffffffffffff, + 0xffffffffffffffff)); + __m512i tmp_iq_1_epi8 = _mm512_shuffle_epi8(iq_shl_epi16, + _mm512_setr_epi64(0x0f0d0b0907050301, + 0xffffffffffffffff, + 0x0f0d0b0907050301, + 0xffffffffffffffff, + 0x0f0d0b0907050301, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff)); + + // Perform 'bitwise OR'. + __m512i iq_packed_epi8 = _mm512_or_si512(tmp_iq_0_epi8, tmp_iq_1_epi8); + + // Store first 9 bytes of the first three 128bit lanes of the AVX512 register. + uint8_t* data = c_prb.get_byte_buffer().data(); + _mm_mask_storeu_epi8(data, lane_write_mask, _mm512_extracti64x2_epi64(iq_packed_epi8, 0)); + _mm_mask_storeu_epi8(data + bytes_per_lane, lane_write_mask, _mm512_extracti64x2_epi64(iq_packed_epi8, 1)); + _mm_mask_storeu_epi8(data + bytes_per_lane * 2, lane_write_mask, _mm512_extracti64x2_epi64(iq_packed_epi8, 2)); + + c_prb.set_stored_size(BYTES_PER_PRB_9BIT_COMPRESSION); +} + +/// \brief Packs 16bit IQ values of the PRB using big-endian format. +/// +/// \param[out] c_prb Compressed PRB object storing packed bytes. +/// \param[in] reg AVX512 register storing 16bit IQ samples of the PRB. +inline void avx512_pack_prb_16b_big_endian(compressed_prb& c_prb, __m512i reg) +{ + static constexpr unsigned BYTES_PER_PRB_NO_COMPRESSION = 48; + static constexpr unsigned write_mask = 0xffffff; + + // Input contains 24 16 bit Iand Q samples. + uint8_t* data = c_prb.get_byte_buffer().data(); + + // Swap bytes to convert from big-endian format and write them directly to the output memory. + const __m512i shuffle_mask_epi8 = _mm512_setr_epi64(0x0607040502030001, + 0x0e0f0c0d0a0b0809, + 0x1617141512131011, + 0x1e1f1c1d1a1b1819, + 0x2627242522232021, + 0x2e2f2c2d2a2b2829, + 0x3637343532333031, + 0x3e3f3c3d3a3b3839); + + __m512i reg_swp_epi16 = _mm512_shuffle_epi8(reg, shuffle_mask_epi8); + _mm512_mask_storeu_epi16(data, write_mask, reg_swp_epi16); + c_prb.set_stored_size(BYTES_PER_PRB_NO_COMPRESSION); +} + /// \brief Packs 16bit IQ values of a resource block using the specified width and big-endian format. /// /// \param[out] c_prb Output PRB storing compressed packed bytes. /// \param[in] reg AVX512 register storing 16bit IQ pairs of the PRB. /// \param[in] iq_width Bit width of the resulting packed IQ samples. -void pack_prb_big_endian(ofh::compressed_prb& c_prb, __m512i reg, unsigned iq_width); +inline void pack_prb_big_endian(ofh::compressed_prb& c_prb, __m512i reg, unsigned iq_width) +{ + if (iq_width == 9) { + return avx512_pack_prb_9b_big_endian(c_prb, reg); + } + if (iq_width == 16) { + return avx512_pack_prb_16b_big_endian(c_prb, reg); + } + report_fatal_error("Unsupported bit width"); +} + +/// \brief Unpacks packed 9bit IQ samples stored as bytes in big-endian format to an array of 16bit signed values. +/// +/// \param[out] unpacked_iq_data A span of 16bit integers, corresponding to \c NOF_CARRIERS_PER_RB unpacked IQ pairs. +/// \param[in] packed_data A span of 27 packed bytes. +/// +/// \note The \c unpacked_iq_data parameter should be sized to store 32 output IQ samples: it is 24 IQ samples of one RB +/// rounded up to 64-byte boundary required by AVX512 intrinsics. +inline void avx512_unpack_prb_9b_be(span unpacked_iq_data, span packed_data) +{ + // Load input, 27 bytes (fits in one AVX512 register). + __m512i packed_data_epi8 = _mm512_mask_loadu_epi8(_mm512_set1_epi64(0), 0x1fffffff, packed_data.data()); + + // Duplicate input words (it is required since below in the code every byte will be used twice: + // to provide MSB bits of the current IQ sample and LSB bits of the previous IQ sample). + __m512i packed_data_0_epi8 = _mm512_mask_permutexvar_epi64( + _mm512_set1_epi64(0), 0xff, _mm512_setr_epi64(0, 1, 1, 2, 2, 3, 0, 0), packed_data_epi8); + + // Swap bytes for little-endian representation. Each pair of bytes contains all bits of a single I or Q sample. + const __m512i shuffle_mask_epi8 = _mm512_setr_epi64(0x0304020301020001, + 0x0708060705060405, + 0x0405030402030102, + 0x0809070806070506, + 0x0506040503040203, + 0x090a080907080607, + 0xffffffffffffffff, + 0xffffffffffffffff); + + __m512i packed_data_0_le_epi16 = _mm512_shuffle_epi8(packed_data_0_epi8, shuffle_mask_epi8); + + // Shift left to align to 16bit boundary, then shift right to sign-extend values. + const __m512i shl_mask_epi16 = + _mm512_set_epi16(7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0); + __m512i unpacked_data_epi16 = _mm512_srai_epi16(_mm512_sllv_epi16(packed_data_0_le_epi16, shl_mask_epi16), 7); + + // Write results to the output buffer. + _mm512_mask_storeu_epi16(unpacked_iq_data.data(), 0x00ffffff, unpacked_data_epi16); +} + +/// \brief Unpacks packed 16bit IQ samples stored as bytes in big-endian format to an array of 16bit signed values. +/// +/// \param[out] unpacked_iq_data A span of 16bit integers, corresponding to \c NOF_CARRIERS_PER_RB unpacked IQ pairs. +/// \param[in] packed_data A span of 48 packed bytes. +inline void avx512_unpack_prb_16b_be(span unpacked_iq_data, span packed_data) +{ + // Load input, 48 bytes (fits in one AVX512 register). + const __mmask32 rw_mask = 0x00ffffff; + __m512i packed_data_epi16 = _mm512_maskz_loadu_epi16(rw_mask, packed_data.data()); + // Swap bytes for Little-endian representation. + const __m512i shuffle_mask_epi8 = _mm512_setr_epi64(0x0607040502030001, + 0x0e0f0c0d0a0b0809, + 0x1617141512131011, + 0x1e1f1c1d1a1b1819, + 0x2627242522232021, + 0x2e2f2c2d2a2b2829, + 0x3637343532333031, + 0x3e3f3c3d3a3b3839); + + __m512i unpacked_data_epi16 = _mm512_shuffle_epi8(packed_data_epi16, shuffle_mask_epi8); + _mm512_mask_storeu_epi16(unpacked_iq_data.data(), rw_mask, unpacked_data_epi16); +} /// \brief Unpacks packed IQ samples stored as bytes in big-endian format to an array of 16bit signed values. /// @@ -44,13 +212,28 @@ void pack_prb_big_endian(ofh::compressed_prb& c_prb, __m512i reg, unsigned iq_wi /// /// \note The \c unpacked_iq_data parameter should be sized to store 32 output IQ samples: it is 24 IQ samples of one RB /// rounded up to 64-byte boundary required by AVX512 intrinsics. -void unpack_prb_big_endian(span unpacked_iq_data, span packed_data, unsigned iq_width); +inline void unpack_prb_big_endian(span unpacked_iq_data, span packed_data, unsigned iq_width) +{ + if (iq_width == 9) { + return avx512_unpack_prb_9b_be(unpacked_iq_data, packed_data); + } + if (iq_width == 16) { + return avx512_unpack_prb_16b_be(unpacked_iq_data, packed_data); + } + report_fatal_error("Unsupported bit width"); +} /// \brief Checks whether the requested bit width is supported by the AVX512 implementation. /// \param[in] iq_width Requested bit width. /// /// \return True in case packing/unpacking with the requested bit width is supported. -bool iq_width_packing_supported(unsigned iq_width); +inline bool iq_width_packing_supported(unsigned iq_width) +{ + if ((iq_width == 9) || (iq_width == 16)) { + return true; + } + return false; +} } // namespace mm512 } // namespace ofh diff --git a/lib/ofh/compression/packing_utils_neon.cpp b/lib/ofh/compression/packing_utils_neon.cpp deleted file mode 100644 index 639309430c..0000000000 --- a/lib/ofh/compression/packing_utils_neon.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* - * - * 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 "packing_utils_neon.h" -#include "srsran/support/error_handling.h" - -using namespace srsran; -using namespace ofh; - -/// Number of bytes used by 1 packed PRB with IQ samples compressed to 9 bits. -static constexpr unsigned BYTES_PER_PRB_9BIT_COMPRESSION = 27; - -bool neon::iq_width_packing_supported(unsigned iq_width) -{ - return iq_width == 9; -} - -/// \brief Reads eight 16bit IQ values from input NEON register and packs them to the first 72 bits of the output NEON -/// register in big-endian format, thus occupying 9 output bytes. -/// -/// \param[in] regs NEON register storing 16bit IQ samples of a resource block. -/// \return NEON register storing 9 packed bytes. -static uint8x16_t pack_neon_register_9b_big_endian(int16x8_t reg) -{ - // Input IQ samples need to be shifted in order to align bits before final packing. - // 0: i0 0 0 0 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 <- rotate right by 1 (shift left by 7, swap bytes later) - // 1: 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 0 0 0 0 <- shift left by 6 - // 2: 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 0 0 0 0 <- shift left by 5 - // 3: 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 0 0 <- shift left by 4 - // 4: 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 0 0 <- shift left by 3 - // 5: 0 0 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 <- shift left by 2 - // 6: 0 0 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 <- shift left by 1 - // 7: 0 0 0 0 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 <- no shift - - // Shift data according to the mask described above. - const int16x8_t shift_mask_s16 = vcombine_s16(vcreate_s16(0x0004000500060007), vcreate_s16(0x0000000100020003)); - int16x8_t iq_shl_s16 = vshlq_s16(reg, shift_mask_s16); - - // Mask 16bit words to keep only 9 shifted bits. - const int16x8_t mask_s16 = vcombine_s16(vcreate_s16(0x1ff03fe07fc0ff80), vcreate_s16(0x01ff03fe07fc0ff8)); - iq_shl_s16 = vandq_s16(iq_shl_s16, mask_s16); - - // Shuffle it and create two new vectors that can be OR'ed to produce final result. Temporal vectors look as follows: - // 0 0 0 0 0 0 0 0 | i0 0 0 0 0 0 0 0 | q1 q0 0 0 0 0 0 0 | i2 i1 i0 0 0 0 0 0 | ... - // i8 i7 i6 i5 i4 i3 i2 i1 | 0 q8 q7 q6 q5 q4 q3 q2 | 0 0 i8 i7 i6 i5 i4 i3 | 0 0 0 q8 q7 q6 q5 q4 | ... - int8x16_t iq_shl_s8 = vreinterpretq_s8_s16(iq_shl_s16); - int8x16_t tmp_iq_0_s8 = - vqtbl1q_s8(iq_shl_s8, vcombine_u8(vcreate_u8(0x0c0a0806040200ff), vcreate_u8(0xffffffffffffff0e))); - int8x16_t tmp_iq_1_s8 = - vqtbl1q_s8(iq_shl_s8, vcombine_u8(vcreate_u8(0x0f0d0b0907050301), vcreate_u8(0xffffffffffffffff))); - - // Perform 'bitwise OR'. - return vorrq_u8(vreinterpretq_u8_s8(tmp_iq_0_s8), vreinterpretq_u8_s8(tmp_iq_1_s8)); -} - -/// \brief Packs 16bit IQ values of the PRB using given bit width and big-endian format. -/// -/// \param[out] c_prb Compressed PRB object storing packed bytes. -/// \param[in] regs NEON registers storing 16bit IQ samples of the PRB. -/// -/// \note Each of the input registers stores four unique REs. -static void pack_prb_9b_big_endian(compressed_prb& c_prb, int16x8x3_t regs) -{ - static constexpr unsigned bytes_per_half_reg = 8; - - // Pack input registers. - uint8x16_t res_packed_bytes_0_u8 = pack_neon_register_9b_big_endian(regs.val[0]); - uint8x16_t res_packed_bytes_1_u8 = pack_neon_register_9b_big_endian(regs.val[1]); - uint8x16_t res_packed_bytes_2_u8 = pack_neon_register_9b_big_endian(regs.val[2]); - - uint8_t* data = c_prb.get_byte_buffer().data(); - - // Store first 9 bytes of every register storing packed bytes. - vst1_u64(reinterpret_cast(data), vreinterpret_u64_u8(vget_low_u8(res_packed_bytes_0_u8))); - vst1q_lane_u8(data + bytes_per_half_reg, res_packed_bytes_0_u8, bytes_per_half_reg); - data += bytes_per_half_reg + 1; - - vst1_u64(reinterpret_cast(data), vreinterpret_u64_u8(vget_low_u8(res_packed_bytes_1_u8))); - vst1q_lane_u8(data + bytes_per_half_reg, res_packed_bytes_1_u8, bytes_per_half_reg); - data += bytes_per_half_reg + 1; - - vst1_u64(reinterpret_cast(data), vreinterpret_u64_u8(vget_low_u8(res_packed_bytes_2_u8))); - vst1q_lane_u8(data + bytes_per_half_reg, res_packed_bytes_2_u8, bytes_per_half_reg); - - c_prb.set_stored_size(BYTES_PER_PRB_9BIT_COMPRESSION); -} - -void neon::pack_prb_big_endian(ofh::compressed_prb& c_prb, int16x8x3_t regs, unsigned iq_width) -{ - if (iq_width == 9) { - return pack_prb_9b_big_endian(c_prb, regs); - } - report_fatal_error("Unsupported bit width"); -} - -/// \brief Unpacks packed 9bit IQ samples stored as bytes in big-endian format to an array of 16bit signed values. -/// -/// \param[out] unpacked_iq_data A sequence of 24 integers, corresponding to \c NOF_CARRIERS_PER_RB unpacked IQ pairs. -/// \param[in] packed_data A sequence of \c BYTES_PER_PRB_9BIT_COMPRESSION packed bytes. -static void unpack_prb_9b_big_endian(span unpacked_iq_data, span packed_data) -{ - // Load input (we need two NEON register to load 27 bytes). - uint8x16x2_t packed_vec_u8x2; - packed_vec_u8x2.val[0] = vld1q_u8(packed_data.data()); - packed_vec_u8x2.val[1] = vld1q_u8(packed_data.data() + 16); - - // Duplicate input words (it is required since below in the code every byte will be used twice: - // to provide MSB bits of the current IQ sample and LSB bits of the previous IQ sample). - uint8x16_t tmp_packed_0_u8 = - vqtbl2q_u8(packed_vec_u8x2, vcombine_u8(vcreate_u8(0x0304020301020001), vcreate_u8(0x0708060705060405))); - uint8x16_t tmp_packed_1_u8 = - vqtbl2q_u8(packed_vec_u8x2, vcombine_u8(vcreate_u8(0x0c0d0b0c0a0b090a), vcreate_u8(0x10110f100e0f0d0e))); - uint8x16_t tmp_packed_2_u8 = - vqtbl2q_u8(packed_vec_u8x2, vcombine_u8(vcreate_u8(0x1516141513141213), vcreate_u8(0x191a181917181617))); - - // Shift left to align to 16bit boundary. - const int16x8_t shl_mask_s16 = vcombine_s16(vcreate_s16(0x0003000200010000), vcreate_s16(0x0007000600050004)); - uint16x8_t shl_tmp_packed_0_u8 = vshlq_u16(vreinterpretq_u16_u8(tmp_packed_0_u8), shl_mask_s16); - uint16x8_t shl_tmp_packed_1_u8 = vshlq_u16(vreinterpretq_u16_u8(tmp_packed_1_u8), shl_mask_s16); - uint16x8_t shl_tmp_packed_2_u8 = vshlq_u16(vreinterpretq_u16_u8(tmp_packed_2_u8), shl_mask_s16); - - // Arithmetically shift right by 7 positions to put bits of interest into LSB positions while preserving the sign. - int16x8_t unpacked_data_0_s16 = vshrq_n_s16(vreinterpretq_s16_u16(shl_tmp_packed_0_u8), 7); - int16x8_t unpacked_data_1_s16 = vshrq_n_s16(vreinterpretq_s16_u16(shl_tmp_packed_1_u8), 7); - int16x8_t unpacked_data_2_s16 = vshrq_n_s16(vreinterpretq_s16_u16(shl_tmp_packed_2_u8), 7); - - // Write results to the output buffer. - vst1q_s16(unpacked_iq_data.data(), unpacked_data_0_s16); - vst1q_s16(unpacked_iq_data.data() + 8, unpacked_data_1_s16); - vst1q_s16(unpacked_iq_data.data() + 16, unpacked_data_2_s16); -} - -void neon::unpack_prb_big_endian(span unpacked_iq_data, span packed_data, unsigned iq_width) -{ - if (iq_width == 9) { - return unpack_prb_9b_big_endian(unpacked_iq_data, packed_data); - } - report_fatal_error("Unsupported bit width"); -} diff --git a/lib/ofh/compression/packing_utils_neon.h b/lib/ofh/compression/packing_utils_neon.h index 79bd1f19aa..ae0bed7652 100644 --- a/lib/ofh/compression/packing_utils_neon.h +++ b/lib/ofh/compression/packing_utils_neon.h @@ -24,30 +24,156 @@ #include "neon_helpers.h" #include "srsran/ofh/compression/compressed_prb.h" +#include "srsran/support/error_handling.h" namespace srsran { namespace ofh { namespace neon { +/// \brief Reads eight 16bit IQ values from input NEON register and packs them to the first 72 bits of the output NEON +/// register in big-endian format, thus occupying 9 output bytes. +/// +/// \param[in] regs NEON register storing 16bit IQ samples of a resource block. +/// \return NEON register storing 9 packed bytes. +inline uint8x16_t pack_neon_register_9b_big_endian(int16x8_t reg) +{ + // Input IQ samples need to be shifted in order to align bits before final packing. + // 0: i0 0 0 0 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 <- rotate right by 1 (shift left by 7, swap bytes later) + // 1: 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 0 0 0 0 <- shift left by 6 + // 2: 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 0 0 0 0 <- shift left by 5 + // 3: 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 0 0 <- shift left by 4 + // 4: 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 0 0 <- shift left by 3 + // 5: 0 0 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 0 0 <- shift left by 2 + // 6: 0 0 0 0 0 0 i8 i7 i6 i5 i4 i3 i2 i1 i0 0 <- shift left by 1 + // 7: 0 0 0 0 0 0 0 q8 q7 q6 q5 q4 q3 q2 q1 q0 <- no shift + + // Shift data according to the mask described above. + const int16x8_t shift_mask_s16 = vcombine_s16(vcreate_s16(0x0004000500060007), vcreate_s16(0x0000000100020003)); + int16x8_t iq_shl_s16 = vshlq_s16(reg, shift_mask_s16); + + // Mask 16bit words to keep only 9 shifted bits. + const int16x8_t mask_s16 = vcombine_s16(vcreate_s16(0x1ff03fe07fc0ff80), vcreate_s16(0x01ff03fe07fc0ff8)); + iq_shl_s16 = vandq_s16(iq_shl_s16, mask_s16); + + // Shuffle it and create two new vectors that can be OR'ed to produce final result. Temporal vectors look as follows: + // 0 0 0 0 0 0 0 0 | i0 0 0 0 0 0 0 0 | q1 q0 0 0 0 0 0 0 | i2 i1 i0 0 0 0 0 0 | ... + // i8 i7 i6 i5 i4 i3 i2 i1 | 0 q8 q7 q6 q5 q4 q3 q2 | 0 0 i8 i7 i6 i5 i4 i3 | 0 0 0 q8 q7 q6 q5 q4 | ... + int8x16_t iq_shl_s8 = vreinterpretq_s8_s16(iq_shl_s16); + int8x16_t tmp_iq_0_s8 = + vqtbl1q_s8(iq_shl_s8, vcombine_u8(vcreate_u8(0x0c0a0806040200ff), vcreate_u8(0xffffffffffffff0e))); + int8x16_t tmp_iq_1_s8 = + vqtbl1q_s8(iq_shl_s8, vcombine_u8(vcreate_u8(0x0f0d0b0907050301), vcreate_u8(0xffffffffffffffff))); + + // Perform 'bitwise OR'. + return vorrq_u8(vreinterpretq_u8_s8(tmp_iq_0_s8), vreinterpretq_u8_s8(tmp_iq_1_s8)); +} + +/// \brief Packs 16bit IQ values of the PRB using given bit width and big-endian format. +/// +/// \param[out] c_prb Compressed PRB object storing packed bytes. +/// \param[in] regs NEON registers storing 16bit IQ samples of the PRB. +/// +/// \note Each of the input registers stores four unique REs. +inline void pack_prb_9b_big_endian(compressed_prb& c_prb, int16x8x3_t regs) +{ + /// Number of bytes used by 1 packed PRB with IQ samples compressed to 9 bits. + static constexpr unsigned BYTES_PER_PRB_9BIT_COMPRESSION = 27; + + static constexpr unsigned bytes_per_half_reg = 8; + + // Pack input registers. + uint8x16_t res_packed_bytes_0_u8 = pack_neon_register_9b_big_endian(regs.val[0]); + uint8x16_t res_packed_bytes_1_u8 = pack_neon_register_9b_big_endian(regs.val[1]); + uint8x16_t res_packed_bytes_2_u8 = pack_neon_register_9b_big_endian(regs.val[2]); + + uint8_t* data = c_prb.get_byte_buffer().data(); + + // Store first 9 bytes of every register storing packed bytes. + vst1_u64(reinterpret_cast(data), vreinterpret_u64_u8(vget_low_u8(res_packed_bytes_0_u8))); + vst1q_lane_u8(data + bytes_per_half_reg, res_packed_bytes_0_u8, bytes_per_half_reg); + data += bytes_per_half_reg + 1; + + vst1_u64(reinterpret_cast(data), vreinterpret_u64_u8(vget_low_u8(res_packed_bytes_1_u8))); + vst1q_lane_u8(data + bytes_per_half_reg, res_packed_bytes_1_u8, bytes_per_half_reg); + data += bytes_per_half_reg + 1; + + vst1_u64(reinterpret_cast(data), vreinterpret_u64_u8(vget_low_u8(res_packed_bytes_2_u8))); + vst1q_lane_u8(data + bytes_per_half_reg, res_packed_bytes_2_u8, bytes_per_half_reg); + + c_prb.set_stored_size(BYTES_PER_PRB_9BIT_COMPRESSION); +} + /// \brief Packs 16bit IQ values of a resource block using the specified width and big-endian format. /// /// \param[out] c_prb Output PRB storing compressed packed bytes. /// \param[in] reg Vector of three NEON registers storing 16bit IQ pairs of the PRB. /// \param[in] iq_width Bit width of the resulting packed IQ samples. -void pack_prb_big_endian(ofh::compressed_prb& c_prb, int16x8x3_t regs, unsigned iq_width); +void pack_prb_big_endian(ofh::compressed_prb& c_prb, int16x8x3_t regs, unsigned iq_width) +{ + if (iq_width == 9) { + return pack_prb_9b_big_endian(c_prb, regs); + } + report_fatal_error("Unsupported bit width"); +} + +/// \brief Unpacks packed 9bit IQ samples stored as bytes in big-endian format to an array of 16bit signed values. +/// +/// \param[out] unpacked_iq_data A sequence of 24 integers, corresponding to \c NOF_CARRIERS_PER_RB unpacked IQ pairs. +/// \param[in] packed_data A sequence of 27 packed bytes. +inline void unpack_prb_9b_big_endian(span unpacked_iq_data, span packed_data) +{ + // Load input (we need two NEON register to load 27 bytes). + uint8x16x2_t packed_vec_u8x2; + packed_vec_u8x2.val[0] = vld1q_u8(packed_data.data()); + packed_vec_u8x2.val[1] = vld1q_u8(packed_data.data() + 16); + + // Duplicate input words (it is required since below in the code every byte will be used twice: + // to provide MSB bits of the current IQ sample and LSB bits of the previous IQ sample). + uint8x16_t tmp_packed_0_u8 = + vqtbl2q_u8(packed_vec_u8x2, vcombine_u8(vcreate_u8(0x0304020301020001), vcreate_u8(0x0708060705060405))); + uint8x16_t tmp_packed_1_u8 = + vqtbl2q_u8(packed_vec_u8x2, vcombine_u8(vcreate_u8(0x0c0d0b0c0a0b090a), vcreate_u8(0x10110f100e0f0d0e))); + uint8x16_t tmp_packed_2_u8 = + vqtbl2q_u8(packed_vec_u8x2, vcombine_u8(vcreate_u8(0x1516141513141213), vcreate_u8(0x191a181917181617))); + + // Shift left to align to 16bit boundary. + const int16x8_t shl_mask_s16 = vcombine_s16(vcreate_s16(0x0003000200010000), vcreate_s16(0x0007000600050004)); + uint16x8_t shl_tmp_packed_0_u8 = vshlq_u16(vreinterpretq_u16_u8(tmp_packed_0_u8), shl_mask_s16); + uint16x8_t shl_tmp_packed_1_u8 = vshlq_u16(vreinterpretq_u16_u8(tmp_packed_1_u8), shl_mask_s16); + uint16x8_t shl_tmp_packed_2_u8 = vshlq_u16(vreinterpretq_u16_u8(tmp_packed_2_u8), shl_mask_s16); + + // Arithmetically shift right by 7 positions to put bits of interest into LSB positions while preserving the sign. + int16x8_t unpacked_data_0_s16 = vshrq_n_s16(vreinterpretq_s16_u16(shl_tmp_packed_0_u8), 7); + int16x8_t unpacked_data_1_s16 = vshrq_n_s16(vreinterpretq_s16_u16(shl_tmp_packed_1_u8), 7); + int16x8_t unpacked_data_2_s16 = vshrq_n_s16(vreinterpretq_s16_u16(shl_tmp_packed_2_u8), 7); + + // Write results to the output buffer. + vst1q_s16(unpacked_iq_data.data(), unpacked_data_0_s16); + vst1q_s16(unpacked_iq_data.data() + 8, unpacked_data_1_s16); + vst1q_s16(unpacked_iq_data.data() + 16, unpacked_data_2_s16); +} /// \brief Unpacks packed IQ samples stored as bytes in big-endian format to an array of 16bit signed values. /// /// \param[out] unpacked_iq_data A sequence of 24 integers, corresponding to \c NOF_CARRIERS_PER_RB unpacked IQ pairs. /// \param[in] packed_data A sequence of input packed bytes. /// \param[in] iq_width Bit width of the packed IQ samples. -void unpack_prb_big_endian(span unpacked_iq_data, span packed_data, unsigned iq_width); +inline void unpack_prb_big_endian(span unpacked_iq_data, span packed_data, unsigned iq_width) +{ + if (iq_width == 9) { + return unpack_prb_9b_big_endian(unpacked_iq_data, packed_data); + } + report_fatal_error("Unsupported bit width"); +} /// \brief Checks whether the requested bit width is supported by the NEON implementation. /// \param[in] iq_width Requested bit width. /// /// \return True in case packing/unpacking with the requested bit width is supported. -bool iq_width_packing_supported(unsigned iq_width); +inline bool iq_width_packing_supported(unsigned iq_width) +{ + return iq_width == 9; +} } // namespace neon } // namespace ofh diff --git a/lib/ofh/ofh_factories.cpp b/lib/ofh/ofh_factories.cpp index 0e60db0fbd..3d3f4c83c6 100644 --- a/lib/ofh/ofh_factories.cpp +++ b/lib/ofh/ofh_factories.cpp @@ -73,23 +73,22 @@ static transmitter_config generate_transmitter_config(const sector_configuration { transmitter_config tx_config; - tx_config.sector = sector_cfg.sector_id; - tx_config.bw = sector_cfg.bw; - tx_config.scs = sector_cfg.scs; - tx_config.cp = sector_cfg.cp; - tx_config.dl_eaxc = sector_cfg.dl_eaxc; - tx_config.ul_eaxc = sector_cfg.ul_eaxc; - tx_config.prach_eaxc = sector_cfg.prach_eaxc; - tx_config.is_prach_cp_enabled = sector_cfg.is_prach_control_plane_enabled; - tx_config.mac_dst_address = sector_cfg.mac_dst_address; - tx_config.mac_src_address = sector_cfg.mac_src_address; - tx_config.tci = sector_cfg.tci; - tx_config.interface = sector_cfg.interface; - tx_config.is_promiscuous_mode_enabled = sector_cfg.is_promiscuous_mode_enabled; - tx_config.mtu_size = sector_cfg.mtu_size; - tx_config.ru_working_bw = sector_cfg.ru_operating_bw; - tx_config.symbol_handler_cfg = { - sector_cfg.tx_window_timing_params, get_nsymb_per_slot(sector_cfg.cp), sector_cfg.scs}; + tx_config.sector = sector_cfg.sector_id; + tx_config.bw = sector_cfg.bw; + tx_config.scs = sector_cfg.scs; + tx_config.cp = sector_cfg.cp; + tx_config.dl_eaxc = sector_cfg.dl_eaxc; + tx_config.ul_eaxc = sector_cfg.ul_eaxc; + tx_config.prach_eaxc = sector_cfg.prach_eaxc; + tx_config.is_prach_cp_enabled = sector_cfg.is_prach_control_plane_enabled; + tx_config.mac_dst_address = sector_cfg.mac_dst_address; + tx_config.mac_src_address = sector_cfg.mac_src_address; + tx_config.tci = sector_cfg.tci; + tx_config.interface = sector_cfg.interface; + tx_config.is_promiscuous_mode_enabled = sector_cfg.is_promiscuous_mode_enabled; + tx_config.mtu_size = sector_cfg.mtu_size; + tx_config.ru_working_bw = sector_cfg.ru_operating_bw; + tx_config.tx_timing_params = sector_cfg.tx_window_timing_params; tx_config.dl_compr_params = sector_cfg.dl_compression_params; tx_config.ul_compr_params = sector_cfg.ul_compression_params; tx_config.prach_compr_params = sector_cfg.prach_compression_params; diff --git a/lib/ofh/receiver/ofh_receiver_impl.cpp b/lib/ofh/receiver/ofh_receiver_impl.cpp index 3a76f2a8af..bdb212f88e 100644 --- a/lib/ofh/receiver/ofh_receiver_impl.cpp +++ b/lib/ofh/receiver/ofh_receiver_impl.cpp @@ -76,9 +76,9 @@ receiver_impl::receiver_impl(const receiver_config& config, receiver_impl_depend { } -ota_symbol_boundary_notifier& receiver_impl::get_ota_symbol_boundary_notifier() +ota_symbol_boundary_notifier* receiver_impl::get_ota_symbol_boundary_notifier() { - return window_checker; + return window_checker.disabled() ? nullptr : &window_checker; } controller& receiver_impl::get_controller() diff --git a/lib/ofh/receiver/ofh_receiver_impl.h b/lib/ofh/receiver/ofh_receiver_impl.h index 5a30500a84..1a24bea3a7 100644 --- a/lib/ofh/receiver/ofh_receiver_impl.h +++ b/lib/ofh/receiver/ofh_receiver_impl.h @@ -61,7 +61,7 @@ class receiver_impl : public receiver receiver_impl(const receiver_config& config, receiver_impl_dependencies&& dependencies); // See interface for documentation. - ota_symbol_boundary_notifier& get_ota_symbol_boundary_notifier() override; + ota_symbol_boundary_notifier* get_ota_symbol_boundary_notifier() override; // See interface for documentation. controller& get_controller() override; diff --git a/lib/ofh/receiver/ofh_rx_window_checker.cpp b/lib/ofh/receiver/ofh_rx_window_checker.cpp index 6183da0c7b..ecff6330ee 100644 --- a/lib/ofh/receiver/ofh_rx_window_checker.cpp +++ b/lib/ofh/receiver/ofh_rx_window_checker.cpp @@ -28,10 +28,11 @@ using namespace ofh; static constexpr unsigned OFH_MAX_NOF_SFN = 256U; rx_window_checker::rx_window_checker(srslog::basic_logger& logger_, - const du_rx_window_timing_parameters& params, + const rx_window_timing_parameters& params, std::chrono::duration symbol_duration) : - timing_parameters(params, symbol_duration), + timing_parameters(params), nof_symbols_in_one_second(std::ceil(std::chrono::seconds(1) / symbol_duration)), + is_disabled(!is_log_enabled(logger_)), nof_symbols(0), statistics(logger_) { @@ -60,7 +61,7 @@ static int calculate_slot_symbol_point_distance(slot_symbol_point lhs, slot_symb get_nof_slots_per_subframe(to_subcarrier_spacing(rhs.get_numerology())) * rhs.get_nof_symbols(); - int a = static_cast(lhs.system_slot()) - static_cast(rhs.system_slot()); + int a = static_cast(lhs.to_uint()) - static_cast(rhs.to_uint()); if (a >= nof_symbols_per_slot_wrap / 2) { return a - nof_symbols_per_slot_wrap; } @@ -72,18 +73,26 @@ static int calculate_slot_symbol_point_distance(slot_symbol_point lhs, slot_symb void rx_window_checker::on_new_symbol(slot_symbol_point symbol_point) { + if (SRSRAN_LIKELY(is_disabled)) { + return; + } + // Build a new slot symbol point that manages that the SFN values in OFH is 1 byte. slot_symbol_point ota_symbol_point = calculate_ofh_slot_symbol_point(symbol_point); // Update the stored slot symbol point as system value. - count_val.store(ota_symbol_point.system_slot(), std::memory_order_release); + count_val.store(ota_symbol_point.to_uint(), std::memory_order_release); // Print the statistics. print_statistics(); } -bool rx_window_checker::update_rx_window_statistics(slot_symbol_point symbol_point) +void rx_window_checker::update_rx_window_statistics(slot_symbol_point symbol_point) { + if (SRSRAN_LIKELY(is_disabled)) { + return; + } + // Store the ota symbol point to use the same value for the early and late points. slot_symbol_point ota_point( symbol_point.get_numerology(), count_val.load(std::memory_order_acquire), symbol_point.get_nof_symbols()); @@ -92,23 +101,19 @@ bool rx_window_checker::update_rx_window_statistics(slot_symbol_point symbol_poi int diff = calculate_slot_symbol_point_distance(ota_point, symbol_point); // Late detected. - if (diff > timing_parameters.sym_end) { + if (diff > static_cast(timing_parameters.sym_end)) { statistics.increment_late_counter(); - - return false; + return; } // Early detected. - if (diff < timing_parameters.sym_start) { + if (diff < static_cast(timing_parameters.sym_start)) { statistics.increment_early_counter(); - - return false; + return; } // On time detected. statistics.increment_on_time_counter(); - - return true; } void rx_window_checker::print_statistics() diff --git a/lib/ofh/receiver/ofh_rx_window_checker.h b/lib/ofh/receiver/ofh_rx_window_checker.h index 3d951075ca..55cc583e30 100644 --- a/lib/ofh/receiver/ofh_rx_window_checker.h +++ b/lib/ofh/receiver/ofh_rx_window_checker.h @@ -65,32 +65,26 @@ class rx_window_checker : public ota_symbol_boundary_notifier uint64_t nof_late_messages() const { return late_counter.load(std::memory_order_relaxed); } }; - /// Reception window timing parameters. - struct rx_timing_parameters { - /// Offset from the current OTA symbol to the first symbol at which UL User-Plane message can be received within its - /// reception window. Must be calculated based on \c Ta4_min parameter. - int sym_start; - /// Offset from the current OTA symbol to the last symbol at which UL User-Plane message can be received within its - /// reception window. Must be calculated based on \c Ta4_max parameter. - int sym_end; - - rx_timing_parameters(const du_rx_window_timing_parameters& params, - std::chrono::duration symbol_duration) : - sym_start(std::ceil(params.Ta4_min / symbol_duration)), sym_end(std::floor(params.Ta4_max / symbol_duration)) - { - } - }; + const rx_window_timing_parameters timing_parameters; + const unsigned nof_symbols_in_one_second; + const bool is_disabled; + unsigned nof_symbols; + rx_window_checker_statistics statistics; + std::atomic count_val; public: rx_window_checker(srslog::basic_logger& logger_, - const du_rx_window_timing_parameters& params, + const rx_window_timing_parameters& params, std::chrono::duration symbol_duration); // See interface for documentation. void on_new_symbol(slot_symbol_point symbol_point) override; - /// Returns true if the given symbol point is within the reception window, otherwise false. - bool update_rx_window_statistics(slot_symbol_point symbol_point); + /// Returns true if the Rx window checker is disabled, otherwise returns false. + bool disabled() const { return is_disabled; } + + /// Updates the Rx window statistics. + void update_rx_window_statistics(slot_symbol_point symbol_point); /// Getters to the number of messages. uint64_t nof_on_time_messages() const { return statistics.nof_on_time_messages(); } @@ -98,15 +92,11 @@ class rx_window_checker : public ota_symbol_boundary_notifier uint64_t nof_late_messages() const { return statistics.nof_late_messages(); } private: + /// Returns true if the given logger is enabled for info level, otherwise false. + static bool is_log_enabled(srslog::basic_logger& logger) { return logger.info.enabled(); } + /// Prints the statistics every second. void print_statistics(); - -private: - const rx_timing_parameters timing_parameters; - const unsigned nof_symbols_in_one_second; - unsigned nof_symbols; - rx_window_checker_statistics statistics; - std::atomic count_val; }; } // namespace ofh diff --git a/lib/ofh/transmitter/helpers.h b/lib/ofh/transmitter/helpers.h index db11e03dba..82edd62bf5 100644 --- a/lib/ofh/transmitter/helpers.h +++ b/lib/ofh/transmitter/helpers.h @@ -30,28 +30,23 @@ namespace srsran { namespace ofh { /// Returns the maximum value between the minimum T1a values in symbol units. -inline unsigned -get_biggest_min_tx_parameter_in_symbols(const du_tx_window_timing_parameters& tx_timing_params, - const std::chrono::duration& symbol_duration_ns) +inline unsigned get_biggest_min_tx_parameter(const tx_window_timing_parameters& tx_timing_params) { - auto max_value = - std::max({tx_timing_params.T1a_min_cp_dl, tx_timing_params.T1a_min_cp_ul, tx_timing_params.T1a_min_up}); - - return std::floor(max_value / symbol_duration_ns); + return std::max({tx_timing_params.sym_cp_dl_end, tx_timing_params.sym_cp_ul_end, tx_timing_params.sym_up_dl_end}); } /// Returns duration of the OFH downlink processing plus the transmission window in symbol units. -inline unsigned calculate_nof_symbols_before_ota(cyclic_prefix cp, - subcarrier_spacing scs, - std::chrono::microseconds dl_processing_time, - const du_tx_window_timing_parameters tx_timing_params) +inline unsigned calculate_nof_symbols_before_ota(cyclic_prefix cp, + subcarrier_spacing scs, + std::chrono::microseconds dl_processing_time, + const tx_window_timing_parameters tx_timing_params) { unsigned nof_symbols = get_nsymb_per_slot(cp); auto symbol_duration_ns = std::chrono::duration(1e6 / (nof_symbols * get_nof_slots_per_subframe(scs))); unsigned dl_processing_time_in_symbols = std::floor(dl_processing_time / symbol_duration_ns); - return dl_processing_time_in_symbols + get_biggest_min_tx_parameter_in_symbols(tx_timing_params, symbol_duration_ns); + return dl_processing_time_in_symbols + get_biggest_min_tx_parameter(tx_timing_params); } } // namespace ofh diff --git a/lib/ofh/transmitter/ofh_downlink_handler_broadcast_impl.h b/lib/ofh/transmitter/ofh_downlink_handler_broadcast_impl.h index 2d9d2b3bbf..49567c63d6 100644 --- a/lib/ofh/transmitter/ofh_downlink_handler_broadcast_impl.h +++ b/lib/ofh/transmitter/ofh_downlink_handler_broadcast_impl.h @@ -51,7 +51,7 @@ struct downlink_handler_broadcast_impl_config { /// Downlink processing time in microseconds. std::chrono::microseconds dl_processing_time; /// Transmission window timing parameters for delay management. - du_tx_window_timing_parameters tx_timing_params; + tx_window_timing_parameters tx_timing_params; }; /// Downlink handler broadcast implementation dependencies. diff --git a/lib/ofh/transmitter/ofh_downlink_handler_impl.h b/lib/ofh/transmitter/ofh_downlink_handler_impl.h index ff6329a1bc..5130a88099 100644 --- a/lib/ofh/transmitter/ofh_downlink_handler_impl.h +++ b/lib/ofh/transmitter/ofh_downlink_handler_impl.h @@ -51,7 +51,7 @@ struct downlink_handler_impl_config { /// Downlink processing time in microseconds. std::chrono::microseconds dl_processing_time; /// Transmission window timing parameters for delay management. - du_tx_window_timing_parameters tx_timing_params; + tx_window_timing_parameters tx_timing_params; }; /// Downlink handler implementation dependencies. diff --git a/lib/ofh/transmitter/ofh_message_transmitter_impl.cpp b/lib/ofh/transmitter/ofh_message_transmitter_impl.cpp index 569441d6f3..abc89635dc 100644 --- a/lib/ofh/transmitter/ofh_message_transmitter_impl.cpp +++ b/lib/ofh/transmitter/ofh_message_transmitter_impl.cpp @@ -27,16 +27,10 @@ using namespace srsran; using namespace ofh; message_transmitter_impl::message_transmitter_impl(srslog::basic_logger& logger_, - const symbol_handler_config& cfg, + const tx_window_timing_parameters& timing_params_, std::unique_ptr gw, std::shared_ptr frame_pool) : - logger(logger_), - pool_ptr(frame_pool), - pool(*pool_ptr), - gateway(std::move(gw)), - timing_params( - cfg.tx_timing_params, - std::chrono::duration(1e6 / (cfg.symbols_per_slot * get_nof_slots_per_subframe(cfg.scs)))) + logger(logger_), pool_ptr(frame_pool), pool(*pool_ptr), gateway(std::move(gw)), timing_params(timing_params_) { srsran_assert(gateway, "Invalid Ethernet gateway"); srsran_assert(pool_ptr, "Invalid frame pool"); diff --git a/lib/ofh/transmitter/ofh_message_transmitter_impl.h b/lib/ofh/transmitter/ofh_message_transmitter_impl.h index 20cebcecea..bc8af1149b 100644 --- a/lib/ofh/transmitter/ofh_message_transmitter_impl.h +++ b/lib/ofh/transmitter/ofh_message_transmitter_impl.h @@ -25,7 +25,7 @@ #include "srsran/ofh/ethernet/ethernet_frame_pool.h" #include "srsran/ofh/ethernet/ethernet_gateway.h" #include "srsran/ofh/timing/ofh_ota_symbol_boundary_notifier.h" -#include "srsran/ofh/transmitter/ofh_transmitter_configuration.h" +#include "srsran/ofh/transmitter/ofh_transmitter_timing_parameters.h" namespace srsran { namespace ofh { @@ -38,40 +38,6 @@ class message_transmitter_impl : public ota_symbol_boundary_notifier /// Maximum number of frames allowed to be transmitted in a single burst. static constexpr unsigned MAX_BURST_SIZE = 64; - /// Internal structure used to store transmission window timing parameters expressed in a number of symbols. - struct tx_timing_parameters { - /// Offset from the current OTA symbol to the first symbol at which DL Control-Plane message can be sent, or in - /// other words it is the offset to the start of DL Control-Plane transmission window. Must be calculated based on - /// \c T1a_max_cp_dl parameter. - unsigned sym_cp_dl_start; - /// Offset from the current OTA symbol to the last symbol at which DL Control-Plane message can be sent within its - /// transmission window. Must be calculated based on \c T1a_min_cp_dl parameter. - unsigned sym_cp_dl_end; - /// Offset from the current OTA symbol to the first symbol at which UL Control-Plane message can be sent within its - /// transmission window. Must be calculated based on \c T1a_max_cp_ul parameter. - unsigned sym_cp_ul_start; - /// Offset from the current OTA symbol to the last symbol at which UL Control-Plane message can be sent within its - /// transmission window. Must be calculated based on \c T1a_min_cp_ul parameter. - unsigned sym_cp_ul_end; - /// Offset from the current OTA symbol to the first symbol at which DL User-Plane message can be sent within its - /// transmission window. Must be calculated based on \c T1a_max_up parameter. - unsigned sym_up_dl_start; - /// Offset from the current OTA symbol to the last symbol at which DL User-Plane message can be sent within its - /// transmission window. Must be calculated based on \c T1a_min_up parameter. - unsigned sym_up_dl_end; - - tx_timing_parameters(const du_tx_window_timing_parameters& params, - std::chrono::duration symbol_duration) : - sym_cp_dl_start(std::floor(params.T1a_max_cp_dl / symbol_duration)), - sym_cp_dl_end(std::ceil(params.T1a_min_cp_dl / symbol_duration)), - sym_cp_ul_start(std::floor(params.T1a_max_cp_ul / symbol_duration)), - sym_cp_ul_end(std::ceil(params.T1a_min_cp_ul / symbol_duration)), - sym_up_dl_start(std::floor(params.T1a_max_up / symbol_duration)), - sym_up_dl_end(std::ceil(params.T1a_min_up / symbol_duration)) - { - } - }; - /// Logger. srslog::basic_logger& logger; /// Ethernet frame pool. @@ -80,11 +46,11 @@ class message_transmitter_impl : public ota_symbol_boundary_notifier /// Gateway handling message transmission. std::unique_ptr gateway; /// Internal representation of timing parameters. - const tx_timing_parameters timing_params; + const tx_window_timing_parameters timing_params; public: message_transmitter_impl(srslog::basic_logger& logger_, - const symbol_handler_config& cfg, + const tx_window_timing_parameters& timing_params_, std::unique_ptr gw, std::shared_ptr frame_pool); diff --git a/lib/ofh/transmitter/ofh_transmitter_factories.cpp b/lib/ofh/transmitter/ofh_transmitter_factories.cpp index e147d955a7..e08938ff69 100644 --- a/lib/ofh/transmitter/ofh_transmitter_factories.cpp +++ b/lib/ofh/transmitter/ofh_transmitter_factories.cpp @@ -127,7 +127,7 @@ create_downlink_manager(const transmitter_config& tx_con dl_config.cp = tx_config.cp; dl_config.scs = tx_config.scs; dl_config.dl_processing_time = tx_config.dl_processing_time; - dl_config.tx_timing_params = tx_config.symbol_handler_cfg.tx_timing_params; + dl_config.tx_timing_params = tx_config.tx_timing_params; dl_config.sector = tx_config.sector; downlink_handler_broadcast_impl_dependencies dl_dependencies; @@ -145,7 +145,7 @@ create_downlink_manager(const transmitter_config& tx_con dl_config.cp = tx_config.cp; dl_config.scs = tx_config.scs; dl_config.dl_processing_time = tx_config.dl_processing_time; - dl_config.tx_timing_params = tx_config.symbol_handler_cfg.tx_timing_params; + dl_config.tx_timing_params = tx_config.tx_timing_params; dl_config.sector = tx_config.sector; downlink_handler_impl_dependencies dl_dependencies; diff --git a/lib/ofh/transmitter/ofh_transmitter_impl.cpp b/lib/ofh/transmitter/ofh_transmitter_impl.cpp index fea97bf940..3c35bb1f2f 100644 --- a/lib/ofh/transmitter/ofh_transmitter_impl.cpp +++ b/lib/ofh/transmitter/ofh_transmitter_impl.cpp @@ -30,7 +30,7 @@ transmitter_impl::transmitter_impl(const transmitter_config& config, transmitter dl_manager(std::move(dependencies.dl_manager)), ul_request_handler(std::move(dependencies.ul_request_handler)), msg_transmitter(*dependencies.logger, - config.symbol_handler_cfg, + config.tx_timing_params, std::move(dependencies.eth_gateway), dependencies.frame_pool), ota_dispatcher(*dependencies.executor, dl_manager->get_ota_symbol_boundary_notifier(), msg_transmitter) diff --git a/lib/ofh/transmitter/ofh_tx_window_checker.h b/lib/ofh/transmitter/ofh_tx_window_checker.h index d7ade6cc23..d2c5fe7044 100644 --- a/lib/ofh/transmitter/ofh_tx_window_checker.h +++ b/lib/ofh/transmitter/ofh_tx_window_checker.h @@ -47,7 +47,7 @@ class tx_window_checker : public ota_symbol_boundary_notifier void on_new_symbol(slot_symbol_point symbol_point) override { // This atomic is only written from a single thread. - count_val.store(symbol_point.system_slot(), std::memory_order::memory_order_release); + count_val.store(symbol_point.to_uint(), std::memory_order::memory_order_release); } /// Returns true if the given slot is already late compared to the current OTA time, otherwise false. diff --git a/lib/pdcp/pdcp_entity_impl.h b/lib/pdcp/pdcp_entity_impl.h index 3856be260d..78ec7bc806 100644 --- a/lib/pdcp/pdcp_entity_impl.h +++ b/lib/pdcp/pdcp_entity_impl.h @@ -41,13 +41,17 @@ class pdcp_entity_impl : public pdcp_entity pdcp_tx_upper_control_notifier& tx_upper_cn, pdcp_rx_upper_data_notifier& rx_upper_dn, pdcp_rx_upper_control_notifier& rx_upper_cn, - timer_factory timers) : + timer_factory ue_dl_timer_factory, + timer_factory ue_ul_timer_factory, + timer_factory ue_ctrl_timer_factory) : logger("PDCP", {ue_index, rb_id, "DL/UL"}), metrics_period(config.custom.metrics_period), - metrics_timer(timers.create_timer()) + metrics_timer(ue_ctrl_timer_factory.create_timer()) { - tx = std::make_unique(ue_index, rb_id, config.get_tx_config(), tx_lower_dn, tx_upper_cn, timers); - rx = std::make_unique(ue_index, rb_id, config.get_rx_config(), rx_upper_dn, rx_upper_cn, timers); + tx = std::make_unique( + ue_index, rb_id, config.get_tx_config(), tx_lower_dn, tx_upper_cn, ue_dl_timer_factory); + rx = std::make_unique( + ue_index, rb_id, config.get_rx_config(), rx_upper_dn, rx_upper_cn, ue_ul_timer_factory); // Tx/Rx interconnect tx->set_status_provider(rx.get()); diff --git a/lib/pdcp/pdcp_entity_rx.cpp b/lib/pdcp/pdcp_entity_rx.cpp index c903007fab..e50dc0530c 100644 --- a/lib/pdcp/pdcp_entity_rx.cpp +++ b/lib/pdcp/pdcp_entity_rx.cpp @@ -34,7 +34,7 @@ pdcp_entity_rx::pdcp_entity_rx(uint32_t ue_index, pdcp_rx_config cfg_, pdcp_rx_upper_data_notifier& upper_dn_, pdcp_rx_upper_control_notifier& upper_cn_, - timer_factory timers_) : + timer_factory ue_ul_timer_factory_) : pdcp_entity_tx_rx_base(rb_id_, cfg_.rb_type, cfg_.rlc_mode, cfg_.sn_size), logger("PDCP", {ue_index, rb_id_, "UL"}), cfg(cfg_), @@ -43,11 +43,11 @@ pdcp_entity_rx::pdcp_entity_rx(uint32_t ue_index, rx_window(create_rx_window(cfg.sn_size)), upper_dn(upper_dn_), upper_cn(upper_cn_), - timers(timers_) + ue_ul_timer_factory(ue_ul_timer_factory_) { // t-Reordering timer if (cfg.t_reordering != pdcp_t_reordering::ms0 && cfg.t_reordering != pdcp_t_reordering::infinity) { - reordering_timer = timers.create_timer(); + reordering_timer = ue_ul_timer_factory.create_timer(); if (static_cast(cfg.t_reordering) > 0) { reordering_timer.set(std::chrono::milliseconds{static_cast(cfg.t_reordering)}, reordering_callback{this}); @@ -473,10 +473,9 @@ bool pdcp_entity_rx::integrity_verify(byte_buffer_view buf, uint32_t count, cons count, bearer_id, direction); - logger.log( - level, (uint8_t*)sec_cfg.k_128_int.value().data(), sec_cfg.k_128_int.value().size(), "Integrity check key."); - logger.log(level, (uint8_t*)mac_exp.data(), mac_exp.size(), "MAC expected."); - logger.log(level, (uint8_t*)mac.data(), mac.size(), "MAC found."); + logger.log(level, "Integrity check key: {}", sec_cfg.k_128_int); + logger.log(level, "MAC expected: {}", mac_exp); + logger.log(level, "MAC found: {}", mac); logger.log(level, buf.begin(), buf.end(), "Integrity check input message. len={}", buf.length()); } @@ -486,7 +485,7 @@ bool pdcp_entity_rx::integrity_verify(byte_buffer_view buf, uint32_t count, cons byte_buffer pdcp_entity_rx::cipher_decrypt(byte_buffer_view& msg, uint32_t count) { logger.log_debug("Cipher decrypt. count={} bearer_id={} dir={}", count, bearer_id, direction); - logger.log_debug((uint8_t*)sec_cfg.k_128_enc.data(), sec_cfg.k_128_enc.size(), "Cipher decrypt key."); + logger.log_debug("Cipher decrypt key: {}", sec_cfg.k_128_enc); logger.log_debug(msg.begin(), msg.end(), "Cipher decrypt input msg."); byte_buffer ct; diff --git a/lib/pdcp/pdcp_entity_rx.h b/lib/pdcp/pdcp_entity_rx.h index 93381c4a91..84311713b0 100644 --- a/lib/pdcp/pdcp_entity_rx.h +++ b/lib/pdcp/pdcp_entity_rx.h @@ -69,7 +69,7 @@ class pdcp_entity_rx final : public pdcp_entity_tx_rx_base, pdcp_rx_config cfg_, pdcp_rx_upper_data_notifier& upper_dn_, pdcp_rx_upper_control_notifier& upper_cn_, - timer_factory timers); + timer_factory ue_ul_timer_factory_); void handle_pdu(byte_buffer_chain buf) override; @@ -126,9 +126,9 @@ class pdcp_entity_rx final : public pdcp_entity_tx_rx_base, logger.log_info( "Security configured: NIA{} NEA{} domain={}", sec_cfg.integ_algo, sec_cfg.cipher_algo, sec_cfg.domain); if (sec_cfg.k_128_int.has_value()) { - logger.log_info(sec_cfg.k_128_int.value().data(), 16, "128 K_int"); + logger.log_info("128 K_int: {}", sec_cfg.k_128_int); } - logger.log_info(sec_cfg.k_128_enc.data(), 16, "128 K_enc"); + logger.log_info("128 K_enc: {}", sec_cfg.k_128_enc); } void set_integrity_protection(security::integrity_enabled integrity_enabled_) final @@ -203,7 +203,7 @@ class pdcp_entity_rx final : public pdcp_entity_tx_rx_base, pdcp_rx_upper_data_notifier& upper_dn; pdcp_rx_upper_control_notifier& upper_cn; - timer_factory timers; + timer_factory ue_ul_timer_factory; /// Creates the rx_window according to sn_size /// \param sn_size Size of the sequence number (SN) diff --git a/lib/pdcp/pdcp_entity_tx.cpp b/lib/pdcp/pdcp_entity_tx.cpp index 3c056dcc81..d832c92b3a 100644 --- a/lib/pdcp/pdcp_entity_tx.cpp +++ b/lib/pdcp/pdcp_entity_tx.cpp @@ -116,7 +116,7 @@ void pdcp_entity_tx::handle_sdu(byte_buffer buf) unique_timer discard_timer = {}; // Only start for finite durations if (cfg.discard_timer.value() != pdcp_discard_timer::infinity) { - discard_timer = timers.create_timer(); + discard_timer = ue_dl_timer_factory.create_timer(); discard_timer.set(std::chrono::milliseconds(static_cast(cfg.discard_timer.value())), discard_callback{this, st.tx_next}); discard_timer.run(); @@ -333,15 +333,15 @@ void pdcp_entity_tx::integrity_generate(security::sec_mac& mac, byte_buffer_view } logger.log_debug("Integrity gen. count={} bearer_id={} dir={}", count, bearer_id, direction); - logger.log_debug((uint8_t*)sec_cfg.k_128_int.value().data(), sec_cfg.k_128_int.value().size(), "Integrity gen key."); + logger.log_debug("Integrity gen key: {}", sec_cfg.k_128_int); logger.log_debug(buf.begin(), buf.end(), "Integrity gen input message."); - logger.log_debug((uint8_t*)mac.data(), mac.size(), "MAC generated."); + logger.log_debug("MAC generated: {}", mac); } void pdcp_entity_tx::cipher_encrypt(byte_buffer_view& buf, uint32_t count) { logger.log_debug("Cipher encrypt. count={} bearer_id={} dir={}", count, bearer_id, direction); - logger.log_debug((uint8_t*)sec_cfg.k_128_enc.data(), sec_cfg.k_128_enc.size(), "Cipher encrypt key."); + logger.log_debug("Cipher encrypt key: {}", sec_cfg.k_128_enc); logger.log_debug(buf.begin(), buf.end(), "Cipher encrypt input msg."); switch (sec_cfg.cipher_algo) { diff --git a/lib/pdcp/pdcp_entity_tx.h b/lib/pdcp/pdcp_entity_tx.h index f3d1b597a0..4a5fcbaefe 100644 --- a/lib/pdcp/pdcp_entity_tx.h +++ b/lib/pdcp/pdcp_entity_tx.h @@ -77,13 +77,13 @@ class pdcp_entity_tx final : public pdcp_entity_tx_rx_base, pdcp_tx_config cfg_, pdcp_tx_lower_notifier& lower_dn_, pdcp_tx_upper_control_notifier& upper_cn_, - timer_factory timers_) : + timer_factory ue_dl_timer_factory_) : pdcp_entity_tx_rx_base(rb_id_, cfg_.rb_type, cfg_.rlc_mode, cfg_.sn_size), logger("PDCP", {ue_index, rb_id_, "DL"}), cfg(cfg_), lower_dn(lower_dn_), upper_cn(upper_cn_), - timers(timers_), + ue_dl_timer_factory(ue_dl_timer_factory_), tx_window(create_tx_window(cfg.sn_size)) { // Validate configuration @@ -179,9 +179,9 @@ class pdcp_entity_tx final : public pdcp_entity_tx_rx_base, logger.log_info( "Security configured: NIA{} NEA{} domain={}", sec_cfg.integ_algo, sec_cfg.cipher_algo, sec_cfg.domain); if (sec_cfg.k_128_int.has_value()) { - logger.log_info(sec_cfg.k_128_int.value().data(), 16, "128 K_int"); + logger.log_info("128 K_int: {}", sec_cfg.k_128_int.value()); } - logger.log_info(sec_cfg.k_128_enc.data(), 16, "128 K_enc"); + logger.log_info("128 K_enc: {}", sec_cfg.k_128_enc); }; void set_integrity_protection(security::integrity_enabled integrity_enabled_) final @@ -224,7 +224,7 @@ class pdcp_entity_tx final : public pdcp_entity_tx_rx_base, pdcp_rx_status_provider* status_provider = nullptr; pdcp_tx_lower_notifier& lower_dn; pdcp_tx_upper_control_notifier& upper_cn; - timer_factory timers; + timer_factory ue_dl_timer_factory; pdcp_tx_state st = {}; security::security_direction direction = security::security_direction::downlink; diff --git a/lib/pdcp/pdcp_factory.cpp b/lib/pdcp/pdcp_factory.cpp index 89a10b159c..7b2e2ac953 100644 --- a/lib/pdcp/pdcp_factory.cpp +++ b/lib/pdcp/pdcp_factory.cpp @@ -36,5 +36,7 @@ std::unique_ptr srsran::create_pdcp_entity(pdcp_entity_creation_mes *msg.tx_upper_cn, *msg.rx_upper_dn, *msg.rx_upper_cn, - msg.timers); + msg.ue_dl_timer_factory, + msg.ue_ul_timer_factory, + msg.ue_ctrl_timer_factory); } diff --git a/lib/phy/upper/channel_processors/pdsch_encoder_hw_impl.cpp b/lib/phy/upper/channel_processors/pdsch_encoder_hw_impl.cpp index 1a8c5bad1f..bcbd783372 100644 --- a/lib/phy/upper/channel_processors/pdsch_encoder_hw_impl.cpp +++ b/lib/phy/upper/channel_processors/pdsch_encoder_hw_impl.cpp @@ -132,8 +132,10 @@ void pdsch_encoder_hw_impl::encode(span codeword, srsran_assert(offset + rm_length <= codeword.size(), "Wrong codeword length."); codeblock = span(codeword).subspan(offset, rm_length); + codeblock_packed.resize(rm_length); } else { codeblock = codeword; + codeblock_packed.resize(codeword.size()); } // Make sure at least one operation is dequeued. diff --git a/lib/phy/upper/channel_processors/pusch/pusch_decoder_hw_impl.h b/lib/phy/upper/channel_processors/pusch/pusch_decoder_hw_impl.h index fdea38afe7..c73013810b 100644 --- a/lib/phy/upper/channel_processors/pusch/pusch_decoder_hw_impl.h +++ b/lib/phy/upper/channel_processors/pusch/pusch_decoder_hw_impl.h @@ -85,6 +85,7 @@ class pusch_decoder_hw_impl : public pusch_decoder, private pusch_decoder_buffer srsran_assert(crc_set.crc24B->get_generator_poly() == crc_generator_poly::CRC24B, "Not a CRC generator of type CRC24B."); srsran_assert(decoder, "Invalid hardware-accelerated PUSCH decoder."); + absolute_cb_ids.resize(MAX_NOF_SEGMENTS); } // See interface for the documentation. diff --git a/lib/phy/upper/channel_processors/pusch/pusch_processor_impl.cpp b/lib/phy/upper/channel_processors/pusch/pusch_processor_impl.cpp index 35c0c170c0..399bab540f 100644 --- a/lib/phy/upper/channel_processors/pusch/pusch_processor_impl.cpp +++ b/lib/phy/upper/channel_processors/pusch/pusch_processor_impl.cpp @@ -223,6 +223,13 @@ void pusch_processor_impl::process(span data, // Get RB mask relative to Point A. It assumes PUSCH is never interleaved. bounded_bitset rb_mask = pdu.freq_alloc.get_prb_mask(pdu.bwp_start_rb, pdu.bwp_size_rb); + // Determine if the PUSCH allocation overlaps with the position of the DC. + bool overlap_dc = false; + if (pdu.dc_position.has_value()) { + unsigned dc_position_prb = pdu.dc_position.value() / NRE; + overlap_dc = rb_mask.test(dc_position_prb); + } + // Get UL-SCH information as if there was no CSI Part 2 in the PUSCH. ulsch_configuration ulsch_config; ulsch_config.tbs = units::bytes(data.size()).to_bits(); @@ -241,6 +248,7 @@ void pusch_processor_impl::process(span data, ulsch_config.dmrs_symbol_mask = pdu.dmrs_symbol_mask; ulsch_config.nof_cdm_groups_without_data = pdu.nof_cdm_groups_without_data; ulsch_config.nof_layers = pdu.nof_tx_layers; + ulsch_config.contains_dc = overlap_dc; ulsch_information info = get_ulsch_information(ulsch_config); // Estimate channel. diff --git a/lib/ran/CMakeLists.txt b/lib/ran/CMakeLists.txt index 976861a435..27dfb63e3b 100644 --- a/lib/ran/CMakeLists.txt +++ b/lib/ran/CMakeLists.txt @@ -19,6 +19,9 @@ # add_library(srsran_ran + cause/ngap_cause_converters.cpp + cause/f1ap_cause_converters.cpp + cause/e1ap_cause_converters.cpp csi_report/csi_report_config_helpers.cpp csi_report/csi_report_on_pucch_helpers.cpp csi_report/csi_report_on_pusch_helpers.cpp diff --git a/lib/ran/cause/e1ap_cause_converters.cpp b/lib/ran/cause/e1ap_cause_converters.cpp new file mode 100644 index 0000000000..ed39fb85de --- /dev/null +++ b/lib/ran/cause/e1ap_cause_converters.cpp @@ -0,0 +1,101 @@ +/* + * + * 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/ran/cause/e1ap_cause_converters.h" +#include "srsran/support/error_handling.h" + +using namespace srsran; + +const uint8_t e1ap_to_ngap_cause_radio_network[] = { + (uint8_t)ngap_cause_radio_network_t::unspecified, // unspecified + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_already_allocated_gnb_cu_cp_ue_e1ap_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_already_allocated_gnb_cu_up_ue_e1ap_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_inconsistent_pair_of_ue_e1ap_id + (uint8_t)ngap_cause_radio_network_t::interaction_with_other_proc, // interaction_with_other_proc + (uint8_t)ngap_cause_radio_network_t::unspecified, // ppdcp_count_wrap_around + (uint8_t)ngap_cause_radio_network_t::unspecified, // not_supported_qci_value + (uint8_t)ngap_cause_radio_network_t::not_supported_5qi_value, // not_supported_5qi_value + (uint8_t)ngap_cause_radio_network_t:: + encryption_and_or_integrity_protection_algorithms_not_supported, // encryption_algorithms_not_supported + (uint8_t)ngap_cause_radio_network_t:: + encryption_and_or_integrity_protection_algorithms_not_supported, // integrity_protection_algorithms_not_supported + (uint8_t)ngap_cause_radio_network_t::up_integrity_protection_not_possible, // up_integrity_protection_not_possible + (uint8_t)ngap_cause_radio_network_t:: + up_confidentiality_protection_not_possible, // up_confidentiality_protection_not_possible + (uint8_t)ngap_cause_radio_network_t::multiple_pdu_session_id_instances, // multiple_pdu_session_id_instances + (uint8_t)ngap_cause_radio_network_t::unknown_pdu_session_id, // unknown_pdu_session_id + (uint8_t)ngap_cause_radio_network_t::multiple_qos_flow_id_instances, // multiple_qos_flow_id_instances + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_qos_flow_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // multiple_drb_id_instances + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_drb_id + (uint8_t)ngap_cause_radio_network_t::invalid_qos_combination, // invalid_qos_combination + (uint8_t)ngap_cause_radio_network_t::unspecified, // proc_cancelled + (uint8_t)ngap_cause_radio_network_t::unspecified, // normal_release + (uint8_t)ngap_cause_radio_network_t::unspecified, // no_radio_res_available + (uint8_t)ngap_cause_radio_network_t::unspecified, // action_desirable_for_radio_reasons + (uint8_t)ngap_cause_radio_network_t::res_not_available_for_the_slice, // res_not_available_for_the_slice + (uint8_t)ngap_cause_radio_network_t::unspecified, // pdcp_cfg_not_supported + (uint8_t)ngap_cause_radio_network_t::unspecified, // ue_dl_max_ip_data_rate_reason + (uint8_t)ngap_cause_radio_network_t::unspecified, // up_integrity_protection_fail + (uint8_t)ngap_cause_radio_network_t::unspecified, // release_due_to_pre_emption + (uint8_t)ngap_cause_radio_network_t::unspecified, // rsn_not_available_for_the_up + (uint8_t)ngap_cause_radio_network_t::unspecified, // npn_not_supported + (uint8_t)ngap_cause_radio_network_t::unspecified, // report_characteristic_empty + (uint8_t)ngap_cause_radio_network_t::unspecified, // existing_meas_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // meas_temporarily_not_available + (uint8_t)ngap_cause_radio_network_t::unspecified // meas_not_supported_for_the_obj +}; + +const uint8_t e1ap_to_ngap_cause_transport[] = { + (uint8_t)ngap_cause_transport_t::transport_res_unavailable, // transport_res_unavailable + (uint8_t)ngap_cause_transport_t::unspecified, // unspecified + (uint8_t)ngap_cause_transport_t::unspecified // unknown_tnl_address_for_iab +}; + +const uint8_t e1ap_to_ngap_cause_misc[] = { + (uint8_t)ngap_cause_misc_t::ctrl_processing_overload, // ctrl_processing_overload + (uint8_t)ngap_cause_misc_t::not_enough_user_plane_processing_res, // not_enough_user_plane_processing_res + (uint8_t)ngap_cause_misc_t::hardware_fail, // hardware_fail + (uint8_t)ngap_cause_misc_t::om_intervention, // om_intervention + (uint8_t)ngap_cause_misc_t::unspecified, // unspecified +}; + +ngap_cause_t srsran::e1ap_to_ngap_cause(e1ap_cause_t e1ap_cause) +{ + ngap_cause_t ngap_cause; + + if (variant_holds_alternative(e1ap_cause)) { + ngap_cause = ngap_cause_radio_network_t( + e1ap_to_ngap_cause_radio_network[uint8_t(variant_get(e1ap_cause))]); + } else if (variant_holds_alternative(e1ap_cause)) { + ngap_cause = + ngap_cause_transport_t(e1ap_to_ngap_cause_transport[uint8_t(variant_get(e1ap_cause))]); + } else if (variant_holds_alternative(e1ap_cause)) { + ngap_cause = variant_get(e1ap_cause); + } else if (variant_holds_alternative(e1ap_cause)) { + ngap_cause = ngap_cause_misc_t(e1ap_to_ngap_cause_misc[uint8_t(variant_get(e1ap_cause))]); + } else { + report_fatal_error("Cannot convert cause to NGAP type: {}", e1ap_cause); + } + + return ngap_cause; +}; diff --git a/lib/ran/cause/f1ap_cause_converters.cpp b/lib/ran/cause/f1ap_cause_converters.cpp new file mode 100644 index 0000000000..4563c67936 --- /dev/null +++ b/lib/ran/cause/f1ap_cause_converters.cpp @@ -0,0 +1,106 @@ +/* + * + * 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/ran/cause/f1ap_cause_converters.h" +#include "srsran/support/error_handling.h" + +using namespace srsran; + +const uint8_t f1ap_to_ngap_cause_radio_network[] = { + (uint8_t)ngap_cause_radio_network_t::unspecified, // unspecified + (uint8_t)ngap_cause_radio_network_t::radio_conn_with_ue_lost, // rl_fail_rlc + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_already_allocated_gnb_cu_ue_f1ap_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_already_allocated_gnb_du_ue_f1ap_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_inconsistent_pair_of_ue_f1ap_id + (uint8_t)ngap_cause_radio_network_t::interaction_with_other_proc, // interaction_with_other_proc + (uint8_t)ngap_cause_radio_network_t::unspecified, // not_supported_qci_value + (uint8_t)ngap_cause_radio_network_t::unspecified, // action_desirable_for_radio_reasons + (uint8_t)ngap_cause_radio_network_t::no_radio_res_available_in_target_cell, // no_radio_res_available + (uint8_t)ngap_cause_radio_network_t::unspecified, // proc_cancelled + (uint8_t)ngap_cause_radio_network_t::unspecified, // normal_release + (uint8_t)ngap_cause_radio_network_t::cell_not_available, // cell_not_available + (uint8_t)ngap_cause_radio_network_t::radio_conn_with_ue_lost, // rl_fail_others + (uint8_t)ngap_cause_radio_network_t::unspecified, // ue_rejection + (uint8_t)ngap_cause_radio_network_t::res_not_available_for_the_slice, // res_not_available_for_the_slice + (uint8_t)ngap_cause_radio_network_t::unspecified, // amf_initiated_abnormal_release + (uint8_t)ngap_cause_radio_network_t::unspecified, // release_due_to_pre_emption + (uint8_t)ngap_cause_radio_network_t::unspecified, // plmn_not_served_by_the_gnb_cu + (uint8_t)ngap_cause_radio_network_t::unspecified, // multiple_drb_id_instances + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_drb_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // multiple_bh_rlc_ch_id_instances + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_bh_rlc_ch_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // cho_cpc_res_tobechanged + (uint8_t)ngap_cause_radio_network_t::unspecified, // npn_not_supported + (uint8_t)ngap_cause_radio_network_t::unspecified, // npn_access_denied + (uint8_t)ngap_cause_radio_network_t::unspecified, // gnb_cu_cell_capacity_exceeded + (uint8_t)ngap_cause_radio_network_t::unspecified, // report_characteristics_empty + (uint8_t)ngap_cause_radio_network_t::unspecified, // existing_meas_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // meas_temporarily_not_available + (uint8_t)ngap_cause_radio_network_t::unspecified, // meas_not_supported_for_the_obj + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_bh_address + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_bap_routing_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // insufficient_ue_cap + (uint8_t)ngap_cause_radio_network_t::unspecified, // scg_activation_deactivation_fail + (uint8_t)ngap_cause_radio_network_t::unspecified, // scg_deactivation_fail_due_to_data_tx + (uint8_t)ngap_cause_radio_network_t::unspecified, // requested_item_not_supported_on_time + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_already_allocated_gnb_cu_mbs_f1ap_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_already_allocated_gnb_du_mbs_f1ap_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_inconsistent_pair_of_mbs_f1ap_id + (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_inconsistent_mrb_id + (uint8_t)ngap_cause_radio_network_t::unspecified // tat_sdt_expiry +}; + +const uint8_t f1ap_to_ngap_cause_transport[] = { + (uint8_t)ngap_cause_transport_t::transport_res_unavailable, // transport_res_unavailable + (uint8_t)ngap_cause_transport_t::unspecified, // unspecified + (uint8_t)ngap_cause_transport_t::unspecified, // unknown_tnl_address_for_iab + (uint8_t)ngap_cause_transport_t::unspecified, // unknown_up_tnl_info_for_iab +}; + +const uint8_t f1ap_to_ngap_cause_misc[] = { + (uint8_t)ngap_cause_misc_t::ctrl_processing_overload, // ctrl_processing_overload + (uint8_t)ngap_cause_misc_t::not_enough_user_plane_processing_res, // not_enough_user_plane_processing_res + (uint8_t)ngap_cause_misc_t::hardware_fail, // hardware_fail + (uint8_t)ngap_cause_misc_t::om_intervention, // om_intervention + (uint8_t)ngap_cause_misc_t::unspecified // unspecified +}; + +ngap_cause_t srsran::f1ap_to_ngap_cause(f1ap_cause_t f1ap_cause) +{ + ngap_cause_t ngap_cause; + + if (variant_holds_alternative(f1ap_cause)) { + ngap_cause = ngap_cause_radio_network_t( + f1ap_to_ngap_cause_radio_network[uint8_t(variant_get(f1ap_cause))]); + } else if (variant_holds_alternative(f1ap_cause)) { + ngap_cause = + ngap_cause_transport_t(f1ap_to_ngap_cause_transport[uint8_t(variant_get(f1ap_cause))]); + } else if (variant_holds_alternative(f1ap_cause)) { + ngap_cause = variant_get(f1ap_cause); + } else if (variant_holds_alternative(f1ap_cause)) { + ngap_cause = ngap_cause_misc_t(f1ap_to_ngap_cause_misc[uint8_t(variant_get(f1ap_cause))]); + } else { + report_fatal_error("Cannot convert cause to F1AP type: {}", f1ap_cause); + } + + return ngap_cause; +}; diff --git a/lib/ran/cause/ngap_cause_converters.cpp b/lib/ran/cause/ngap_cause_converters.cpp new file mode 100644 index 0000000000..8c51416bbf --- /dev/null +++ b/lib/ran/cause/ngap_cause_converters.cpp @@ -0,0 +1,250 @@ +/* + * + * 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/ran/cause/ngap_cause_converters.h" +#include "srsran/support/error_handling.h" + +using namespace srsran; + +const uint8_t ngap_to_f1ap_cause_radio_network[] = { + (uint8_t)f1ap_cause_radio_network_t::unspecified, // unspecified + (uint8_t)f1ap_cause_radio_network_t::unspecified, // txnrelocoverall_expiry + (uint8_t)f1ap_cause_radio_network_t::unspecified, // successful_ho + (uint8_t)f1ap_cause_radio_network_t::unspecified, // release_due_to_ngran_generated_reason + (uint8_t)f1ap_cause_radio_network_t::unspecified, // release_due_to_5gc_generated_reason + (uint8_t)f1ap_cause_radio_network_t::unspecified, // ho_cancelled + (uint8_t)f1ap_cause_radio_network_t::unspecified, // partial_ho + (uint8_t)f1ap_cause_radio_network_t::unspecified, // ho_fail_in_target_5_gc_ngran_node_or_target_sys + (uint8_t)f1ap_cause_radio_network_t::unspecified, // ho_target_not_allowed + (uint8_t)f1ap_cause_radio_network_t::unspecified, // tngrelocoverall_expiry + (uint8_t)f1ap_cause_radio_network_t::unspecified, // tngrelocprep_expiry + (uint8_t)f1ap_cause_radio_network_t::unspecified, // cell_not_available + (uint8_t)f1ap_cause_radio_network_t::unspecified, // unknown_target_id + (uint8_t)f1ap_cause_radio_network_t::unspecified, // no_radio_res_available_in_target_cell + (uint8_t)f1ap_cause_radio_network_t::unspecified, // unknown_local_ue_ngap_id + (uint8_t)f1ap_cause_radio_network_t::unspecified, // inconsistent_remote_ue_ngap_id + (uint8_t)f1ap_cause_radio_network_t::unspecified, // ho_desirable_for_radio_reason + (uint8_t)f1ap_cause_radio_network_t::unspecified, // time_crit_ho + (uint8_t)f1ap_cause_radio_network_t::unspecified, // res_optim_ho + (uint8_t)f1ap_cause_radio_network_t::unspecified, // reduce_load_in_serving_cell + (uint8_t)f1ap_cause_radio_network_t::unspecified, // user_inactivity + (uint8_t)f1ap_cause_radio_network_t::unspecified, // radio_conn_with_ue_lost + (uint8_t)f1ap_cause_radio_network_t::unspecified, // radio_res_not_available + (uint8_t)f1ap_cause_radio_network_t::unspecified, // invalid_qos_combination + (uint8_t)f1ap_cause_radio_network_t::unspecified, // fail_in_radio_interface_proc + (uint8_t)f1ap_cause_radio_network_t::unspecified, // interaction_with_other_proc + (uint8_t)f1ap_cause_radio_network_t::unspecified, // unknown_pdu_session_id + (uint8_t)f1ap_cause_radio_network_t::unspecified, // unkown_qos_flow_id + (uint8_t)f1ap_cause_radio_network_t::unspecified, // multiple_pdu_session_id_instances + (uint8_t)f1ap_cause_radio_network_t::unspecified, // multiple_qos_flow_id_instances + (uint8_t)f1ap_cause_radio_network_t::unspecified, // encryption_and_or_integrity_protection_algorithms_not_supported + (uint8_t)f1ap_cause_radio_network_t::unspecified, // ng_intra_sys_ho_triggered + (uint8_t)f1ap_cause_radio_network_t::unspecified, // ng_inter_sys_ho_triggered + (uint8_t)f1ap_cause_radio_network_t::unspecified, // xn_ho_triggered + (uint8_t)f1ap_cause_radio_network_t::unspecified, // not_supported_5qi_value + (uint8_t)f1ap_cause_radio_network_t::unspecified, // ue_context_transfer + (uint8_t)f1ap_cause_radio_network_t::unspecified, // ims_voice_eps_fallback_or_rat_fallback_triggered + (uint8_t)f1ap_cause_radio_network_t::unspecified, // up_integrity_protection_not_possible + (uint8_t)f1ap_cause_radio_network_t::unspecified, // up_confidentiality_protection_not_possible + (uint8_t)f1ap_cause_radio_network_t::unspecified, // slice_not_supported + (uint8_t)f1ap_cause_radio_network_t::unspecified, // ue_in_rrc_inactive_state_not_reachable + (uint8_t)f1ap_cause_radio_network_t::unspecified, // redirection + (uint8_t)f1ap_cause_radio_network_t::unspecified, // res_not_available_for_the_slice + (uint8_t)f1ap_cause_radio_network_t::unspecified, // ue_max_integrity_protected_data_rate_reason + (uint8_t)f1ap_cause_radio_network_t::unspecified, // release_due_to_cn_detected_mob + (uint8_t)f1ap_cause_radio_network_t::unspecified, // n26_interface_not_available + (uint8_t)f1ap_cause_radio_network_t::unspecified, // release_due_to_pre_emption + (uint8_t)f1ap_cause_radio_network_t::unspecified, // multiple_location_report_ref_id_instances + (uint8_t)f1ap_cause_radio_network_t::unspecified, // rsn_not_available_for_the_up + (uint8_t)f1ap_cause_radio_network_t::unspecified, // npn_access_denied + (uint8_t)f1ap_cause_radio_network_t::unspecified, // cag_only_access_denied + (uint8_t)f1ap_cause_radio_network_t::unspecified, // insufficient_ue_cap + (uint8_t)f1ap_cause_radio_network_t::unspecified, // redcap_ue_not_supported + (uint8_t)f1ap_cause_radio_network_t::unspecified, // unknown_mbs_session_id + (uint8_t)f1ap_cause_radio_network_t::unspecified, // indicated_mbs_session_area_info_not_served_by_the_gnb + (uint8_t)f1ap_cause_radio_network_t::unspecified, // inconsistent_slice_info_for_the_session + (uint8_t)f1ap_cause_radio_network_t::unspecified // misaligned_assoc_for_multicast_unicast +}; + +const uint8_t ngap_to_f1ap_cause_transport[] = { + (uint8_t)f1ap_cause_transport_t::transport_res_unavailable, // transport_res_unavailable + (uint8_t)f1ap_cause_transport_t::unspecified // unspecified +}; + +const uint8_t ngap_to_f1ap_cause_misc[] = { + (uint8_t)cause_misc_t::unspecified, // ctrl_processing_overload + (uint8_t)cause_misc_t::unspecified, // not_enough_user_plane_processing_res + (uint8_t)cause_misc_t::unspecified, // hardware_fail + (uint8_t)cause_misc_t::unspecified, // om_intervention + (uint8_t)cause_misc_t::unspecified, // unknown_plmn_or_sn_pn + (uint8_t)cause_misc_t::unspecified, // unspecified +}; + +f1ap_cause_t srsran::ngap_to_f1ap_cause(ngap_cause_t ngap_cause) +{ + f1ap_cause_t f1ap_cause; + + if (variant_holds_alternative(ngap_cause)) { + f1ap_cause = f1ap_cause_radio_network_t( + ngap_to_f1ap_cause_radio_network[uint8_t(variant_get(ngap_cause))]); + } else if (variant_holds_alternative(ngap_cause)) { + f1ap_cause = + f1ap_cause_transport_t(ngap_to_f1ap_cause_transport[uint8_t(variant_get(ngap_cause))]); + } else if (variant_holds_alternative(ngap_cause)) { + switch (variant_get(ngap_cause)) { + case cause_nas_t::normal_release: + f1ap_cause = f1ap_cause_radio_network_t::normal_release; + break; + case cause_nas_t::authentication_fail: + f1ap_cause = f1ap_cause_radio_network_t::unspecified; + break; + case cause_nas_t::deregister: + f1ap_cause = f1ap_cause_radio_network_t::normal_release; + break; + case cause_nas_t::unspecified: + f1ap_cause = f1ap_cause_radio_network_t::unspecified; + break; + default: + report_fatal_error("Cannot convert cause to F1AP type: {}", ngap_cause); + } + } else if (variant_holds_alternative(ngap_cause)) { + f1ap_cause = variant_get(ngap_cause); + } else if (variant_holds_alternative(ngap_cause)) { + f1ap_cause = cause_misc_t(ngap_to_f1ap_cause_misc[uint8_t(variant_get(ngap_cause))]); + } else { + report_fatal_error("Cannot convert cause to F1AP type: {}", ngap_cause); + } + + return f1ap_cause; +}; + +const uint8_t ngap_to_e1ap_cause_radio_network[] = { + (uint8_t)e1ap_cause_radio_network_t::unspecified, // unspecified + (uint8_t)e1ap_cause_radio_network_t::unspecified, // txnrelocoverall_expiry + (uint8_t)e1ap_cause_radio_network_t::unspecified, // successful_ho + (uint8_t)e1ap_cause_radio_network_t::unspecified, // release_due_to_ngran_generated_reason + (uint8_t)e1ap_cause_radio_network_t::unspecified, // release_due_to_5gc_generated_reason + (uint8_t)e1ap_cause_radio_network_t::unspecified, // ho_cancelled + (uint8_t)e1ap_cause_radio_network_t::unspecified, // partial_ho + (uint8_t)e1ap_cause_radio_network_t::unspecified, // ho_fail_in_target_5_gc_ngran_node_or_target_sys + (uint8_t)e1ap_cause_radio_network_t::unspecified, // ho_target_not_allowed + (uint8_t)e1ap_cause_radio_network_t::unspecified, // tngrelocoverall_expiry + (uint8_t)e1ap_cause_radio_network_t::unspecified, // tngrelocprep_expiry + (uint8_t)e1ap_cause_radio_network_t::unspecified, // cell_not_available + (uint8_t)e1ap_cause_radio_network_t::unspecified, // unknown_target_id + (uint8_t)e1ap_cause_radio_network_t::unspecified, // no_radio_res_available_in_target_cell + (uint8_t)e1ap_cause_radio_network_t::unspecified, // unknown_local_ue_ngap_id + (uint8_t)e1ap_cause_radio_network_t::unspecified, // inconsistent_remote_ue_ngap_id + (uint8_t)e1ap_cause_radio_network_t::unspecified, // ho_desirable_for_radio_reason + (uint8_t)e1ap_cause_radio_network_t::unspecified, // time_crit_ho + (uint8_t)e1ap_cause_radio_network_t::unspecified, // res_optim_ho + (uint8_t)e1ap_cause_radio_network_t::unspecified, // reduce_load_in_serving_cell + (uint8_t)e1ap_cause_radio_network_t::unspecified, // user_inactivity + (uint8_t)e1ap_cause_radio_network_t::unspecified, // radio_conn_with_ue_lost + (uint8_t)e1ap_cause_radio_network_t::unspecified, // radio_res_not_available + (uint8_t)e1ap_cause_radio_network_t::unspecified, // invalid_qos_combination + (uint8_t)e1ap_cause_radio_network_t::unspecified, // fail_in_radio_interface_proc + (uint8_t)e1ap_cause_radio_network_t::unspecified, // interaction_with_other_proc + (uint8_t)e1ap_cause_radio_network_t::unspecified, // unknown_pdu_session_id + (uint8_t)e1ap_cause_radio_network_t::unspecified, // unkown_qos_flow_id + (uint8_t)e1ap_cause_radio_network_t::unspecified, // multiple_pdu_session_id_instances + (uint8_t)e1ap_cause_radio_network_t::unspecified, // multiple_qos_flow_id_instances + (uint8_t)e1ap_cause_radio_network_t::unspecified, // encryption_and_or_integrity_protection_algorithms_not_supported + (uint8_t)e1ap_cause_radio_network_t::unspecified, // ng_intra_sys_ho_triggered + (uint8_t)e1ap_cause_radio_network_t::unspecified, // ng_inter_sys_ho_triggered + (uint8_t)e1ap_cause_radio_network_t::unspecified, // xn_ho_triggered + (uint8_t)e1ap_cause_radio_network_t::unspecified, // not_supported_5qi_value + (uint8_t)e1ap_cause_radio_network_t::unspecified, // ue_context_transfer + (uint8_t)e1ap_cause_radio_network_t::unspecified, // ims_voice_eps_fallback_or_rat_fallback_triggered + (uint8_t)e1ap_cause_radio_network_t::unspecified, // up_integrity_protection_not_possible + (uint8_t)e1ap_cause_radio_network_t::unspecified, // up_confidentiality_protection_not_possible + (uint8_t)e1ap_cause_radio_network_t::unspecified, // slice_not_supported + (uint8_t)e1ap_cause_radio_network_t::unspecified, // ue_in_rrc_inactive_state_not_reachable + (uint8_t)e1ap_cause_radio_network_t::unspecified, // redirection + (uint8_t)e1ap_cause_radio_network_t::unspecified, // res_not_available_for_the_slice + (uint8_t)e1ap_cause_radio_network_t::unspecified, // ue_max_integrity_protected_data_rate_reason + (uint8_t)e1ap_cause_radio_network_t::unspecified, // release_due_to_cn_detected_mob + (uint8_t)e1ap_cause_radio_network_t::unspecified, // n26_interface_not_available + (uint8_t)e1ap_cause_radio_network_t::unspecified, // release_due_to_pre_emption + (uint8_t)e1ap_cause_radio_network_t::unspecified, // multiple_location_report_ref_id_instances + (uint8_t)e1ap_cause_radio_network_t::unspecified, // rsn_not_available_for_the_up + (uint8_t)e1ap_cause_radio_network_t::unspecified, // npn_access_denied + (uint8_t)e1ap_cause_radio_network_t::unspecified, // cag_only_access_denied + (uint8_t)e1ap_cause_radio_network_t::unspecified, // insufficient_ue_cap + (uint8_t)e1ap_cause_radio_network_t::unspecified, // redcap_ue_not_supported + (uint8_t)e1ap_cause_radio_network_t::unspecified, // unknown_mbs_session_id + (uint8_t)e1ap_cause_radio_network_t::unspecified, // indicated_mbs_session_area_info_not_served_by_the_gnb + (uint8_t)e1ap_cause_radio_network_t::unspecified, // inconsistent_slice_info_for_the_session + (uint8_t)e1ap_cause_radio_network_t::unspecified // misaligned_assoc_for_multicast_unicast +}; + +const uint8_t ngap_to_e1ap_cause_transport[] = { + (uint8_t)e1ap_cause_transport_t::transport_res_unavailable, // transport_res_unavailable + (uint8_t)e1ap_cause_transport_t::unspecified // unspecified +}; + +const uint8_t ngap_to_e1ap_cause_misc[] = { + (uint8_t)cause_misc_t::unspecified, // ctrl_processing_overload + (uint8_t)cause_misc_t::unspecified, // not_enough_user_plane_processing_res + (uint8_t)cause_misc_t::unspecified, // hardware_fail + (uint8_t)cause_misc_t::unspecified, // om_intervention + (uint8_t)cause_misc_t::unspecified, // unknown_plmn_or_sn_pn + (uint8_t)cause_misc_t::unspecified // unspecified +}; + +e1ap_cause_t srsran::ngap_to_e1ap_cause(ngap_cause_t ngap_cause) +{ + e1ap_cause_t e1ap_cause; + + if (variant_holds_alternative(ngap_cause)) { + e1ap_cause = e1ap_cause_radio_network_t( + ngap_to_e1ap_cause_radio_network[uint8_t(variant_get(ngap_cause))]); + } else if (variant_holds_alternative(ngap_cause)) { + e1ap_cause = + e1ap_cause_transport_t(ngap_to_e1ap_cause_transport[uint8_t(variant_get(ngap_cause))]); + } else if (variant_holds_alternative(ngap_cause)) { + switch (variant_get(ngap_cause)) { + case cause_nas_t::normal_release: + e1ap_cause = e1ap_cause_radio_network_t::normal_release; + break; + case cause_nas_t::authentication_fail: + e1ap_cause = e1ap_cause_radio_network_t::unspecified; + break; + case cause_nas_t::deregister: + e1ap_cause = e1ap_cause_radio_network_t::normal_release; + break; + case cause_nas_t::unspecified: + e1ap_cause = e1ap_cause_radio_network_t::unspecified; + break; + default: + report_fatal_error("Cannot convert cause to E1AP type: {}", ngap_cause); + } + } else if (variant_holds_alternative(ngap_cause)) { + e1ap_cause = variant_get(ngap_cause); + } else if (variant_holds_alternative(ngap_cause)) { + e1ap_cause = cause_misc_t(ngap_to_e1ap_cause_misc[uint8_t(variant_get(ngap_cause))]); + } else { + report_fatal_error("Cannot convert cause to E1AP type: {}", ngap_cause); + } + + return e1ap_cause; +}; diff --git a/lib/ran/pdsch/dlsch_info.cpp b/lib/ran/pdsch/dlsch_info.cpp index 0eb310b5d6..48d6794aa7 100644 --- a/lib/ran/pdsch/dlsch_info.cpp +++ b/lib/ran/pdsch/dlsch_info.cpp @@ -27,6 +27,8 @@ using namespace srsran; dlsch_information srsran::get_dlsch_information(const dlsch_configuration& config) { + using namespace units::literals; + dlsch_information result = {}; // Get shared channel parameters. @@ -84,5 +86,12 @@ dlsch_information srsran::get_dlsch_information(const dlsch_configuration& confi // Number of bits used for shared channel. result.nof_dl_sch_bits = units::bits(nof_re_dl_sch * config.nof_layers * modulation_order); + // Count the number of rate matched bits that overlap with the DC position. + if (config.contains_dc) { + result.nof_dc_overlap_bits = units::bits(config.nof_symbols * modulation_order); + } else { + result.nof_dc_overlap_bits = 0_bits; + } + return result; -} \ No newline at end of file +} diff --git a/lib/ran/pusch/pusch_tpmi_select.cpp b/lib/ran/pusch/pusch_tpmi_select.cpp index 87d3137623..61ea9854ca 100644 --- a/lib/ran/pusch/pusch_tpmi_select.cpp +++ b/lib/ran/pusch/pusch_tpmi_select.cpp @@ -22,18 +22,25 @@ #include "srsran/ran/pusch/pusch_tpmi_select.h" #include "srsran/adt/complex.h" +#include "srsran/adt/span.h" +#include "srsran/adt/static_vector.h" #include "srsran/ran/precoding/precoding_weight_matrix.h" +#include "srsran/ran/pusch/pusch_constants.h" #include "srsran/ran/srs/srs_channel_matrix.h" #include "srsran/support/math_utils.h" +#include #include #include +#include using namespace srsran; static constexpr cf_t sqrt1_2(M_SQRT1_2, 0); - static constexpr cf_t sqrt1_2j(0, M_SQRT1_2); +static constexpr cf_t dot5(0.5, 0); +static constexpr cf_t dot5j(0, 0.5); +// TS38.211 Table 6.3.1.5-1 static const std::array codebook_1layer_2port = { {precoding_weight_matrix({sqrt1_2, 0}, 1, 2), precoding_weight_matrix({0, sqrt1_2}, 1, 2), @@ -42,44 +49,103 @@ static const std::array codebook_1layer_2port = { precoding_weight_matrix({sqrt1_2, sqrt1_2j}, 1, 2), precoding_weight_matrix({sqrt1_2, -sqrt1_2j}, 1, 2)}}; -static pusch_tpmi_select_info get_tpmi_select_info_1x2(const srs_channel_matrix& channel, float noise_variance) +// TS38.211 Table 6.3.1.5-3 +static const std::array codebook_1layer_4port = {{ + // TPMI Index 0-7 + precoding_weight_matrix({dot5, 0, 0, 0}, 1, 4), + precoding_weight_matrix({0, dot5, 0, 0}, 1, 4), + precoding_weight_matrix({0, 0, dot5, 0}, 1, 4), + precoding_weight_matrix({0, 0, 0, dot5}, 1, 4), + precoding_weight_matrix({dot5, 0, dot5, 0}, 1, 4), + precoding_weight_matrix({dot5, 0, -dot5, 0}, 1, 4), + precoding_weight_matrix({dot5, 0, dot5j, 0}, 1, 4), + precoding_weight_matrix({dot5, 0, -dot5j, 0}, 1, 4), + // TPMI Index 8-15 + precoding_weight_matrix({0, dot5, 0, dot5}, 1, 4), + precoding_weight_matrix({0, dot5, 0, -dot5}, 1, 4), + precoding_weight_matrix({0, dot5, 0, dot5j}, 1, 4), + precoding_weight_matrix({0, dot5, 0, -dot5j}, 1, 4), + precoding_weight_matrix({dot5, dot5, dot5, dot5}, 1, 4), + precoding_weight_matrix({dot5, dot5, dot5j, dot5j}, 1, 4), + precoding_weight_matrix({dot5, dot5, -dot5, -dot5}, 1, 4), + precoding_weight_matrix({dot5, dot5, -dot5j, -dot5j}, 1, 4), + // TPMI Index 16-23 + precoding_weight_matrix({dot5, dot5j, dot5, dot5j}, 1, 4), + precoding_weight_matrix({dot5, dot5j, dot5j, -dot5}, 1, 4), + precoding_weight_matrix({dot5, dot5j, -dot5, -dot5j}, 1, 4), + precoding_weight_matrix({dot5, dot5j, -dot5j, dot5}, 1, 4), + precoding_weight_matrix({dot5, -dot5, dot5, -dot5}, 1, 4), + precoding_weight_matrix({dot5, -dot5, dot5j, -dot5j}, 1, 4), + precoding_weight_matrix({dot5, -dot5, -dot5, dot5}, 1, 4), + precoding_weight_matrix({dot5, -dot5, -dot5j, dot5j}, 1, 4), + // TPMI Index 24-27 + precoding_weight_matrix({dot5, -dot5j, dot5, -dot5j}, 1, 4), + precoding_weight_matrix({dot5, -dot5j, dot5j, dot5}, 1, 4), + precoding_weight_matrix({dot5, -dot5j, -dot5, dot5j}, 1, 4), + precoding_weight_matrix({dot5, -dot5j, -dot5j, -dot5}, 1, 4), +}}; + +static pusch_tpmi_select_info::tpmi_info get_tpmi_select_info_1layer(const srs_channel_matrix& channel, + float noise_variance) { + unsigned nof_rx_ports = channel.get_nof_rx_ports(); + unsigned nof_tx_ports = channel.get_nof_tx_ports(); + + // Select codebook in function of the number of transmit ports. + span codebook = codebook_1layer_2port; + if (nof_tx_ports == 4) { + codebook = codebook_1layer_4port; + } + float best_sinr = -std::numeric_limits::infinity(); unsigned best_tpmi = 0; // Iterate possible TPMIs. - for (unsigned tpmi = 0, tpmi_end = codebook_1layer_2port.size(); tpmi != tpmi_end; ++tpmi) { + for (unsigned tpmi = 0, tpmi_end = codebook.size(); tpmi != tpmi_end; ++tpmi) { // Select precoding matrix. - const precoding_weight_matrix& w = codebook_1layer_2port[tpmi]; - cf_t w_p0 = w.get_coefficient(0, 0); - cf_t w_p1 = w.get_coefficient(0, 1); + const precoding_weight_matrix& weights = codebook[tpmi]; - cf_t h_00 = channel.get_coefficient(0, 0); - cf_t h_01 = channel.get_coefficient(0, 1); + float signal_power = 0; - cf_t wh = w_p0 * h_00 + w_p1 * h_01; - float whhw = abs_sq(wh); + // Combine channel coefficients with precoding weights. + for (unsigned i_rx_port = 0; i_rx_port != nof_rx_ports; ++i_rx_port) { + cf_t sum = 0; + for (unsigned i_tx_port = 0; i_tx_port != nof_tx_ports; ++i_tx_port) { + // Select precoding weight. + cf_t w = weights.get_coefficient(0, i_tx_port); + // Select channel coefficient. + cf_t h = channel.get_coefficient(i_rx_port, i_tx_port); - float sinr = whhw / noise_variance; + // Sum absolute square. + sum += h * w; + } + + signal_power += abs_sq(sum); + } + + float sinr = signal_power / noise_variance; if (sinr > best_sinr) { best_sinr = sinr; best_tpmi = tpmi; } } - return {{best_tpmi, convert_power_to_dB(best_sinr)}}; + return {best_tpmi, convert_power_to_dB(best_sinr)}; } pusch_tpmi_select_info srsran::get_tpmi_select_info(const srs_channel_matrix& channel, float noise_variance) { - unsigned nof_tx_ports = channel.get_nof_tx_ports(); - unsigned nof_rx_ports = channel.get_nof_rx_ports(); + unsigned nof_tx_ports = channel.get_nof_tx_ports(); + unsigned nof_rx_ports = channel.get_nof_rx_ports(); + unsigned max_nof_layers = std::min(nof_tx_ports, nof_rx_ports); + + static_vector info; - // Two transmit, one receive ports. - if ((nof_tx_ports == 2) && (nof_rx_ports == 1)) { - return get_tpmi_select_info_1x2(channel, noise_variance); + // Calculate TPMI select information for 1 layer. + if (max_nof_layers >= 1) { + info.emplace_back(get_tpmi_select_info_1layer(channel, noise_variance)); } // Return invalid information. - return {}; + return {info}; } \ No newline at end of file diff --git a/lib/ran/pusch/ulsch_info.cpp b/lib/ran/pusch/ulsch_info.cpp index e620e26b91..a5ee011d8c 100644 --- a/lib/ran/pusch/ulsch_info.cpp +++ b/lib/ran/pusch/ulsch_info.cpp @@ -349,5 +349,12 @@ ulsch_information srsran::get_ulsch_information(const ulsch_configuration& confi // Number of bits used for CSI Part 2. result.nof_csi_part2_bits = units::bits(result.nof_csi_part2_re * config.nof_layers * modulation_order); + // Count the number of rate matched bits that overlap with the DC position. + if (config.contains_dc) { + result.nof_dc_overlap_bits = units::bits(config.nof_symbols * modulation_order); + } else { + result.nof_dc_overlap_bits = 0_bits; + } + return result; } diff --git a/lib/rlc/rlc_rx_am_entity.cpp b/lib/rlc/rlc_rx_am_entity.cpp index 4c75a5e47f..b3001a4a84 100644 --- a/lib/rlc/rlc_rx_am_entity.cpp +++ b/lib/rlc/rlc_rx_am_entity.cpp @@ -211,10 +211,19 @@ void rlc_rx_am_entity::handle_data_pdu(byte_buffer_slice buf) * - reassemble the RLC SDU from AMD PDU(s) with SN = x, remove RLC headers when doing so and deliver * the reassembled RLC SDU to upper layer; */ - rlc_rx_am_sdu_info& rx_sdu = (*rx_window)[header.sn]; - logger.log_info("RX SDU. sn={} sdu_len={}", header.sn, rx_sdu.sdu.length()); - metrics.metrics_add_sdus(1, rx_sdu.sdu.length()); - upper_dn.on_new_sdu(std::move(rx_sdu.sdu)); + rlc_rx_am_sdu_info& sdu_info = (*rx_window)[header.sn]; + expected sdu = reassemble_sdu(sdu_info, header.sn); + if (!sdu) { + logger.log_error("Dropped SDU, failed to reassemble. sn={}", header.sn); + metrics.metrics_add_lost_pdus(1); + // Do not pass empty SDU to upper layers and continue as normal to maintain state + } else { + logger.log_info("RX SDU. sn={} sdu_len={}", header.sn, sdu.value().length()); + metrics.metrics_add_sdus(1, sdu.value().length()); + upper_dn.on_new_sdu(std::move(sdu.value())); + } + // Release all buffers of sdu_data; keep "fully_received == true" for correct construction of status report + sdu_info.sdu_data = {}; /* * - if x = RX_Highest_Status, @@ -333,9 +342,8 @@ bool rlc_rx_am_entity::handle_full_data_sdu(const rlc_am_pdu_header& header, byt // Add new SN to RX window if no segments have been received yet rlc_rx_am_sdu_info& rx_sdu = rx_window->has_sn(header.sn) ? (*rx_window)[header.sn] : rx_window->add_sn(header.sn); - // Full SDU received. Clear segments and use full payload as SDU. - rx_sdu.segments.clear(); - rx_sdu.sdu = std::move(payload); + // Store the full SDU and flag it as complete. + rx_sdu.sdu_data = std::move(payload); rx_sdu.fully_received = true; rx_sdu.has_gap = false; return true; @@ -365,16 +373,6 @@ bool rlc_rx_am_entity::handle_segment_data_sdu(const rlc_am_pdu_header& header, // Check whether all segments have been received update_segment_inventory(rx_sdu); - logger.log_debug("Updated segment inventory. {}", rx_sdu); - if (rx_sdu.fully_received) { - // Assemble SDU from segments - for (const rlc_rx_am_sdu_segment& segm : rx_sdu.segments) { - logger.log_debug("Chaining segment. so={} len={}", segm.so, segm.payload.length()); - rx_sdu.sdu.append(segm.payload.copy()); - } - rx_sdu.segments.clear(); - logger.log_debug("Assembled SDU from segments. sn={} sdu_len={}", header.sn, rx_sdu.sdu.length()); - } return stored; } @@ -382,8 +380,13 @@ bool rlc_rx_am_entity::store_segment(rlc_rx_am_sdu_info& sdu_info, rlc_rx_am_sdu { // Section 5.2.3.2.2, discard segments with overlapping bytes - std::set::iterator cur_segment = sdu_info.segments.begin(); - while (cur_segment != sdu_info.segments.end()) { + if (!variant_holds_alternative(sdu_info.sdu_data)) { + // put an empty set + sdu_info.sdu_data = rlc_rx_am_sdu_info::segment_set_t{}; + } + rlc_rx_am_sdu_info::segment_set_t& segments = variant_get(sdu_info.sdu_data); + auto cur_segment = segments.begin(); + while (cur_segment != segments.end()) { uint32_t cur_last_byte = cur_segment->so + cur_segment->payload.length() - 1; uint32_t new_last_byte = new_segment.so + new_segment.payload.length() - 1; if (new_segment.so > cur_last_byte) { @@ -438,9 +441,9 @@ bool rlc_rx_am_entity::store_segment(rlc_rx_am_sdu_info& sdu_info, rlc_rx_am_sdu rlc_rx_am_sdu_segment cut_segment{*cur_segment}; cut_segment.payload.advance(new_last_byte + 1 - cur_segment->so); cut_segment.so = new_last_byte + 1; - sdu_info.segments.erase(cur_segment++); + segments.erase(cur_segment++); // insert cut segment as close as possible before (next) current segment - this is faster than plain insert - sdu_info.segments.insert(cur_segment, std::move(cut_segment)); + segments.insert(cur_segment, std::move(cut_segment)); // exit loop and insert new segment afterwards break; } @@ -448,16 +451,19 @@ bool rlc_rx_am_entity::store_segment(rlc_rx_am_sdu_info& sdu_info, rlc_rx_am_sdu // cur: bcde // new: ...abcdef... // remove current segment, check next segment - sdu_info.segments.erase(cur_segment++); + segments.erase(cur_segment++); } // insert new segment as close as possible before current segment - this is faster than plain insert - sdu_info.segments.insert(cur_segment, std::move(new_segment)); + segments.insert(cur_segment, std::move(new_segment)); return true; } void rlc_rx_am_entity::update_segment_inventory(rlc_rx_am_sdu_info& rx_sdu) const { - if (rx_sdu.segments.empty()) { + srsran_assert(variant_holds_alternative(rx_sdu.sdu_data), + "Invalid sdu_data variant for update of segment inventory"); + rlc_rx_am_sdu_info::segment_set_t& segments = variant_get(rx_sdu.sdu_data); + if (segments.empty()) { rx_sdu.fully_received = false; rx_sdu.has_gap = false; return; @@ -465,7 +471,7 @@ void rlc_rx_am_entity::update_segment_inventory(rlc_rx_am_sdu_info& rx_sdu) cons // Check for gaps and if all segments have been received uint32_t next_byte = 0; - for (const rlc_rx_am_sdu_segment& segm : rx_sdu.segments) { + for (const rlc_rx_am_sdu_segment& segm : segments) { if (segm.so != next_byte) { // Found gap: set flags and return rx_sdu.has_gap = true; @@ -485,6 +491,48 @@ void rlc_rx_am_entity::update_segment_inventory(rlc_rx_am_sdu_info& rx_sdu) cons rx_sdu.fully_received = false; } +expected rlc_rx_am_entity::reassemble_sdu(rlc_rx_am_sdu_info& sdu_info, uint32_t sn) +{ + // Sanity check + if (!sdu_info.fully_received) { + logger.log_error("Cannot reassemble SDU not marked as fully_received. sn={} {}", sn, sdu_info); + return {default_error_t{}}; + } + + expected sdu = byte_buffer_chain::create(); + if (!sdu) { + logger.log_error("Failed to create SDU buffer. sn={} {}", sn, sdu_info); + return {default_error_t{}}; + } + + if (variant_holds_alternative(sdu_info.sdu_data)) { + // Handling for full SDU + byte_buffer_slice& payload = variant_get(sdu_info.sdu_data); + if (!sdu.value().append(std::move(payload))) { + logger.log_error("Failed to append segment in SDU buffer. sn={} {}", sn, sdu_info); + return {default_error_t{}}; + } + } else if (variant_holds_alternative(sdu_info.sdu_data)) { + rlc_rx_am_sdu_info::segment_set_t& segments = variant_get(sdu_info.sdu_data); + for (const rlc_rx_am_sdu_segment& segm : segments) { + logger.log_debug("Chaining segment. sn={} so={} len={}", sn, segm.so, segm.payload.length()); + if (!sdu.value().append(segm.payload.copy())) { + logger.log_error("Failed to append segment in SDU buffer. sn={} so={} len={} {}", + sn, + segm.so, + segm.payload.length(), + sdu_info); + return {default_error_t{}}; + } + } + logger.log_debug("Assembled SDU from segments. sn={} sdu_len={}", sn, sdu.value().length()); + } else { + logger.log_error("Unhandled variant of sdu_data. sn={} {}", sn, sdu_info); + } + + return sdu; +} + void rlc_rx_am_entity::refresh_status_report() { status_builder->reset(); @@ -513,11 +561,16 @@ void rlc_rx_am_entity::refresh_status_report() logger.log_debug("Adding nack={}.", nack); status_builder->push_nack(nack); } else if (not(*rx_window)[i].fully_received) { + srsran_assert(variant_holds_alternative((*rx_window)[i].sdu_data), + "Invalid sdu_data variant of incomplete SDU in rx_window. sn={}", + i); + rlc_rx_am_sdu_info::segment_set_t& segments = + variant_get((*rx_window)[i].sdu_data); // Some segments were received, but not all. // NACK non consecutive missing bytes uint32_t last_so = 0; bool have_last_segment = false; - for (auto segm = (*rx_window)[i].segments.begin(); segm != (*rx_window)[i].segments.end(); segm++) { + for (auto segm = segments.begin(); segm != segments.end(); segm++) { if (segm->so != last_so) { // Some bytes were not received rlc_am_status_nack nack; @@ -531,8 +584,7 @@ void rlc_rx_am_entity::refresh_status_report() // Sanity check if (nack.so_start > nack.so_end) { // Print segment list - for (auto segm_it = (*rx_window)[i].segments.begin(); segm_it != (*rx_window)[i].segments.end(); - segm_it++) { + for (auto segm_it = segments.begin(); segm_it != segments.end(); segm_it++) { logger.log_error("Segment: so={} len={}", segm_it->so, segm_it->payload.length()); } logger.log_error("Invalid segment offsets in nack={} for segment so={}.", nack, segm->so); diff --git a/lib/rlc/rlc_rx_am_entity.h b/lib/rlc/rlc_rx_am_entity.h index 96986d6d15..82dd285476 100644 --- a/lib/rlc/rlc_rx_am_entity.h +++ b/lib/rlc/rlc_rx_am_entity.h @@ -25,6 +25,7 @@ #include "rlc_am_interconnect.h" #include "rlc_am_pdu.h" #include "rlc_rx_entity.h" +#include "srsran/adt/expected.h" #include "srsran/support/executors/task_executor.h" #include "srsran/support/sdu_window.h" #include "srsran/support/timers.h" @@ -45,16 +46,16 @@ struct rlc_rx_am_sdu_segment_cmp { bool operator()(const rlc_rx_am_sdu_segment& a, const rlc_rx_am_sdu_segment& b) const { return a.so < b.so; } }; -/// Container to collect received SDU segments and to assemble the SDU upon completion +/// Container for buffering of received SDUs or SDU segments until fully received. struct rlc_rx_am_sdu_info { - // TODO: Refactor this struct. - // Move the following rlc_rx_am methods here: - // - add segments without duplicates - // - assemble SDU - bool fully_received = false; - bool has_gap = false; - std::set segments; // Set of segments with SO as key - byte_buffer_chain sdu = {}; + using segment_set_t = std::set; // Set of segments with SO as key + + /// Flags the SDU as fully received or not. + bool fully_received = false; + /// Indicates a gap (i.e. a missing segment) among all already received segments. + bool has_gap = false; + /// Buffer for either a full SDU or a set of SDU segments. + variant sdu_data; }; /// \brief Rx state variables @@ -283,6 +284,13 @@ class rlc_rx_am_entity : public rlc_rx_entity, public rlc_rx_am_status_provider /// \param rx_sdu Container/Info object to be inspected void update_segment_inventory(rlc_rx_am_sdu_info& rx_sdu) const; + /// Reassembles a fully received SDU from buffered segment(s) in the SDU info object. + /// + /// \param sdu_info The SDU info to be reassembled. + /// \param sn Sequence number (for logging). + /// \return The reassembled SDU in case of success, default_error_t{} otherwise. + expected reassemble_sdu(rlc_rx_am_sdu_info& sdu_info, uint32_t sn); + /// Rebuilds the cached status_report according to missing SDUs and SDU segments in rx_window /// and resets the rx_window_changed flag void refresh_status_report(); @@ -324,12 +332,23 @@ struct formatter { auto format(const srsran::rlc_rx_am_sdu_info& info, FormatContext& ctx) -> decltype(std::declval().out()) { - return format_to(ctx.out(), - "nof_segments={} has_gap={} fully_received={} sdu_len={}", - info.segments.size(), - info.has_gap, - info.fully_received, - info.sdu.length()); + if (srsran::variant_holds_alternative(info.sdu_data)) { + // full SDU + const srsran::byte_buffer_slice& payload = srsran::variant_get(info.sdu_data); + return format_to( + ctx.out(), "has_gap={} fully_received={} sdu_len={}", info.has_gap, info.fully_received, payload.length()); + } else if (srsran::variant_holds_alternative(info.sdu_data)) { + // segmented SDU + const srsran::rlc_rx_am_sdu_info::segment_set_t& segments = + srsran::variant_get(info.sdu_data); + return format_to(ctx.out(), + "has_gap={} fully_received={} nof_segments={}", + info.has_gap, + info.fully_received, + segments.size()); + } + // unset default case - neither full SDU nor segmented SDU + return format_to(ctx.out(), "has_gap={} fully_received={}", info.has_gap, info.fully_received); } }; diff --git a/lib/rlc/rlc_rx_tm_entity.cpp b/lib/rlc/rlc_rx_tm_entity.cpp index a4b66c9cec..94bf80e855 100644 --- a/lib/rlc/rlc_rx_tm_entity.cpp +++ b/lib/rlc/rlc_rx_tm_entity.cpp @@ -41,11 +41,19 @@ rlc_rx_tm_entity::rlc_rx_tm_entity(uint32_t du_index, void rlc_rx_tm_entity::handle_pdu(byte_buffer_slice buf) { - metrics.metrics_add_pdus(1, buf.length()); + size_t pdu_len = buf.length(); + metrics.metrics_add_pdus(1, pdu_len); pcap.push_pdu(pcap_context, buf); - logger.log_info(buf.begin(), buf.end(), "RX SDU. sdu_len={}", buf.length()); - metrics.metrics_add_sdus(1, buf.length()); - upper_dn.on_new_sdu(std::move(buf)); + expected sdu = byte_buffer_chain::create(std::move(buf)); + if (!sdu) { + logger.log_error("Dropped SDU, failed to create SDU buffer. sdu_len={}", pdu_len); + metrics.metrics_add_lost_pdus(1); + return; + } + + logger.log_info(sdu.value().begin(), sdu.value().end(), "RX SDU. sdu_len={}", sdu.value().length()); + metrics.metrics_add_sdus(1, sdu.value().length()); + upper_dn.on_new_sdu(std::move(sdu.value())); } diff --git a/lib/rlc/rlc_rx_um_entity.cpp b/lib/rlc/rlc_rx_um_entity.cpp index 10ea88c3ec..885900d4f6 100644 --- a/lib/rlc/rlc_rx_um_entity.cpp +++ b/lib/rlc/rlc_rx_um_entity.cpp @@ -82,13 +82,22 @@ void rlc_rx_um_entity::handle_pdu(byte_buffer_slice buf) // check if PDU contains a SN if (header.si == rlc_si_field::full_sdu) { + size_t sdu_len = payload.length(); + expected sdu = byte_buffer_chain::create(std::move(payload)); + if (!sdu) { + logger.log_error("Dropped SDU, failed to create SDU buffer. sdu_len={}", sdu_len); + metrics.metrics_add_lost_pdus(1); + return; + } + // deliver to upper layer - logger.log_info("RX SDU. sdu_len={}", payload.length()); - metrics.metrics_add_sdus(1, payload.length()); - upper_dn.on_new_sdu(std::move(payload)); + logger.log_info("RX SDU. sdu_len={}", sdu.value().length()); + metrics.metrics_add_sdus(1, sdu.value().length()); + upper_dn.on_new_sdu(std::move(sdu.value())); // Nothing else to do here ... return; } + if (sn_invalid_for_rx_buffer(header.sn)) { logger.log_info("Discarded PDU. sn={} payload_len={}", header.sn, payload.length()); // Nothing else to do here ... @@ -110,10 +119,19 @@ void rlc_rx_um_entity::handle_pdu(byte_buffer_slice buf) * - reassemble the RLC SDU from all byte segments with SN = x, remove RLC headers and deliver the reassembled * RLC SDU to upper layer; */ - rlc_rx_um_sdu_info& rx_sdu = (*rx_window)[header.sn]; - logger.log_info("RX SDU. sn={} sdu_len={}", header.sn, rx_sdu.sdu.length()); - metrics.metrics_add_sdus(1, rx_sdu.sdu.length()); - upper_dn.on_new_sdu(std::move(rx_sdu.sdu)); + rlc_rx_um_sdu_info& sdu_info = (*rx_window)[header.sn]; + expected sdu = reassemble_sdu(sdu_info, header.sn); + if (!sdu) { + logger.log_error("Dropped SDU, failed to reassemble. sn={}", header.sn); + metrics.metrics_add_lost_pdus(1); + // Do not pass empty SDU to upper layers and continue as normal to maintain state + } else { + logger.log_info("RX SDU. sn={} sdu_len={}", header.sn, sdu.value().length()); + metrics.metrics_add_sdus(1, sdu.value().length()); + upper_dn.on_new_sdu(std::move(sdu.value())); + } + // Release all segments + sdu_info.segments.clear(); /* * - if x = RX_Next_Reassembly: @@ -281,16 +299,6 @@ bool rlc_rx_um_entity::handle_segment_data_sdu(const rlc_um_pdu_header& header, // Check whether all segments have been received update_segment_inventory(rx_sdu); - logger.log_debug("Updated segment inventory. {}", rx_sdu); - if (rx_sdu.fully_received) { - // Assemble SDU from segments - for (const rlc_rx_um_sdu_segment& segm : rx_sdu.segments) { - logger.log_debug("Chaining segment. so={} len={}", segm.so, segm.payload.length()); - rx_sdu.sdu.append(segm.payload.copy()); - } - rx_sdu.segments.clear(); - logger.log_debug("Assembled SDU from segments. sn={} sdu_len={}", header.sn, rx_sdu.sdu.length()); - } return stored; } @@ -299,7 +307,7 @@ bool rlc_rx_um_entity::store_segment(rlc_rx_um_sdu_info& sdu_info, rlc_rx_um_sdu // Section 5.2.2.2.2; Although not supposed to happen in UM, we check and discard segments with overlapping bytes // as described in Section 5.2.3.2.2 for AM. - std::set::iterator cur_segment = sdu_info.segments.begin(); + auto cur_segment = sdu_info.segments.begin(); while (cur_segment != sdu_info.segments.end()) { uint32_t cur_last_byte = cur_segment->so + cur_segment->payload.length() - 1; uint32_t new_last_byte = new_segment.so + new_segment.payload.length() - 1; @@ -402,6 +410,36 @@ void rlc_rx_um_entity::update_segment_inventory(rlc_rx_um_sdu_info& rx_sdu) cons rx_sdu.fully_received = false; } +expected rlc_rx_um_entity::reassemble_sdu(rlc_rx_um_sdu_info& sdu_info, uint32_t sn) +{ + // Sanity check + if (!sdu_info.fully_received) { + logger.log_error("Cannot reassemble SDU not marked as fully_received. sn={} {}", sn, sdu_info); + return {default_error_t{}}; + } + + expected sdu = byte_buffer_chain::create(); + if (!sdu) { + logger.log_error("Failed to create SDU buffer. sn={} {}", sn, sdu_info); + return {default_error_t{}}; + } + + for (const rlc_rx_um_sdu_segment& segm : sdu_info.segments) { + logger.log_debug("Chaining segment. sn={} so={} len={}", sn, segm.so, segm.payload.length()); + if (!sdu.value().append(segm.payload.copy())) { + logger.log_error("Failed to append segment in SDU buffer. sn={} so={} len={} {}", + sn, + segm.so, + segm.payload.length(), + sdu_info); + return {default_error_t{}}; + } + } + logger.log_debug("Assembled SDU from segments. sn={} sdu_len={}", sn, sdu.value().length()); + + return sdu; +} + // TS 38.322 v16.2.0 Sec. 5.2.2.2.4 void rlc_rx_um_entity::on_expired_reassembly_timer() { diff --git a/lib/rlc/rlc_rx_um_entity.h b/lib/rlc/rlc_rx_um_entity.h index 46fb6d01f9..584de56bd1 100644 --- a/lib/rlc/rlc_rx_um_entity.h +++ b/lib/rlc/rlc_rx_um_entity.h @@ -24,6 +24,7 @@ #include "rlc_rx_entity.h" #include "rlc_um_pdu.h" +#include "srsran/adt/expected.h" #include "srsran/support/executors/task_executor.h" #include "srsran/support/sdu_window.h" #include "srsran/support/timers.h" @@ -44,16 +45,16 @@ struct rlc_rx_um_sdu_segment_cmp { bool operator()(const rlc_rx_um_sdu_segment& a, const rlc_rx_um_sdu_segment& b) const { return a.so < b.so; } }; -/// Container to collect received SDU segments and to assemble the SDU upon completion +/// Container for buffering of received SDU segments until fully received. struct rlc_rx_um_sdu_info { - // TODO: Refactor this struct. - // Move the following rlc_rx_um methods here: - // - add segments without duplicates - // - assemble SDU - bool fully_received = false; - bool has_gap = false; - std::set segments; // Set of segments with SO as key - byte_buffer_chain sdu = {}; + using segment_set_t = std::set; // Set of segments with SO as key + + /// Flags the SDU as fully received or not. + bool fully_received = false; + /// Indicates a gap (i.e. a missing segment) among all already received segments. + bool has_gap = false; + /// Buffer for set of SDU segments. + segment_set_t segments; }; /// \brief Rx state variables @@ -144,6 +145,13 @@ class rlc_rx_um_entity : public rlc_rx_entity /// \param rx_sdu Container/Info object to be inspected void update_segment_inventory(rlc_rx_um_sdu_info& rx_sdu) const; + /// Reassembles a fully received SDU from buffered segments in the SDU info object. + /// + /// \param sdu_info The SDU info to be reassembled. + /// \param sn Sequence number (for logging). + /// \return The reassembled SDU in case of success, default_error_t{} otherwise. + expected reassemble_sdu(rlc_rx_um_sdu_info& sdu_info, uint32_t sn); + /// Creates the rx_window according to sn_size /// \param sn_size Size of the sequence number (SN) /// \return unique pointer to rx_window instance @@ -178,11 +186,10 @@ struct formatter { -> decltype(std::declval().out()) { return format_to(ctx.out(), - "nof_segments={} has_gap={} fully_received={} sdu_len={}", - info.segments.size(), + "has_gap={} fully_received={} nof_segments={}", info.has_gap, info.fully_received, - info.sdu.length()); + info.segments.size()); } }; diff --git a/lib/rlc/rlc_sdu_queue_lockfree.h b/lib/rlc/rlc_sdu_queue_lockfree.h index 4a0072df5b..39bd88b313 100644 --- a/lib/rlc/rlc_sdu_queue_lockfree.h +++ b/lib/rlc/rlc_sdu_queue_lockfree.h @@ -211,6 +211,19 @@ class rlc_sdu_queue_lockfree /// \return The number of buffered SDU bytes that are not marked as discarded. uint32_t size_bytes() const { return n_bytes.load(std::memory_order_relaxed); } + /// \brief Container for return value of \c get_state function. + struct state { + uint32_t n_sdus; ///< Number of buffered SDUs that are not marked as discarded. + uint32_t n_bytes; ///< Number of buffered bytes that are not marked as discarded. + }; + + /// \brief Reads the state of the queue, i.e. number of buffered SDUs and bytes that are not marked as discarded. + /// + /// This function may be called by any thread. + /// + /// \return Current state of the queue. + state get_state() const { return {size_sdus(), size_bytes()}; } + /// \brief Checks if the internal queue is empty. /// /// This function may be called by any thread. @@ -293,7 +306,7 @@ class rlc_sdu_queue_lockfree namespace fmt { template <> -struct formatter { +struct formatter { template auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { @@ -301,10 +314,10 @@ struct formatter { } template - auto format(const srsran::rlc_sdu_queue_lockfree& q, FormatContext& ctx) + auto format(const srsran::rlc_sdu_queue_lockfree::state& state, FormatContext& ctx) -> decltype(std::declval().out()) { - return format_to(ctx.out(), "queued_sdus={} queued_bytes={}", q.size_sdus(), q.size_bytes()); + return format_to(ctx.out(), "queued_sdus={} queued_bytes={}", state.n_sdus, state.n_bytes); } }; diff --git a/lib/rlc/rlc_tx_am_entity.cpp b/lib/rlc/rlc_tx_am_entity.cpp index 94f1c6c4e5..30d6b78894 100644 --- a/lib/rlc/rlc_tx_am_entity.cpp +++ b/lib/rlc/rlc_tx_am_entity.cpp @@ -79,12 +79,16 @@ void rlc_tx_am_entity::handle_sdu(rlc_sdu sdu) sdu.time_of_arrival = std::chrono::high_resolution_clock::now(); size_t sdu_length = sdu.buf.length(); if (sdu_queue.write(sdu)) { - logger.log_info( - sdu.buf.begin(), sdu.buf.end(), "TX SDU. sdu_len={} pdcp_sn={} {}", sdu.buf.length(), sdu.pdcp_sn, sdu_queue); + logger.log_info(sdu.buf.begin(), + sdu.buf.end(), + "TX SDU. sdu_len={} pdcp_sn={} {}", + sdu.buf.length(), + sdu.pdcp_sn, + sdu_queue.get_state()); metrics.metrics_add_sdus(1, sdu_length); handle_changed_buffer_state(); } else { - logger.log_warning("Dropped SDU. sdu_len={} pdcp_sn={} {}", sdu_length, sdu.pdcp_sn, sdu_queue); + logger.log_warning("Dropped SDU. sdu_len={} pdcp_sn={} {}", sdu_length, sdu.pdcp_sn, sdu_queue.get_state()); metrics.metrics_add_lost_sdus(1); } } @@ -192,7 +196,7 @@ size_t rlc_tx_am_entity::build_new_pdu(span rlc_pdu_buf) // Read new SDU from TX queue rlc_sdu sdu; - logger.log_debug("Reading SDU from sdu_queue. {}", sdu_queue); + logger.log_debug("Reading SDU from sdu_queue. {}", sdu_queue.get_state()); if (not sdu_queue.read(sdu)) { logger.log_debug("SDU queue empty. grant_len={}", grant_len); return 0; diff --git a/lib/rlc/rlc_tx_tm_entity.cpp b/lib/rlc/rlc_tx_tm_entity.cpp index c8d28220e9..eace43776e 100644 --- a/lib/rlc/rlc_tx_tm_entity.cpp +++ b/lib/rlc/rlc_tx_tm_entity.cpp @@ -49,11 +49,11 @@ void rlc_tx_tm_entity::handle_sdu(rlc_sdu sdu_) { size_t sdu_len = sdu_.buf.length(); if (sdu_queue.write(sdu_)) { - logger.log_info(sdu_.buf.begin(), sdu_.buf.end(), "TX SDU. sdu_len={} {}", sdu_len, sdu_queue); + logger.log_info(sdu_.buf.begin(), sdu_.buf.end(), "TX SDU. sdu_len={} {}", sdu_len, sdu_queue.get_state()); metrics.metrics_add_sdus(1, sdu_len); handle_changed_buffer_state(); } else { - logger.log_info("Dropped SDU. sdu_len={} {}", sdu_len, sdu_queue); + logger.log_info("Dropped SDU. sdu_len={} {}", sdu_len, sdu_queue.get_state()); metrics.metrics_add_lost_sdus(1); } } @@ -73,7 +73,7 @@ size_t rlc_tx_tm_entity::pull_pdu(span mac_sdu_buf) // Get a new SDU, if none is currently being transmitted if (sdu.buf.empty()) { - logger.log_debug("Reading SDU from sdu_queue. {}", sdu_queue); + logger.log_debug("Reading SDU from sdu_queue. {}", sdu_queue.get_state()); if (not sdu_queue.read(sdu)) { logger.log_debug("SDU queue empty. grant_len={}", grant_len); return 0; diff --git a/lib/rlc/rlc_tx_um_entity.cpp b/lib/rlc/rlc_tx_um_entity.cpp index 06fb338292..5c8cc25493 100644 --- a/lib/rlc/rlc_tx_um_entity.cpp +++ b/lib/rlc/rlc_tx_um_entity.cpp @@ -62,11 +62,11 @@ void rlc_tx_um_entity::handle_sdu(rlc_sdu sdu_) "TX SDU. sdu_len={} pdcp_sn={} {}", sdu_.buf.length(), sdu_.pdcp_sn, - sdu_queue); + sdu_queue.get_state()); metrics.metrics_add_sdus(1, sdu_length); handle_changed_buffer_state(); } else { - logger.log_info("Dropped SDU. sdu_len={} pdcp_sn={} {}", sdu_length, sdu_.pdcp_sn, sdu_queue); + logger.log_info("Dropped SDU. sdu_len={} pdcp_sn={} {}", sdu_length, sdu_.pdcp_sn, sdu_queue.get_state()); metrics.metrics_add_lost_sdus(1); } } @@ -104,7 +104,7 @@ size_t rlc_tx_um_entity::pull_pdu(span mac_sdu_buf) // Get a new SDU, if none is currently being transmitted if (sdu.buf.empty()) { srsran_sanity_check(next_so == 0, "New TX SDU, but next_so={} > 0.", next_so); - logger.log_debug("Reading SDU from sdu_queue. {}", sdu_queue); + logger.log_debug("Reading SDU from sdu_queue. {}", sdu_queue.get_state()); if (not sdu_queue.read(sdu)) { logger.log_debug("SDU queue empty. grant_len={}", grant_len); return {}; diff --git a/lib/rrc/ue/adapters/pdcp_adapters.h b/lib/rrc/ue/adapters/pdcp_adapters.h index d807782171..eaf8c6e004 100644 --- a/lib/rrc/ue/adapters/pdcp_adapters.h +++ b/lib/rrc/ue/adapters/pdcp_adapters.h @@ -22,10 +22,9 @@ #pragma once -#include "srsran/f1ap/cu_cp/f1ap_cu.h" #include "srsran/pdcp/pdcp_rx.h" #include "srsran/pdcp/pdcp_tx.h" -#include "srsran/rrc/rrc.h" +#include "srsran/ran/cause/ngap_cause.h" namespace srsran { namespace srs_cu_cp { @@ -48,26 +47,30 @@ class pdcp_rrc_ue_rx_adapter : public pdcp_rx_upper_data_notifier class pdcp_rx_control_rrc_ue_adapter : public pdcp_rx_upper_control_notifier { public: - explicit pdcp_rx_control_rrc_ue_adapter() - { - // TODO: connect a RRC handler - srslog::fetch_basic_logger("PDCP").debug("No RRC handler for PDCP Rx control events. All events will be ignored."); - } + pdcp_rx_control_rrc_ue_adapter() = default; void on_protocol_failure() override { - srslog::fetch_basic_logger("PDCP").warning("Ignoring on_protocol_failure() from PDCP Rx: No RRC handler."); + srslog::fetch_basic_logger("PDCP").warning("Requesting UE release. Cause: Received protocol failure from PDCP Rx"); + cause = cause_protocol_t::unspecified; } void on_integrity_failure() override { - srslog::fetch_basic_logger("PDCP").warning("Ignoring on_integrity_failure() from PDCP Rx: No RRC handler."); + srslog::fetch_basic_logger("PDCP").warning("Requesting UE release. Cause: Received integrity failure from PDCP Rx"); + cause = cause_protocol_t::unspecified; } void on_max_count_reached() override { - srslog::fetch_basic_logger("PDCP").warning("Ignoring on_max_count_reached() from PDCP Rx: No RRC handler."); + srslog::fetch_basic_logger("PDCP").warning("Requesting UE release. Cause: Max count reached from PDCP Rx"); + cause = cause_protocol_t::unspecified; } + + ngap_cause_t get_failure_cause() { return cause; } + +private: + ngap_cause_t cause; }; /// Adapter between PDCP and RRC UE for DL PDUs @@ -93,21 +96,24 @@ class pdcp_rrc_ue_tx_adapter : public pdcp_tx_lower_notifier class pdcp_tx_control_rrc_ue_adapter : public pdcp_tx_upper_control_notifier { public: - explicit pdcp_tx_control_rrc_ue_adapter() - { - // TODO: connect a RRC handler - srslog::fetch_basic_logger("PDCP").debug("No RRC handler for PDCP Tx control events. All events will be ignored."); - } + pdcp_tx_control_rrc_ue_adapter() = default; void on_protocol_failure() override { - srslog::fetch_basic_logger("PDCP").warning("Ignoring on_protocol_failure() from PDCP Tx: No RRC handler."); + srslog::fetch_basic_logger("PDCP").warning("Requesting UE release. Cause: Received protocol failure from PDCP Tx"); + cause = cause_protocol_t::unspecified; } void on_max_count_reached() override { - srslog::fetch_basic_logger("PDCP").warning("Ignoring on_max_count_reached() from PDCP Tx: No RRC handler."); + srslog::fetch_basic_logger("PDCP").warning("Requesting UE release. Cause: Max count reached from PDCP Tx"); + cause = cause_protocol_t::unspecified; } + + ngap_cause_t get_failure_cause() { return cause; } + +private: + ngap_cause_t cause; }; } // namespace srs_cu_cp diff --git a/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.cpp b/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.cpp index c2f1886cf3..a3a47c1fd8 100644 --- a/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.cpp +++ b/lib/rrc/ue/procedures/rrc_reconfiguration_procedure.cpp @@ -22,7 +22,7 @@ #include "rrc_reconfiguration_procedure.h" #include "../rrc_asn1_helpers.h" -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/ngap_cause.h" using namespace srsran; using namespace srsran::srs_cu_cp; @@ -81,11 +81,11 @@ void rrc_reconfiguration_procedure::operator()(coro_context>& c // Notify NGAP to request UE context release from AMF CORO_AWAIT_VALUE(release_request_sent, ngap_ctrl_notifier.on_ue_context_release_request( - {context.ue_index, {}, cause_radio_network_t::release_due_to_ngran_generated_reason})); + {context.ue_index, {}, ngap_cause_radio_network_t::release_due_to_ngran_generated_reason})); if (!release_request_sent) { // If NGAP release request was not sent to AMF, release UE from DU processor, RRC and F1AP - CORO_AWAIT( - du_processor_notifier.on_ue_context_release_command({context.ue_index, cause_radio_network_t::unspecified})); + CORO_AWAIT(du_processor_notifier.on_ue_context_release_command( + {context.ue_index, ngap_cause_radio_network_t::unspecified})); } } diff --git a/lib/rrc/ue/procedures/rrc_reestablishment_procedure.cpp b/lib/rrc/ue/procedures/rrc_reestablishment_procedure.cpp index 196af31a68..2b106dd783 100644 --- a/lib/rrc/ue/procedures/rrc_reestablishment_procedure.cpp +++ b/lib/rrc/ue/procedures/rrc_reestablishment_procedure.cpp @@ -114,7 +114,7 @@ void rrc_reestablishment_procedure::operator()(coro_context>& c "\"{}\" for old_ue={} failed. Requesting UE context release", name(), reestablishment_context.ue_index); // Release the old UE ue_context_release_request.ue_index = context.ue_index; - ue_context_release_request.cause = cause_radio_network_t::unspecified; + ue_context_release_request.cause = ngap_cause_radio_network_t::unspecified; CORO_AWAIT(ngap_ctrl_notifier.on_ue_context_release_request(ue_context_release_request)); } else { logger.log_debug("\"{}\" for old_ue={} finalized", name(), reestablishment_context.ue_index); @@ -157,7 +157,7 @@ async_task rrc_reestablishment_procedure::handle_rrc_reestablishment_fallb logger.log_debug("old_ue={} was not fully attached yet. Requesting UE context release", reestablishment_context.ue_index); ue_context_release_request.ue_index = reestablishment_context.ue_index; - ue_context_release_request.cause = cause_radio_network_t::unspecified; + ue_context_release_request.cause = ngap_cause_radio_network_t::unspecified; CORO_AWAIT(ngap_ctrl_notifier.on_ue_context_release_request(ue_context_release_request)); } diff --git a/lib/rrc/ue/procedures/rrc_setup_procedure.cpp b/lib/rrc/ue/procedures/rrc_setup_procedure.cpp index 1fb969c723..a5a0eff330 100644 --- a/lib/rrc/ue/procedures/rrc_setup_procedure.cpp +++ b/lib/rrc/ue/procedures/rrc_setup_procedure.cpp @@ -91,7 +91,10 @@ void rrc_setup_procedure::send_rrc_setup() dl_ccch_msg_s dl_ccch_msg; dl_ccch_msg.msg.set_c1().set_rrc_setup(); rrc_setup_s& rrc_setup = dl_ccch_msg.msg.c1().rrc_setup(); - fill_asn1_rrc_setup_msg(rrc_setup, du_to_cu_container, transaction.id()); + if (!fill_asn1_rrc_setup_msg(rrc_setup, du_to_cu_container, transaction.id())) { + logger.log_warning("ASN1 RRC setup message fill failed"); + return; + } rrc_ue.on_new_dl_ccch(dl_ccch_msg); } diff --git a/lib/rrc/ue/rrc_asn1_helpers.h b/lib/rrc/ue/rrc_asn1_helpers.h index 7059313f8e..fdca2ea466 100644 --- a/lib/rrc/ue/rrc_asn1_helpers.h +++ b/lib/rrc/ue/rrc_asn1_helpers.h @@ -34,7 +34,8 @@ namespace srs_cu_cp { /// \brief Fills ASN.1 RRC Setup struct. /// \param[out] rrc_setup The RRC Setup ASN.1 struct to fill. /// \param[in] init_ul_rrc_transfer_msg The Init_UL_RRC_Transfer message received by the CU. -inline void +/// \return True on success, otherwise false. +inline bool fill_asn1_rrc_setup_msg(asn1::rrc_nr::rrc_setup_s& rrc_setup, const byte_buffer& mcg, uint8_t rrc_transaction_id) { using namespace asn1::rrc_nr; @@ -49,8 +50,12 @@ fill_asn1_rrc_setup_msg(asn1::rrc_nr::rrc_setup_s& rrc_setup, const byte_buffer& // Copy cell config from DU_to_CU_RRC_Container to master cell group auto& master_cell_group = setup_ies.master_cell_group; - master_cell_group.resize(mcg.length()); + if (!master_cell_group.resize(mcg.length())) { + return false; + } + std::copy(mcg.begin(), mcg.end(), master_cell_group.begin()); + return true; } /// Extracts transaction id of RRC Setup complete message. diff --git a/lib/rrc/ue/rrc_ue_impl.cpp b/lib/rrc/ue/rrc_ue_impl.cpp index 97347abd38..4175147451 100644 --- a/lib/rrc/ue/rrc_ue_impl.cpp +++ b/lib/rrc/ue/rrc_ue_impl.cpp @@ -171,7 +171,7 @@ byte_buffer rrc_ue_impl::get_packed_handover_preparation_message() return pack_into_pdu(ho_prep, "handover preparation info"); } -void rrc_ue_impl::on_ue_release_required(const cause_t& cause) +void rrc_ue_impl::on_ue_release_required(const ngap_cause_t& cause) { task_sched.schedule_async_task( launch_async([this, ngap_release_result = bool{false}, cause](coro_context>& ctx) mutable { diff --git a/lib/rrc/ue/rrc_ue_impl.h b/lib/rrc/ue/rrc_ue_impl.h index 09af9b8c15..93a6fbde81 100644 --- a/lib/rrc/ue/rrc_ue_impl.h +++ b/lib/rrc/ue/rrc_ue_impl.h @@ -109,7 +109,7 @@ class rrc_ue_impl final : public rrc_ue_interface // rrc_ue_setup_proc_notifier void on_new_dl_ccch(const asn1::rrc_nr::dl_ccch_msg_s& dl_ccch_msg) override; - void on_ue_release_required(const cause_t& cause) override; + void on_ue_release_required(const ngap_cause_t& cause) override; // rrc_ue_security_mode_command_proc_notifier void on_new_dl_dcch(srb_id_t srb_id, const asn1::rrc_nr::dl_dcch_msg_s& dl_ccch_msg) override; diff --git a/lib/rrc/ue/rrc_ue_message_handlers.cpp b/lib/rrc/ue/rrc_ue_message_handlers.cpp index 3ece31208d..c2d150c070 100644 --- a/lib/rrc/ue/rrc_ue_message_handlers.cpp +++ b/lib/rrc/ue/rrc_ue_message_handlers.cpp @@ -45,7 +45,7 @@ void rrc_ue_impl::handle_ul_ccch_pdu(byte_buffer pdu) if (ul_ccch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or ul_ccch_msg.msg.type().value != ul_ccch_msg_type_c::types_opts::c1) { logger.log_error(pdu.begin(), pdu.end(), "Failed to unpack CCCH UL PDU"); - on_ue_release_required(cause_radio_network_t::unspecified); + on_ue_release_required(ngap_cause_radio_network_t::unspecified); return; } } @@ -63,7 +63,7 @@ void rrc_ue_impl::handle_ul_ccch_pdu(byte_buffer pdu) break; default: logger.log_error("Unsupported CCCH UL message type"); - on_ue_release_required(cause_radio_network_t::unspecified); + on_ue_release_required(ngap_cause_radio_network_t::unspecified); } } @@ -72,7 +72,7 @@ void rrc_ue_impl::handle_rrc_setup_request(const asn1::rrc_nr::rrc_setup_request // Perform various checks to make sure we can serve the RRC Setup Request if (not cu_cp_notifier.on_ue_setup_request()) { logger.log_error("Sending Connection Reject. Cause: RRC connections not allowed"); - on_ue_release_required(cause_radio_network_t::unspecified); + on_ue_release_required(ngap_cause_radio_network_t::unspecified); return; } @@ -80,7 +80,7 @@ void rrc_ue_impl::handle_rrc_setup_request(const asn1::rrc_nr::rrc_setup_request // If the DU to CU container is missing, assume the DU can't serve the UE, so the CU-CP should reject the UE, see // TS 38.473 section 8.4.1.2. logger.log_debug("Sending rrcReject. Cause: DU is not able to serve the UE"); - on_ue_release_required(cause_radio_network_t::unspecified); + on_ue_release_required(ngap_cause_radio_network_t::unspecified); return; } @@ -103,7 +103,7 @@ void rrc_ue_impl::handle_rrc_setup_request(const asn1::rrc_nr::rrc_setup_request break; default: logger.log_error("Unsupported RrcSetupRequest"); - on_ue_release_required(cause_radio_network_t::unspecified); + on_ue_release_required(ngap_cause_radio_network_t::unspecified); return; } context.connection_cause.value = request_ies.establishment_cause.value; @@ -200,13 +200,15 @@ void rrc_ue_impl::handle_ul_dcch_pdu(const srb_id_t srb_id, byte_buffer pdcp_pdu } // Unpack PDCP PDU - byte_buffer rrc_pdu = context.srbs.at(srb_id).unpack_pdcp_pdu(std::move(pdcp_pdu)); - if (rrc_pdu.empty()) { - logger.log_warning("Dropping empty PDU. RX {}", srb_id); - logger.log_debug("original len={}, new_len={}", pdcp_pdu.length(), rrc_pdu.length()); + pdcp_result pdcp_unpacking_result = context.srbs.at(srb_id).unpack_pdcp_pdu(std::move(pdcp_pdu)); + if (!pdcp_unpacking_result.is_successful()) { + logger.log_info("Requesting UE release. Cause: PDCP unpacking failed with {}", + pdcp_unpacking_result.get_failure_cause()); + on_ue_release_required(pdcp_unpacking_result.get_failure_cause()); return; } + byte_buffer rrc_pdu = pdcp_unpacking_result.get_pdu(); handle_pdu(srb_id, std::move(rrc_pdu)); } @@ -286,7 +288,7 @@ async_task rrc_ue_impl::handle_handover_reconfiguration_complete_expected( [this, timeout_ms, transaction_id, transaction = rrc_transaction{}](coro_context>& ctx) mutable { CORO_BEGIN(ctx); - logger.log_debug("Awaiting RRC Reconfiguration Complete"); + logger.log_debug("Awaiting RRC Reconfiguration Complete (timeout={}ms)", timeout_ms.count()); // create new transaction for RRC Reconfiguration procedure transaction = event_mng->transactions.create_transaction(transaction_id, timeout_ms); @@ -297,7 +299,7 @@ async_task rrc_ue_impl::handle_handover_reconfiguration_complete_expected( logger.log_debug("Received RRC Reconfiguration Complete after HO"); procedure_result = true; } else { - logger.log_debug("Did not receive RRC Reconfiguration Complete after HO (timeout)"); + logger.log_debug("Did not receive RRC Reconfiguration Complete after HO. Cause: timeout"); } CORO_RETURN(procedure_result); @@ -345,9 +347,17 @@ rrc_ue_release_context rrc_ue_impl::get_rrc_ue_release_context() dl_dcch_msg.msg.set_c1().set_rrc_release().crit_exts.set_rrc_release(); // pack DL CCCH msg - release_context.rrc_release_pdu = + pdcp_result pdcp_packing_result = context.srbs.at(srb_id_t::srb1).pack_rrc_pdu(pack_into_pdu(dl_dcch_msg, "RRCRelease")); - release_context.srb_id = srb_id_t::srb1; + if (!pdcp_packing_result.is_successful()) { + logger.log_info("Requesting UE release. Cause: PDCP packing failed with {}", + pdcp_packing_result.get_failure_cause()); + on_ue_release_required(pdcp_packing_result.get_failure_cause()); + return release_context; + } + + release_context.rrc_release_pdu = pdcp_packing_result.get_pdu(); + release_context.srb_id = srb_id_t::srb1; // Log Tx message log_rrc_message(logger, Tx, release_context.rrc_release_pdu, dl_dcch_msg, "DCCH DL"); @@ -366,7 +376,8 @@ rrc_ue_release_context rrc_ue_impl::get_rrc_ue_release_context() optional rrc_ue_impl::generate_meas_config(optional current_meas_config) { // (Re-)generate measurement config and return result. - context.meas_cfg = measurement_notifier.on_measurement_config_request(context.cell.cgi.nci, current_meas_config); + context.meas_cfg = + measurement_notifier.on_measurement_config_request(context.ue_index, context.cell.cgi.nci, current_meas_config); return context.meas_cfg; } diff --git a/lib/rrc/ue/rrc_ue_message_senders.cpp b/lib/rrc/ue/rrc_ue_message_senders.cpp index 3ef6839397..fc80548bd9 100644 --- a/lib/rrc/ue/rrc_ue_message_senders.cpp +++ b/lib/rrc/ue/rrc_ue_message_senders.cpp @@ -56,7 +56,15 @@ void rrc_ue_impl::send_dl_dcch(srb_id_t srb_id, const dl_dcch_msg_s& dl_dcch_msg log_rrc_message(logger, Tx, pdu, dl_dcch_msg, "DCCH DL"); // pack PDCP PDU and send down the stack - byte_buffer pdcp_pdu = context.srbs.at(srb_id).pack_rrc_pdu(std::move(pdu)); + auto pdcp_packing_result = context.srbs.at(srb_id).pack_rrc_pdu(std::move(pdu)); + if (!pdcp_packing_result.is_successful()) { + logger.log_info("Requesting UE release. Cause: PDCP packing failed with {}", + pdcp_packing_result.get_failure_cause()); + on_ue_release_required(pdcp_packing_result.get_failure_cause()); + return; + } + + byte_buffer pdcp_pdu = pdcp_packing_result.get_pdu(); logger.log_debug(pdcp_pdu.begin(), pdcp_pdu.end(), "TX {} PDU", context.ue_index, context.c_rnti, srb_id); f1ap_pdu_notifier.on_new_rrc_pdu(srb_id, std::move(pdcp_pdu)); } diff --git a/lib/rrc/ue/rrc_ue_srb_context.h b/lib/rrc/ue/rrc_ue_srb_context.h index a5dc7a5984..711276bdc8 100644 --- a/lib/rrc/ue/rrc_ue_srb_context.h +++ b/lib/rrc/ue/rrc_ue_srb_context.h @@ -28,6 +28,17 @@ namespace srsran { namespace srs_cu_cp { +struct pdcp_result { + variant result; + + /// Whether the packing/unpacking was successful. + bool is_successful() const { return variant_holds_alternative(result); } + + ngap_cause_t get_failure_cause() const { return variant_get(result); } + + byte_buffer get_pdu() const { return byte_buffer{variant_get(result)}; } +}; + /// Additional context of a SRB containing notifiers to PDCP, i.e. SRB1 and SRB2. struct srb_pdcp_context { std::unique_ptr entity; @@ -47,7 +58,10 @@ struct srb_pdcp_context { srb_pdcp.tx_upper_cn = &rrc_tx_control_notifier; srb_pdcp.rx_upper_dn = &rrc_rx_data_notifier; srb_pdcp.rx_upper_cn = &rrc_rx_control_notifier; - srb_pdcp.timers = timers; + // Uplink, Downlink and Control run in the same executor, hence all timer factories are the same. + srb_pdcp.ue_dl_timer_factory = timers; + srb_pdcp.ue_ul_timer_factory = timers; + srb_pdcp.ue_ctrl_timer_factory = timers; // create PDCP entity entity = create_pdcp_entity(srb_pdcp); @@ -103,17 +117,33 @@ class ue_srb_context } // Add ciphering and integrity protection to an RRC PDU. - byte_buffer pack_rrc_pdu(byte_buffer rrc_pdu) + pdcp_result pack_rrc_pdu(byte_buffer rrc_pdu) { pdcp_context.entity->get_tx_upper_data_interface().handle_sdu(std::move(rrc_pdu)); - return pdcp_context.pdcp_tx_notifier.get_pdcp_pdu(); + + byte_buffer packed_pdu = pdcp_context.pdcp_tx_notifier.get_pdcp_pdu(); + + // If the PDCP layer failed to pack the PDU, return the failure cause. + if (packed_pdu.empty()) { + return pdcp_result{pdcp_context.rrc_tx_control_notifier.get_failure_cause()}; + } + + return pdcp_result{std::move(packed_pdu)}; } // Decipher and verify integrity of an PDCP PDU. - byte_buffer unpack_pdcp_pdu(byte_buffer pdcp_pdu) + pdcp_result unpack_pdcp_pdu(byte_buffer pdcp_pdu) { pdcp_context.entity->get_rx_lower_interface().handle_pdu(byte_buffer_chain{std::move(pdcp_pdu)}); - return pdcp_context.rrc_rx_data_notifier.get_rrc_pdu(); + + byte_buffer unpacked_pdu = pdcp_context.rrc_rx_data_notifier.get_rrc_pdu(); + + // If the PDCP layer failed to unpack the PDU, return the failure cause. + if (unpacked_pdu.empty()) { + return pdcp_result{pdcp_context.rrc_rx_control_notifier.get_failure_cause()}; + } + + return pdcp_result{std::move(unpacked_pdu)}; } private: diff --git a/lib/ru/dummy/ru_dummy_impl.h b/lib/ru/dummy/ru_dummy_impl.h index ab0e74f966..4e487215e1 100644 --- a/lib/ru/dummy/ru_dummy_impl.h +++ b/lib/ru/dummy/ru_dummy_impl.h @@ -81,6 +81,12 @@ class ru_dummy_impl : public radio_unit, // See ru_controller for documentation. void stop() override; + // See interface for documentation. + bool set_tx_gain(unsigned port_id, double gain_dB) override { return false; } + + // See interface for documentation. + bool set_rx_gain(unsigned port_id, double gain_dB) override { return false; } + // See ru_downlink_plane_handler for documentation. void handle_dl_data(const resource_grid_context& context, const resource_grid_reader& grid) override { diff --git a/lib/ru/generic/ru_controller_generic_impl.cpp b/lib/ru/generic/ru_controller_generic_impl.cpp index c7bdba6cf0..9e46b497dd 100644 --- a/lib/ru/generic/ru_controller_generic_impl.cpp +++ b/lib/ru/generic/ru_controller_generic_impl.cpp @@ -57,3 +57,13 @@ void ru_controller_generic_impl::stop() low_phy->stop(); } } + +bool ru_controller_generic_impl::set_tx_gain(unsigned port_id, double gain_dB) +{ + return radio.get_management_plane().set_tx_gain(port_id, gain_dB); +} + +bool ru_controller_generic_impl::set_rx_gain(unsigned port_id, double gain_dB) +{ + return radio.get_management_plane().set_rx_gain(port_id, gain_dB); +} diff --git a/lib/ru/generic/ru_controller_generic_impl.h b/lib/ru/generic/ru_controller_generic_impl.h index 633546d3e7..2a31596881 100644 --- a/lib/ru/generic/ru_controller_generic_impl.h +++ b/lib/ru/generic/ru_controller_generic_impl.h @@ -44,6 +44,12 @@ class ru_controller_generic_impl : public ru_controller // See interface for documentation. void stop() override; + // See interface for documentation. + bool set_tx_gain(unsigned port_id, double gain_dB) override; + + // See interface for documentation. + bool set_rx_gain(unsigned port_id, double gain_dB) override; + private: std::vector low_phy_crtl; radio_session& radio; diff --git a/lib/ru/ofh/ru_ofh_controller_impl.h b/lib/ru/ofh/ru_ofh_controller_impl.h index 5507c8ef89..490abae985 100644 --- a/lib/ru/ofh/ru_ofh_controller_impl.h +++ b/lib/ru/ofh/ru_ofh_controller_impl.h @@ -44,6 +44,12 @@ class ru_ofh_controller_impl : public ru_controller // See interface for documentation. void stop() override; + // See interface for documentation. + bool set_tx_gain(unsigned port_id, double gain_dB) override { return false; } + + // See interface for documentation. + bool set_rx_gain(unsigned port_id, double gain_dB) override { return false; } + private: srslog::basic_logger& logger; std::vector sector_controllers; diff --git a/lib/ru/ofh/ru_ofh_impl.cpp b/lib/ru/ofh/ru_ofh_impl.cpp index c089b6bb7c..40f936ab0d 100644 --- a/lib/ru/ofh/ru_ofh_impl.cpp +++ b/lib/ru/ofh/ru_ofh_impl.cpp @@ -74,7 +74,9 @@ ru_ofh_impl::ru_ofh_impl(const ru_ofh_impl_config& config, ru_ofh_impl_dependenc // Add the sectors notifiers. for (auto& sector : sectors) { notifiers.push_back(§or->get_transmitter().get_ota_symbol_boundary_notifier()); - notifiers.push_back(§or->get_receiver().get_ota_symbol_boundary_notifier()); + if (auto* notifier = sector->get_receiver().get_ota_symbol_boundary_notifier()) { + notifiers.push_back(notifier); + } // Configure the error handler for the OFH sectors. sector->set_error_notifier(error_handler); diff --git a/lib/ru/ofh/ru_ofh_timing_notifier_impl.cpp b/lib/ru/ofh/ru_ofh_timing_notifier_impl.cpp index 88bd62495f..6a43fc33a3 100644 --- a/lib/ru/ofh/ru_ofh_timing_notifier_impl.cpp +++ b/lib/ru/ofh/ru_ofh_timing_notifier_impl.cpp @@ -25,28 +25,8 @@ using namespace srsran; -void ru_ofh_timing_notifier_impl::notify_new_slot(ofh::slot_symbol_point symbol_point) -{ - // For the first slot, update the current_slot member and return. - // Returning at this point will notify the upper layers on the next slot, instead of notifying a random one. - if (!current_slot.valid()) { - current_slot = symbol_point.get_slot(); - return; - } - - // Nothing to do if the slot did not change. - if (symbol_point.get_slot() == current_slot) { - return; - } - - current_slot = symbol_point.get_slot(); - timing_notifier.on_tti_boundary(current_slot + nof_slot_offset_du_ru); -} - void ru_ofh_timing_notifier_impl::on_new_symbol(ofh::slot_symbol_point symbol_point) { - notify_new_slot(symbol_point); - if (symbol_point.get_symbol_index() == half_slot_symbol) { timing_notifier.on_ul_half_slot_boundary(symbol_point.get_slot()); } @@ -54,14 +34,9 @@ void ru_ofh_timing_notifier_impl::on_new_symbol(ofh::slot_symbol_point symbol_po if (symbol_point.get_symbol_index() == full_slot_symbol) { timing_notifier.on_ul_full_slot_boundary(symbol_point.get_slot()); } -} -ru_ofh_timing_notifier_impl::ru_ofh_timing_notifier_impl(unsigned nof_slot_offset_du_ru_, - unsigned nof_symbols_per_slot, - ru_timing_notifier& timing_notifier_) : - nof_slot_offset_du_ru(nof_slot_offset_du_ru_), - half_slot_symbol(nof_symbols_per_slot / 2U - 1U), - full_slot_symbol(nof_symbols_per_slot - 1U), - timing_notifier(timing_notifier_) -{ + // New slots start on symbol index 0. + if (symbol_point.get_symbol_index() == 0) { + timing_notifier.on_tti_boundary(symbol_point.get_slot() + nof_slot_offset_du_ru); + } } diff --git a/lib/ru/ofh/ru_ofh_timing_notifier_impl.h b/lib/ru/ofh/ru_ofh_timing_notifier_impl.h index 035c1907e1..8c297a054a 100644 --- a/lib/ru/ofh/ru_ofh_timing_notifier_impl.h +++ b/lib/ru/ofh/ru_ofh_timing_notifier_impl.h @@ -35,21 +35,22 @@ class ru_ofh_timing_notifier_impl : public ofh::ota_symbol_boundary_notifier public: ru_ofh_timing_notifier_impl(unsigned nof_slot_offset_du_ru_, unsigned nof_symbols_per_slot, - ru_timing_notifier& timing_notifier_); + ru_timing_notifier& timing_notifier_) : + nof_slot_offset_du_ru(nof_slot_offset_du_ru_), + half_slot_symbol(nof_symbols_per_slot / 2U - 1U), + full_slot_symbol(nof_symbols_per_slot - 1U), + timing_notifier(timing_notifier_) + { + } // See interface for documentation. void on_new_symbol(ofh::slot_symbol_point symbol_point) override; -private: - /// Notifies a new slot. - void notify_new_slot(ofh::slot_symbol_point symbol_point); - private: const unsigned nof_slot_offset_du_ru; const unsigned half_slot_symbol; const unsigned full_slot_symbol; ru_timing_notifier& timing_notifier; - slot_point current_slot; }; } // namespace srsran diff --git a/lib/scheduler/cell/resource_grid.cpp b/lib/scheduler/cell/resource_grid.cpp index f4b836657e..f1e8b36987 100644 --- a/lib/scheduler/cell/resource_grid.cpp +++ b/lib/scheduler/cell/resource_grid.cpp @@ -69,6 +69,20 @@ void carrier_subslot_resource_grid::fill(ofdm_symbol_range symbols, span crb_list) +{ + srsran_sanity_check(symbols.stop() <= NOF_OFDM_SYM_PER_SLOT_NORMAL_CP, "OFDM symbols out-of-bounds"); + + // carrier bitmap RB bit=0 corresponds to CRB=carrier offset. Thus, we need to shift the CRB interval. + for (unsigned i = symbols.start(); i < symbols.stop(); ++i) { + for (uint16_t crb : crb_list) { + srsran_sanity_check(rb_dims().contains(crb), "CRB interval out-of-bounds"); + crb -= offset(); + slot_rbs.reset(crb + i * nof_rbs()); + } + } +} + bool carrier_subslot_resource_grid::collides(ofdm_symbol_range symbols, crb_interval crbs) const { srsran_sanity_check(rb_dims().contains(crbs), "CRB interval out-of-bounds"); @@ -177,6 +191,12 @@ void cell_slot_resource_grid::fill(subcarrier_spacing scs, ofdm_symbol_range ofd carrier.subslot_rbs.fill(ofdm_symbols, crbs); } +void cell_slot_resource_grid::clear(subcarrier_spacing scs, ofdm_symbol_range ofdm_symbols, span crbs) +{ + auto& carrier = get_carrier(scs); + carrier.subslot_rbs.clear(ofdm_symbols, crbs); +} + bool cell_slot_resource_grid::collides(grant_info grant) const { const carrier_resource_grid& carrier = get_carrier(grant.scs); diff --git a/lib/scheduler/cell/resource_grid.h b/lib/scheduler/cell/resource_grid.h index e6a355638f..9cc0c3e635 100644 --- a/lib/scheduler/cell/resource_grid.h +++ b/lib/scheduler/cell/resource_grid.h @@ -99,6 +99,11 @@ class carrier_subslot_resource_grid /// \param crbs List of CRBs, where CRB=0 corresponds to the CRB closest to pointA. void fill(ofdm_symbol_range symbols, span crb_list); + /// Clears allocated symbol x CRB list in the carrier resource grid. + /// \param symbols OFDM symbol interval of the allocation. Interval must fall within [0, 14). + /// \param crbs List of CRBs, where CRB=0 corresponds to the CRB closest to pointA. + void clear(ofdm_symbol_range symbols, span crb_list); + /// Checks whether the provided symbol x CRB range collides with any other allocation in the carrier resource grid. /// \param symbols OFDM symbol interval of the allocation. Interval must fall within [0, 14). /// \param crbs CRB interval, where CRB=0 corresponds to the CRB closest to pointA. @@ -158,6 +163,11 @@ class cell_slot_resource_grid void fill(grant_info grant); void fill(subcarrier_spacing scs, ofdm_symbol_range ofdm_symbols, span crbs); + /// Clears allocated symbol x RB range in the cell resource grid. + /// \param symbols OFDM symbol interval of the allocation. + /// \param prbs PRB interval of the allocation. PRB=0 corresponds to the first PRB of the BWP. + void clear(subcarrier_spacing scs, ofdm_symbol_range ofdm_symbols, span crbs); + /// Checks whether the provided symbol x RB range collides with any other allocation in the cell resource grid. /// \param grant contains the symbol x RB range whose available we want to check. /// \return true if at least one symbol x RB of grant is currently occupied in the resource grid. diff --git a/lib/scheduler/common_scheduling/csi_rs_scheduler.cpp b/lib/scheduler/common_scheduling/csi_rs_scheduler.cpp index c0dc17780e..4554de6e9a 100644 --- a/lib/scheduler/common_scheduling/csi_rs_scheduler.cpp +++ b/lib/scheduler/common_scheduling/csi_rs_scheduler.cpp @@ -59,9 +59,9 @@ static csi_rs_info build_csi_rs_info(const bwp_configuration& bwp_cfg, const nzp fill_csi_rs_info_res_map(csi_rs, nzp_csi_rs_res.res_mapping); - csi_rs.scrambling_id = nzp_csi_rs_res.scrambling_id; - csi_rs.power_ctrl_offset_profile_nr = nzp_csi_rs_res.pwr_ctrl_offset; - csi_rs.power_ctrl_offset_ss_profile_nr = + csi_rs.scrambling_id = nzp_csi_rs_res.scrambling_id; + csi_rs.power_ctrl_offset = nzp_csi_rs_res.pwr_ctrl_offset; + csi_rs.power_ctrl_offset_ss = nzp_csi_rs_res.pwr_ctrl_offset_ss_db.has_value() ? *nzp_csi_rs_res.pwr_ctrl_offset_ss_db : 0; return csi_rs; diff --git a/lib/scheduler/config/serving_cell_config_factory.cpp b/lib/scheduler/config/serving_cell_config_factory.cpp index 500c1b1cfa..495e1e2274 100644 --- a/lib/scheduler/config/serving_cell_config_factory.cpp +++ b/lib/scheduler/config/serving_cell_config_factory.cpp @@ -324,7 +324,12 @@ srsran::config_helpers::generate_k2_candidates(cyclic_prefix cp, const tdd_ul_dl // allocations in the same slot for same UE. if (nof_dl_slots > nof_ul_slots) { break; - } else if (k2 > tdd_period_slots) { + } + // [Implementation-defined] For UL heavy TDD configuration, we avoid allocating PUSCH too far in the future. + // Reason: Scheduling PUSCH at slot k2 > nof_ul_slots results in CRC=KO when tested with COTS UE. + if (k2 > nof_ul_slots) { + // Remove last added PUSCH Time Domain resource since k2 > nof_ul_slots. + result.pop_back(); break; } } diff --git a/lib/scheduler/logging/scheduler_event_logger.cpp b/lib/scheduler/logging/scheduler_event_logger.cpp index 9aeb428f75..ae8733ab4a 100644 --- a/lib/scheduler/logging/scheduler_event_logger.cpp +++ b/lib/scheduler/logging/scheduler_event_logger.cpp @@ -181,7 +181,7 @@ void scheduler_event_logger::enqueue_impl(const bsr_event& bsr) fmtbuf, "{}{}: {}", i == 0 ? "" : " ", bsr.reported_lcgs[i].lcg_id, bsr.reported_lcgs[i].nof_bytes); } } - fmt::format_to(fmtbuf, "}} to_alloc={:B}", bsr.tot_ul_pending_bytes); + fmt::format_to(fmtbuf, "}} pending_bytes={}", bsr.tot_ul_pending_bytes); } } diff --git a/lib/scheduler/pdcch_scheduling/pdcch_resource_allocator_impl.cpp b/lib/scheduler/pdcch_scheduling/pdcch_resource_allocator_impl.cpp index e301fb8b38..dad883a749 100644 --- a/lib/scheduler/pdcch_scheduling/pdcch_resource_allocator_impl.cpp +++ b/lib/scheduler/pdcch_scheduling/pdcch_resource_allocator_impl.cpp @@ -188,6 +188,12 @@ pdcch_ul_information* pdcch_resource_allocator_impl::alloc_ul_pdcch_helper(cell_ search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0)) ? "0_0" : "0_1"; + // Populate power offsets. + if (not cell_cfg.nzp_csi_rs_list.empty() and cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.has_value()) { + // [Implementation-defined] It is assumed that same powerControlOffset and powerControlOffsetSS is configured in + // NZP-CSI-RS-Resource across all resources. + pdcch.ctx.tx_pwr.pwr_ctrl_offset_ss = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.value(); + } // Allocate a position for UL PDCCH in CORESET. pdcch_slot_allocator& pdcch_alloc = get_pdcch_slot_alloc(slot_alloc.slot); @@ -231,6 +237,12 @@ pdcch_dl_information* pdcch_resource_allocator_impl::alloc_dl_pdcch_helper(cell_ search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0)) ? "1_0" : "1_1"; + // Populate power offsets. + if (not cell_cfg.nzp_csi_rs_list.empty() and cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.has_value()) { + // [Implementation-defined] It is assumed that same powerControlOffset and powerControlOffsetSS is configured in + // NZP-CSI-RS-Resource across all resources. + pdcch.ctx.tx_pwr.pwr_ctrl_offset_ss = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.value(); + } // Allocate a position for DL PDCCH in CORESET. pdcch_slot_allocator& pdcch_alloc = get_pdcch_slot_alloc(slot_alloc.slot); diff --git a/lib/scheduler/pdcch_scheduling/pdcch_slot_resource_allocator.cpp b/lib/scheduler/pdcch_scheduling/pdcch_slot_resource_allocator.cpp index f4f3304c8e..ea0a612c34 100644 --- a/lib/scheduler/pdcch_scheduling/pdcch_slot_resource_allocator.cpp +++ b/lib/scheduler/pdcch_scheduling/pdcch_slot_resource_allocator.cpp @@ -95,6 +95,12 @@ bool pdcch_slot_allocator::cancel_last_pdcch(cell_slot_resource_allocator& slot_ return false; } + // Clear allocation on resource grid. + const coreset_configuration& cs_cfg = *records.back().pdcch_ctx->coreset_cfg; + const crb_index_list& pdcch_crbs = records.back().pdcch_candidate_crbs[dfs_tree.back().dci_iter_index]; + ofdm_symbol_range symbols{0, (uint8_t)cs_cfg.duration}; + slot_alloc.dl_res_grid.clear(records.back().pdcch_ctx->bwp_cfg->scs, symbols, pdcch_crbs); + dfs_tree.pop_back(); records.pop_back(); return true; diff --git a/lib/scheduler/policy/scheduler_time_rr.cpp b/lib/scheduler/policy/scheduler_time_rr.cpp index c5c0a709c2..e20082271f 100644 --- a/lib/scheduler/policy/scheduler_time_rr.cpp +++ b/lib/scheduler/policy/scheduler_time_rr.cpp @@ -273,6 +273,16 @@ static alloc_outcome alloc_dl_ue(const ue& u, // Limit the grant PRBs. if (not is_retx and dl_new_tx_max_nof_rbs_per_ue_per_slot.has_value()) { mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, dl_new_tx_max_nof_rbs_per_ue_per_slot.value()); + // [Implementation-defined] + // Check whether to allocate all remaining RBs or not. This is done to ensure we allocate only X nof. UEs for + // which dl_new_tx_max_nof_rbs_per_ue_per_slot was computed. One way is by checking if the emtpy interval is + // less than 2 times the required RBs. If so, allocate all remaining RBs. NOTE: This approach won't hold good in + // case of low traffic scenario. + const unsigned twice_grant_crbs_length = + rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs * 2, 0).length(); + if (twice_grant_crbs_length < (mcs_prbs.n_prbs * 2)) { + mcs_prbs.n_prbs = twice_grant_crbs_length; + } } if (mcs_prbs.n_prbs == 0) { @@ -375,8 +385,7 @@ static alloc_outcome alloc_ul_ue(const ue& u, static_vector search_spaces = ue_cc.get_active_ul_search_spaces(pdcch_slot, preferred_dci_rnti_type); for (const search_space_info* ss : search_spaces) { - if (ss->cfg->is_search_space0() or - ss->cfg->get_id() != ue_cc.cfg().cfg_dedicated().init_dl_bwp.pdcch_cfg->search_spaces.back().get_id()) { + if (ss->cfg->is_search_space0()) { continue; } @@ -426,6 +435,14 @@ static alloc_outcome alloc_ul_ue(const ue& u, // Limit the grant PRBs. if (not is_retx and not schedule_sr_only and ul_new_tx_max_nof_rbs_per_ue_per_slot.has_value()) { mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, ul_new_tx_max_nof_rbs_per_ue_per_slot.value()); + // [Implementation-defined] + // Check whether it's the last UE to be scheduled in this slot i.e. if the emtpy interval is less than 2 times + // the required RBs. If so, allocate all remaining RBs. + const unsigned twice_grant_crbs_length = + rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs * 2, 0).length(); + if (twice_grant_crbs_length < (mcs_prbs.n_prbs * 2)) { + mcs_prbs.n_prbs = twice_grant_crbs_length; + } } // NOTE: this should never happen, but it's safe not to proceed if we get n_prbs == 0. if (mcs_prbs.n_prbs == 0) { @@ -510,16 +527,16 @@ void scheduler_time_rr::dl_sched(ue_pdsch_allocator& pdsch_alloc, }; next_dl_ue_index = round_robin_apply(ues, next_dl_ue_index, retx_ue_function); - // Second, schedule UEs with SRB data. - auto srb_newtx_ue_function = [this, &res_grid, &pdsch_alloc](const ue& u) { - return alloc_dl_ue(u, res_grid, pdsch_alloc, false, true, logger); - }; - next_dl_ue_index = round_robin_apply(ues, next_dl_ue_index, srb_newtx_ue_function); - - // Then, schedule new transmissions. const unsigned dl_new_tx_max_nof_rbs_per_ue_per_slot = compute_max_nof_rbs_per_ue_per_slot(ues, true, res_grid, expert_cfg); if (dl_new_tx_max_nof_rbs_per_ue_per_slot > 0) { + // Second, schedule UEs with SRB data. + auto srb_newtx_ue_function = [this, &res_grid, &pdsch_alloc, dl_new_tx_max_nof_rbs_per_ue_per_slot](const ue& u) { + return alloc_dl_ue(u, res_grid, pdsch_alloc, false, true, logger, dl_new_tx_max_nof_rbs_per_ue_per_slot); + }; + next_dl_ue_index = round_robin_apply(ues, next_dl_ue_index, srb_newtx_ue_function); + + // Then, schedule new transmissions. auto tx_ue_function = [this, &res_grid, &pdsch_alloc, dl_new_tx_max_nof_rbs_per_ue_per_slot](const ue& u) { return alloc_dl_ue(u, res_grid, pdsch_alloc, false, false, logger, dl_new_tx_max_nof_rbs_per_ue_per_slot); }; diff --git a/lib/scheduler/pucch_scheduling/pucch_allocator.h b/lib/scheduler/pucch_scheduling/pucch_allocator.h index 0df31b42dc..6896f723b0 100644 --- a/lib/scheduler/pucch_scheduling/pucch_allocator.h +++ b/lib/scheduler/pucch_scheduling/pucch_allocator.h @@ -110,6 +110,12 @@ class pucch_allocator virtual pucch_uci_bits remove_ue_uci_from_pucch(cell_slot_resource_allocator& slot_alloc, rnti_t crnti, const ue_cell_configuration& ue_cell_cfg) = 0; + + /// Returns whether a PUCCH grant using common PUCCH resource already exists at a given slot for a UE. + /// \param[in] rnti RNTI of the UE. + /// \param[in] sl_tx Slot to search PUCCH grants. + /// \return Returns true if a PUCCH grant using common PUCCH resource exits. False, otherwise. + virtual bool has_common_pucch_f1_grant(rnti_t rnti, slot_point sl_tx) const = 0; }; } // namespace srsran diff --git a/lib/scheduler/pucch_scheduling/pucch_allocator_impl.cpp b/lib/scheduler/pucch_scheduling/pucch_allocator_impl.cpp index 1484f30acb..d8094df2c5 100644 --- a/lib/scheduler/pucch_scheduling/pucch_allocator_impl.cpp +++ b/lib/scheduler/pucch_scheduling/pucch_allocator_impl.cpp @@ -180,6 +180,16 @@ optional pucch_allocator_impl::alloc_ded_pucch_harq_ack_ue(cell_resour // Retrieve the existing PUCCH grants. const existing_pucch_grants existing_grants = get_existing_pucch_grants(pucchs, crnti, pucch_slot_alloc.slot); + // [Implementation-defined] Multiplexing of common and dedicated PUCCH grants are not yet supported. + if (existing_grants.format1_harq_common_grant != nullptr) { + logger.debug( + "rnti={}: PUCCH HARQ-ACK for slot={} not allocated. Cause: Multiplexing of common and dedicated PUCCH grants " + "are not supported", + crnti, + pucch_slot_alloc.slot); + return nullopt; + } + const unsigned harq_ack_bits_increment = 1; // Case 1) If there is a PUCCH format 2 grant, update it. @@ -855,7 +865,7 @@ void pucch_allocator_impl::remove_pucch_format1_from_grants(cell_slot_resource_a auto* it_harq = std::find_if(pucchs.begin(), pucchs.end(), [crnti, sl_tx, this](pucch_info& pucch) { return pucch.crnti == crnti and pucch.format == pucch_format::FORMAT_1 and pucch.format_1.sr_bits == sr_nof_bits::no_sr and pucch.format_1.harq_ack_nof_bits > 0 and - not is_pucch_f1_grant_common(crnti, sl_tx); + not has_common_pucch_f1_grant(crnti, sl_tx); }); if (it_harq != pucchs.end()) { @@ -1143,7 +1153,7 @@ pucch_allocator_impl::get_existing_pucch_grants(static_vector& pucchs, rnti_t rnti, slot_point sl_ack); diff --git a/lib/scheduler/support/mcs_tbs_calculator.cpp b/lib/scheduler/support/mcs_tbs_calculator.cpp index 4f62e147a6..155c262b7f 100644 --- a/lib/scheduler/support/mcs_tbs_calculator.cpp +++ b/lib/scheduler/support/mcs_tbs_calculator.cpp @@ -39,7 +39,8 @@ static ulsch_configuration build_ulsch_info(const pusch_config_params& pusch_c const ue_cell_configuration& ue_cell_cfg, unsigned tbs_bytes, sch_mcs_description mcs_info, - unsigned nof_prbs) + unsigned nof_prbs, + bool contains_dc) { ulsch_configuration ulsch_info{.tbs = static_cast(tbs_bytes * NOF_BITS_PER_BYTE), .mcs_descr = mcs_info, @@ -53,7 +54,8 @@ static ulsch_configuration build_ulsch_info(const pusch_config_params& pusch_c .dmrs_symbol_mask = pusch_cfg.dmrs.dmrs_symb_pos, .nof_cdm_groups_without_data = static_cast(pusch_cfg.dmrs.num_dmrs_cdm_grps_no_data), - .nof_layers = pusch_cfg.nof_layers}; + .nof_layers = pusch_cfg.nof_layers, + .contains_dc = contains_dc}; ulsch_info.alpha_scaling = alpha_scaling_to_float( ue_cell_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pusch_cfg.value().uci_cfg.value().scaling); @@ -162,7 +164,8 @@ static void update_ulsch_info(ulsch_configuration& ulsch_cfg, unsigned tbs_bytes optional srsran::compute_dl_mcs_tbs(const pdsch_config_params& pdsch_params, const ue_cell_configuration& ue_cell_cfg, sch_mcs_index max_mcs, - unsigned nof_prbs) + unsigned nof_prbs, + bool contains_dc) { // The maximum supported code rate is 0.95, as per TS38.214, Section 5.1.3. The maximum code rate is defined for DL, // but we consider the same value for UL. @@ -190,7 +193,8 @@ optional srsran::compute_dl_mcs_tbs(const pdsch_config_params& pd .dmrs_symbol_mask = pdsch_params.dmrs.dmrs_symb_pos, .nof_cdm_groups_without_data = static_cast(pdsch_params.dmrs.num_dmrs_cdm_grps_no_data), - .nof_layers = pdsch_params.nof_layers}; + .nof_layers = pdsch_params.nof_layers, + .contains_dc = contains_dc}; float effective_code_rate = get_dlsch_information(dlsch_info).get_effective_code_rate(); @@ -207,9 +211,10 @@ optional srsran::compute_dl_mcs_tbs(const pdsch_config_params& pd .tb_scaling_field = pdsch_params.tb_scaling_field, .n_prb = nof_prbs}); - dlsch_info.tbs = static_cast(tbs_bits); - dlsch_info.mcs_descr = mcs_info; - effective_code_rate = get_dlsch_information(dlsch_info).get_effective_code_rate(); + dlsch_info.tbs = static_cast(tbs_bits); + dlsch_info.mcs_descr = mcs_info; + dlsch_info.contains_dc = contains_dc; + effective_code_rate = get_dlsch_information(dlsch_info).get_effective_code_rate(); } // If no MCS such that effective code rate <= 0.95, return an empty optional object. @@ -224,7 +229,8 @@ optional srsran::compute_dl_mcs_tbs(const pdsch_config_params& pd optional srsran::compute_ul_mcs_tbs(const pusch_config_params& pusch_cfg, const ue_cell_configuration& ue_cell_cfg, sch_mcs_index max_mcs, - unsigned nof_prbs) + unsigned nof_prbs, + bool contains_dc) { // The maximum supported code rate is 0.95, as per TS38.214, Section 5.1.3. The maximum code rate is defined for DL, // but we consider the same value for UL. @@ -244,7 +250,7 @@ optional srsran::compute_ul_mcs_tbs(const pusch_config_params& pu NOF_BITS_PER_BYTE; // > Compute the effective code rate. - ulsch_configuration ulsch_cfg = build_ulsch_info(pusch_cfg, ue_cell_cfg, tbs_bytes, mcs_info, nof_prbs); + ulsch_configuration ulsch_cfg = build_ulsch_info(pusch_cfg, ue_cell_cfg, tbs_bytes, mcs_info, nof_prbs, contains_dc); float effective_code_rate = get_ulsch_information(ulsch_cfg).get_effective_code_rate(); // > Decrease the MCS and recompute TBS until the effective code rate is not above the 0.95 threshold. diff --git a/lib/scheduler/support/mcs_tbs_calculator.h b/lib/scheduler/support/mcs_tbs_calculator.h index 1c857fa307..fea75143f6 100644 --- a/lib/scheduler/support/mcs_tbs_calculator.h +++ b/lib/scheduler/support/mcs_tbs_calculator.h @@ -52,7 +52,8 @@ struct sch_mcs_tbs { optional compute_dl_mcs_tbs(const pdsch_config_params& pdsch_params, const ue_cell_configuration& ue_cell_cfg, sch_mcs_index max_mcs, - unsigned nof_prbs); + unsigned nof_prbs, + bool contains_dc); /// \brief Computes the PUSCH MCS and TBS such that the effective code rate does not exceed 0.95. /// @@ -61,11 +62,13 @@ optional compute_dl_mcs_tbs(const pdsch_config_params& pdsch_para /// \param[in] max_mcs Initial value to be applied for the MCS; the final MCS might be lowered if the effective /// code rate is above 0.95. /// \param[in] nof_prbs Maximum number of PRBs available for the PUSCH transmission. +/// \param[in] contains_dc Set to true if the transmission overlaps with the position of the DC. /// \return The MCS and TBS, if for these values the effective code rate does not exceed 0.95; else, it returns an empty /// optional object. optional compute_ul_mcs_tbs(const pusch_config_params& pusch_params, const ue_cell_configuration& ue_cell_cfg, sch_mcs_index max_mcs, - unsigned nof_prbs); + unsigned nof_prbs, + bool contains_dc); } // namespace srsran diff --git a/lib/scheduler/support/sch_pdu_builder.cpp b/lib/scheduler/support/sch_pdu_builder.cpp index a2d4bb3913..95a4f516f1 100644 --- a/lib/scheduler/support/sch_pdu_builder.cpp +++ b/lib/scheduler/support/sch_pdu_builder.cpp @@ -202,9 +202,11 @@ pusch_config_params srsran::get_pusch_config_f0_0_c_rnti(const ue_cell_configura // According to TS 38.214, Section 6.1.4.2, nof_oh_prb is set equal to xOverhead, when set; else nof_oh_prb = 0. // NOTE: x_overhead::not_set is mapped to 0. - pusch.nof_oh_prb = ue_cell_cfg.cfg_dedicated().pdsch_serv_cell_cfg.has_value() - ? static_cast(ue_cell_cfg.cfg_dedicated().pdsch_serv_cell_cfg.value().x_ov_head) - : static_cast(x_overhead::not_set); + pusch.nof_oh_prb = + ue_cell_cfg.cfg_dedicated().ul_config.has_value() and + ue_cell_cfg.cfg_dedicated().ul_config.value().pusch_scell_cfg.has_value() + ? static_cast(ue_cell_cfg.cfg_dedicated().ul_config.value().pusch_scell_cfg.value().x_ov_head) + : static_cast(x_overhead::not_set); // TODO: verify if this needs to be set depending on some configuration. pusch.nof_harq_ack_bits = nof_harq_ack_bits; @@ -264,9 +266,11 @@ pusch_config_params srsran::get_pusch_config_f0_1_c_rnti(const ue_cell_configura // According to TS 38.214, Section 6.1.4.2, nof_oh_prb is set equal to xOverhead, when set; else nof_oh_prb = 0. // NOTE: x_overhead::not_set is mapped to 0. - pusch.nof_oh_prb = ue_cell_cfg.cfg_dedicated().pdsch_serv_cell_cfg.has_value() - ? static_cast(ue_cell_cfg.cfg_dedicated().pdsch_serv_cell_cfg.value().x_ov_head) - : static_cast(x_overhead::not_set); + pusch.nof_oh_prb = + ue_cell_cfg.cfg_dedicated().ul_config.has_value() and + ue_cell_cfg.cfg_dedicated().ul_config.value().pusch_scell_cfg.has_value() + ? static_cast(ue_cell_cfg.cfg_dedicated().ul_config.value().pusch_scell_cfg.value().x_ov_head) + : static_cast(x_overhead::not_set); // TODO: verify if this needs to be set depending on some configuration. pusch.nof_harq_ack_bits = nof_harq_ack_bits; @@ -320,6 +324,16 @@ void srsran::build_pdsch_f1_0_si_rnti(pdsch_information& pdsch pdsch.ss_set_type = dci_cfg.system_information_indicator == 0 ? search_space_set_type::type0 : search_space_set_type::type0A; pdsch.dci_fmt = dci_dl_format::f1_0; + + // Populate power offsets. + if (not cell_cfg.nzp_csi_rs_list.empty()) { + // [Implementation-defined] It is assumed that same powerControlOffset and powerControlOffsetSS is configured in + // NZP-CSI-RS-Resource across all resources. + pdsch.tx_pwr_info.pwr_ctrl_offset = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset; + if (cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.has_value()) { + pdsch.tx_pwr_info.pwr_ctrl_offset_ss = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.value(); + } + } } void srsran::build_pdsch_f1_0_p_rnti(pdsch_information& pdsch, @@ -356,6 +370,16 @@ void srsran::build_pdsch_f1_0_p_rnti(pdsch_information& pdsch, pdsch.is_interleaved = dci_cfg.vrb_to_prb_mapping > 0; pdsch.ss_set_type = search_space_set_type::type2; pdsch.dci_fmt = dci_dl_format::f1_0; + + // Populate power offsets. + if (not cell_cfg.nzp_csi_rs_list.empty()) { + // [Implementation-defined] It is assumed that same powerControlOffset and powerControlOffsetSS is configured in + // NZP-CSI-RS-Resource across all resources. + pdsch.tx_pwr_info.pwr_ctrl_offset = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset; + if (cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.has_value()) { + pdsch.tx_pwr_info.pwr_ctrl_offset_ss = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.value(); + } + } } void srsran::build_pdsch_f1_0_ra_rnti(pdsch_information& pdsch, @@ -397,6 +421,16 @@ void srsran::build_pdsch_f1_0_ra_rnti(pdsch_information& pdsch pdsch.is_interleaved = dci_cfg.vrb_to_prb_mapping > 0; pdsch.ss_set_type = search_space_set_type::type1; pdsch.dci_fmt = dci_dl_format::f1_0; + + // Populate power offsets. + if (not cell_cfg.nzp_csi_rs_list.empty()) { + // [Implementation-defined] It is assumed that same powerControlOffset and powerControlOffsetSS is configured in + // NZP-CSI-RS-Resource across all resources. + pdsch.tx_pwr_info.pwr_ctrl_offset = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset; + if (cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.has_value()) { + pdsch.tx_pwr_info.pwr_ctrl_offset_ss = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.value(); + } + } } void srsran::build_pdsch_f1_0_tc_rnti(pdsch_information& pdsch, @@ -437,6 +471,16 @@ void srsran::build_pdsch_f1_0_tc_rnti(pdsch_information& pdsch pdsch.harq_id = to_harq_id(dci_cfg.harq_process_number); pdsch.nof_layers = 1U; + // Populate power offsets. + if (not cell_cfg.nzp_csi_rs_list.empty()) { + // [Implementation-defined] It is assumed that same powerControlOffset and powerControlOffsetSS is configured in + // NZP-CSI-RS-Resource across all resources. + pdsch.tx_pwr_info.pwr_ctrl_offset = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset; + if (cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.has_value()) { + pdsch.tx_pwr_info.pwr_ctrl_offset_ss = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.value(); + } + } + // One Codeword. pdsch_codeword& cw = pdsch.codewords.emplace_back(); cw.new_data = is_new_data; @@ -488,6 +532,16 @@ void srsran::build_pdsch_f1_0_c_rnti(pdsch_information& pdsch, pdsch.n_id = get_pdsch_n_id(cell_cfg.pci, bwp_dl_ded, dci_dl_format::f1_0, ss_info.cfg->is_common_search_space()); pdsch.nof_layers = 1; + // Populate power offsets. + if (not cell_cfg.nzp_csi_rs_list.empty()) { + // [Implementation-defined] It is assumed that same powerControlOffset and powerControlOffsetSS is configured in + // NZP-CSI-RS-Resource across all resources. + pdsch.tx_pwr_info.pwr_ctrl_offset = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset; + if (cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.has_value()) { + pdsch.tx_pwr_info.pwr_ctrl_offset_ss = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.value(); + } + } + // One Codeword. pdsch_codeword& cw = pdsch.codewords.emplace_back(); cw.new_data = is_new_data; @@ -545,6 +599,16 @@ void srsran::build_pdsch_f1_1_c_rnti(pdsch_information& pdsch, // Beamforming and precoding. pdsch.precoding = cs_mgr.get_precoding(pdsch_cfg.nof_layers, prbs); + + // Populate power offsets. + if (not cell_cfg.nzp_csi_rs_list.empty()) { + // [Implementation-defined] It is assumed that same powerControlOffset and powerControlOffsetSS is configured in + // NZP-CSI-RS-Resource across all resources. + pdsch.tx_pwr_info.pwr_ctrl_offset = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset; + if (cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.has_value()) { + pdsch.tx_pwr_info.pwr_ctrl_offset_ss = cell_cfg.nzp_csi_rs_list.front().pwr_ctrl_offset_ss_db.value(); + } + } } void srsran::build_pusch_f0_0_tc_rnti(pusch_information& pusch, diff --git a/lib/scheduler/uci_scheduling/uci_allocator.h b/lib/scheduler/uci_scheduling/uci_allocator.h index 9bf41a419b..c662716e95 100644 --- a/lib/scheduler/uci_scheduling/uci_allocator.h +++ b/lib/scheduler/uci_scheduling/uci_allocator.h @@ -93,6 +93,12 @@ class uci_allocator /// \param[in] crnti C-RNTI of the UE. /// \return Returns number of PDSCHs scheduled if UCI allocation if found, 0 otherwise. virtual uint8_t get_scheduled_pdsch_counter_in_ue_uci(cell_slot_resource_allocator& slot_alloc, rnti_t crnti) = 0; + + /// Returns whether a UCI HARQ-ACK allocated on common PUCCH resource exists at a given slot or not. + /// \param[in] crnti C-RNTI of the UE. + /// \param[in] sl_tx UCI slot. + /// \return Returns true if a UCI HARQ-ACK allocated on common PUCCH resource exists. False, otherwise. + virtual bool has_uci_harq_on_common_pucch_res(rnti_t crnti, slot_point sl_tx) = 0; }; } // namespace srsran diff --git a/lib/scheduler/uci_scheduling/uci_allocator_impl.cpp b/lib/scheduler/uci_scheduling/uci_allocator_impl.cpp index 31e7b7bf47..88db3bedfe 100644 --- a/lib/scheduler/uci_scheduling/uci_allocator_impl.cpp +++ b/lib/scheduler/uci_scheduling/uci_allocator_impl.cpp @@ -361,3 +361,8 @@ uint8_t uci_allocator_impl::get_scheduled_pdsch_counter_in_ue_uci(cell_slot_reso } return uci->scheduled_dl_pdcch_counter; } + +bool uci_allocator_impl::has_uci_harq_on_common_pucch_res(rnti_t rnti, slot_point sl_tx) +{ + return pucch_alloc.has_common_pucch_f1_grant(rnti, sl_tx); +} diff --git a/lib/scheduler/uci_scheduling/uci_allocator_impl.h b/lib/scheduler/uci_scheduling/uci_allocator_impl.h index a56af83b6c..a8e350f341 100644 --- a/lib/scheduler/uci_scheduling/uci_allocator_impl.h +++ b/lib/scheduler/uci_scheduling/uci_allocator_impl.h @@ -60,6 +60,8 @@ class uci_allocator_impl final : public uci_allocator uint8_t get_scheduled_pdsch_counter_in_ue_uci(cell_slot_resource_allocator& slot_alloc, rnti_t crnti) override; + bool has_uci_harq_on_common_pucch_res(rnti_t rnti, slot_point sl_tx) override; + private: // \brief Information cached by the UCI scheduler relative to the UCIs scheduled in the cell resource grid. Store // here any information that does not need to be stored in the PUCCH and PUSCH PDUs and does not need to be sent to diff --git a/lib/scheduler/uci_scheduling/uci_scheduler.h b/lib/scheduler/uci_scheduling/uci_scheduler.h index c475e4f312..f455c40f52 100644 --- a/lib/scheduler/uci_scheduling/uci_scheduler.h +++ b/lib/scheduler/uci_scheduling/uci_scheduler.h @@ -34,8 +34,7 @@ class uci_scheduler /// Schedules the SR opportunities and CSI occasions. /// \param[out,in] res_alloc struct with scheduling results. - /// \param[in] sl_tx slot for which the SR should be allocated. - virtual void run_slot(cell_resource_allocator& res_alloc, slot_point sl_tx) = 0; + virtual void run_slot(cell_resource_allocator& res_alloc) = 0; }; } // namespace srsran diff --git a/lib/scheduler/uci_scheduling/uci_scheduler_impl.cpp b/lib/scheduler/uci_scheduling/uci_scheduler_impl.cpp index 766f47c7df..435adac02f 100644 --- a/lib/scheduler/uci_scheduling/uci_scheduler_impl.cpp +++ b/lib/scheduler/uci_scheduling/uci_scheduler_impl.cpp @@ -33,77 +33,253 @@ uci_scheduler_impl::uci_scheduler_impl(const cell_configuration& cell_cfg_, ue_repository& ues_) : cell_cfg(cell_cfg_), uci_alloc(uci_alloc_), ues(ues_), logger(srslog::fetch_basic_logger("SCHED")) { + // Max size of the UCI resource slot wheel, dimensioned based on the UCI periods. + periodic_uci_slot_wheel.resize(std::max(MAX_SR_PERIOD, MAX_CSI_REPORT_PERIOD)); + + // Pre-reserve space for the UEs that will be added. + updated_ues.reserve(MAX_NOF_DU_UES); } -uci_scheduler_impl::~uci_scheduler_impl() = default; +uci_scheduler_impl::~uci_scheduler_impl() {} -void uci_scheduler_impl::run_slot(cell_resource_allocator& cell_alloc, slot_point sl_tx) +void uci_scheduler_impl::run_slot(cell_resource_allocator& cell_alloc) { - for (auto& user : ues) { - // At this point, we assume the config validator ensures there is pCell. - auto& ue_cell = user->get_pcell(); + // Initial allocation: we allocate opportunities all over the grid. + schedule_updated_ues_ucis(cell_alloc); - if (not ue_cell.is_active() or not ue_cell.cfg().cfg_dedicated().ul_config.has_value()) { - continue; - } + // Only allocate in the farthest slot in the grid, as the previous part of the allocation grid has been completed + // at the first this function was called. + schedule_slot_ucis(cell_alloc[cell_alloc.max_ul_slot_alloc_delay]); +} - optional> csi_period_and_offset; - if (ue_cell.cfg().cfg_dedicated().csi_meas_cfg.has_value()) { - // We assume we only use the first CSI report configuration. - const unsigned csi_report_cfg_idx = 0; - const auto& csi_report_cfg = - ue_cell.cfg().cfg_dedicated().csi_meas_cfg.value().csi_report_cfg_list[csi_report_cfg_idx]; - - csi_period_and_offset.emplace(std::pair{ - csi_report_periodicity_to_uint(variant_get( - csi_report_cfg.report_cfg_type) - .report_slot_period), - variant_get(csi_report_cfg.report_cfg_type) - .report_slot_offset}); - } +void uci_scheduler_impl::add_resource(rnti_t crnti, unsigned res_offset, unsigned res_period, bool is_sr) +{ + // For each offset in the periodic UCI slot wheel. + for (unsigned wheel_offset = res_offset; wheel_offset < periodic_uci_slot_wheel.size(); wheel_offset += res_period) { + auto& slot_wheel = periodic_uci_slot_wheel[wheel_offset]; - if (ue_cell.is_pucch_grid_inited()) { - // Only allocate in the farthest slot in the grid, as the previous part of the allocation grid has been completed - // at the first this function was called. - schedule_uci(cell_alloc[cell_alloc.max_ul_slot_alloc_delay], user->crnti, ue_cell, csi_period_and_offset); + // Check if the UE is already in the slot wheel. + auto it = std::find_if(slot_wheel.begin(), slot_wheel.end(), [crnti](const auto& r) { return r.rnti == crnti; }); + + if (it == slot_wheel.end()) { + // New UE. Create a new element in the list with either SR or CSI resource. + slot_wheel.push_back(periodic_uci_info{crnti, is_sr ? 1U : 0U, is_sr ? 0U : 1U}); + } else { + // Resource for UE already exists. Increment the resource counter. + if (is_sr) { + it->sr_counter++; + } else { + it->csi_counter++; + } } - // Initial allocation: we allocate opportunities all over the grid. - else { - for (unsigned n = 0; n != cell_alloc.max_ul_slot_alloc_delay + 1; ++n) { - schedule_uci(cell_alloc[n], user->crnti, ue_cell, csi_period_and_offset); + } +} + +void uci_scheduler_impl::rem_resource(rnti_t crnti, unsigned res_offset, unsigned res_period, bool is_sr) +{ + auto log_error = [&]() { + logger.error("cell={} c-rnti={}: Unable to remove {} PUCCH resource for period={} offset={}", + cell_cfg.cell_index, + crnti, + is_sr ? "SR" : "CSI", + res_period, + res_offset); + }; + + for (unsigned wheel_offset = res_offset; wheel_offset < periodic_uci_slot_wheel.size(); wheel_offset += res_period) { + auto& slot_wheel = periodic_uci_slot_wheel[wheel_offset]; + + auto it = std::find_if(slot_wheel.begin(), slot_wheel.end(), [crnti](const auto& r) { return r.rnti == crnti; }); + if (it != slot_wheel.end()) { + if (is_sr) { + if (it->sr_counter == 0) { + log_error(); + continue; + } + it->sr_counter--; + } else { + if (it->csi_counter == 0) { + log_error(); + continue; + } + it->csi_counter--; + } + + if (it->sr_counter == 0 and it->csi_counter == 0) { + // Move resource to last position and delete it to avoid O(N) removal. + if (it != slot_wheel.end() - 1) { + auto last_it = slot_wheel.end() - 1; + std::swap(*it, *last_it); + } + slot_wheel.pop_back(); } - ue_cell.set_pucch_grid_inited(); + } else { + log_error(); } } } -void uci_scheduler_impl::schedule_uci(cell_slot_resource_allocator& slot_alloc, - rnti_t crnti, - const ue_cell& user, - optional> csi_period_and_offset) +void uci_scheduler_impl::add_ue(const ue_cell_configuration& ue_cfg) { - if (not cell_cfg.is_fully_ul_enabled(slot_alloc.slot)) { + if (not ue_cfg.cfg_dedicated().ul_config.has_value() or + not ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pucch_cfg.has_value()) { return; } - // At this point, we assume the UE has a \c ul_config, a \c pucch_cfg and a \c sr_res_list. - const auto& sr_resource_cfg_list = - user.cfg().cfg_dedicated().ul_config.value().init_ul_bwp.pucch_cfg.value().sr_res_list; + // Save SR resources in the slot wheel. + const auto& sr_resource_cfg_list = ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pucch_cfg.value().sr_res_list; + for (unsigned i = 0; i != sr_resource_cfg_list.size(); ++i) { + const auto& sr_res = sr_resource_cfg_list[i]; + srsran_assert(sr_res.period >= sr_periodicity::sl_1, "Minimum supported SR periodicity is 1 slot."); + + unsigned period_slots = sr_periodicity_to_slot(sr_res.period); + add_resource(ue_cfg.crnti, sr_res.offset, period_slots, true); + } - // NOTE: Allocating the CSI after the SR helps the PUCCH allocation to compute the number of allocated UCI bits and - // the corresponding number of PRBs for the PUCCH Format 2 over a PUCCH F2 grant is within PUCCH capacity. + if (ue_cfg.cfg_dedicated().csi_meas_cfg.has_value()) { + // We assume we only use the first CSI report configuration. + const unsigned csi_report_cfg_idx = 0; + const auto& csi_report_cfg = ue_cfg.cfg_dedicated().csi_meas_cfg.value().csi_report_cfg_list[csi_report_cfg_idx]; + const auto& period_pucch = + variant_get(csi_report_cfg.report_cfg_type); - for (const auto& sr_res : sr_resource_cfg_list) { - srsran_assert(sr_res.period >= sr_periodicity::sl_1, "Minimum supported SR periodicity is 1 slot."); + unsigned period_slots = csi_report_periodicity_to_uint(period_pucch.report_slot_period); + add_resource(ue_cfg.crnti, period_pucch.report_slot_offset, period_slots, false); + } + + // Register the UE in the list of recently configured UEs. + updated_ues.push_back(ue_cfg.crnti); +} + +void uci_scheduler_impl::reconf_ue(const ue_cell_configuration& new_ue_cfg, const ue_cell_configuration& old_ue_cfg) +{ + // Detect whether there are any differences in the old and new UE cell config. + if (new_ue_cfg.cfg_dedicated().ul_config.has_value() and old_ue_cfg.cfg_dedicated().ul_config.has_value() and + new_ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pucch_cfg.has_value() and + old_ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pucch_cfg.has_value()) { + // Both old and new UE config have PUCCH config. + const auto& new_pucch = new_ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pucch_cfg.value(); + const auto& old_pucch = old_ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pucch_cfg.value(); - if ((slot_alloc.slot - sr_res.offset).to_uint() % sr_periodicity_to_slot(sr_res.period) == 0) { - uci_alloc.uci_allocate_sr_opportunity(slot_alloc, crnti, user.cfg()); + if (new_pucch.sr_res_list == old_pucch.sr_res_list and + new_ue_cfg.cfg_dedicated().csi_meas_cfg == old_ue_cfg.cfg_dedicated().csi_meas_cfg) { + // Nothing changed. + return; } } - if (csi_period_and_offset.has_value()) { - if ((slot_alloc.slot - csi_period_and_offset->second).to_uint() % csi_period_and_offset->first == 0) { - uci_alloc.uci_allocate_csi_opportunity(slot_alloc, crnti, user.cfg()); + rem_ue(old_ue_cfg); + add_ue(new_ue_cfg); +} + +void uci_scheduler_impl::rem_ue(const ue_cell_configuration& ue_cfg) +{ + if (not ue_cfg.cfg_dedicated().ul_config.has_value() or + not ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pucch_cfg.has_value()) { + return; + } + + const auto& sr_resource_cfg_list = ue_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pucch_cfg.value().sr_res_list; + for (unsigned i = 0; i != sr_resource_cfg_list.size(); ++i) { + const auto& sr_res = sr_resource_cfg_list[i]; + + unsigned period_slots = sr_periodicity_to_slot(sr_res.period); + rem_resource(ue_cfg.crnti, sr_res.offset, period_slots, true); + } + + if (ue_cfg.cfg_dedicated().csi_meas_cfg.has_value()) { + // We assume we only use the first CSI report configuration. + const unsigned csi_report_cfg_idx = 0; + const auto& csi_report_cfg = ue_cfg.cfg_dedicated().csi_meas_cfg.value().csi_report_cfg_list[csi_report_cfg_idx]; + const auto& period_pucch = + variant_get(csi_report_cfg.report_cfg_type); + + unsigned period_slots = csi_report_periodicity_to_uint(period_pucch.report_slot_period); + rem_resource(ue_cfg.crnti, period_pucch.report_slot_offset, period_slots, false); + } +} + +const ue_cell_configuration* uci_scheduler_impl::get_ue_cfg(rnti_t rnti) const +{ + auto* u = ues.find_by_rnti(rnti); + if (u != nullptr) { + auto* ue_cc = u->find_cell(cell_cfg.cell_index); + if (ue_cc != nullptr) { + return &ue_cc->cfg(); } } + return nullptr; +} + +void uci_scheduler_impl::schedule_slot_ucis(cell_slot_resource_allocator& slot_alloc) +{ + // For the provided slot, check if there are any pending UCI resources to allocate, and allocate them. + auto& slot_ucis = periodic_uci_slot_wheel[slot_alloc.slot.to_uint() % periodic_uci_slot_wheel.size()]; + for (auto it = slot_ucis.begin(); it != slot_ucis.end();) { + const periodic_uci_info& uci_info = *it; + const ue_cell_configuration* ue_cfg = get_ue_cfg(uci_info.rnti); + + if (ue_cfg == nullptr) { + logger.error("cell={} c-rnti={}: UE for which {} is being scheduled was not found (slot={})", + cell_cfg.cell_index, + uci_info.rnti, + it->sr_counter > 0 ? "SR" : (it->csi_counter > 0 ? "CSI" : "invalid UCI"), + slot_alloc.slot); + it = slot_ucis.erase(it); + continue; + } + + // Schedule SR PUCCH first. + // NOTE: Allocating the CSI after the SR helps the PUCCH allocation to compute the number of allocated UCI bits and + // the corresponding number of PRBs for the PUCCH Format 2 over a PUCCH F2 grant is within PUCCH capacity. + if (uci_info.sr_counter > 0) { + uci_alloc.uci_allocate_sr_opportunity(slot_alloc, uci_info.rnti, *ue_cfg); + } + + // Schedule CSI PUCCH. + if (uci_info.csi_counter > 0) { + uci_alloc.uci_allocate_csi_opportunity(slot_alloc, uci_info.rnti, *ue_cfg); + } + + ++it; + } +} + +void uci_scheduler_impl::schedule_updated_ues_ucis(cell_resource_allocator& cell_alloc) +{ + // For all UEs whose config has been recently updated, schedule their UCIs up until one slot before the farthest + // slot in the resource grid. + // TODO: Remove UCIs from the list of updated UEs when the UE is removed, if they have no HARQ-ACK. + for (rnti_t rnti : updated_ues) { + const ue_cell_configuration* ue_cfg = get_ue_cfg(rnti); + if (ue_cfg == nullptr) { + logger.error("cell={} c-rnti={}: UE for which UCI is being scheduled was not found.", cell_cfg.cell_index, rnti); + continue; + } + + // Schedule UCI up to the farthest slot. + for (unsigned n = 0; n != cell_alloc.max_ul_slot_alloc_delay; ++n) { + auto& slot_ucis = periodic_uci_slot_wheel[(cell_alloc.slot_tx() + n).to_uint() % periodic_uci_slot_wheel.size()]; + + for (const periodic_uci_info& uci_info : slot_ucis) { + if (uci_info.rnti == rnti) { + // Schedule SR PUCCHs first. + // NOTE: Allocating the CSI after the SR helps the PUCCH allocation to compute the number of allocated UCI + // bits and the corresponding number of PRBs for the PUCCH Format 2 over a PUCCH F2 grant is within PUCCH + // capacity. + if (uci_info.sr_counter > 0) { + uci_alloc.uci_allocate_sr_opportunity(cell_alloc[n], rnti, *ue_cfg); + } + + // Schedule CSI + if (uci_info.csi_counter > 0) { + uci_alloc.uci_allocate_csi_opportunity(cell_alloc[n], rnti, *ue_cfg); + } + } + } + } + } + + // Clear the list of updated UEs. + updated_ues.clear(); } diff --git a/lib/scheduler/uci_scheduling/uci_scheduler_impl.h b/lib/scheduler/uci_scheduling/uci_scheduler_impl.h index 7bf366085e..57fc6fabf7 100644 --- a/lib/scheduler/uci_scheduling/uci_scheduler_impl.h +++ b/lib/scheduler/uci_scheduling/uci_scheduler_impl.h @@ -40,14 +40,31 @@ class uci_scheduler_impl final : public uci_scheduler ~uci_scheduler_impl() override; - void run_slot(cell_resource_allocator& res_alloc, slot_point sl_tx) override; + void run_slot(cell_resource_allocator& res_alloc) override; + + void add_ue(const ue_cell_configuration& ue_cfg); + + void reconf_ue(const ue_cell_configuration& new_ue_cfg, const ue_cell_configuration& old_ue_cfg); + + void rem_ue(const ue_cell_configuration& ue_cfg); private: - // Helper that schedules the SR and CSI for a given user at a given slot. - void schedule_uci(cell_slot_resource_allocator& slot_alloc, - rnti_t crnti, - const ue_cell& user, - optional> csi_period_and_offset); + /// Information on currently configured UE UCI resource to periodically schedule. + struct periodic_uci_info { + rnti_t rnti = rnti_t::INVALID_RNTI; + unsigned sr_counter = 0; + unsigned csi_counter = 0; + }; + + // Helper to fetch a UE cell config. + const ue_cell_configuration* get_ue_cfg(rnti_t rnti) const; + // Helper that schedules the SR and CSI for a given slot. + void schedule_slot_ucis(cell_slot_resource_allocator& slot_alloc); + // Helper that schedules the SR and CSI for UEs that were recently updated. + void schedule_updated_ues_ucis(cell_resource_allocator& res_alloc); + + void add_resource(rnti_t crnti, unsigned offset, unsigned period, bool is_sr); + void rem_resource(rnti_t crnti, unsigned offset, unsigned period, bool is_sr); // Cell configuration. const cell_configuration& cell_cfg; @@ -56,6 +73,14 @@ class uci_scheduler_impl final : public uci_scheduler ue_repository& ues; srslog::basic_logger& logger; + + // Storage of the periodic UCIs to be scheduled in the resource grid. Each position of the vector represents a slot + // in a ring-like structure (ie slot % WHEEL_SIZE). Each of these vector indexes/slots contains a list of periodic + // UCI information to be scheduled in the respective slot. + std::vector> periodic_uci_slot_wheel; + + // UEs whose configuration has been updated in between the last and current slot indications. + std::vector updated_ues; }; } // namespace srsran diff --git a/lib/scheduler/ue_scheduling/ue_cell.cpp b/lib/scheduler/ue_scheduling/ue_cell.cpp index f96110f441..6519dc28da 100644 --- a/lib/scheduler/ue_scheduling/ue_cell.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell.cpp @@ -265,7 +265,9 @@ ue_cell::get_active_dl_search_spaces(slot_point pdcch_slo "Invalid required dci-rnti parameter"); for (const search_space_configuration& ss : ue_cfg->cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_common.search_spaces) { - active_search_spaces.push_back(&ue_cfg->search_space(ss.get_id())); + if (pdcch_helper::is_pdcch_monitoring_active(pdcch_slot, ss)) { + active_search_spaces.push_back(&ue_cfg->search_space(ss.get_id())); + } } return active_search_spaces; } @@ -305,6 +307,10 @@ ue_cell::get_active_dl_search_spaces(slot_point pdcch_slo } } + if (not pdcch_helper::is_pdcch_monitoring_active(pdcch_slot, *ss.cfg)) { + return false; + } + if (ss.get_pdcch_candidates(get_aggregation_level(channel_state_manager().get_wideband_cqi(), ss, true), pdcch_slot) .empty()) { return false; @@ -330,7 +336,9 @@ ue_cell::get_active_ul_search_spaces(slot_point pdcch_slo "Invalid required dci-rnti parameter"); for (const search_space_configuration& ss : ue_cfg->cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_common.search_spaces) { - active_search_spaces.push_back(&ue_cfg->search_space(ss.get_id())); + if (pdcch_helper::is_pdcch_monitoring_active(pdcch_slot, ss)) { + active_search_spaces.push_back(&ue_cfg->search_space(ss.get_id())); + } } return active_search_spaces; } @@ -370,6 +378,10 @@ ue_cell::get_active_ul_search_spaces(slot_point pdcch_slo } } + if (not pdcch_helper::is_pdcch_monitoring_active(pdcch_slot, *ss.cfg)) { + return false; + } + if (ss.get_pdcch_candidates(get_aggregation_level(channel_state_manager().get_wideband_cqi(), ss, false), pdcch_slot) .empty()) { diff --git a/lib/scheduler/ue_scheduling/ue_cell.h b/lib/scheduler/ue_scheduling/ue_cell.h index 4ce20e8631..c6d9cf1f77 100644 --- a/lib/scheduler/ue_scheduling/ue_cell.h +++ b/lib/scheduler/ue_scheduling/ue_cell.h @@ -134,10 +134,6 @@ class ue_cell is_fallback_mode = fallback_state_; } - bool is_pucch_grid_inited() const { return is_pucch_alloc_grid_initialized; } - - void set_pucch_grid_inited() { is_pucch_alloc_grid_initialized = true; } - /// \brief Get UE channel state handler. ue_channel_state_manager& channel_state_manager() { return channel_state; } const ue_channel_state_manager& channel_state_manager() const { return channel_state; } @@ -162,8 +158,6 @@ class ue_cell /// already have applied a dedicated config. bool is_fallback_mode = false; - bool is_pucch_alloc_grid_initialized = false; - metrics ue_metrics; ue_channel_state_manager channel_state; diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index 9969785b42..81989a255f 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -285,7 +285,16 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr optional mcs_tbs_info; // If it's a new Tx, compute the MCS and TBS. if (h_dl.empty()) { - mcs_tbs_info = compute_dl_mcs_tbs(pdsch_cfg, ue_cell_cfg, adjusted_mcs, grant.crbs.length()); + // As \c txDirectCurrentLocation, in \c SCS-SpecificCarrier, TS 38.331, "If this field (\c txDirectCurrentLocation) + // is absent for downlink within ServingCellConfigCommon and ServingCellConfigCommonSIB, the UE assumes the default + // value of 3300 (i.e. "Outside the carrier")". + bool contains_dc = false; + if (cell_cfg.dl_cfg_common.freq_info_dl.scs_carrier_list.back().tx_direct_current_location.has_value()) { + contains_dc = dc_offset_helper::is_contained( + cell_cfg.dl_cfg_common.freq_info_dl.scs_carrier_list.back().tx_direct_current_location.value(), grant.crbs); + } + + mcs_tbs_info = compute_dl_mcs_tbs(pdsch_cfg, ue_cell_cfg, adjusted_mcs, grant.crbs.length(), contains_dc); } else { // It is a retx. mcs_tbs_info.emplace( @@ -538,12 +547,6 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr expert_cfg.max_puschs_per_slot); return alloc_outcome::skip_slot; } - if (pusch_alloc.result.ul.puschs.size() >= - expert_cfg.max_ul_grants_per_slot - static_cast(pusch_alloc.result.ul.pucchs.size())) { - logger.info("Failed to allocate PUSCH. Cause: Max number of UL grants per slot {} was reached.", - expert_cfg.max_puschs_per_slot); - return alloc_outcome::skip_slot; - } // Verify there is space in PUSCH and PDCCH result lists for new allocations. if (pusch_alloc.result.ul.puschs.full() or pdcch_alloc.result.dl.ul_pdcchs.full()) { @@ -560,16 +563,17 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr } } + const unsigned nof_pucch_grants = + std::count_if(pusch_alloc.result.ul.pucchs.begin(), + pusch_alloc.result.ul.pucchs.end(), + [&u](const pucch_info& pucch_grant) { return pucch_grant.crnti == u.crnti; }); + // [Implementation-defined] We skip allocation of PUSCH if there is already a PUCCH grant scheduled over the same slot // and the UE is in fallback mode. // NOTE: This is due to the lack of clarity of the TS when it comes to define what \c betaOffsets to use for PUSCH // when the UE does not have a dedicated configuration. if (ue_cc->is_in_fallback_mode()) { - const auto* pucch_grant_it = - std::find_if(pusch_alloc.result.ul.pucchs.begin(), - pusch_alloc.result.ul.pucchs.end(), - [&u](const pucch_info& pucch_grant) { return pucch_grant.crnti == u.crnti; }); - if (pucch_grant_it != pusch_alloc.result.ul.pucchs.end()) { + if (nof_pucch_grants != 0) { logger.debug("rnti={} Allocation of PUSCH in slot={} skipped. Cause: this UE is in fallback mode and has " "PUCCH grants scheduled", u.crnti, @@ -578,6 +582,27 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr } } + // [Implementation-defined] We skip allocation of PUSCH if there is already a PUCCH grant scheduled using common PUCCH + // resources. + if (get_uci_alloc(grant.cell_index).has_uci_harq_on_common_pucch_res(u.crnti, pusch_alloc.slot)) { + logger.debug("ue={} rnti={} Allocation of PUSCH in slot={} skipped. Cause: UE has PUCCH grant using common PUCCH " + "resources scheduled", + u.ue_index, + u.crnti, + pusch_alloc.slot); + return alloc_outcome::skip_ue; + } + + // When checking the number of remaining grants for PUSCH, take into account that the PUCCH grants for this UE will be + // removed when multiplexing the UCI on PUSCH. + if (pusch_alloc.result.ul.puschs.size() >= + expert_cfg.max_ul_grants_per_slot - + (static_cast(pusch_alloc.result.ul.pucchs.size()) - nof_pucch_grants)) { + logger.info("Failed to allocate PUSCH. Cause: Max number of UL grants per slot {} was reached.", + expert_cfg.max_puschs_per_slot); + return alloc_outcome::skip_slot; + } + // Verify CRBs allocation. if (not ss_info->ul_crb_lims.contains(grant.crbs)) { logger.warning("rnti={} Failed to allocate PUSCH. Cause: CRBs {} allocated outside the BWP {}", @@ -646,7 +671,10 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr optional mcs_tbs_info; // If it's a new Tx, compute the MCS and TBS from SNR, payload size, and available RBs. if (h_ul.empty()) { - mcs_tbs_info = compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, grant.mcs, grant.crbs.length()); + bool contains_dc = + dc_offset_helper::is_contained(cell_cfg.expert_cfg.ue.initial_ul_dc_offset, cell_cfg.nof_ul_prbs, grant.crbs); + + mcs_tbs_info = compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, grant.mcs, grant.crbs.length(), contains_dc); } // If it's a reTx, fetch the MCS and TBS from the previous transmission. else { diff --git a/lib/scheduler/ue_scheduling/ue_event_manager.cpp b/lib/scheduler/ue_scheduling/ue_event_manager.cpp index 1738487da5..96f211aaf0 100644 --- a/lib/scheduler/ue_scheduling/ue_event_manager.cpp +++ b/lib/scheduler/ue_scheduling/ue_event_manager.cpp @@ -21,9 +21,9 @@ */ #include "ue_event_manager.h" -#include "../config/sched_config_manager.h" #include "../logging/scheduler_event_logger.h" #include "../logging/scheduler_metrics_handler.h" +#include "../uci_scheduling/uci_scheduler_impl.h" using namespace srsran; @@ -156,6 +156,12 @@ void ue_event_manager::handle_ue_creation(ue_config_update_event ev) du_cell_index_t pcell_index = u->get_pcell().cell_index; ue_db.add_ue(std::move(u)); + // Update UCI scheduler with new UE UCI resources. + const auto& added_ue = ue_db[ueidx]; + for (unsigned i = 0; i != added_ue.nof_cells(); ++i) { + du_cells[pcell_index].uci_sched->add_ue(added_ue.get_cell(to_ue_cell_index(i)).cfg()); + } + // Log Event. ev_logger.enqueue(scheduler_event_logger::ue_creation_event{ueidx, rnti, pcell_index}); }); @@ -171,6 +177,27 @@ void ue_event_manager::handle_ue_reconfiguration(ue_config_update_event ev) ev.abort(); return; } + auto& u = ue_db[ue_idx]; + + // Update UE UCI resources in UCI scheduler. + for (unsigned i = 0; i != u.nof_cells(); ++i) { + auto& ue_cc = u.get_cell(to_ue_cell_index(i)); + if (not ev.next_config().contains(ue_cc.cell_index)) { + // UE carrier is being removed. + du_cells[ue_cc.cell_index].uci_sched->rem_ue(ue_cc.cfg()); + } else { + // UE carrier is being reconfigured. + du_cells[ue_cc.cell_index].uci_sched->reconf_ue(ev.next_config().ue_cell_cfg(ue_cc.cell_index), ue_cc.cfg()); + } + } + for (unsigned i = 0; i != ev.next_config().nof_cells(); ++i) { + auto& new_ue_cc_cfg = ev.next_config().ue_cell_cfg(to_ue_cell_index(i)); + auto* ue_cc = u.find_cell(new_ue_cc_cfg.cell_cfg_common.cell_index); + if (ue_cc == nullptr) { + // New UE carrier is being added. + du_cells[new_ue_cc_cfg.cell_cfg_common.cell_index].uci_sched->add_ue(new_ue_cc_cfg); + } + } // Configure existing UE. ue_db[ue_idx].handle_reconfiguration_request(ue_reconf_command{ev.next_config()}); @@ -189,7 +216,13 @@ void ue_event_manager::handle_ue_deletion(ue_config_delete_event ev) logger.warning("Received request to delete ue={} that does not exist", ue_idx); return; } - const rnti_t rnti = ue_db[ue_idx].crnti; + const auto& u = ue_db[ue_idx]; + const rnti_t rnti = u.crnti; + + // Update UCI scheduling by removing existing UE UCI resources. + for (unsigned i = 0; i != u.nof_cells(); ++i) { + du_cells[u.get_cell(to_ue_cell_index(i)).cell_index].uci_sched->rem_ue(u.get_pcell().cfg()); + } // Scheduler UE removal from repository. ue_db.schedule_ue_rem(std::move(ev)); @@ -615,7 +648,9 @@ void ue_event_manager::run(slot_point sl, du_cell_index_t cell_index) process_cell_specific(cell_index); } -void ue_event_manager::add_cell(cell_resource_allocator& cell_res_grid, ue_srb0_scheduler& srb0_sched) +void ue_event_manager::add_cell(cell_resource_allocator& cell_res_grid, + ue_srb0_scheduler& srb0_sched, + uci_scheduler_impl& uci_sched) { const du_cell_index_t cell_index = cell_res_grid.cell_index(); srsran_assert(not cell_exists(cell_index), "Overwriting cell configurations not supported"); @@ -623,6 +658,7 @@ void ue_event_manager::add_cell(cell_resource_allocator& cell_res_grid, ue_srb0_ du_cells[cell_index].cfg = &cell_res_grid.cfg; du_cells[cell_index].res_grid = &cell_res_grid; du_cells[cell_index].srb0_sched = &srb0_sched; + du_cells[cell_index].uci_sched = &uci_sched; } bool ue_event_manager::cell_exists(du_cell_index_t cell_index) const diff --git a/lib/scheduler/ue_scheduling/ue_event_manager.h b/lib/scheduler/ue_scheduling/ue_event_manager.h index 33e28856ef..5be34e8bbf 100644 --- a/lib/scheduler/ue_scheduling/ue_event_manager.h +++ b/lib/scheduler/ue_scheduling/ue_event_manager.h @@ -35,6 +35,7 @@ namespace srsran { class scheduler_metrics_handler; class scheduler_event_logger; +class uci_scheduler_impl; /// \brief Class used to manage events that arrive to the scheduler and are directed at UEs. /// This class acts as a facade for several of the ue_scheduler subcomponents, managing the asynchronous configuration @@ -47,7 +48,7 @@ class ue_event_manager final : public sched_ue_configuration_handler, ue_event_manager(ue_repository& ue_db, scheduler_metrics_handler& metrics_handler, scheduler_event_logger& ev_logger); ~ue_event_manager(); - void add_cell(cell_resource_allocator& cell_res_grid, ue_srb0_scheduler& srb0_sched); + void add_cell(cell_resource_allocator& cell_res_grid, ue_srb0_scheduler& srb0_sched, uci_scheduler_impl& uci_sched); /// UE Add/Mod/Remove interface. void handle_ue_creation(ue_config_update_event ev) override; @@ -123,8 +124,11 @@ class ue_event_manager final : public sched_ue_configuration_handler, cell_resource_allocator* res_grid = nullptr; - /// Reference to SRB0 and other bearers scheduler + // Reference to SRB0 and other bearers scheduler ue_srb0_scheduler* srb0_sched = nullptr; + + // Reference to the CSI and SR UCI scheduler. + uci_scheduler_impl* uci_sched = nullptr; }; std::array du_cells{}; diff --git a/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp b/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp index 9af2c1f677..911d130608 100644 --- a/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp +++ b/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp @@ -42,7 +42,7 @@ void ue_scheduler_impl::add_cell(const ue_scheduler_cell_params& params) { ue_res_grid_view.add_cell(*params.cell_res_alloc); cells[params.cell_index] = std::make_unique(expert_cfg, params, ue_db); - event_mng.add_cell(*params.cell_res_alloc, cells[params.cell_index]->srb0_sched); + event_mng.add_cell(*params.cell_res_alloc, cells[params.cell_index]->srb0_sched, cells[params.cell_index]->uci_sched); ue_alloc.add_cell(params.cell_index, *params.pdcch_sched, *params.uci_alloc, *params.cell_res_alloc); } @@ -164,7 +164,7 @@ void ue_scheduler_impl::run_slot(slot_point slot_tx, du_cell_index_t cell_index) ue_alloc.slot_indication(); // Schedule periodic UCI (SR and CSI) before any UL grants. - cells[cell_index]->uci_sched.run_slot(*cells[cell_index]->cell_res_alloc, slot_tx); + cells[cell_index]->uci_sched.run_slot(*cells[cell_index]->cell_res_alloc); // Run cell-specific SRB0 scheduler. cells[cell_index]->srb0_sched.run_slot(*cells[cell_index]->cell_res_alloc); diff --git a/lib/sdap/sdap_entity_impl.h b/lib/sdap/sdap_entity_impl.h index 77987d8461..428aa9d884 100644 --- a/lib/sdap/sdap_entity_impl.h +++ b/lib/sdap/sdap_entity_impl.h @@ -36,15 +36,8 @@ namespace srs_cu_up { class sdap_entity_impl : public sdap_entity, public sdap_tx_sdu_handler { public: - sdap_entity_impl(uint32_t ue_index_, - pdu_session_id_t psi_, - unique_timer& ue_inactivity_timer_, - sdap_rx_sdu_notifier& rx_sdu_notifier_) : - logger("SDAP", {ue_index_, psi_}), - ue_index(ue_index_), - psi(psi_), - ue_inactivity_timer(ue_inactivity_timer_), - rx_sdu_notifier(rx_sdu_notifier_) + sdap_entity_impl(uint32_t ue_index_, pdu_session_id_t psi_, sdap_rx_sdu_notifier& rx_sdu_notifier_) : + logger("SDAP", {ue_index_, psi_}), ue_index(ue_index_), psi(psi_), rx_sdu_notifier(rx_sdu_notifier_) { } ~sdap_entity_impl() override = default; @@ -86,12 +79,12 @@ class sdap_entity_impl : public sdap_entity, public sdap_tx_sdu_handler // create TX mapping std::unique_ptr tx = - std::make_unique(ue_index, psi, qfi, drb_id, ue_inactivity_timer, tx_pdu_notifier); + std::make_unique(ue_index, psi, qfi, drb_id, tx_pdu_notifier); tx_map.insert({qfi, std::move(tx)}); // create RX mapping std::unique_ptr rx = - std::make_unique(ue_index, psi, qfi, drb_id, ue_inactivity_timer, rx_sdu_notifier); + std::make_unique(ue_index, psi, qfi, drb_id, rx_sdu_notifier); rx_map.insert({drb_id, std::move(rx)}); } @@ -116,7 +109,6 @@ class sdap_entity_impl : public sdap_entity, public sdap_tx_sdu_handler sdap_session_logger logger; uint32_t ue_index; pdu_session_id_t psi; - unique_timer& ue_inactivity_timer; sdap_rx_sdu_notifier& rx_sdu_notifier; std::unordered_map> tx_map; diff --git a/lib/sdap/sdap_entity_rx_impl.h b/lib/sdap/sdap_entity_rx_impl.h index a80c7786f5..ac1e50ebb7 100644 --- a/lib/sdap/sdap_entity_rx_impl.h +++ b/lib/sdap/sdap_entity_rx_impl.h @@ -24,7 +24,6 @@ #include "sdap_session_logger.h" #include "srsran/sdap/sdap.h" -#include "srsran/support/timers.h" namespace srsran { @@ -37,13 +36,8 @@ class sdap_entity_rx_impl : public sdap_rx_pdu_handler pdu_session_id_t psi, qos_flow_id_t qfi_, drb_id_t drb_id_, - unique_timer& ue_inactivity_timer_, sdap_rx_sdu_notifier& sdu_notifier_) : - logger("SDAP", {ue_index, psi, qfi_, drb_id_, "UL"}), - qfi(qfi_), - drb_id(drb_id_), - ue_inactivity_timer(ue_inactivity_timer_), - sdu_notifier(sdu_notifier_) + logger("SDAP", {ue_index, psi, qfi_, drb_id_, "UL"}), qfi(qfi_), drb_id(drb_id_), sdu_notifier(sdu_notifier_) { } @@ -52,7 +46,6 @@ class sdap_entity_rx_impl : public sdap_rx_pdu_handler // pass through with qfi logger.log_debug("RX SDU. {} sdu_len={}", qfi, pdu.length()); sdu_notifier.on_new_sdu(std::move(pdu), qfi); - ue_inactivity_timer.run(); } drb_id_t get_drb_id() const { return drb_id; } @@ -61,7 +54,6 @@ class sdap_entity_rx_impl : public sdap_rx_pdu_handler sdap_session_trx_logger logger; qos_flow_id_t qfi; drb_id_t drb_id; - unique_timer& ue_inactivity_timer; sdap_rx_sdu_notifier& sdu_notifier; }; diff --git a/lib/sdap/sdap_entity_tx_impl.h b/lib/sdap/sdap_entity_tx_impl.h index 33bb76df30..a2db2cd8e5 100644 --- a/lib/sdap/sdap_entity_tx_impl.h +++ b/lib/sdap/sdap_entity_tx_impl.h @@ -24,7 +24,6 @@ #include "sdap_session_logger.h" #include "srsran/sdap/sdap.h" -#include "srsran/support/timers.h" namespace srsran { @@ -37,13 +36,8 @@ class sdap_entity_tx_impl pdu_session_id_t psi, qos_flow_id_t qfi_, drb_id_t drb_id_, - unique_timer& ue_inactivity_timer_, sdap_tx_pdu_notifier& pdu_notifier_) : - logger("SDAP", {ue_index, psi, qfi_, drb_id_, "DL"}), - qfi(qfi_), - drb_id(drb_id_), - ue_inactivity_timer(ue_inactivity_timer_), - pdu_notifier(pdu_notifier_) + logger("SDAP", {ue_index, psi, qfi_, drb_id_, "DL"}), qfi(qfi_), drb_id(drb_id_), pdu_notifier(pdu_notifier_) { } @@ -52,7 +46,6 @@ class sdap_entity_tx_impl // pass through logger.log_debug("TX PDU. {} pdu_len={}", qfi, sdu.length()); pdu_notifier.on_new_pdu(std::move(sdu)); - ue_inactivity_timer.run(); } drb_id_t get_drb_id() const { return drb_id; } @@ -61,7 +54,6 @@ class sdap_entity_tx_impl sdap_session_trx_logger logger; qos_flow_id_t qfi; drb_id_t drb_id; - unique_timer& ue_inactivity_timer; sdap_tx_pdu_notifier& pdu_notifier; }; diff --git a/lib/sdap/sdap_factory.cpp b/lib/sdap/sdap_factory.cpp index ba83e39cef..a921d63c79 100644 --- a/lib/sdap/sdap_factory.cpp +++ b/lib/sdap/sdap_factory.cpp @@ -34,6 +34,5 @@ using namespace srs_cu_up; std::unique_ptr srsran::srs_cu_up::create_sdap(sdap_entity_creation_message& msg) { - return std::make_unique( - msg.ue_index, msg.pdu_session_id, msg.ue_inactivity_timer, *msg.rx_sdu_notifier); + return std::make_unique(msg.ue_index, msg.pdu_session_id, *msg.rx_sdu_notifier); } diff --git a/lib/security/security.cpp b/lib/security/security.cpp index c33c831a50..b36b28ee5b 100644 --- a/lib/security/security.cpp +++ b/lib/security/security.cpp @@ -84,17 +84,17 @@ void security_context::generate_as_keys() // Generate K_up_enc and K_up_int security::generate_k_up(as_keys.k_up_enc, as_keys.k_up_int, k, sel_algos.cipher_algo, sel_algos.integ_algo); - logger.debug(k.data(), k.size(), "K_gNB"); - logger.debug(as_keys.k_rrc_int.data(), as_keys.k_rrc_int.size(), "RRC Integrity Key"); - logger.debug(as_keys.k_rrc_enc.data(), as_keys.k_rrc_enc.size(), "RRC Encryption Key"); - logger.debug(as_keys.k_up_int.data(), as_keys.k_up_int.size(), "UP Integrity Key"); - logger.debug(as_keys.k_up_enc.data(), as_keys.k_up_enc.size(), "UP Encryption Key"); + logger.info("K_gNB: {}", k); + logger.info("RRC Integrity Key: {}", as_keys.k_rrc_int); + logger.info("RRC Encryption Key: {}", as_keys.k_rrc_enc); + logger.info("UP Integrity Key: {}", as_keys.k_up_int); + logger.info("UP Encryption Key: {}", as_keys.k_up_enc); } void security_context::horizontal_key_derivation(pci_t target_pci, unsigned target_ssb_arfcn) { logger.info("Regenerating KgNB with PCI={}, SSB-ARFCN={}", target_pci, target_ssb_arfcn); - logger.info(k.data(), k.size(), "Old K_gNB (k_gnb)"); + logger.info("Old K_gNB: {}", k); // Generate K_NG-RAN* sec_key k_ng_ran_star; diff --git a/lib/srslog/formatters/text_formatter.cpp b/lib/srslog/formatters/text_formatter.cpp index 6a3f50fac1..e2bb64605b 100644 --- a/lib/srslog/formatters/text_formatter.cpp +++ b/lib/srslog/formatters/text_formatter.cpp @@ -73,6 +73,9 @@ static void format_metadata(const detail::log_entry_metadata& metadata, fmt::mem ctx_buffer.push_back('\0'); fmt::format_to(buffer, "[{: >8}] ", ctx_buffer.data()); } + if (metadata.log_label != nullptr && !metadata.log_label->empty()) { + fmt::format_to(buffer, "{}", *metadata.log_label); + } } void text_formatter::format(detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) diff --git a/lib/srsvec/accumulate.cpp b/lib/srsvec/accumulate.cpp index 83067ab5b7..dd57a6a8c7 100644 --- a/lib/srsvec/accumulate.cpp +++ b/lib/srsvec/accumulate.cpp @@ -21,22 +21,24 @@ */ #include "srsran/srsvec/accumulate.h" #include "simd.h" +#include "srsran/support/srsran_assert.h" +#include using namespace srsran; using namespace srsvec; static float accumulate_f_simd(const float* x, unsigned len) { - float result = 0; + float result = 0; + unsigned i = 0; - unsigned i = 0; #if SRSRAN_SIMD_F_SIZE if (len >= SRSRAN_SIMD_F_SIZE) { - simd_f_t simd_result = srsran_simd_f_loadu(&x[i]); + simd_f_t simd_result = srsran_simd_f_loadu(x + i); i += SRSRAN_SIMD_F_SIZE; for (unsigned simd_end = SRSRAN_SIMD_F_SIZE * (len / SRSRAN_SIMD_F_SIZE); i != simd_end; i += SRSRAN_SIMD_F_SIZE) { - simd_f_t simd_x = srsran_simd_f_loadu(&x[i]); + simd_f_t simd_x = srsran_simd_f_loadu(x + i); simd_result = srsran_simd_f_add(simd_x, simd_result); } @@ -57,4 +59,4 @@ static float accumulate_f_simd(const float* x, unsigned len) float srsran::srsvec::accumulate(span x) { return accumulate_f_simd(x.data(), x.size()); -} \ No newline at end of file +} diff --git a/lib/srsvec/add.cpp b/lib/srsvec/add.cpp index d49eef95b2..676cd3657c 100644 --- a/lib/srsvec/add.cpp +++ b/lib/srsvec/add.cpp @@ -33,26 +33,26 @@ static void add_fff_simd(const float* x, const float* y, float* z, std::size_t l #if SRSRAN_SIMD_F_SIZE if (SIMD_IS_ALIGNED(x) && SIMD_IS_ALIGNED(y) && SIMD_IS_ALIGNED(z)) { for (std::size_t i_end = (len / SRSRAN_SIMD_F_SIZE) * SRSRAN_SIMD_F_SIZE; i != i_end; i += SRSRAN_SIMD_F_SIZE) { - simd_f_t a = srsran_simd_f_load(&x[i]); - simd_f_t b = srsran_simd_f_load(&y[i]); + simd_f_t a = srsran_simd_f_load(x + i); + simd_f_t b = srsran_simd_f_load(y + i); simd_f_t r = srsran_simd_f_add(a, b); - srsran_simd_f_store(&z[i], r); + srsran_simd_f_store(z + i, r); } } else { for (std::size_t i_end = (len / SRSRAN_SIMD_F_SIZE) * SRSRAN_SIMD_F_SIZE; i != i_end; i += SRSRAN_SIMD_F_SIZE) { - simd_f_t a = srsran_simd_f_loadu(&x[i]); - simd_f_t b = srsran_simd_f_loadu(&y[i]); + simd_f_t a = srsran_simd_f_loadu(x + i); + simd_f_t b = srsran_simd_f_loadu(y + i); simd_f_t r = srsran_simd_f_add(a, b); - srsran_simd_f_storeu(&z[i], r); + srsran_simd_f_storeu(z + i, r); } } #endif - for (; i != len; i++) { + for (; i != len; ++i) { z[i] = x[i] + y[i]; } } @@ -64,26 +64,26 @@ static void add_sss_simd(const int16_t* x, const int16_t* y, int16_t* z, std::si #if SRSRAN_SIMD_S_SIZE if (SIMD_IS_ALIGNED(x) && SIMD_IS_ALIGNED(y) && SIMD_IS_ALIGNED(z)) { for (std::size_t i_end = (len / SRSRAN_SIMD_S_SIZE) * SRSRAN_SIMD_S_SIZE; i != i_end; i += SRSRAN_SIMD_S_SIZE) { - simd_s_t a = srsran_simd_s_load(&x[i]); - simd_s_t b = srsran_simd_s_load(&y[i]); + simd_s_t a = srsran_simd_s_load(x + i); + simd_s_t b = srsran_simd_s_load(y + i); simd_s_t r = srsran_simd_s_add(a, b); - srsran_simd_s_store(&z[i], r); + srsran_simd_s_store(z + i, r); } } else { for (std::size_t i_end = (len / SRSRAN_SIMD_S_SIZE) * SRSRAN_SIMD_S_SIZE; i != i_end; i += SRSRAN_SIMD_S_SIZE) { - simd_s_t a = srsran_simd_s_loadu(&x[i]); - simd_s_t b = srsran_simd_s_loadu(&y[i]); + simd_s_t a = srsran_simd_s_loadu(x + i); + simd_s_t b = srsran_simd_s_loadu(y + i); simd_s_t r = srsran_simd_s_add(a, b); - srsran_simd_s_storeu(&z[i], r); + srsran_simd_s_storeu(z + i, r); } } #endif /* SRSRAN_SIMD_S_SIZE */ - for (; i != len; i++) { + for (; i != len; ++i) { z[i] = x[i] + y[i]; } } @@ -95,26 +95,26 @@ static void add_bbb_simd(const int8_t* x, const int8_t* y, int8_t* z, std::size_ #if SRSRAN_SIMD_S_SIZE if (SIMD_IS_ALIGNED(x) && SIMD_IS_ALIGNED(y) && SIMD_IS_ALIGNED(z)) { for (std::size_t i_end = (len / SRSRAN_SIMD_B_SIZE) * SRSRAN_SIMD_B_SIZE; i != i_end; i += SRSRAN_SIMD_B_SIZE) { - simd_b_t a = srsran_simd_b_load(&x[i]); - simd_b_t b = srsran_simd_b_load(&y[i]); + simd_b_t a = srsran_simd_b_load(x + i); + simd_b_t b = srsran_simd_b_load(y + i); simd_b_t r = srsran_simd_b_add(a, b); - srsran_simd_b_store(&z[i], r); + srsran_simd_b_store(z + i, r); } } else { for (std::size_t i_end = (len / SRSRAN_SIMD_B_SIZE) * SRSRAN_SIMD_B_SIZE; i != i_end; i += SRSRAN_SIMD_B_SIZE) { - simd_b_t a = srsran_simd_b_loadu(&x[i]); - simd_b_t b = srsran_simd_b_loadu(&y[i]); + simd_b_t a = srsran_simd_b_loadu(x + i); + simd_b_t b = srsran_simd_b_loadu(y + i); simd_b_t r = srsran_simd_b_add(a, b); - srsran_simd_b_storeu(&z[i], r); + srsran_simd_b_storeu(z + i, r); } } #endif /* SRSRAN_SIMD_S_SIZE */ - for (; i != len; i++) { + for (; i != len; ++i) { z[i] = x[i] + y[i]; } } @@ -124,7 +124,10 @@ void srsran::srsvec::add(span x, span y, span z) srsran_srsvec_assert_size(x, y); srsran_srsvec_assert_size(x, z); - add_fff_simd((float*)x.data(), (float*)y.data(), (float*)z.data(), 2 * z.size()); + add_fff_simd(reinterpret_cast(x.data()), + reinterpret_cast(y.data()), + reinterpret_cast(z.data()), + 2 * z.size()); } void srsran::srsvec::add(span x, span y, span z) @@ -149,4 +152,4 @@ void srsran::srsvec::add(span x, span y, span -void unpack_8bit(span unpacked, InType value) +static void unpack_8bit(span unpacked, InType value) { srsran_assert(unpacked.size() == 8, "The amount of data to pack (i.e., {}) must be eight.", unpacked.size()); @@ -48,7 +46,7 @@ void unpack_8bit(span unpacked, InType value) } template -RetType pack_8bit(span unpacked) +static RetType pack_8bit(span unpacked) { srsran_assert(unpacked.size() == 8, "The amount of data to pack (i.e., {}) must be eight.", unpacked.size()); @@ -65,13 +63,11 @@ RetType pack_8bit(span unpacked) return static_cast(packed); } -} // namespace - span srsran::srsvec::bit_unpack(span bits, unsigned value, unsigned nof_bits) { srsran_assert(bits.size() >= nof_bits, "Input span size is too small"); - for (unsigned i = 0; i < nof_bits; i++) { + for (unsigned i = 0; i != nof_bits; ++i) { bits[i] = (value >> (nof_bits - i - 1)) & 0x1; } @@ -214,7 +210,7 @@ unsigned srsran::srsvec::bit_pack(span& bits, unsigned nof_bits) unsigned value = 0; - for (unsigned i = 0; i < nof_bits; i++) { + for (unsigned i = 0; i != nof_bits; ++i) { value |= (unsigned)bits[i] << (nof_bits - i - 1U); } @@ -230,7 +226,7 @@ unsigned srsran::srsvec::bit_pack(span bits) unsigned value = 0; - for (unsigned i = 0, nof_bits = bits.size(); i != nof_bits; i++) { + for (unsigned i = 0, nof_bits = bits.size(); i != nof_bits; ++i) { value |= (unsigned)bits[i] << (nof_bits - i - 1U); } @@ -335,14 +331,15 @@ void srsran::srsvec::copy_offset(srsran::bit_buffer& output, span unsigned i_word = 0; #ifdef __AVX2__ for (unsigned i_word_end = (nof_full_words / 32) * 32; i_word != i_word_end; i_word += 32) { - __m256i word0 = _mm256_loadu_si256(reinterpret_cast(&input[input_start_word + i_word])); - __m256i word1 = _mm256_loadu_si256(reinterpret_cast(&input[input_start_word + i_word + 1])); - word0 = _mm256_and_si256(_mm256_slli_epi32(word0, input_start_mod), + __m256i word0 = _mm256_loadu_si256(reinterpret_cast(input.data() + input_start_word + i_word)); + __m256i word1 = + _mm256_loadu_si256(reinterpret_cast(input.data() + input_start_word + i_word + 1)); + word0 = _mm256_and_si256(_mm256_slli_epi32(word0, input_start_mod), _mm256_set1_epi8(mask_msb_ones(bits_per_word - input_start_mod))); - word1 = _mm256_and_si256(_mm256_srli_epi32(word1, bits_per_word - input_start_mod), + word1 = _mm256_and_si256(_mm256_srli_epi32(word1, bits_per_word - input_start_mod), _mm256_set1_epi8(mask_lsb_ones(input_start_mod))); - __m256i word = _mm256_or_si256(word0, word1); - _mm256_storeu_si256(reinterpret_cast<__m256i*>(&buffer[i_word]), word); + __m256i word = _mm256_or_si256(word0, word1); + _mm256_storeu_si256(reinterpret_cast<__m256i*>(buffer.data() + i_word), word); } #endif // __AVX2__ for (; i_word != nof_full_words; ++i_word) { diff --git a/lib/srsvec/clip.cpp b/lib/srsvec/clip.cpp index d033e9af03..0ae4ccdb78 100644 --- a/lib/srsvec/clip.cpp +++ b/lib/srsvec/clip.cpp @@ -25,7 +25,7 @@ using namespace srsran; -unsigned srsvec::clip(span y, span x, const float threshold) +unsigned srsvec::clip(span y, span x, float threshold) { srsran_srsvec_assert_size(x, y); @@ -48,14 +48,14 @@ unsigned srsvec::clip(span y, span x, const float threshold) return nof_clipped_samples; } -unsigned srsvec::clip_iq(span y, span x, const float threshold) +unsigned srsvec::clip_iq(span y, span x, float threshold) { span x_fp = span(reinterpret_cast(x.data()), 2 * x.size()); span y_fp = span(reinterpret_cast(y.data()), 2 * x.size()); return srsvec::clip(y_fp, x_fp, threshold); } -unsigned srsvec::clip_magnitude(span y, span x, const float threshold) +unsigned srsvec::clip_magnitude(span y, span x, float threshold) { srsran_srsvec_assert_size(x, y); diff --git a/lib/srsvec/compare.cpp b/lib/srsvec/compare.cpp index 1cd2504eb9..473ec88138 100644 --- a/lib/srsvec/compare.cpp +++ b/lib/srsvec/compare.cpp @@ -21,7 +21,6 @@ */ #include "srsran/srsvec/compare.h" - #include "simd.h" #include "srsran/support/math_utils.h" #include @@ -29,18 +28,19 @@ using namespace srsran; using namespace srsvec; -const char* srsran::srsvec::detail::find(span input, char value) +const char* srsran::srsvec::detail::find(span input, const char* value) { // Input index. unsigned index = 0; + char v = *value; #ifdef HAVE_AVX2 // Advances the input index to either the first SIMD word that contains value or the last index rounded to 32. for (unsigned simd_index_end = 32 * (input.size() / 32); index != simd_index_end; index += 32) { // Load 32 consecutive words starting at index. - __m256i simd_input = _mm256_loadu_si256((__m256i*)&input[index]); + __m256i simd_input = _mm256_loadu_si256(reinterpret_cast(input.data() + index)); // Compare the 32 words with the value. - __m256i simd_eq_filler_bit = _mm256_cmpeq_epi8(_mm256_set1_epi8(value), simd_input); + __m256i simd_eq_filler_bit = _mm256_cmpeq_epi8(_mm256_set1_epi8(v), simd_input); // Get the MSB of each word. unsigned mask = _mm256_movemask_epi8(simd_eq_filler_bit); // If it is not zero, it means at least one of the words in the SIMD register is equal to value. @@ -55,9 +55,9 @@ const char* srsran::srsvec::detail::find(span input, char value) // Advances the input index to either the first SIMD word that contains value or the last index rounded to 16. for (unsigned simd_index_end = 16 * (input.size() / 16); index != simd_index_end; index += 16) { // Load 16 consecutive words starting at index. - int8x16_t simd_input = vld1q_s8(reinterpret_cast(&input[index])); + int8x16_t simd_input = vld1q_s8(reinterpret_cast(input.data() + index)); // Compare the 16 words with the value. - uint8x16_t mask_u8 = vceqq_s8(vdupq_n_s8((int8_t)value), simd_input); + uint8x16_t mask_u8 = vceqq_s8(vdupq_n_s8(int8_t(v)), simd_input); uint8_t mask = vmaxvq_u8(mask_u8); if (mask != 0) { found = true; @@ -67,9 +67,9 @@ const char* srsran::srsvec::detail::find(span input, char value) // Advances the input index to either the first SIMD word that contains value or the last index rounded to 8. for (unsigned simd_index_end = 8 * (input.size() / 8); !found && index != simd_index_end; index += 8) { // Load 8 consecutive words starting at index. - int8x8_t simd_input = vld1_s8(reinterpret_cast(&input[index])); + int8x8_t simd_input = vld1_s8(reinterpret_cast(input.data() + index)); // Compare the 8 words with the value. - uint8x8_t mask_u8 = vceq_s8(vdup_n_s8((int8_t)value), simd_input); + uint8x8_t mask_u8 = vceq_s8(vdup_n_s8(int8_t(v)), simd_input); uint8_t mask = vmaxv_u8(mask_u8); if (mask != 0) { break; @@ -80,7 +80,7 @@ const char* srsran::srsvec::detail::find(span input, char value) // Keeps iterating from the current index to the end. for (; index != input.size(); ++index) { // Early return if a word is equal to value. - if (input[index] == value) { + if (input[index] == v) { return &input[index]; } } @@ -91,10 +91,10 @@ const char* srsran::srsvec::detail::find(span input, char value) std::pair srsran::srsvec::max_abs_element(span x) { + unsigned i = 0; + unsigned len = x.size(); unsigned max_index = 0; - float max_abs2 = 0.0F; - - unsigned i = 0, len = x.size(); + float max_abs2 = 0; #if SRSRAN_SIMD_CF_SIZE // Prepare range of indexes in SIMD register. @@ -109,8 +109,8 @@ std::pair srsran::srsvec::max_abs_element(span x) for (unsigned simd_end = SRSRAN_SIMD_CF_SIZE * (len / SRSRAN_SIMD_CF_SIZE); i != simd_end; i += SRSRAN_SIMD_CF_SIZE) { // Load 2 SIMD words of floats. - simd_f_t simd_x1 = srsran_simd_f_loadu((float*)&x[i]); - simd_f_t simd_x2 = srsran_simd_f_loadu((float*)&x[i + SRSRAN_SIMD_CF_SIZE / 2]); + simd_f_t simd_x1 = srsran_simd_f_loadu(reinterpret_cast(x.data() + i)); + simd_f_t simd_x2 = srsran_simd_f_loadu(reinterpret_cast(x.data() + i + SRSRAN_SIMD_CF_SIZE / 2)); // Calculates the squares. simd_f_t simd_mul1 = srsran_simd_f_mul(simd_x1, simd_x1); @@ -155,10 +155,10 @@ std::pair srsran::srsvec::max_abs_element(span x) std::pair srsran::srsvec::max_element(span x) { + unsigned i = 0; + unsigned len = x.size(); unsigned max_index = 0; - float max_x = 0.0F; - - unsigned i = 0, len = x.size(); + float max_x = 0; #if SRSRAN_SIMD_CF_SIZE // Prepare range of indexes in SIMD register. @@ -173,7 +173,7 @@ std::pair srsran::srsvec::max_element(span x) for (unsigned simd_end = SRSRAN_SIMD_CF_SIZE * (len / SRSRAN_SIMD_CF_SIZE); i != simd_end; i += SRSRAN_SIMD_CF_SIZE) { // Load SIMD word of floats. - simd_f_t simd_x = srsran_simd_f_loadu((float*)&x[i]); + simd_f_t simd_x = srsran_simd_f_loadu(reinterpret_cast(x.data() + i)); // Get SIMD selector mask. simd_sel_t res = srsran_simd_f_max(simd_x, simd_max_values); @@ -206,4 +206,4 @@ std::pair srsran::srsvec::max_element(span x) } return {max_index, max_x}; -} \ No newline at end of file +} diff --git a/lib/srsvec/conversion.cpp b/lib/srsvec/conversion.cpp index 9981c03730..168ade643b 100644 --- a/lib/srsvec/conversion.cpp +++ b/lib/srsvec/conversion.cpp @@ -21,7 +21,6 @@ */ #include "srsran/srsvec/conversion.h" - #include "simd.h" using namespace srsran; @@ -35,32 +34,32 @@ static inline void convert_fi_simd(const float* x, int16_t* z, float scale, unsi simd_f_t s = srsran_simd_f_set1(scale); if (SIMD_IS_ALIGNED(x) && SIMD_IS_ALIGNED(z)) { for (; i + SRSRAN_SIMD_S_SIZE < len + 1; i += SRSRAN_SIMD_S_SIZE) { - simd_f_t a = srsran_simd_f_load(&x[i]); - simd_f_t b = srsran_simd_f_load(&x[i + SRSRAN_SIMD_F_SIZE]); + simd_f_t a = srsran_simd_f_load(x + i); + simd_f_t b = srsran_simd_f_load(x + i + SRSRAN_SIMD_F_SIZE); simd_f_t sa = srsran_simd_f_mul(a, s); simd_f_t sb = srsran_simd_f_mul(b, s); simd_s_t i16 = srsran_simd_convert_2f_s(sa, sb); - srsran_simd_s_store(&z[i], i16); + srsran_simd_s_store(z + i, i16); } } else { for (; i + SRSRAN_SIMD_S_SIZE < len + 1; i += SRSRAN_SIMD_S_SIZE) { - simd_f_t a = srsran_simd_f_loadu(&x[i]); - simd_f_t b = srsran_simd_f_loadu(&x[i + SRSRAN_SIMD_F_SIZE]); + simd_f_t a = srsran_simd_f_loadu(x + i); + simd_f_t b = srsran_simd_f_loadu(x + i + SRSRAN_SIMD_F_SIZE); simd_f_t sa = srsran_simd_f_mul(a, s); simd_f_t sb = srsran_simd_f_mul(b, s); simd_s_t i16 = srsran_simd_convert_2f_s(sa, sb); - srsran_simd_s_storeu(&z[i], i16); + srsran_simd_s_storeu(z + i, i16); } } #endif /* SRSRAN_SIMD_F_SIZE && SRSRAN_SIMD_S_SIZE */ - for (; i != len; i++) { + for (; i != len; ++i) { z[i] = static_cast(std::round(x[i] * scale)); } } @@ -97,7 +96,7 @@ static inline void convert_if_simd(float* z, const int16_t* x, float scale, unsi __m256i input_vec = _mm256_maskz_loadu_epi16(mask, reinterpret_cast(x + i)); // Convert the int16_t elements to float and scale them. - __m512 float_vec = _mm512_cvtepi32_ps(_mm512_cvtepi16_epi32(input_vec)); + __m512 float_vec = _mm512_maskz_cvtepi32_ps(mask, _mm512_maskz_cvtepi16_epi32(mask, input_vec)); float_vec = _mm512_mul_ps(float_vec, scale512); // Store the result back to memory. @@ -125,7 +124,7 @@ static inline void convert_if_simd(float* z, const int16_t* x, float scale, unsi } #endif // defined(__AVX__) && defined(__AVX2__) - for (; i != len; i++) { + for (; i != len; ++i) { z[i] = static_cast(x[i]) * gain; } } diff --git a/lib/srsvec/convolution.cpp b/lib/srsvec/convolution.cpp index 714a11a351..234e7b1d02 100644 --- a/lib/srsvec/convolution.cpp +++ b/lib/srsvec/convolution.cpp @@ -27,11 +27,7 @@ using namespace srsran; using namespace srsvec; -namespace srsran { -namespace srsvec { -namespace detail { - -void multiply_and_accumulate(span out, span x, span y) +void srsran::srsvec::detail::multiply_and_accumulate(span out, span x, span y) { unsigned y_mid = y.size() / 2; unsigned out_start = y_mid - (y.size() % 2 == 0 ? 1 : 0); @@ -44,15 +40,13 @@ void multiply_and_accumulate(span out, span x, span x_chunk = x.subspan(x_start, x_end - x_start); - unsigned i = 0; - - for (unsigned i_end = x_chunk.size(); i != i_end; ++i) { + for (unsigned i = 0, i_end = x_chunk.size(); i != i_end; ++i) { out[i + out_start] += x_chunk[i] * y[y_index]; } } } -void multiply_and_accumulate(span out, span x, span y) +void srsran::srsvec::detail::multiply_and_accumulate(span out, span x, span y) { unsigned y_mid = y.size() / 2; unsigned out_start = (y_mid - (y.size() % 2 == 0 ? 1 : 0)) * 2; @@ -69,11 +63,11 @@ void multiply_and_accumulate(span out, span x, span out, span x, span out, span x, span y) +void srsran::srsvec::detail::multiply_and_accumulate(span out, span x, span y) { unsigned y_mid = y.size() / 2; unsigned out_start = y_mid - (y.size() % 2 == 0 ? 1 : 0); @@ -103,14 +97,8 @@ void multiply_and_accumulate(span out, span x, span x_chunk = x.subspan(x_start, x_end - x_start); - unsigned i = 0; - - for (unsigned i_end = x_chunk.size(); i != i_end; ++i) { + for (unsigned i = 0, i_end = x_chunk.size(); i != i_end; ++i) { out[i + out_start] += x_chunk[i] * y[y_index]; } } } - -} // namespace detail -} // namespace srsvec -} // namespace srsran diff --git a/lib/srsvec/division.cpp b/lib/srsvec/division.cpp index 4d41732e5f..8762771b20 100644 --- a/lib/srsvec/division.cpp +++ b/lib/srsvec/division.cpp @@ -24,6 +24,7 @@ #include "simd.h" using namespace srsran; +using namespace srsvec; static void divide_fff_simd(float* result, const float* num, const float* den, std::size_t len) { @@ -31,12 +32,12 @@ static void divide_fff_simd(float* result, const float* num, const float* den, s #if SRSRAN_SIMD_F_SIZE for (; i + SRSRAN_SIMD_F_SIZE < len + 1; i += SRSRAN_SIMD_F_SIZE) { - simd_f_t num_simd = srsran_simd_f_loadu(&num[i]); - simd_f_t den_simd = srsran_simd_f_loadu(&den[i]); + simd_f_t num_simd = srsran_simd_f_loadu(num + i); + simd_f_t den_simd = srsran_simd_f_loadu(den + i); simd_f_t r = srsran_simd_f_mul(num_simd, srsran_simd_f_rcp(den_simd)); - srsran_simd_f_storeu(&result[i], r); + srsran_simd_f_storeu(result + i, r); } #endif @@ -56,4 +57,4 @@ void srsran::srsvec::divide(span result, span numerator, spa srsran_srsvec_assert_size(result, denominator); divide_fff_simd(result.data(), numerator.data(), denominator.data(), result.size()); -} \ No newline at end of file +} diff --git a/lib/srsvec/dot_prod.cpp b/lib/srsvec/dot_prod.cpp index f6e158d212..8fc7e9dc9a 100644 --- a/lib/srsvec/dot_prod.cpp +++ b/lib/srsvec/dot_prod.cpp @@ -21,24 +21,26 @@ */ #include "srsran/srsvec/dot_prod.h" - #include "simd.h" using namespace srsran; +using namespace srsvec; cf_t srsran::srsvec::dot_prod(span x, span y) { - cf_t result = 0; srsran_srsvec_assert_size(x, y); - unsigned i = 0, len = x.size(); + cf_t result = 0; + unsigned i = 0; + unsigned len = x.size(); + #if SRSRAN_SIMD_CF_SIZE if (len >= SRSRAN_SIMD_CF_SIZE) { simd_cf_t simd_result = srsran_simd_cf_zero(); for (unsigned simd_end = SRSRAN_SIMD_CF_SIZE * (len / SRSRAN_SIMD_CF_SIZE); i != simd_end; i += SRSRAN_SIMD_CF_SIZE) { - simd_cf_t simd_x = srsran_simd_cfi_loadu(&x[i]); - simd_cf_t simd_y = srsran_simd_cfi_loadu(&y[i]); + simd_cf_t simd_x = srsran_simd_cfi_loadu(x.data() + i); + simd_cf_t simd_y = srsran_simd_cfi_loadu(y.data() + i); simd_result = srsran_simd_cf_add(srsran_simd_cf_conjprod(simd_x, simd_y), simd_result); } diff --git a/lib/srsvec/modulus_square.cpp b/lib/srsvec/modulus_square.cpp index 56544d700e..86557d6129 100644 --- a/lib/srsvec/modulus_square.cpp +++ b/lib/srsvec/modulus_square.cpp @@ -24,6 +24,7 @@ #include "simd.h" using namespace srsran; +using namespace srsvec; static void modulus_square_simd(float* result, const cf_t* input, std::size_t len) { @@ -32,8 +33,8 @@ static void modulus_square_simd(float* result, const cf_t* input, std::size_t le #if SRSRAN_SIMD_CF_SIZE for (unsigned simd_end = SRSRAN_SIMD_CF_SIZE * (len / SRSRAN_SIMD_CF_SIZE); i != simd_end; i += SRSRAN_SIMD_CF_SIZE) { // Load 2 SIMD words of floats. - simd_f_t simd_input1 = srsran_simd_f_loadu((float*)&input[i]); - simd_f_t simd_input2 = srsran_simd_f_loadu((float*)&input[i + SRSRAN_SIMD_CF_SIZE / 2]); + simd_f_t simd_input1 = srsran_simd_f_loadu(reinterpret_cast(input + i)); + simd_f_t simd_input2 = srsran_simd_f_loadu(reinterpret_cast(input + i + SRSRAN_SIMD_CF_SIZE / 2)); // Calculates the squares. simd_f_t simd_mul1 = srsran_simd_f_mul(simd_input1, simd_input1); @@ -42,7 +43,7 @@ static void modulus_square_simd(float* result, const cf_t* input, std::size_t le // Horizontally add the values in pair, it results in adding the squared real and imaginary parts. simd_f_t simd_abs2 = srsran_simd_f_hadd(simd_mul1, simd_mul2); - srsran_simd_f_storeu(&result[i], simd_abs2); + srsran_simd_f_storeu(result + i, simd_abs2); } #endif // SRSRAN_SIMD_CF_SIZE @@ -57,5 +58,6 @@ static void modulus_square_simd(float* result, const cf_t* input, std::size_t le void srsran::srsvec::modulus_square(span result, span input) { srsran_srsvec_assert_size(result, input); + modulus_square_simd(result.data(), input.data(), input.size()); -} \ No newline at end of file +} diff --git a/lib/srsvec/prod.cpp b/lib/srsvec/prod.cpp index 71543d0eaf..4c90b20079 100644 --- a/lib/srsvec/prod.cpp +++ b/lib/srsvec/prod.cpp @@ -21,9 +21,8 @@ */ #include "srsran/srsvec/prod.h" -#include - #include "simd.h" +#include using namespace srsran; using namespace srsvec; @@ -35,21 +34,21 @@ static void prod_fff_simd(const float* x, const float* y, float* z, std::size_t #if SRSRAN_SIMD_F_SIZE if (SIMD_IS_ALIGNED(x) && SIMD_IS_ALIGNED(y) && SIMD_IS_ALIGNED(z)) { for (; i + SRSRAN_SIMD_F_SIZE < len + 1; i += SRSRAN_SIMD_F_SIZE) { - simd_f_t a = srsran_simd_f_load(&x[i]); - simd_f_t b = srsran_simd_f_load(&y[i]); + simd_f_t a = srsran_simd_f_load(x + i); + simd_f_t b = srsran_simd_f_load(y + i); simd_f_t r = srsran_simd_f_mul(a, b); - srsran_simd_f_store(&z[i], r); + srsran_simd_f_store(z + i, r); } } else { for (; i + SRSRAN_SIMD_F_SIZE < len + 1; i += SRSRAN_SIMD_F_SIZE) { - simd_f_t a = srsran_simd_f_loadu(&x[i]); - simd_f_t b = srsran_simd_f_loadu(&y[i]); + simd_f_t a = srsran_simd_f_loadu(x + i); + simd_f_t b = srsran_simd_f_loadu(y + i); simd_f_t r = srsran_simd_f_mul(a, b); - srsran_simd_f_storeu(&z[i], r); + srsran_simd_f_storeu(z + i, r); } } #endif @@ -66,21 +65,21 @@ static void prod_ccc_simd(const cf_t* x, const cf_t* y, cf_t* z, std::size_t len #if SRSRAN_SIMD_CF_SIZE if (SIMD_IS_ALIGNED(x) && SIMD_IS_ALIGNED(y) && SIMD_IS_ALIGNED(z)) { for (std::size_t i_end = (len / SRSRAN_SIMD_CF_SIZE) * SRSRAN_SIMD_CF_SIZE; i != i_end; i += SRSRAN_SIMD_CF_SIZE) { - simd_cf_t a = srsran_simd_cfi_load(&x[i]); - simd_cf_t b = srsran_simd_cfi_load(&y[i]); + simd_cf_t a = srsran_simd_cfi_load(x + i); + simd_cf_t b = srsran_simd_cfi_load(y + i); simd_cf_t r = srsran_simd_cf_prod(a, b); - srsran_simd_cfi_store(&z[i], r); + srsran_simd_cfi_store(z + i, r); } } else { for (std::size_t i_end = (len / SRSRAN_SIMD_CF_SIZE) * SRSRAN_SIMD_CF_SIZE; i != i_end; i += SRSRAN_SIMD_CF_SIZE) { - simd_cf_t a = srsran_simd_cfi_loadu(&x[i]); - simd_cf_t b = srsran_simd_cfi_loadu(&y[i]); + simd_cf_t a = srsran_simd_cfi_loadu(x + i); + simd_cf_t b = srsran_simd_cfi_loadu(y + i); simd_cf_t r = srsran_simd_cf_prod(a, b); - srsran_simd_cfi_storeu(&z[i], r); + srsran_simd_cfi_storeu(z + i, r); } } #endif @@ -97,21 +96,21 @@ static void prod_conj_ccc_simd(const cf_t* x, const cf_t* y, cf_t* z, std::size_ #if SRSRAN_SIMD_CF_SIZE if (SIMD_IS_ALIGNED(x) && SIMD_IS_ALIGNED(y) && SIMD_IS_ALIGNED(z)) { for (std::size_t i_end = (len / SRSRAN_SIMD_CF_SIZE) * SRSRAN_SIMD_CF_SIZE; i != i_end; i += SRSRAN_SIMD_CF_SIZE) { - simd_cf_t a = srsran_simd_cfi_load(&x[i]); - simd_cf_t b = srsran_simd_cfi_load(&y[i]); + simd_cf_t a = srsran_simd_cfi_load(x + i); + simd_cf_t b = srsran_simd_cfi_load(y + i); simd_cf_t r = srsran_simd_cf_conjprod(a, b); - srsran_simd_cfi_store(&z[i], r); + srsran_simd_cfi_store(z + i, r); } } else { for (std::size_t i_end = (len / SRSRAN_SIMD_CF_SIZE) * SRSRAN_SIMD_CF_SIZE; i != i_end; i += SRSRAN_SIMD_CF_SIZE) { - simd_cf_t a = srsran_simd_cfi_loadu(&x[i]); - simd_cf_t b = srsran_simd_cfi_loadu(&y[i]); + simd_cf_t a = srsran_simd_cfi_loadu(x + i); + simd_cf_t b = srsran_simd_cfi_loadu(y + i); simd_cf_t r = srsran_simd_cf_conjprod(a, b); - srsran_simd_cfi_storeu(&z[i], r); + srsran_simd_cfi_storeu(z + i, r); } } #endif @@ -143,4 +142,4 @@ void srsran::srsvec::prod_conj(span x, span y, span x, float h, span z) { srsran_srsvec_assert_size(x, z); - sc_prod_fff_simd((float*)x.data(), h, (float*)z.data(), 2 * x.size()); + sc_prod_fff_simd(reinterpret_cast(x.data()), h, reinterpret_cast(z.data()), 2 * x.size()); } void srsran::srsvec::sc_prod(span x, float h, span z) diff --git a/lib/srsvec/simd.h b/lib/srsvec/simd.h index 50b480a18e..8dffeec60f 100644 --- a/lib/srsvec/simd.h +++ b/lib/srsvec/simd.h @@ -57,29 +57,44 @@ inline bool is_simd_addr_aligned(const void* addr, uintptr_t mask) return (addr_i & mask) == 0; } -/* - * SIMD Vector bit alignment - */ -#ifdef HAVE_AVX512 -#define SIMD_BYTE_ALIGN 64 -#define SIMD_IS_ALIGNED(PTR) is_simd_addr_aligned(PTR, 0x3f) +/// +/// SIMD Vector bit alignment. +/// +#ifdef HAVE_AVX512 +constexpr unsigned SIMD_BYTE_ALIGN = 64; +inline bool SIMD_IS_ALIGNED(const void* ptr) +{ + return is_simd_addr_aligned(ptr, 0x3f); +} #else /* HAVE_AVX512 */ #ifdef HAVE_AVX -#define SIMD_BYTE_ALIGN 32 -#define SIMD_IS_ALIGNED(PTR) is_simd_addr_aligned(PTR, 0x1f) +constexpr unsigned SIMD_BYTE_ALIGN = 32; +inline bool SIMD_IS_ALIGNED(const void* ptr) +{ + return is_simd_addr_aligned(ptr, 0x1f); +} #else /* HAVE_AVX */ #ifdef HAVE_SSE -#define SIMD_BYTE_ALIGN 16 -#define SIMD_IS_ALIGNED(PTR) is_simd_addr_aligned(PTR, 0x0f) -#else /* HAVE_SSE */ -#define SIMD_BYTE_ALIGN 16 -#define SIMD_IS_ALIGNED(PTR) (true) +constexpr unsigned SIMD_BYTE_ALIGN = 16; +inline bool SIMD_IS_ALIGNED(const void* ptr) +{ + return is_simd_addr_aligned(ptr, 0x0f); +} +#else /* HAVE_SSE */ +constexpr unsigned SIMD_BYTE_ALIGN = 16; +inline bool SIMD_IS_ALIGNED(const void* ptr) +{ + return true; +} #endif /* HAVE_SSE */ #endif /* HAVE_AVX */ #endif /* HAVE_AVX512 */ -/* Memory Sizes for Single Floating Point and fixed point */ +/// +/// Memory Sizes for Single Floating Point and fixed point. +/// + #ifdef HAVE_AVX512 #define SRSRAN_SIMD_F_SIZE 16 @@ -147,25 +162,31 @@ inline bool is_simd_addr_aligned(const void* addr, uintptr_t mask) #if SRSRAN_SIMD_F_SIZE -/* Data types */ +/// +/// Data types. +/// + #ifdef HAVE_AVX512 -typedef __m512 simd_f_t; +using simd_f_t = __m512; #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 -typedef __m256 simd_f_t; +using simd_f_t = __m256; #else /* HAVE_AVX2 */ #ifdef HAVE_SSE -typedef __m128 simd_f_t; +using simd_f_t = __m128; #else /* HAVE_NEON */ #ifdef HAVE_NEON -typedef float32x4_t simd_f_t; +using simd_f_t = float32x4_t; #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ -/* Single precision Floating point functions */ -static inline simd_f_t srsran_simd_f_load(const float* ptr) +/// +/// Single precision Floating point functions. +/// + +inline simd_f_t srsran_simd_f_load(const float* ptr) { #ifdef HAVE_AVX512 return _mm512_load_ps(ptr); @@ -184,7 +205,7 @@ static inline simd_f_t srsran_simd_f_load(const float* ptr) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_loadu(const float* ptr) +inline simd_f_t srsran_simd_f_loadu(const float* ptr) { #ifdef HAVE_AVX512 return _mm512_loadu_ps(ptr); @@ -203,7 +224,7 @@ static inline simd_f_t srsran_simd_f_loadu(const float* ptr) #endif /* HAVE_AVX512 */ } -static inline void srsran_simd_f_store(float* ptr, simd_f_t simdreg) +inline void srsran_simd_f_store(float* ptr, simd_f_t simdreg) { #ifdef HAVE_AVX512 _mm512_store_ps(ptr, simdreg); @@ -222,7 +243,7 @@ static inline void srsran_simd_f_store(float* ptr, simd_f_t simdreg) #endif /* HAVE_AVX512 */ } -static inline void srsran_simd_f_storeu(float* ptr, simd_f_t simdreg) +inline void srsran_simd_f_storeu(float* ptr, simd_f_t simdreg) { #ifdef HAVE_AVX512 _mm512_storeu_ps(ptr, simdreg); @@ -241,7 +262,7 @@ static inline void srsran_simd_f_storeu(float* ptr, simd_f_t simdreg) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_set1(float x) +inline simd_f_t srsran_simd_f_set1(float x) { #ifdef HAVE_AVX512 return _mm512_set1_ps(x); @@ -260,7 +281,7 @@ static inline simd_f_t srsran_simd_f_set1(float x) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_mul(simd_f_t a, simd_f_t b) +inline simd_f_t srsran_simd_f_mul(simd_f_t a, simd_f_t b) { #ifdef HAVE_AVX512 return _mm512_mul_ps(a, b); @@ -279,7 +300,7 @@ static inline simd_f_t srsran_simd_f_mul(simd_f_t a, simd_f_t b) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_rcp(simd_f_t a) +inline simd_f_t srsran_simd_f_rcp(simd_f_t a) { #ifdef HAVE_AVX512 return _mm512_rcp14_ps(a); @@ -298,7 +319,7 @@ static inline simd_f_t srsran_simd_f_rcp(simd_f_t a) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_addsub(simd_f_t a, simd_f_t b) +inline simd_f_t srsran_simd_f_addsub(simd_f_t a, simd_f_t b) { #ifdef HAVE_AVX512 __m512 r = _mm512_add_ps(a, b); @@ -311,18 +332,17 @@ static inline simd_f_t srsran_simd_f_addsub(simd_f_t a, simd_f_t b) return _mm_addsub_ps(a, b); #else /* HAVE_SSE */ #ifdef HAVE_NEON // CURRENTLY USES GENERIC IMPLEMENTATION FOR NEON - float* a_ptr = (float*)&a; - float* b_ptr = (float*)&b; - simd_f_t ret; - float* c_ptr = (float*)&ret; - for (int i = 0; i < 4; i++) { + const float* a_ptr = reinterpret_cast(&a); + const float* b_ptr = reinterpret_cast(&b); + simd_f_t ret; + float* c_ptr = reinterpret_cast(&ret); + for (int i = 0; i != 4; ++i) { if (i % 2 == 0) { c_ptr[i] = a_ptr[i] - b_ptr[i]; } else { c_ptr[i] = a_ptr[i] + b_ptr[i]; } } - return ret; #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ @@ -330,7 +350,7 @@ static inline simd_f_t srsran_simd_f_addsub(simd_f_t a, simd_f_t b) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_sub(simd_f_t a, simd_f_t b) +inline simd_f_t srsran_simd_f_sub(simd_f_t a, simd_f_t b) { #ifdef HAVE_AVX512 return _mm512_sub_ps(a, b); @@ -349,7 +369,7 @@ static inline simd_f_t srsran_simd_f_sub(simd_f_t a, simd_f_t b) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_add(simd_f_t a, simd_f_t b) +inline simd_f_t srsran_simd_f_add(simd_f_t a, simd_f_t b) { #ifdef HAVE_AVX512 return _mm512_add_ps(a, b); @@ -368,7 +388,7 @@ static inline simd_f_t srsran_simd_f_add(simd_f_t a, simd_f_t b) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_fma(simd_f_t acc, simd_f_t a, simd_f_t b) +inline simd_f_t srsran_simd_f_fma(simd_f_t acc, simd_f_t a, simd_f_t b) { #ifdef HAVE_AVX512 return _mm512_fmadd_ps(a, b, acc); @@ -387,7 +407,7 @@ static inline simd_f_t srsran_simd_f_fma(simd_f_t acc, simd_f_t a, simd_f_t b) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_zero(void) +inline simd_f_t srsran_simd_f_zero() { #ifdef HAVE_AVX512 return _mm512_setzero_ps(); @@ -406,7 +426,7 @@ static inline simd_f_t srsran_simd_f_zero(void) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_swap(simd_f_t a) +inline simd_f_t srsran_simd_f_swap(simd_f_t a) { #ifdef HAVE_AVX512 return _mm512_permute_ps(a, 0b10110001); @@ -425,7 +445,7 @@ static inline simd_f_t srsran_simd_f_swap(simd_f_t a) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_hadd(simd_f_t a, simd_f_t b) +inline simd_f_t srsran_simd_f_hadd(simd_f_t a, simd_f_t b) { #ifdef HAVE_AVX512 const __m512i idx1 = _mm512_setr_epi32((0b00000), @@ -466,7 +486,7 @@ static inline simd_f_t srsran_simd_f_hadd(simd_f_t a, simd_f_t b) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_sqrt(simd_f_t a) +inline simd_f_t srsran_simd_f_sqrt(simd_f_t a) { #ifdef HAVE_AVX512 return _mm512_sqrt_ps(a); @@ -482,17 +502,20 @@ static inline simd_f_t srsran_simd_f_sqrt(simd_f_t a) sqrt_reciprocal = vmulq_f32(vrsqrtsq_f32(vmulq_f32(a, sqrt_reciprocal), sqrt_reciprocal), sqrt_reciprocal); float32x4_t result = vmulq_f32(a, sqrt_reciprocal); - /* Detect zeros in NEON 1/sqrtf for preventing NaN */ - float32x4_t zeros = vmovq_n_f32(0); /* Zero vector */ - uint32x4_t mask = vceqq_f32(a, zeros); /* Zero vector mask */ - return vbslq_f32(mask, zeros, result); /* Force zero results and return */ + // Detect zeros in NEON 1/sqrtf for preventing NaN. + // Zero vector. + float32x4_t zeros = vmovq_n_f32(0); + // Zero vector mask. + uint32x4_t mask = vceqq_f32(a, zeros); + // Force zero results and return. + return vbslq_f32(mask, zeros, result); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_neg(simd_f_t a) +inline simd_f_t srsran_simd_f_neg(simd_f_t a) { #ifdef HAVE_AVX512 return _mm512_xor_ps(_mm512_set1_ps(-0.0f), a); @@ -511,7 +534,7 @@ static inline simd_f_t srsran_simd_f_neg(simd_f_t a) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_neg_mask(simd_f_t a, simd_f_t mask) +inline simd_f_t srsran_simd_f_neg_mask(simd_f_t a, simd_f_t mask) { #ifdef HAVE_AVX512 return _mm512_xor_ps(mask, a); @@ -530,7 +553,7 @@ static inline simd_f_t srsran_simd_f_neg_mask(simd_f_t a, simd_f_t mask) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_abs(simd_f_t a) +inline simd_f_t srsran_simd_f_abs(simd_f_t a) { #ifdef HAVE_AVX512 return _mm512_andnot_ps(_mm512_set1_ps(-0.0f), a); @@ -549,17 +572,17 @@ static inline simd_f_t srsran_simd_f_abs(simd_f_t a) #endif /* HAVE_AVX512 */ } -static inline void srsran_simd_f_fprintf(FILE* stream, simd_f_t a) +inline void srsran_simd_f_fprintf(std::FILE* stream, simd_f_t a) { float x[SRSRAN_SIMD_F_SIZE]; srsran_simd_f_storeu(x, a); - fprintf(stream, "["); - for (int i = 0; i < SRSRAN_SIMD_F_SIZE; i++) { - fprintf(stream, "%+2.5f, ", x[i]); + std::fprintf(stream, "["); + for (float f : x) { + std::fprintf(stream, "%+2.5f, ", f); } - fprintf(stream, "];\n"); + std::fprintf(stream, "];\n"); } #endif /* SRSRAN_SIMD_F_SIZE */ @@ -567,21 +590,24 @@ static inline void srsran_simd_f_fprintf(FILE* stream, simd_f_t a) #if SRSRAN_SIMD_CF_SIZE #ifdef HAVE_NEON -typedef float32x4x2_t simd_cf_t; +using simd_cf_t = float32x4x2_t; #else -typedef struct { +struct simd_cf_t { simd_f_t re; simd_f_t im; -} simd_cf_t; +}; #endif -/* Complex Single precission Floating point functions */ -static inline simd_cf_t srsran_simd_cfi_load(const cf_t* ptr) +/// +/// Complex Single precision Floating point functions. +/// + +inline simd_cf_t srsran_simd_cfi_load(const cf_t* ptr) { simd_cf_t ret; #ifdef HAVE_AVX512 - __m512 in1 = _mm512_load_ps((float*)(ptr)); - __m512 in2 = _mm512_load_ps((float*)(ptr + SRSRAN_SIMD_CF_SIZE / 2)); + __m512 in1 = _mm512_load_ps(reinterpret_cast(ptr)); + __m512 in2 = _mm512_load_ps(reinterpret_cast(ptr + SRSRAN_SIMD_CF_SIZE / 2)); ret.re = _mm512_permutex2var_ps( in1, _mm512_setr_epi32(0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e), @@ -592,19 +618,19 @@ static inline simd_cf_t srsran_simd_cfi_load(const cf_t* ptr) in2); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - __m256 in1 = _mm256_permute_ps(_mm256_load_ps((float*)(ptr)), 0b11011000); - __m256 in2 = _mm256_permute_ps(_mm256_load_ps((float*)(ptr + 4)), 0b11011000); + __m256 in1 = _mm256_permute_ps(_mm256_load_ps(reinterpret_cast(ptr)), 0b11011000); + __m256 in2 = _mm256_permute_ps(_mm256_load_ps(reinterpret_cast(ptr + 4)), 0b11011000); ret.re = _mm256_unpacklo_ps(in1, in2); ret.im = _mm256_unpackhi_ps(in1, in2); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - __m128 i1 = _mm_load_ps((float*)(ptr)); - __m128 i2 = _mm_load_ps((float*)(ptr + 2)); + __m128 i1 = _mm_load_ps(reinterpret_cast(ptr)); + __m128 i2 = _mm_load_ps(reinterpret_cast(ptr + 2)); ret.re = _mm_shuffle_ps(i1, i2, _MM_SHUFFLE(2, 0, 2, 0)); ret.im = _mm_shuffle_ps(i1, i2, _MM_SHUFFLE(3, 1, 3, 1)); #else #ifdef HAVE_NEON - ret = vld2q_f32((float*)(ptr)); + ret = vld2q_f32(reinterpret_cast(ptr)); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ @@ -612,13 +638,12 @@ static inline simd_cf_t srsran_simd_cfi_load(const cf_t* ptr) return ret; } -/* Complex Single precission Floating point functions */ -static inline simd_cf_t srsran_simd_cfi_loadu(const cf_t* ptr) +inline simd_cf_t srsran_simd_cfi_loadu(const cf_t* ptr) { simd_cf_t ret; #ifdef HAVE_AVX512 - __m512 in1 = _mm512_loadu_ps((float*)(ptr)); - __m512 in2 = _mm512_loadu_ps((float*)(ptr + SRSRAN_SIMD_CF_SIZE / 2)); + __m512 in1 = _mm512_loadu_ps(reinterpret_cast(ptr)); + __m512 in2 = _mm512_loadu_ps(reinterpret_cast(ptr + SRSRAN_SIMD_CF_SIZE / 2)); ret.re = _mm512_permutex2var_ps( in1, _mm512_setr_epi32(0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e), @@ -629,19 +654,19 @@ static inline simd_cf_t srsran_simd_cfi_loadu(const cf_t* ptr) in2); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - __m256 in1 = _mm256_permute_ps(_mm256_loadu_ps((float*)(ptr)), 0b11011000); - __m256 in2 = _mm256_permute_ps(_mm256_loadu_ps((float*)(ptr + 4)), 0b11011000); + __m256 in1 = _mm256_permute_ps(_mm256_loadu_ps(reinterpret_cast(ptr)), 0b11011000); + __m256 in2 = _mm256_permute_ps(_mm256_loadu_ps(reinterpret_cast(ptr + 4)), 0b11011000); ret.re = _mm256_unpacklo_ps(in1, in2); ret.im = _mm256_unpackhi_ps(in1, in2); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - __m128 i1 = _mm_loadu_ps((float*)(ptr)); - __m128 i2 = _mm_loadu_ps((float*)(ptr + 2)); + __m128 i1 = _mm_loadu_ps(reinterpret_cast(ptr)); + __m128 i2 = _mm_loadu_ps(reinterpret_cast(ptr + 2)); ret.re = _mm_shuffle_ps(i1, i2, _MM_SHUFFLE(2, 0, 2, 0)); ret.im = _mm_shuffle_ps(i1, i2, _MM_SHUFFLE(3, 1, 3, 1)); #else #ifdef HAVE_NEON - ret = vld2q_f32((float*)(ptr)); + ret = vld2q_f32(reinterpret_cast(ptr)); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ @@ -649,7 +674,7 @@ static inline simd_cf_t srsran_simd_cfi_loadu(const cf_t* ptr) return ret; } -static inline simd_cf_t srsran_simd_cf_load(const float* re, const float* im) +inline simd_cf_t srsran_simd_cf_load(const float* re, const float* im) { simd_cf_t ret; #ifdef HAVE_AVX512 @@ -663,7 +688,7 @@ static inline simd_cf_t srsran_simd_cf_load(const float* re, const float* im) #ifdef HAVE_SSE ret.re = _mm_load_ps(re); ret.im = _mm_load_ps(im); -#else /*HAVE_NEON*/ +#else /* HAVE_NEON */ #ifdef HAVE_NEON ret.val[0] = vld1q_f32(re); ret.val[1] = vld1q_f32(im); @@ -674,7 +699,7 @@ static inline simd_cf_t srsran_simd_cf_load(const float* re, const float* im) return ret; } -static inline simd_cf_t srsran_simd_cf_loadu(const float* re, const float* im) +inline simd_cf_t srsran_simd_cf_loadu(const float* re, const float* im) { simd_cf_t ret; #ifdef HAVE_AVX512 @@ -688,7 +713,7 @@ static inline simd_cf_t srsran_simd_cf_loadu(const float* re, const float* im) #ifdef HAVE_SSE ret.re = _mm_loadu_ps(re); ret.im = _mm_loadu_ps(im); -#else /*HAVE_NEON*/ +#else /* HAVE_NEON */ #ifdef HAVE_NEON ret.val[0] = vld1q_f32(re); ret.val[1] = vld1q_f32(im); @@ -699,7 +724,7 @@ static inline simd_cf_t srsran_simd_cf_loadu(const float* re, const float* im) return ret; } -static inline void srsran_simd_cfi_store(cf_t* ptr, simd_cf_t simdreg) +inline void srsran_simd_cfi_store(cf_t* ptr, simd_cf_t simdreg) { #ifdef HAVE_AVX512 __m512 s1 = _mm512_permutex2var_ps( @@ -710,28 +735,28 @@ static inline void srsran_simd_cfi_store(cf_t* ptr, simd_cf_t simdreg) simdreg.re, _mm512_setr_epi32(0x08, 0x18, 0x09, 0x19, 0x0a, 0x1a, 0x0b, 0x1b, 0x0c, 0x1c, 0x0d, 0x1d, 0x0e, 0x1e, 0x0f, 0x1f), simdreg.im); - _mm512_store_ps((float*)(ptr), s1); - _mm512_store_ps((float*)(ptr + 8), s2); + _mm512_store_ps(reinterpret_cast(ptr), s1); + _mm512_store_ps(reinterpret_cast(ptr + 8), s2); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 __m256 out1 = _mm256_permute_ps(simdreg.re, 0b11011000); __m256 out2 = _mm256_permute_ps(simdreg.im, 0b11011000); - _mm256_store_ps((float*)(ptr), _mm256_unpacklo_ps(out1, out2)); - _mm256_store_ps((float*)(ptr + 4), _mm256_unpackhi_ps(out1, out2)); + _mm256_store_ps(reinterpret_cast(ptr), _mm256_unpacklo_ps(out1, out2)); + _mm256_store_ps(reinterpret_cast(ptr + 4), _mm256_unpackhi_ps(out1, out2)); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - _mm_store_ps((float*)(ptr), _mm_unpacklo_ps(simdreg.re, simdreg.im)); - _mm_store_ps((float*)(ptr + 2), _mm_unpackhi_ps(simdreg.re, simdreg.im)); -#else /*HAVE_NEON*/ + _mm_store_ps(reinterpret_cast(ptr), _mm_unpacklo_ps(simdreg.re, simdreg.im)); + _mm_store_ps(reinterpret_cast(ptr + 2), _mm_unpackhi_ps(simdreg.re, simdreg.im)); +#else /* HAVE_NEON */ #ifdef HAVE_NEON - vst2q_f32((float*)(ptr), simdreg); + vst2q_f32(reinterpret_cast(ptr), simdreg); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ } -static inline void srsran_simd_cfi_storeu(cf_t* ptr, simd_cf_t simdreg) +inline void srsran_simd_cfi_storeu(cf_t* ptr, simd_cf_t simdreg) { #ifdef HAVE_AVX512 __m512 s1 = _mm512_permutex2var_ps( @@ -742,28 +767,28 @@ static inline void srsran_simd_cfi_storeu(cf_t* ptr, simd_cf_t simdreg) simdreg.re, _mm512_setr_epi32(0x08, 0x18, 0x09, 0x19, 0x0a, 0x1a, 0x0b, 0x1b, 0x0c, 0x1c, 0x0d, 0x1d, 0x0e, 0x1e, 0x0f, 0x1f), simdreg.im); - _mm512_storeu_ps((float*)(ptr), s1); - _mm512_storeu_ps((float*)(ptr + 8), s2); + _mm512_storeu_ps(reinterpret_cast(ptr), s1); + _mm512_storeu_ps(reinterpret_cast(ptr + 8), s2); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 __m256 out1 = _mm256_permute_ps(simdreg.re, 0b11011000); __m256 out2 = _mm256_permute_ps(simdreg.im, 0b11011000); - _mm256_storeu_ps((float*)(ptr), _mm256_unpacklo_ps(out1, out2)); - _mm256_storeu_ps((float*)(ptr + 4), _mm256_unpackhi_ps(out1, out2)); + _mm256_storeu_ps(reinterpret_cast(ptr), _mm256_unpacklo_ps(out1, out2)); + _mm256_storeu_ps(reinterpret_cast(ptr + 4), _mm256_unpackhi_ps(out1, out2)); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - _mm_storeu_ps((float*)(ptr), _mm_unpacklo_ps(simdreg.re, simdreg.im)); - _mm_storeu_ps((float*)(ptr + 2), _mm_unpackhi_ps(simdreg.re, simdreg.im)); -#else /*HAVE_NEON*/ + _mm_storeu_ps(reinterpret_cast(ptr), _mm_unpacklo_ps(simdreg.re, simdreg.im)); + _mm_storeu_ps(reinterpret_cast(ptr + 2), _mm_unpackhi_ps(simdreg.re, simdreg.im)); +#else /* HAVE_NEON */ #ifdef HAVE_NEON - vst2q_f32((float*)(ptr), simdreg); + vst2q_f32(reinterpret_cast(ptr), simdreg); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ } -static inline void srsran_simd_cf_store(float* re, float* im, simd_cf_t simdreg) +inline void srsran_simd_cf_store(float* re, float* im, simd_cf_t simdreg) { #ifdef HAVE_AVX512 _mm512_store_ps(re, simdreg.re); @@ -774,19 +799,19 @@ static inline void srsran_simd_cf_store(float* re, float* im, simd_cf_t simdreg) _mm256_store_ps(im, simdreg.im); #else /* HAVE_AVX512 */ #ifdef HAVE_SSE - _mm_store_ps((float*)re, simdreg.re); - _mm_store_ps((float*)im, simdreg.im); -#else /*HAVE_NEON*/ + _mm_store_ps(re, simdreg.re); + _mm_store_ps(im, simdreg.im); +#else /* HAVE_NEON */ #ifdef HAVE_NEON - vst1q_f32((float*)re, simdreg.val[0]); - vst1q_f32((float*)im, simdreg.val[1]); + vst1q_f32(re, simdreg.val[0]); + vst1q_f32(im, simdreg.val[1]); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ } -static inline void srsran_simd_cf_storeu(float* re, float* im, simd_cf_t simdreg) +inline void srsran_simd_cf_storeu(float* re, float* im, simd_cf_t simdreg) { #ifdef HAVE_AVX512 _mm512_storeu_ps(re, simdreg.re); @@ -797,28 +822,28 @@ static inline void srsran_simd_cf_storeu(float* re, float* im, simd_cf_t simdreg _mm256_storeu_ps(im, simdreg.im); #else /* HAVE_AVX512 */ #ifdef HAVE_SSE - _mm_storeu_ps((float*)re, simdreg.re); - _mm_storeu_ps((float*)im, simdreg.im); -#else /*HAVE_NEON*/ + _mm_storeu_ps(re, simdreg.re); + _mm_storeu_ps(im, simdreg.im); +#else /* HAVE_NEON */ #ifdef HAVE_NEON - vst1q_f32((float*)re, simdreg.val[0]); - vst1q_f32((float*)im, simdreg.val[1]); + vst1q_f32(re, simdreg.val[0]); + vst1q_f32(im, simdreg.val[1]); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_cf_re(simd_cf_t in) +inline simd_f_t srsran_simd_cf_re(simd_cf_t in) { #ifdef HAVE_NEON simd_f_t out = in.val[0]; #else simd_f_t out = in.re; -#endif /*HAVE_NEON*/ +#endif /* HAVE_NEON */ #ifndef HAVE_AVX512 #ifdef HAVE_AVX2 - /* Permute for AVX registers (mis SSE registers) */ + // Permute for AVX registers (reorders data across 128-bit registers). const __m256i idx = _mm256_setr_epi32(0, 2, 4, 6, 1, 3, 5, 7); out = _mm256_permutevar8x32_ps(out, idx); #endif /* HAVE_AVX2 */ @@ -826,16 +851,16 @@ static inline simd_f_t srsran_simd_cf_re(simd_cf_t in) return out; } -static inline simd_f_t srsran_simd_cf_im(simd_cf_t in) +inline simd_f_t srsran_simd_cf_im(simd_cf_t in) { #ifdef HAVE_NEON simd_f_t out = in.val[1]; #else simd_f_t out = in.im; -#endif /*HAVE_NEON*/ +#endif /* HAVE_NEON */ #ifndef HAVE_AVX512 #ifdef HAVE_AVX2 - /* Permute for AVX registers (mis SSE registers) */ + // Permute for AVX registers (reorders data across 128-bit registers). const __m256i idx = _mm256_setr_epi32(0, 2, 4, 6, 1, 3, 5, 7); out = _mm256_permutevar8x32_ps(out, idx); #endif /* HAVE_AVX2 */ @@ -843,7 +868,7 @@ static inline simd_f_t srsran_simd_cf_im(simd_cf_t in) return out; } -static inline simd_cf_t srsran_simd_cf_set1(cf_t x) +inline simd_cf_t srsran_simd_cf_set1(cf_t x) { simd_cf_t ret; #ifdef HAVE_AVX512 @@ -857,7 +882,7 @@ static inline simd_cf_t srsran_simd_cf_set1(cf_t x) #ifdef HAVE_SSE ret.re = _mm_set1_ps(x.real()); ret.im = _mm_set1_ps(x.imag()); -#else /*HAVE_NEON*/ +#else /* HAVE_NEON */ #ifdef HAVE_NEON ret.val[0] = vdupq_n_f32(x.real()); ret.val[1] = vdupq_n_f32(x.imag()); @@ -868,7 +893,7 @@ static inline simd_cf_t srsran_simd_cf_set1(cf_t x) return ret; } -static inline simd_cf_t srsran_simd_cf_prod(simd_cf_t a, simd_cf_t b) +inline simd_cf_t srsran_simd_cf_prod(simd_cf_t a, simd_cf_t b) { simd_cf_t ret; #ifdef HAVE_AVX512 @@ -898,7 +923,7 @@ static inline simd_cf_t srsran_simd_cf_prod(simd_cf_t a, simd_cf_t b) return ret; } -static inline simd_cf_t srsran_simd_cf_conjprod(simd_cf_t a, simd_cf_t b) +inline simd_cf_t srsran_simd_cf_conjprod(simd_cf_t a, simd_cf_t b) { simd_cf_t ret; #ifdef HAVE_AVX512 @@ -923,7 +948,7 @@ static inline simd_cf_t srsran_simd_cf_conjprod(simd_cf_t a, simd_cf_t b) return ret; } -static inline simd_cf_t srsran_simd_cf_add(simd_cf_t a, simd_cf_t b) +inline simd_cf_t srsran_simd_cf_add(simd_cf_t a, simd_cf_t b) { simd_cf_t ret; #ifdef HAVE_AVX512 @@ -948,7 +973,7 @@ static inline simd_cf_t srsran_simd_cf_add(simd_cf_t a, simd_cf_t b) return ret; } -static inline simd_cf_t srsran_simd_cf_sub(simd_cf_t a, simd_cf_t b) +inline simd_cf_t srsran_simd_cf_sub(simd_cf_t a, simd_cf_t b) { simd_cf_t ret; #ifdef HAVE_AVX512 @@ -973,13 +998,13 @@ static inline simd_cf_t srsran_simd_cf_sub(simd_cf_t a, simd_cf_t b) return ret; } -static inline simd_f_t srsran_simd_cf_norm_sq(simd_cf_t a) +inline simd_f_t srsran_simd_cf_norm_sq(simd_cf_t a) { return srsran_simd_f_fma( srsran_simd_f_mul(srsran_simd_cf_re(a), srsran_simd_cf_re(a)), srsran_simd_cf_im(a), srsran_simd_cf_im(a)); } -static inline simd_cf_t srsran_simd_cf_mul(simd_cf_t a, simd_f_t b) +inline simd_cf_t srsran_simd_cf_mul(simd_cf_t a, simd_f_t b) { simd_cf_t ret; #ifdef HAVE_AVX512 @@ -1005,7 +1030,7 @@ static inline simd_cf_t srsran_simd_cf_mul(simd_cf_t a, simd_f_t b) return ret; } -static inline simd_cf_t srsran_simd_cf_rcp(simd_cf_t a) +inline simd_cf_t srsran_simd_cf_rcp(simd_cf_t a) { simd_cf_t ret; #ifdef HAVE_AVX512 @@ -1050,7 +1075,7 @@ static inline simd_cf_t srsran_simd_cf_rcp(simd_cf_t a) return ret; } -static inline simd_cf_t srsran_simd_cf_neg(simd_cf_t a) +inline simd_cf_t srsran_simd_cf_neg(simd_cf_t a) { simd_cf_t ret; #if HAVE_NEON @@ -1063,7 +1088,7 @@ static inline simd_cf_t srsran_simd_cf_neg(simd_cf_t a) return ret; } -static inline simd_cf_t srsran_simd_cf_neg_mask(simd_cf_t a, simd_f_t mask) +inline simd_cf_t srsran_simd_cf_neg_mask(simd_cf_t a, simd_f_t mask) { simd_cf_t ret; #ifndef HAVE_AVX512 @@ -1081,7 +1106,7 @@ static inline simd_cf_t srsran_simd_cf_neg_mask(simd_cf_t a, simd_f_t mask) return ret; } -static inline simd_cf_t srsran_simd_cf_conj(simd_cf_t a) +inline simd_cf_t srsran_simd_cf_conj(simd_cf_t a) { simd_cf_t ret; #if HAVE_NEON @@ -1094,7 +1119,7 @@ static inline simd_cf_t srsran_simd_cf_conj(simd_cf_t a) return ret; } -static inline simd_cf_t srsran_simd_cf_mulj(simd_cf_t a) +inline simd_cf_t srsran_simd_cf_mulj(simd_cf_t a) { simd_cf_t ret; #if HAVE_NEON @@ -1107,7 +1132,7 @@ static inline simd_cf_t srsran_simd_cf_mulj(simd_cf_t a) return ret; } -static inline simd_cf_t srsran_simd_cf_zero(void) +inline simd_cf_t srsran_simd_cf_zero() { simd_cf_t ret; #ifdef HAVE_AVX512 @@ -1132,17 +1157,17 @@ static inline simd_cf_t srsran_simd_cf_zero(void) return ret; } -static inline void srsran_simd_cf_fprintf(FILE* stream, simd_cf_t a) +inline void srsran_simd_cf_fprintf(std::FILE* stream, simd_cf_t a) { cf_t x[SRSRAN_SIMD_CF_SIZE]; srsran_simd_cfi_storeu(x, a); - fprintf(stream, "["); - for (int i = 0; i < SRSRAN_SIMD_CF_SIZE; i++) { - fprintf(stream, "%+2.5f%+2.5fi, ", x[i].real(), x[i].imag()); + std::fprintf(stream, "["); + for (auto c : x) { + std::fprintf(stream, "%+2.5f%+2.5fi, ", c.real(), c.imag()); } - fprintf(stream, "];\n"); + std::fprintf(stream, "];\n"); } #endif /* SRSRAN_SIMD_CF_SIZE */ @@ -1150,64 +1175,64 @@ static inline void srsran_simd_cf_fprintf(FILE* stream, simd_cf_t a) #if SRSRAN_SIMD_I_SIZE #ifdef HAVE_AVX512 -typedef __m512i simd_i_t; -typedef __mmask16 simd_sel_t; +using simd_i_t = __m512i; +using simd_sel_t = __mmask16; #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 -typedef __m256i simd_i_t; -typedef __m256 simd_sel_t; +using simd_i_t = __m256i; +using simd_sel_t = __m256; #else /* HAVE_AVX2 */ #ifdef HAVE_SSE -typedef __m128i simd_i_t; -typedef __m128 simd_sel_t; +using simd_i_t = __m128i; +using simd_sel_t = __m128; #else /* HAVE_AVX2 */ #ifdef HAVE_NEON -typedef int32x4_t simd_i_t; -typedef uint32x4_t simd_sel_t; +using simd_i_t = int32x4_t; +using simd_sel_t = uint32x4_t; #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ -static inline simd_i_t srsran_simd_i_load(int32_t* x) +inline simd_i_t srsran_simd_i_load(const int32_t* x) { #ifdef HAVE_AVX512 - return _mm512_load_epi32((__m512i*)x); + return _mm512_load_epi32(reinterpret_cast(x)); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - return _mm256_load_si256((__m256i*)x); + return _mm256_load_si256(reinterpret_cast(x)); #else #ifdef HAVE_SSE - return _mm_load_si128((__m128i*)x); + return _mm_load_si128(reinterpret_cast(x)); #else #ifdef HAVE_NEON - return vld1q_s32((int*)x); + return vld1q_s32(reinterpret_cast(x)); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ } -static inline void srsran_simd_i_store(int32_t* x, simd_i_t reg) +inline void srsran_simd_i_store(int32_t* x, simd_i_t reg) { #ifdef HAVE_AVX512 - _mm512_store_epi32((__m512i*)x, reg); + _mm512_store_epi32(reinterpret_cast<__m512i*>(x), reg); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - _mm256_store_si256((__m256i*)x, reg); + _mm256_store_si256(reinterpret_cast<__m256i*>(x), reg); #else #ifdef HAVE_SSE - _mm_store_si128((__m128i*)x, reg); + _mm_store_si128(reinterpret_cast<__m128i*>(x), reg); #else #ifdef HAVE_NEON - vst1q_s32((int*)x, reg); -#endif /*HAVE_NEON*/ + vst1q_s32(reinterpret_cast(x), reg); +#endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ } -static inline simd_i_t srsran_simd_i_set1(int x) +inline simd_i_t srsran_simd_i_set1(int x) { #ifdef HAVE_AVX512 return _mm512_set1_epi32(x); @@ -1226,7 +1251,7 @@ static inline simd_i_t srsran_simd_i_set1(int x) #endif /* HAVE_AVX512 */ } -static inline simd_i_t srsran_simd_i_add(simd_i_t a, simd_i_t b) +inline simd_i_t srsran_simd_i_add(simd_i_t a, simd_i_t b) { #ifdef HAVE_AVX512 return _mm512_add_epi32(a, b); @@ -1245,7 +1270,7 @@ static inline simd_i_t srsran_simd_i_add(simd_i_t a, simd_i_t b) #endif /* HAVE_AVX512 */ } -static inline simd_i_t srsran_simd_i_mul(simd_i_t a, simd_i_t b) +inline simd_i_t srsran_simd_i_mul(simd_i_t a, simd_i_t b) { #ifdef HAVE_AVX512 return _mm512_mullo_epi32(a, b); @@ -1264,7 +1289,7 @@ static inline simd_i_t srsran_simd_i_mul(simd_i_t a, simd_i_t b) #endif /* HAVE_AVX512 */ } -static inline simd_i_t srsran_simd_i_and(simd_i_t a, simd_i_t b) +inline simd_i_t srsran_simd_i_and(simd_i_t a, simd_i_t b) { #ifdef HAVE_AVX512 return _mm512_and_si512(a, b); @@ -1283,7 +1308,7 @@ static inline simd_i_t srsran_simd_i_and(simd_i_t a, simd_i_t b) #endif /* HAVE_AVX512 */ } -static inline simd_sel_t srsran_simd_sel_and(simd_sel_t a, simd_sel_t b) +inline simd_sel_t srsran_simd_sel_and(simd_sel_t a, simd_sel_t b) { #ifdef HAVE_AVX512 return _kand_mask16(a, b); @@ -1302,7 +1327,7 @@ static inline simd_sel_t srsran_simd_sel_and(simd_sel_t a, simd_sel_t b) #endif /* HAVE_AVX512 */ } -static inline simd_sel_t srsran_simd_f_max(simd_f_t a, simd_f_t b) +inline simd_sel_t srsran_simd_f_max(simd_f_t a, simd_f_t b) { #ifdef HAVE_AVX512 return _mm512_cmp_ps_mask(a, b, _CMP_GT_OS); @@ -1321,7 +1346,7 @@ static inline simd_sel_t srsran_simd_f_max(simd_f_t a, simd_f_t b) #endif /* HAVE_AVX512 */ } -static inline simd_sel_t srsran_simd_f_min(simd_f_t a, simd_f_t b) +inline simd_sel_t srsran_simd_f_min(simd_f_t a, simd_f_t b) { #ifdef HAVE_AVX512 return _mm512_cmp_ps_mask(a, b, _CMP_LT_OS); @@ -1340,7 +1365,7 @@ static inline simd_sel_t srsran_simd_f_min(simd_f_t a, simd_f_t b) #endif /* HAVE_AVX512 */ } -static inline simd_f_t srsran_simd_f_select(simd_f_t a, simd_f_t b, simd_sel_t selector) +inline simd_f_t srsran_simd_f_select(simd_f_t a, simd_f_t b, simd_sel_t selector) { #ifdef HAVE_AVX512 return _mm512_mask_blend_ps(selector, (__m512)a, (__m512)b); @@ -1359,7 +1384,7 @@ static inline simd_f_t srsran_simd_f_select(simd_f_t a, simd_f_t b, simd_sel_t s #endif /* HAVE_AVX512 */ } -static inline simd_cf_t srsran_simd_cf_select(simd_cf_t a, simd_cf_t b, simd_sel_t selector) +inline simd_cf_t srsran_simd_cf_select(simd_cf_t a, simd_cf_t b, simd_sel_t selector) { simd_cf_t ret; #ifdef HAVE_AVX512 @@ -1386,7 +1411,7 @@ static inline simd_cf_t srsran_simd_cf_select(simd_cf_t a, simd_cf_t b, simd_sel return ret; } -static inline simd_i_t srsran_simd_i_select(simd_i_t a, simd_i_t b, simd_sel_t selector) +inline simd_i_t srsran_simd_i_select(simd_i_t a, simd_i_t b, simd_sel_t selector) { #ifdef HAVE_AVX512 return (__m512i)_mm512_mask_blend_ps(selector, (__m512)a, (__m512)b); @@ -1405,36 +1430,36 @@ static inline simd_i_t srsran_simd_i_select(simd_i_t a, simd_i_t b, simd_sel_t s #endif /* HAVE_AVX512 */ } -#endif /* SRSRAN_SIMD_I_SIZE*/ +#endif /* SRSRAN_SIMD_I_SIZE */ #if SRSRAN_SIMD_S_SIZE #ifdef HAVE_AVX512 -typedef __m512i simd_s_t; +using simd_s_t = __m512i; #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 -typedef __m256i simd_s_t; +using simd_s_t = __m256i; #else /* HAVE_AVX2 */ #ifdef HAVE_SSE -typedef __m128i simd_s_t; +using simd_s_t = __m128i; #else /* HAVE_SSE */ #ifdef HAVE_NEON -typedef int16x8_t simd_s_t; +using simd_s_t = int16x8_t; #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ -static inline simd_s_t srsran_simd_s_load(const int16_t* ptr) +inline simd_s_t srsran_simd_s_load(const int16_t* ptr) { #ifdef HAVE_AVX512 return _mm512_load_si512(ptr); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - return _mm256_load_si256((__m256i*)ptr); + return _mm256_load_si256(reinterpret_cast(ptr)); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - return _mm_load_si128((__m128i*)ptr); + return _mm_load_si128(reinterpret_cast(ptr)); #else /* HAVE_SSE */ #ifdef HAVE_NEON return vld1q_s16(ptr); @@ -1444,16 +1469,16 @@ static inline simd_s_t srsran_simd_s_load(const int16_t* ptr) #endif /* HAVE_AVX512 */ } -static inline simd_s_t srsran_simd_s_loadu(const int16_t* ptr) +inline simd_s_t srsran_simd_s_loadu(const int16_t* ptr) { #ifdef HAVE_AVX512 return _mm512_loadu_si512(ptr); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - return _mm256_loadu_si256((__m256i*)ptr); + return _mm256_loadu_si256(reinterpret_cast(ptr)); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - return _mm_loadu_si128((__m128i*)ptr); + return _mm_loadu_si128(reinterpret_cast(ptr)); #else /* HAVE_SSE */ #ifdef HAVE_NEON return vld1q_s16(ptr); @@ -1463,16 +1488,16 @@ static inline simd_s_t srsran_simd_s_loadu(const int16_t* ptr) #endif /* HAVE_AVX512 */ } -static inline void srsran_simd_s_store(int16_t* ptr, simd_s_t simdreg) +inline void srsran_simd_s_store(int16_t* ptr, simd_s_t simdreg) { #ifdef HAVE_AVX512 _mm512_store_si512(ptr, simdreg); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - _mm256_store_si256((__m256i*)ptr, simdreg); + _mm256_store_si256(reinterpret_cast<__m256i*>(ptr), simdreg); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - _mm_store_si128((__m128i*)ptr, simdreg); + _mm_store_si128(reinterpret_cast<__m128i*>(ptr), simdreg); #else /* HAVE_SSE */ #ifdef HAVE_NEON vst1q_s16(ptr, simdreg); @@ -1482,16 +1507,16 @@ static inline void srsran_simd_s_store(int16_t* ptr, simd_s_t simdreg) #endif /* HAVE_AVX512 */ } -static inline void srsran_simd_s_storeu(int16_t* ptr, simd_s_t simdreg) +inline void srsran_simd_s_storeu(int16_t* ptr, simd_s_t simdreg) { #ifdef HAVE_AVX512 _mm512_storeu_si512(ptr, simdreg); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - _mm256_storeu_si256((__m256i*)ptr, simdreg); + _mm256_storeu_si256(reinterpret_cast<__m256i*>(ptr), simdreg); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - _mm_storeu_si128((__m128i*)ptr, simdreg); + _mm_storeu_si128(reinterpret_cast<__m128i*>(ptr), simdreg); #else /* HAVE_SSE */ #ifdef HAVE_NEON vst1q_s16(ptr, simdreg); @@ -1500,7 +1525,7 @@ static inline void srsran_simd_s_storeu(int16_t* ptr, simd_s_t simdreg) #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ } -static inline simd_s_t srsran_simd_s_zero(void) +inline simd_s_t srsran_simd_s_zero() { #ifdef HAVE_AVX512 return _mm512_setzero_si512(); @@ -1519,7 +1544,7 @@ static inline simd_s_t srsran_simd_s_zero(void) #endif /* HAVE_AVX512 */ } -static inline simd_s_t srsran_simd_s_mul(simd_s_t a, simd_s_t b) +inline simd_s_t srsran_simd_s_mul(simd_s_t a, simd_s_t b) { #ifdef HAVE_AVX512 return _mm512_mullo_epi16(a, b); @@ -1538,7 +1563,7 @@ static inline simd_s_t srsran_simd_s_mul(simd_s_t a, simd_s_t b) #endif /* HAVE_AVX512 */ } -static inline simd_s_t srsran_simd_s_neg(simd_s_t a, simd_s_t b) +inline simd_s_t srsran_simd_s_neg(simd_s_t a, simd_s_t b) { #ifdef HAVE_AVX512 __m256i a0 = _mm512_extracti64x4_epi64(a, 0); @@ -1556,18 +1581,17 @@ static inline simd_s_t srsran_simd_s_neg(simd_s_t a, simd_s_t b) return _mm_sign_epi16(a, b); #else /* HAVE_SSE */ #ifdef HAVE_NEON - /* Taken and modified from sse2neon.h licensed under MIT - * Source: https://github.com/DLTcollab/sse2neon - */ + // Taken and modified from sse2neon.h licensed under MIT. + // Source: https://github.com/DLTcollab/sse2neon int16x8_t zero = vdupq_n_s16(0); - // signed shift right: faster than vclt + // Signed shift right: faster than vclt. // (b < 0) ? 0xffff : 0 uint16x8_t ltMask = vreinterpretq_u16_s16(vshrq_n_s16(b, 15)); // (b == 0) ? 0xffff : 0 int16x8_t zeroMask = vreinterpretq_s16_u16(vceqq_s16(b, zero)); // -a int16x8_t neg = vnegq_s16(a); - // bitwise select either a or neg based on ltMask + // Bitwise select either a or neg based on ltMask. int16x8_t masked = vbslq_s16(ltMask, neg, a); // res = masked & (~zeroMask) int16x8_t res = vbicq_s16(masked, zeroMask); @@ -1578,7 +1602,7 @@ static inline simd_s_t srsran_simd_s_neg(simd_s_t a, simd_s_t b) #endif /* HAVE_AVX512 */ } -static inline simd_s_t srsran_simd_s_add(simd_s_t a, simd_s_t b) +inline simd_s_t srsran_simd_s_add(simd_s_t a, simd_s_t b) { #ifdef HAVE_AVX512 return _mm512_add_epi16(a, b); @@ -1597,7 +1621,7 @@ static inline simd_s_t srsran_simd_s_add(simd_s_t a, simd_s_t b) #endif /* HAVE_AVX512 */ } -static inline simd_s_t srsran_simd_s_sub(simd_s_t a, simd_s_t b) +inline simd_s_t srsran_simd_s_sub(simd_s_t a, simd_s_t b) { #ifdef HAVE_AVX512 return _mm512_sub_epi16(a, b); @@ -1620,78 +1644,64 @@ static inline simd_s_t srsran_simd_s_sub(simd_s_t a, simd_s_t b) #if SRSRAN_SIMD_C16_SIZE -typedef -#ifdef HAVE_AVX512 - struct { - union { - __m512i m512; - int16_t i16[32]; - } re; - union { - __m512i m512; - int16_t i16[32]; - } im; -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 - struct { - union { - __m256i m256; - int16_t i16[16]; - } re; - union { - __m256i m256; - int16_t i16[16]; - } im; +#ifdef HAVE_AVX512 +struct simd_c16_t { + __m512i re; + __m512i im; +}; +#else /* HAVE_AVX512 */ +#ifdef HAVE_AVX2 +struct simd_c16_t { + __m256i re; + __m256i im; +}; #else #ifdef HAVE_SSE - struct { - union { - __m128i m128; - int16_t i16[8]; - } re; - union { - __m128i m128; - int16_t i16[8]; - } im; +struct simd_c16_t { + __m128i re; + __m128i im; +}; #else #ifdef HAVE_NEON - union { +struct simd_c16_t { int16x8x2_t m128; - int16_t i16[16]; +}; #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ -} simd_c16_t; -/* Fixed point precision (16-bit) functions */ -static inline simd_c16_t srsran_simd_c16i_load(const c16_t* ptr) +/// +/// Fixed point precision (16-bit) functions. +/// + +inline simd_c16_t srsran_simd_c16i_load(const c16_t* ptr) { simd_c16_t ret; #ifdef HAVE_AVX512 - __m512i in1 = _mm512_load_si512((__m512i*)(ptr)); - __m512i in2 = _mm512_load_si512((__m512i*)(ptr + 8)); - ret.re.m512 = _mm512_mask_blend_epi16( + __m512i in1 = _mm512_load_si512(reinterpret_cast(ptr)); + __m512i in2 = _mm512_load_si512(reinterpret_cast(ptr + 8)); + ret.re = _mm512_mask_blend_epi16( 0xaaaaaaaa, in1, _mm512_shufflelo_epi16(_mm512_shufflehi_epi16(in2, 0b10100000), 0b10100000)); - ret.im.m512 = _mm512_mask_blend_epi16( + ret.im = _mm512_mask_blend_epi16( 0xaaaaaaaa, _mm512_shufflelo_epi16(_mm512_shufflehi_epi16(in1, 0b11110101), 0b11110101), in2); #else /* HAVE_AVX2 */ #ifdef HAVE_AVX2 - __m256i in1 = _mm256_load_si256((__m256i*)(ptr)); - __m256i in2 = _mm256_load_si256((__m256i*)(ptr + 8)); - ret.re.m256 = + __m256i in1 = _mm256_load_si256(reinterpret_cast(ptr)); + __m256i in2 = _mm256_load_si256(reinterpret_cast(ptr + 8)); + ret.re = _mm256_blend_epi16(in1, _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(in2, 0b10100000), 0b10100000), 0b10101010); - ret.im.m256 = + ret.im = _mm256_blend_epi16(_mm256_shufflelo_epi16(_mm256_shufflehi_epi16(in1, 0b11110101), 0b11110101), in2, 0b10101010); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - __m128i in1 = _mm_load_si128((__m128i*)(ptr)); - __m128i in2 = _mm_load_si128((__m128i*)(ptr + 8)); - ret.re.m128 = _mm_blend_epi16(in1, _mm_shufflelo_epi16(_mm_shufflehi_epi16(in2, 0b10100000), 0b10100000), 0b10101010); - ret.im.m128 = _mm_blend_epi16(_mm_shufflelo_epi16(_mm_shufflehi_epi16(in1, 0b11110101), 0b11110101), in2, 0b10101010); + __m128i in1 = _mm_load_si128(reinterpret_cast(ptr)); + __m128i in2 = _mm_load_si128(reinterpret_cast(ptr + 8)); + ret.re = _mm_blend_epi16(in1, _mm_shufflelo_epi16(_mm_shufflehi_epi16(in2, 0b10100000), 0b10100000), 0b10101010); + ret.im = _mm_blend_epi16(_mm_shufflelo_epi16(_mm_shufflehi_epi16(in1, 0b11110101), 0b11110101), in2, 0b10101010); #else /* HAVE_SSE*/ #ifdef HAVE_NEON - ret.m128 = vld2q_s16((int16_t*)(ptr)); + ret.m128 = vld2q_s16(reinterpret_cast(ptr)); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ @@ -1699,153 +1709,153 @@ static inline simd_c16_t srsran_simd_c16i_load(const c16_t* ptr) return ret; } -static inline simd_c16_t srsran_simd_c16_load(const int16_t* re, const int16_t* im) +inline simd_c16_t srsran_simd_c16_load(const int16_t* re, const int16_t* im) { simd_c16_t ret; #ifdef HAVE_AVX2 - ret.re.m256 = _mm256_load_si256((__m256i*)(re)); - ret.im.m256 = _mm256_load_si256((__m256i*)(im)); + ret.re = _mm256_load_si256(reinterpret_cast(re)); + ret.im = _mm256_load_si256(reinterpret_cast(im)); #else #ifdef HAVE_SSE - ret.re.m128 = _mm_load_si128((__m128i*)(re)); - ret.im.m128 = _mm_load_si128((__m128i*)(im)); + ret.re = _mm_load_si128(reinterpret_cast(re)); + ret.im = _mm_load_si128(reinterpret_cast(im)); #else /* HAVE_SSE*/ #ifdef HAVE_NEON - ret.m128.val[0] = vld1q_s16((int16_t*)(re)); - ret.m128.val[1] = vld1q_s16((int16_t*)(im)); + ret.m128.val[0] = vld1q_s16(re); + ret.m128.val[1] = vld1q_s16(im); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ return ret; } -static inline simd_c16_t srsran_simd_c16_loadu(const int16_t* re, const int16_t* im) +inline simd_c16_t srsran_simd_c16_loadu(const int16_t* re, const int16_t* im) { simd_c16_t ret; #ifdef HAVE_AVX2 - ret.re.m256 = _mm256_loadu_si256((__m256i*)(re)); - ret.im.m256 = _mm256_loadu_si256((__m256i*)(im)); + ret.re = _mm256_loadu_si256(reinterpret_cast(re)); + ret.im = _mm256_loadu_si256(reinterpret_cast(im)); #else #ifdef HAVE_SSE - ret.re.m128 = _mm_loadu_si128((__m128i*)(re)); - ret.im.m128 = _mm_loadu_si128((__m128i*)(im)); + ret.re = _mm_loadu_si128(reinterpret_cast(re)); + ret.im = _mm_loadu_si128(reinterpret_cast(im)); #else /* HAVE_SSE*/ #ifdef HAVE_NEON - ret.m128.val[0] = vld1q_s16((int16_t*)(re)); - ret.m128.val[1] = vld1q_s16((int16_t*)(im)); + ret.m128.val[0] = vld1q_s16(re); + ret.m128.val[1] = vld1q_s16(im); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ return ret; } -static inline void srsran_simd_c16i_store(c16_t* ptr, simd_c16_t simdreg) +inline void srsran_simd_c16i_store(c16_t* ptr, simd_c16_t simdreg) { #ifdef HAVE_AVX2 - __m256i re_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.re.m256, 0b10110001), 0b10110001); - __m256i im_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.im.m256, 0b10110001), 0b10110001); - _mm256_store_si256((__m256i*)(ptr), _mm256_blend_epi16(simdreg.re.m256, im_sw, 0b10101010)); - _mm256_store_si256((__m256i*)(ptr + 8), _mm256_blend_epi16(re_sw, simdreg.im.m256, 0b10101010)); + __m256i re_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.re, 0b10110001), 0b10110001); + __m256i im_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.im, 0b10110001), 0b10110001); + _mm256_store_si256(reinterpret_cast<__m256i*>(ptr), _mm256_blend_epi16(simdreg.re, im_sw, 0b10101010)); + _mm256_store_si256(reinterpret_cast<__m256i*>(ptr + 8), _mm256_blend_epi16(re_sw, simdreg.im, 0b10101010)); #else #ifdef HAVE_SSE - __m128i re_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.re.m128, 0b10110001), 0b10110001); - __m128i im_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.im.m128, 0b10110001), 0b10110001); - _mm_store_si128((__m128i*)(ptr), _mm_blend_epi16(simdreg.re.m128, im_sw, 0b10101010)); - _mm_store_si128((__m128i*)(ptr + 8), _mm_blend_epi16(re_sw, simdreg.im.m128, 0b10101010)); -#else /*HAVE_NEON*/ + __m128i re_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.re, 0b10110001), 0b10110001); + __m128i im_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.im, 0b10110001), 0b10110001); + _mm_store_si128(reinterpret_cast<__m128i*>(ptr), _mm_blend_epi16(simdreg.re, im_sw, 0b10101010)); + _mm_store_si128(reinterpret_cast<__m128i*>(ptr + 8), _mm_blend_epi16(re_sw, simdreg.im, 0b10101010)); +#else /* HAVE_NEON */ #ifdef HAVE_NEON - vst2q_s16((int16_t*)(ptr), simdreg.m128); + vst2q_s16(reinterpret_cast(ptr), simdreg.m128); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ } -static inline void srsran_simd_c16i_storeu(c16_t* ptr, simd_c16_t simdreg) +inline void srsran_simd_c16i_storeu(c16_t* ptr, simd_c16_t simdreg) { #ifdef HAVE_AVX2 - __m256i re_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.re.m256, 0b10110001), 0b10110001); - __m256i im_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.im.m256, 0b10110001), 0b10110001); - _mm256_storeu_si256((__m256i*)(ptr), _mm256_blend_epi16(simdreg.re.m256, im_sw, 0b10101010)); - _mm256_storeu_si256((__m256i*)(ptr + 8), _mm256_blend_epi16(re_sw, simdreg.im.m256, 0b10101010)); + __m256i re_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.re, 0b10110001), 0b10110001); + __m256i im_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.im, 0b10110001), 0b10110001); + _mm256_storeu_si256(reinterpret_cast<__m256i*>(ptr), _mm256_blend_epi16(simdreg.re, im_sw, 0b10101010)); + _mm256_storeu_si256(reinterpret_cast<__m256i*>(ptr + 8), _mm256_blend_epi16(re_sw, simdreg.im, 0b10101010)); #else #ifdef HAVE_SSE - __m128i re_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.re.m128, 0b10110001), 0b10110001); - __m128i im_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.im.m128, 0b10110001), 0b10110001); - _mm_storeu_si128((__m128i*)(ptr), _mm_blend_epi16(simdreg.re.m128, im_sw, 0b10101010)); - _mm_storeu_si128((__m128i*)(ptr + 8), _mm_blend_epi16(re_sw, simdreg.im.m128, 0b10101010)); -#else /*HAVE_NEON*/ + __m128i re_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.re, 0b10110001), 0b10110001); + __m128i im_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.im, 0b10110001), 0b10110001); + _mm_storeu_si128(reinterpret_cast<__m128i*>(ptr), _mm_blend_epi16(simdreg.re, im_sw, 0b10101010)); + _mm_storeu_si128(reinterpret_cast<__m128i*>(ptr + 8), _mm_blend_epi16(re_sw, simdreg.im, 0b10101010)); +#else /* HAVE_NEON */ #ifdef HAVE_NEON - vst2q_s16((int16_t*)(ptr), simdreg.m128); + vst2q_s16(reinterpret_cast(ptr), simdreg.m128); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ } -static inline void srsran_simd_c16_store(int16_t* re, int16_t* im, simd_c16_t simdreg) +inline void srsran_simd_c16_store(int16_t* re, int16_t* im, simd_c16_t simdreg) { #ifdef HAVE_AVX2 - _mm256_store_si256((__m256i*)re, simdreg.re.m256); - _mm256_store_si256((__m256i*)im, simdreg.im.m256); + _mm256_store_si256(reinterpret_cast<__m256i*>(re), simdreg.re); + _mm256_store_si256(reinterpret_cast<__m256i*>(im), simdreg.im); #else #ifdef HAVE_SSE - _mm_store_si128((__m128i*)re, simdreg.re.m128); - _mm_store_si128((__m128i*)im, simdreg.im.m128); + _mm_store_si128(reinterpret_cast<__m128i*>(re), simdreg.re); + _mm_store_si128(reinterpret_cast<__m128i*>(im), simdreg.im); #else #ifdef HAVE_NEON - vst1q_s16((int16_t*)re, simdreg.m128.val[0]); - vst1q_s16((int16_t*)im, simdreg.m128.val[1]); + vst1q_s16(re, simdreg.m128.val[0]); + vst1q_s16(im, simdreg.m128.val[1]); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ } -static inline void srsran_simd_c16_storeu(int16_t* re, int16_t* im, simd_c16_t simdreg) +inline void srsran_simd_c16_storeu(int16_t* re, int16_t* im, simd_c16_t simdreg) { #ifdef HAVE_AVX2 - _mm256_storeu_si256((__m256i*)re, simdreg.re.m256); - _mm256_storeu_si256((__m256i*)im, simdreg.im.m256); + _mm256_storeu_si256(reinterpret_cast<__m256i*>(re), simdreg.re); + _mm256_storeu_si256(reinterpret_cast<__m256i*>(im), simdreg.im); #else #ifdef HAVE_SSE - _mm_storeu_si128((__m128i*)re, simdreg.re.m128); - _mm_storeu_si128((__m128i*)im, simdreg.im.m128); + _mm_storeu_si128(reinterpret_cast<__m128i*>(re), simdreg.re); + _mm_storeu_si128(reinterpret_cast<__m128i*>(im), simdreg.im); #else #ifdef HAVE_NEON - vst1q_s16((int16_t*)re, simdreg.m128.val[0]); - vst1q_s16((int16_t*)im, simdreg.m128.val[1]); + vst1q_s16(re, simdreg.m128.val[0]); + vst1q_s16(im, simdreg.m128.val[1]); #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ } -static inline simd_c16_t srsran_simd_c16_prod(simd_c16_t a, simd_c16_t b) +inline simd_c16_t srsran_simd_c16_prod(simd_c16_t a, simd_c16_t b) { simd_c16_t ret; #ifdef HAVE_AVX2 - ret.re.m256 = _mm256_sub_epi16(_mm256_mulhrs_epi16(a.re.m256, _mm256_slli_epi16(b.re.m256, 1)), - _mm256_mulhrs_epi16(a.im.m256, _mm256_slli_epi16(b.im.m256, 1))); - ret.im.m256 = _mm256_add_epi16(_mm256_mulhrs_epi16(a.re.m256, _mm256_slli_epi16(b.im.m256, 1)), - _mm256_mulhrs_epi16(a.im.m256, _mm256_slli_epi16(b.re.m256, 1))); + ret.re = _mm256_sub_epi16(_mm256_mulhrs_epi16(a.re, _mm256_slli_epi16(b.re, 1)), + _mm256_mulhrs_epi16(a.im, _mm256_slli_epi16(b.im, 1))); + ret.im = _mm256_add_epi16(_mm256_mulhrs_epi16(a.re, _mm256_slli_epi16(b.im, 1)), + _mm256_mulhrs_epi16(a.im, _mm256_slli_epi16(b.re, 1))); #else #ifdef HAVE_SSE - ret.re.m128 = _mm_sub_epi16(_mm_mulhrs_epi16(a.re.m128, _mm_slli_epi16(b.re.m128, 1)), - _mm_mulhrs_epi16(a.im.m128, _mm_slli_epi16(b.im.m128, 1))); - ret.im.m128 = _mm_add_epi16(_mm_mulhrs_epi16(a.re.m128, _mm_slli_epi16(b.im.m128, 1)), - _mm_mulhrs_epi16(a.im.m128, _mm_slli_epi16(b.re.m128, 1))); + ret.re = + _mm_sub_epi16(_mm_mulhrs_epi16(a.re, _mm_slli_epi16(b.re, 1)), _mm_mulhrs_epi16(a.im, _mm_slli_epi16(b.im, 1))); + ret.im = + _mm_add_epi16(_mm_mulhrs_epi16(a.re, _mm_slli_epi16(b.im, 1)), _mm_mulhrs_epi16(a.im, _mm_slli_epi16(b.re, 1))); #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ return ret; } -static inline simd_c16_t srsran_simd_c16_add(simd_c16_t a, simd_c16_t b) +inline simd_c16_t srsran_simd_c16_add(simd_c16_t a, simd_c16_t b) { simd_c16_t ret; #ifdef HAVE_AVX2 - ret.re.m256 = _mm256_add_epi16(a.re.m256, b.re.m256); - ret.im.m256 = _mm256_add_epi16(a.im.m256, b.im.m256); + ret.re = _mm256_add_epi16(a.re, b.re); + ret.im = _mm256_add_epi16(a.im, b.im); #else #ifdef HAVE_SSE - ret.re.m128 = _mm_add_epi16(a.re.m128, b.re.m128); - ret.im.m128 = _mm_add_epi16(a.im.m128, b.im.m128); + ret.re = _mm_add_epi16(a.re, b.re); + ret.im = _mm_add_epi16(a.im, b.im); #else #ifdef HAVE_NEON ret.m128.val[0] = vaddq_s16(a.m128.val[0], a.m128.val[0]); @@ -1856,16 +1866,16 @@ static inline simd_c16_t srsran_simd_c16_add(simd_c16_t a, simd_c16_t b) return ret; } -static inline simd_c16_t srsran_simd_c16_zero(void) +inline simd_c16_t srsran_simd_c16_zero() { simd_c16_t ret; #ifdef HAVE_AVX2 - ret.re.m256 = _mm256_setzero_si256(); - ret.im.m256 = _mm256_setzero_si256(); + ret.re = _mm256_setzero_si256(); + ret.im = _mm256_setzero_si256(); #else #ifdef HAVE_SSE - ret.re.m128 = _mm_setzero_si128(); - ret.im.m128 = _mm_setzero_si128(); + ret.re = _mm_setzero_si128(); + ret.im = _mm_setzero_si128(); #else #ifdef HAVE_NEON ret.m128.val[0] = vdupq_n_s16(0); @@ -1880,7 +1890,7 @@ static inline simd_c16_t srsran_simd_c16_zero(void) #if SRSRAN_SIMD_F_SIZE && SRSRAN_SIMD_S_SIZE -static inline simd_s_t srsran_simd_convert_2f_s(simd_f_t a, simd_f_t b) +inline simd_s_t srsran_simd_convert_2f_s(simd_f_t a, simd_f_t b) { #ifdef HAVE_AVX512 __m512 aa = _mm512_permutex2var_ps( @@ -1896,10 +1906,10 @@ static inline simd_s_t srsran_simd_convert_2f_s(simd_f_t a, simd_f_t b) return _mm512_packs_epi32(ai, bi); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - __m256 aa = _mm256_round_ps(_mm256_permute2f128_ps(a, b, 0x20), _MM_FROUND_NINT); - __m256 bb = _mm256_round_ps(_mm256_permute2f128_ps(a, b, 0x31), _MM_FROUND_NINT); - __m256i ai = _mm256_cvtps_epi32(aa); - __m256i bi = _mm256_cvtps_epi32(bb); + __m256 aa = _mm256_round_ps(_mm256_permute2f128_ps(a, b, 0x20), _MM_FROUND_NINT); + __m256 bb = _mm256_round_ps(_mm256_permute2f128_ps(a, b, 0x31), _MM_FROUND_NINT); + __m256i ai = _mm256_cvtps_epi32(aa); + __m256i bi = _mm256_cvtps_epi32(bb); return _mm256_packs_epi32(ai, bi); #else #ifdef HAVE_SSE @@ -1922,33 +1932,37 @@ static inline simd_s_t srsran_simd_convert_2f_s(simd_f_t a, simd_f_t b) #endif /* SRSRAN_SIMD_F_SIZE && SRSRAN_SIMD_C16_SIZE */ #if SRSRAN_SIMD_B_SIZE -/* Data types */ + +/// +/// Data types. +/// + #ifdef HAVE_AVX512 -typedef __m512i simd_b_t; +using simd_b_t = __m512i; #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 -typedef __m256i simd_b_t; +using simd_b_t = __m256i; #else /* HAVE_AVX2 */ #ifdef HAVE_SSE -typedef __m128i simd_b_t; +using simd_b_t = __m128i; #else /* HAVE_NEON */ #ifdef HAVE_NEON -typedef int8x16_t simd_b_t; +using simd_b_t = int8x16_t; #endif /* HAVE_NEON */ #endif /* HAVE_SSE */ #endif /* HAVE_AVX2 */ #endif /* HAVE_AVX512 */ -static inline simd_b_t srsran_simd_b_load(const int8_t* ptr) +inline simd_b_t srsran_simd_b_load(const int8_t* ptr) { #ifdef HAVE_AVX512 return _mm512_load_si512(ptr); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - return _mm256_load_si256((__m256i*)ptr); + return _mm256_load_si256(reinterpret_cast(ptr)); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - return _mm_load_si128((__m128i*)ptr); + return _mm_load_si128(reinterpret_cast(ptr)); #else /* HAVE_SSE */ #ifdef HAVE_NEON return vld1q_s8(ptr); @@ -1958,16 +1972,16 @@ static inline simd_b_t srsran_simd_b_load(const int8_t* ptr) #endif /* HAVE_AVX512 */ } -static inline simd_b_t srsran_simd_b_loadu(const int8_t* ptr) +inline simd_b_t srsran_simd_b_loadu(const int8_t* ptr) { #ifdef HAVE_AVX512 return _mm512_loadu_si512(ptr); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - return _mm256_loadu_si256((__m256i*)ptr); + return _mm256_loadu_si256(reinterpret_cast(ptr)); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - return _mm_loadu_si128((__m128i*)ptr); + return _mm_loadu_si128(reinterpret_cast(ptr)); #else /* HAVE_SSE */ #ifdef HAVE_NEON return vld1q_s8(ptr); @@ -1977,16 +1991,16 @@ static inline simd_b_t srsran_simd_b_loadu(const int8_t* ptr) #endif /* HAVE_AVX512 */ } -static inline void srsran_simd_b_store(int8_t* ptr, simd_b_t simdreg) +inline void srsran_simd_b_store(int8_t* ptr, simd_b_t simdreg) { #ifdef HAVE_AVX512 _mm512_store_si512(ptr, simdreg); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - _mm256_store_si256((__m256i*)ptr, simdreg); + _mm256_store_si256(reinterpret_cast<__m256i*>(ptr), simdreg); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - _mm_store_si128((__m128i*)ptr, simdreg); + _mm_store_si128(reinterpret_cast<__m128i*>(ptr), simdreg); #else /* HAVE_SSE */ #ifdef HAVE_NEON vst1q_s8(ptr, simdreg); @@ -1996,16 +2010,16 @@ static inline void srsran_simd_b_store(int8_t* ptr, simd_b_t simdreg) #endif /* HAVE_AVX512 */ } -static inline void srsran_simd_b_storeu(int8_t* ptr, simd_b_t simdreg) +inline void srsran_simd_b_storeu(int8_t* ptr, simd_b_t simdreg) { #ifdef HAVE_AVX512 _mm512_storeu_si512(ptr, simdreg); #else /* HAVE_AVX512 */ #ifdef HAVE_AVX2 - _mm256_storeu_si256((__m256i*)ptr, simdreg); + _mm256_storeu_si256(reinterpret_cast<__m256i*>(ptr), simdreg); #else /* HAVE_AVX2 */ #ifdef HAVE_SSE - _mm_storeu_si128((__m128i*)ptr, simdreg); + _mm_storeu_si128(reinterpret_cast<__m128i*>(ptr), simdreg); #else /* HAVE_SSE */ #ifdef HAVE_NEON vst1q_s8(ptr, simdreg); @@ -2015,7 +2029,7 @@ static inline void srsran_simd_b_storeu(int8_t* ptr, simd_b_t simdreg) #endif /* HAVE_AVX512 */ } -static inline simd_b_t srsran_simd_b_xor(simd_b_t a, simd_b_t b) +inline simd_b_t srsran_simd_b_xor(simd_b_t a, simd_b_t b) { #ifdef HAVE_AVX512 return _mm512_xor_epi32(a, b); @@ -2034,7 +2048,7 @@ static inline simd_b_t srsran_simd_b_xor(simd_b_t a, simd_b_t b) #endif /* HAVE_AVX512 */ } -static inline simd_b_t srsran_simd_b_add(simd_b_t a, simd_b_t b) +inline simd_b_t srsran_simd_b_add(simd_b_t a, simd_b_t b) { #ifdef HAVE_AVX512 return _mm512_adds_epi8(a, b); @@ -2053,7 +2067,7 @@ static inline simd_b_t srsran_simd_b_add(simd_b_t a, simd_b_t b) #endif /* HAVE_AVX512 */ } -static inline simd_b_t srsran_simd_b_sub(simd_b_t a, simd_b_t b) +inline simd_b_t srsran_simd_b_sub(simd_b_t a, simd_b_t b) { #ifdef HAVE_AVX512 return _mm512_subs_epi8(a, b); @@ -2072,7 +2086,7 @@ static inline simd_b_t srsran_simd_b_sub(simd_b_t a, simd_b_t b) #endif /* HAVE_AVX512 */ } -static inline simd_b_t srsran_simd_b_neg(simd_b_t a, simd_b_t b) +inline simd_b_t srsran_simd_b_neg(simd_b_t a, simd_b_t b) { #ifdef HAVE_AVX512 __m256i a0 = _mm512_extracti64x4_epi64(a, 0); @@ -2090,18 +2104,17 @@ static inline simd_b_t srsran_simd_b_neg(simd_b_t a, simd_b_t b) return _mm_sign_epi8(a, b); #else /* HAVE_SSE */ #ifdef HAVE_NEON - /* Taken and modified from sse2neon.h licensed under MIT - * Source: https://github.com/DLTcollab/sse2neon - */ + // Taken and modified from sse2neon.h licensed under MIT. + // Source: https://github.com/DLTcollab/sse2neon int8x16_t zero = vdupq_n_s8(0); - // signed shift right: faster than vclt + // Signed shift right: faster than vclt. // (b < 0) ? 0xff : 0 uint8x16_t ltMask = vreinterpretq_u8_s8(vshrq_n_s8(b, 7)); // (b == 0) ? 0xff : 0 int8x16_t zeroMask = vreinterpretq_s8_u8(vceqq_s8(b, zero)); // -a int8x16_t neg = vnegq_s8(a); - // bitwise select either a or neg based on ltMask + // Bitwise select either a or neg based on ltMask. int8x16_t masked = vbslq_s8(ltMask, neg, a); // res = masked & (~zeroMask) int8x16_t res = vbicq_s8(masked, zeroMask); @@ -2112,6 +2125,6 @@ static inline simd_b_t srsran_simd_b_neg(simd_b_t a, simd_b_t b) #endif /* HAVE_AVX512 */ } -#endif /*SRSRAN_SIMD_B_SIZE */ +#endif /* SRSRAN_SIMD_B_SIZE */ } // namespace srsran diff --git a/lib/srsvec/subtract.cpp b/lib/srsvec/subtract.cpp index 621e0e9c90..8c76e3fc2f 100644 --- a/lib/srsvec/subtract.cpp +++ b/lib/srsvec/subtract.cpp @@ -52,7 +52,7 @@ static void subtract_fff_simd(float* z, const float* x, const float* y, std::siz } #endif - for (; i != len; i++) { + for (; i != len; ++i) { z[i] = x[i] - y[i]; } } @@ -83,7 +83,7 @@ static void subtract_sss_simd(int16_t* z, const int16_t* x, const int16_t* y, st } #endif /* SRSRAN_SIMD_S_SIZE */ - for (; i != len; i++) { + for (; i != len; ++i) { z[i] = x[i] - y[i]; } } @@ -114,7 +114,7 @@ static void subtract_bbb_simd(int8_t* z, const int8_t* x, const int8_t* y, std:: } #endif /* SRSRAN_SIMD_S_SIZE */ - for (; i != len; i++) { + for (; i != len; ++i) { z[i] = x[i] - y[i]; } } @@ -152,4 +152,4 @@ void srsran::srsvec::subtract(span z, span x, span 64U, "memory blocks must be larger than the segment control header"); } -namespace { - -/// \brief Linear allocator for memory_block obtained from byte_buffer_segment_pool. -struct memory_arena_linear_allocator { - /// Pointer to the memory block obtained from byte_buffer_segment_pool. - void* mem_block = nullptr; - /// Size of the memory block in bytes. - size_t mem_block_size; - /// Offset in bytes from the beginning of the memory block, determining where the next allocation will be made. - size_t offset = 0; - - memory_arena_linear_allocator(void* mem_block_, size_t mem_block_size_) noexcept : - mem_block(mem_block_), mem_block_size(mem_block_size_) - { - } - - void* allocate(size_t sz, size_t al) noexcept - { - void* p = align_next(static_cast(mem_block) + offset, al); - offset = (static_cast(p) - static_cast(mem_block)) + sz; - return p; - } - - bool empty() const { return mem_block == nullptr; } - - size_t space_left() const { return mem_block_size - offset; } -}; - -/// Allocator for byte_buffer control_block that will leverage the \c memory_arena_linear_allocator. -template -struct control_block_allocator { -public: - using value_type = T; - - template - struct rebind { - typedef control_block_allocator other; - }; - - control_block_allocator(memory_arena_linear_allocator& arena_) noexcept : arena(&arena_), mem_block(arena->mem_block) - { - } - - control_block_allocator(const control_block_allocator& other) noexcept = default; - - template ::value, int> = 0> - control_block_allocator(const control_block_allocator& other) noexcept : - arena(other.arena), mem_block(other.mem_block) - { - } - - control_block_allocator& operator=(const control_block_allocator& other) noexcept = default; - - value_type* allocate(size_t n) noexcept - { - srsran_sanity_check(n == 1, "control_block_allocator can only allocate one control block at a time."); - srsran_sanity_check(arena != nullptr and not arena->empty(), "Memory arena is empty"); - srsran_assert(arena->space_left() >= sizeof(value_type), "control_block_allocator memory block size is too small."); - memory_arena_linear_allocator* arena_ptr = arena; - // This allocator is only used for one single allocation (the shared_ptr control block). The arena may become - // dangling after we exit the head_segment alloc function as well. So, it is safer to just set this pointer to null. - arena = nullptr; - - return static_cast(arena_ptr->allocate(sizeof(value_type), alignof(std::max_align_t))); - } - - void deallocate(value_type* p, size_t n) noexcept - { - // Note: at this stage the arena ptr is probably dangling. Do not touch it. - srsran_sanity_check(n == 1, "control_block_allocator can only deallocate one control block at a time."); - - // Note: The pointer p, while within the memory block, might be misaligned. For this reason, we stored the - // original memory block pointer, which we now use for the deallocation. - detail::get_default_byte_buffer_segment_pool().deallocate_node(mem_block); - } - - bool operator==(const control_block_allocator& other) const { return mem_block == other.mem_block; } - bool operator!=(const control_block_allocator& other) const { return !(*this == other); } - -private: - template - friend struct control_block_allocator; - - memory_arena_linear_allocator* arena = nullptr; - void* mem_block = nullptr; -}; - -} // namespace +// ------- byte_buffer class ------- void byte_buffer::control_block::destroy_node(node_t* node) const { node->~node_t(); if (node != segment_in_cb_memory_block) { - detail::byte_buffer_segment_pool::get_instance().deallocate_node(node); + if (not this->malloc_fallback or detail::byte_buffer_segment_pool::get_instance().owns_segment(node)) { + detail::byte_buffer_segment_pool::get_instance().deallocate_node(node); + } else { + delete[] reinterpret_cast(node); + } } } @@ -157,6 +75,43 @@ byte_buffer::control_block::~control_block() } } +void byte_buffer::control_block::destroy_cb() +{ + bool pool_used = not this->malloc_fallback or detail::byte_buffer_segment_pool::get_instance().owns_segment(this); + this->~control_block(); + if (pool_used) { + detail::get_default_byte_buffer_segment_pool().deallocate_node(this); + } else { + delete[] reinterpret_cast(this); + } +} + +// ----- byte_buffer ----- + +byte_buffer::byte_buffer(fallback_allocation_tag tag, span other) noexcept +{ + // Append new head segment to linked list with fallback allocator mode. + node_t* n = add_head_segment(DEFAULT_FIRST_SEGMENT_HEADROOM, true); + srsran_sanity_check(n != nullptr, "Should never fail to append segment if fallback is enabled"); + + bool var = this->append(other); + srsran_sanity_check(var, "Should never fail to append segment if fallback is enabled"); + (void)var; +} + +byte_buffer::byte_buffer(fallback_allocation_tag tag, const byte_buffer& other) noexcept +{ + // Append new head segment to linked list with fallback allocator mode. + node_t* n = add_head_segment(DEFAULT_FIRST_SEGMENT_HEADROOM, true); + srsran_sanity_check(n != nullptr, "Should never fail to append segment if fallback is enabled"); + + for (span seg : other.segments()) { + bool var = this->append(seg); + srsran_sanity_check(var, "Should never fail to append segment if fallback is enabled"); + (void)var; + } +} + bool byte_buffer::append(span bytes) { if (bytes.empty()) { @@ -192,7 +147,7 @@ bool byte_buffer::append(const byte_buffer& other) return false; } for (node_t* seg = other.ctrl_blk_ptr->segments.head; seg != nullptr; seg = seg->next) { - auto other_it = seg->begin(); + auto* other_it = seg->begin(); while (other_it != seg->end()) { if (ctrl_blk_ptr->segments.tail->tailroom() == 0 and not append_segment(0)) { return false; @@ -217,7 +172,7 @@ bool byte_buffer::append(byte_buffer&& other) *this = std::move(other); return true; } - if (not other.ctrl_blk_ptr.unique()) { + if (not other.ctrl_blk_ptr->ref_count.unique()) { // Use lvalue append. return append(other); } @@ -249,58 +204,70 @@ bool byte_buffer::append(byte_buffer&& other) return true; } -byte_buffer::node_t* byte_buffer::create_head_segment(size_t headroom) +byte_buffer::node_t* byte_buffer::add_head_segment(size_t headroom, bool use_fallback) { - static auto& pool = detail::get_default_byte_buffer_segment_pool(); - static const size_t block_size = pool.memory_block_size(); + auto& pool = detail::get_default_byte_buffer_segment_pool(); + const size_t block_size = pool.memory_block_size(); // Allocate new node. void* mem_block = pool.allocate_node(block_size); if (mem_block == nullptr) { - // Pool is depleted. - byte_buffer::warn_alloc_failure(); - return nullptr; + if (not use_fallback) { + // Pool is depleted. + byte_buffer::warn_alloc_failure(); + return nullptr; + } + // Use heap as fallback. + mem_block = new uint8_t[block_size]; } + // Construct linear allocator pointing to allocated segment memory block. + linear_memory_allocator arena{mem_block, block_size}; + // Create control block using allocator. - memory_arena_linear_allocator arena{mem_block, block_size}; - ctrl_blk_ptr = std::allocate_shared(control_block_allocator{arena}); - if (ctrl_blk_ptr == nullptr) { - byte_buffer::warn_alloc_failure(); - pool.deallocate_node(mem_block); - return nullptr; - } + void* cb_region = arena.allocate(sizeof(control_block), alignof(control_block)); + ctrl_blk_ptr = new (cb_region) control_block{}; + srsran_sanity_check(ctrl_blk_ptr != nullptr, "Something went wrong with the creation of the control block"); + ctrl_blk_ptr->malloc_fallback = use_fallback; // For first segment of byte_buffer, add a headroom. - void* segment_start = arena.allocate(sizeof(node_t), alignof(node_t)); - srsran_assert(block_size > arena.offset, "The memory block provided by the pool is too small"); - size_t segment_size = block_size - arena.offset; - void* payload_start = arena.allocate(segment_size, 1); - node_t* node = new (segment_start) + void* segment_header_region = arena.allocate(sizeof(node_t), alignof(node_t)); + size_t segment_size = arena.nof_bytes_left(); + void* payload_start = arena.allocate(segment_size, 1); + node_t* node = new (segment_header_region) node_t(span{static_cast(payload_start), segment_size}, std::min(headroom, segment_size)); + srsran_sanity_check(node != nullptr, "Something went wrong with the creation of the segment"); // Register segment as sharing the same memory block with control block. ctrl_blk_ptr->segment_in_cb_memory_block = node; + // Append new segment to linked list. + ctrl_blk_ptr->segments.push_back(*node); + return node; } byte_buffer::node_t* byte_buffer::create_segment(size_t headroom) { - static auto& pool = detail::get_default_byte_buffer_segment_pool(); - static const size_t block_size = pool.memory_block_size(); + auto& pool = detail::get_default_byte_buffer_segment_pool(); + const size_t block_size = pool.memory_block_size(); // Allocate memory block. void* mem_block = pool.allocate_node(block_size); if (mem_block == nullptr) { - byte_buffer::warn_alloc_failure(); - return nullptr; + if (not ctrl_blk_ptr->malloc_fallback) { + byte_buffer::warn_alloc_failure(); + return nullptr; + } + // Use malloc as fallback. + mem_block = new uint8_t[block_size]; } - memory_arena_linear_allocator arena{mem_block, block_size}; - void* segment_start = arena.allocate(sizeof(node_t), alignof(node_t)); - srsran_assert(block_size > arena.offset, "The memory block provided by the pool is too small"); - size_t segment_size = block_size - arena.offset; + // Create a linear allocator pointing to the allocated memory block. + linear_memory_allocator arena{mem_block, block_size}; + + void* segment_start = arena.allocate(sizeof(node_t), alignof(node_t)); + size_t segment_size = arena.nof_bytes_left(); void* payload_start = arena.allocate(segment_size, 1); return new (segment_start) node_t(span{static_cast(payload_start), segment_size}, std::min(headroom, segment_size)); @@ -308,12 +275,13 @@ byte_buffer::node_t* byte_buffer::create_segment(size_t headroom) bool byte_buffer::append_segment(size_t headroom_suggestion) { - node_t* segment = - not has_ctrl_block() ? create_head_segment(headroom_suggestion) : create_segment(headroom_suggestion); + if (not has_ctrl_block()) { + return add_head_segment(headroom_suggestion) != nullptr; + } + node_t* segment = create_segment(headroom_suggestion); if (segment == nullptr) { return false; } - // Append new segment to linked list. ctrl_blk_ptr->segments.push_back(*segment); return true; @@ -321,13 +289,13 @@ bool byte_buffer::append_segment(size_t headroom_suggestion) bool byte_buffer::prepend_segment(size_t headroom_suggestion) { - // Note: Add HEADROOM for first segment. - node_t* segment = - not has_ctrl_block() ? create_head_segment(headroom_suggestion) : create_segment(headroom_suggestion); + if (not has_ctrl_block()) { + return add_head_segment(headroom_suggestion) != nullptr; + } + node_t* segment = create_segment(headroom_suggestion); if (segment == nullptr) { return false; } - // Prepend new segment to linked list. ctrl_blk_ptr->segments.push_front(*segment); return true; @@ -387,10 +355,9 @@ bool byte_buffer::prepend(byte_buffer&& other) // the byte buffer is empty. Prepending is the same as appending. return append(std::move(other)); } - if (not other.ctrl_blk_ptr.unique()) { + if (not other.ctrl_blk_ptr->ref_count.unique()) { // Deep copy of segments. - prepend(other); - return true; + return prepend(other); } // This is the last reference to "other". Shallow copy, except control segment. @@ -521,7 +488,7 @@ bool byte_buffer::resize(size_t new_sz) } if (new_sz > prev_len) { for (size_t to_add = new_sz - prev_len; to_add > 0;) { - if (empty() or ctrl_blk_ptr->segments.tail->tailroom() == 0) { + if (not has_ctrl_block() or ctrl_blk_ptr->segments.tail->tailroom() == 0) { if (not append_segment(0)) { return false; } diff --git a/lib/support/cpu_architecture_info.cpp b/lib/support/cpu_architecture_info.cpp new file mode 100644 index 0000000000..4aaa8a29a8 --- /dev/null +++ b/lib/support/cpu_architecture_info.cpp @@ -0,0 +1,275 @@ +/* + * + * 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/support/cpu_architecture_info.h" +#include "srsran/adt/interval.h" +#include "srsran/support/format_utils.h" +#include "srsran/support/sysinfo.h" +#include +#include +#include +#include +#include +#include +#ifdef NUMA_SUPPORT +#include +#endif + +using namespace srsran; + +/// Converts the string containing a CPU index to an unsigned integer number. +static unsigned parse_one_cpu(const std::string& value) +{ + return std::stoi(value); +} + +/// Parses string representing range of CPU indexes in the format 'M-N'. +static interval parse_cpu_range(const std::string& value) +{ + std::vector range; + std::stringstream ss(value); + + while (ss.good()) { + std::string str; + std::getline(ss, str, '-'); + auto parse_result = parse_one_cpu(str); + range.push_back(parse_result); + } + return interval(range[0], range[1]); +} + +// Obtain CPU description at the start of the application. This value is affected by commands or tools like taskset, +// which limit the number of cores available to the application. However, frameworks (e.g. DPDK) that affect the +// affinities of the main thread in the main() function will not affect this value. +const cpu_architecture_info::cpu_description cpu_architecture_info::cpu_desc = + cpu_architecture_info::discover_cpu_architecture(); + +cpu_architecture_info::cpu_description cpu_architecture_info::discover_cpu_architecture() +{ + // Clean-up cgroups possibly left from a previous run. + cleanup_cgroups(); + + cpu_description cpuinfo; + cpu_set_t& cpuset = cpuinfo.cpuset; + + // Discover host CPU architecture. + CPU_ZERO(&cpuset); + cpuinfo.allowed_cpus.resize(CPU_SETSIZE); + + if (::sched_getaffinity(0, sizeof(cpuset), &cpuset) == 0) { + cpuinfo.nof_available_cpus = CPU_COUNT(&cpuset); + // Save bitmask of CPUs the calling thread is allowed to run on. + for (unsigned i = 0; i != CPU_SETSIZE; ++i) { + if (CPU_ISSET(i, &cpuset)) { + cpuinfo.allowed_cpus.set(i); + } + } + } else { + // Parse /proc/self/status + std::ifstream file("/proc/self/status"); + std::string line; + while (std::getline(file, line)) { + // Look for allowed CPUs mask in hexadecimal format. + if (line.find("Cpus_allowed_list") != std::string::npos) { + const std::regex re("([0-9]+)(.*)$"); + std::smatch m; + std::regex_search(line, m, re); + std::istringstream iss(line.substr(m.position())); + while (iss.good()) { + std::string str; + std::getline(iss, str, ','); + if (str.find('-') != std::string::npos) { + auto range = parse_cpu_range(str); + cpuinfo.allowed_cpus.fill(range.start(), range.stop() + 1); + } else { + auto cpu_idx = parse_one_cpu(str); + cpuinfo.allowed_cpus.set(cpu_idx); + } + } + break; + } + } + } + cpuinfo.nof_available_cpus = cpuinfo.allowed_cpus.count(); + cpuinfo.max_cpu_id = cpuinfo.allowed_cpus.find_highest(); + cpuinfo.nof_cpus = cpuinfo.max_cpu_id + 1; + cpuinfo.allowed_cpus.resize(cpuinfo.nof_cpus); + + // Parse /sys/devices/system/cpu/cpu*/topology/thread_siblings_list to get the lists of logical cores belonging to + // the same physical core. + std::set thread_siblings_set; + std::string cpu_system_path = "/sys/devices/system/cpu"; + ::DIR* dir = ::opendir(cpu_system_path.c_str()); + if (dir) { + ::dirent* entry; + struct ::stat info; + + while ((entry = ::readdir(dir))) { + const std::regex re("^cpu[0-9]+"); + std::string cpu_name = entry->d_name; + if (!std::regex_match(cpu_name, re)) { + continue; + } + std::string cpu_siblings_path = fmt::format("{}/{}/topology/thread_siblings_list", cpu_system_path, cpu_name); + if (::stat(cpu_siblings_path.c_str(), &info) < 0) { + continue; + } + std::ifstream file(cpu_siblings_path); + std::string str_mask; + std::getline(file, str_mask); + thread_siblings_set.insert(str_mask); + } + ::closedir(dir); + } + for (const auto& mask : thread_siblings_set) { + cpuinfo.logical_cpu_lists.emplace_back(); + auto& bitmask = cpuinfo.logical_cpu_lists.back(); + bitmask.resize(cpuinfo.nof_cpus); + + std::istringstream iss(mask); + while (iss.good()) { + std::string str; + std::getline(iss, str, ','); + if (str.find('-') != std::string::npos) { + auto range = parse_cpu_range(str); + bitmask.fill(range.start(), range.stop() + 1); + } else { + auto cpu_idx = parse_one_cpu(str); + bitmask.set(cpu_idx); + } + } + } + +#ifdef NUMA_SUPPORT + if (::numa_available() == -1) { + return cpuinfo; + } + cpuinfo.nof_cpus = ::numa_num_configured_cpus(); + cpuinfo.nof_available_cpus = ::numa_num_task_cpus(); + cpuinfo.nof_numa_nodes = ::numa_num_configured_nodes(); + + // Save bitmasks of the CPUs pertaining to each NUMA node. + for (unsigned node = 0; node != cpuinfo.nof_numa_nodes; ++node) { + ::bitmask* mask = ::numa_allocate_cpumask(); + ::numa_node_to_cpus(node, mask); + + cpuinfo.node_cpus.emplace_back(numa_num_configured_cpus()); + cpuinfo.node_cpus[node].from_uint64(*mask->maskp); + ::numa_free_cpumask(mask); + } +#else + std::string node_system_path = "/sys/devices/system/node"; + dir = ::opendir(node_system_path.c_str()); + if (dir) { + ::dirent* entry; + while ((entry = ::readdir(dir))) { + const std::regex re("^node[0-9]+"); + std::string entry_name = entry->d_name; + if (std::regex_match(entry_name, re)) { + ++cpuinfo.nof_numa_nodes; + } + } + ::closedir(dir); + } +#endif // NUMA_SUPPORT + + return cpuinfo; +} + +static std::string print_cpus_list(const bounded_bitset<1024>& cpus_mask) +{ + auto cpu_ids = cpus_mask.get_bit_positions(); + if (cpu_ids.empty()) { + return "[]"; + } + fmt::memory_buffer fmt_format_buf; + fmt::format_to(fmt_format_buf, "["); + for (unsigned idx = 0, e = cpu_ids.size(); idx != e; ++idx) { + fmt::format_to(fmt_format_buf, "{}{}", cpu_ids[idx], (idx == cpu_ids.size() - 1) ? "]" : ","); + } + return to_string(fmt_format_buf); +} + +void cpu_architecture_info::print_cpu_info(srslog::basic_logger& logger) const +{ + fmt::memory_buffer fmt_buf; + fmt::format_to(fmt_buf, + "{} {}, {} NUMA {}.\n", + cpu_desc.nof_cpus, + (cpu_desc.nof_cpus > 1) ? "CPUs" : "CPU", + cpu_desc.nof_numa_nodes, + cpu_desc.nof_numa_nodes > 1 ? "nodes" : "node"); + +#ifdef NUMA_SUPPORT + fmt::format_to(fmt_buf, "CPUs per NUMA node:\n{{"); + for (unsigned node = 0; node != cpu_desc.nof_numa_nodes; ++node) { + fmt::format_to(fmt_buf, "\n Node {} CPUs: {}", node, print_cpus_list(cpu_desc.node_cpus[node])); + } + fmt::format_to(fmt_buf, "\n}}\n"); +#endif + + fmt::format_to(fmt_buf, "CPUs per each physical CPU core:\n{{"); + for (unsigned core_id = 0, e = cpu_desc.logical_cpu_lists.size(); core_id != e; ++core_id) { + fmt::format_to(fmt_buf, "\n {}: {}", core_id, print_cpus_list(cpu_desc.logical_cpu_lists[core_id])); + } + fmt::format_to(fmt_buf, "\n}}\n"); + fmt::format_to(fmt_buf, + "{} CPUs available to the application: {}", + cpu_desc.nof_available_cpus, + print_cpus_list(cpu_desc.allowed_cpus)); + + logger.debug("Detected CPU topology: {}", to_c_str(fmt_buf)); +} + +bounded_bitset<1024> cpu_architecture_info::get_node_cpu_mask(unsigned node_id) const +{ +#ifndef NUMA_SUPPORT + srslog::fetch_basic_logger("GNB").debug("NUMA topology not available, please compile the code with libnuma support"); + return {}; +#else + + if (node_id >= cpu_desc.nof_numa_nodes) { + srslog::fetch_basic_logger("GNB").debug("Requested node ID exceeds number of NUMA nodes configured in the system"); + return {}; + } + return cpu_desc.node_cpus[node_id]; +#endif +} + +bool cpu_architecture_info::run_on_numa_node(unsigned node_id) const +{ +#ifndef NUMA_SUPPORT + srslog::fetch_basic_logger("GNB").debug("NUMA topology not available, please compile the code with libnuma support"); + return false; +#else + + if (node_id >= cpu_desc.nof_numa_nodes) { + srslog::fetch_basic_logger("GNB").debug("Requested node ID exceeds number of NUMA nodes configured in the system"); + return false; + } + ::bitmask* mask = ::numa_allocate_nodemask(); + ::numa_bitmask_setbit(mask, node_id); + ::numa_bind(mask); + ::numa_free_nodemask(mask); + return true; +#endif +} diff --git a/lib/support/executors/task_execution_manager.cpp b/lib/support/executors/task_execution_manager.cpp index 189b74bbd2..00d9c3aad1 100644 --- a/lib/support/executors/task_execution_manager.cpp +++ b/lib/support/executors/task_execution_manager.cpp @@ -331,7 +331,7 @@ struct worker_pool_context final : public common_task_execution_context qsizes; + std::array qsizes{}; for (unsigned i = 0; i != params.queues.size(); ++i) { qsizes[i] = params.queues[i].size; } @@ -484,7 +484,7 @@ struct priority_multiqueue_worker_context static std::unique_ptr create(const execution_config_helper::priority_multiqueue_worker& params) { - std::array qsizes; + std::array qsizes{}; for (unsigned i = 0; i != params.queues.size(); ++i) { qsizes[i] = params.queues[i].size; } diff --git a/lib/support/network/io_broker_epoll.cpp b/lib/support/network/io_broker_epoll.cpp index a9aa53c7d5..ebac5178a4 100644 --- a/lib/support/network/io_broker_epoll.cpp +++ b/lib/support/network/io_broker_epoll.cpp @@ -138,18 +138,25 @@ void io_broker_epoll::thread_loop() /// file descriptors can be added while the epoll_wait() is blocking. bool io_broker_epoll::register_fd(int fd, recv_callback_t handler) { + logger.debug("Registering file descriptor. fd={}", fd); + + { + // add event handler to map before registering it at epoll control + std::lock_guard lock(event_handler_mutex); + event_handler.insert({fd, std::make_unique(handler)}); + } + struct epoll_event ev = {}; ev.data.fd = fd; ev.events = EPOLLIN; if (::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { logger.error("Failed to register file descriptor. fd={} error={}", fd, strerror(errno)); + // remove event handler from map + std::lock_guard lock(event_handler_mutex); + event_handler.erase(fd); return false; } - std::lock_guard lock(event_handler_mutex); - event_handler.insert({fd, std::make_unique(handler)}); - - logger.debug("Registered file descriptor. fd={}", fd); return true; } diff --git a/lib/support/network/transport_layer_address.cpp b/lib/support/network/transport_layer_address.cpp index bf54745f8f..d6003bd4cd 100644 --- a/lib/support/network/transport_layer_address.cpp +++ b/lib/support/network/transport_layer_address.cpp @@ -23,97 +23,101 @@ #include "srsran/support/io/transport_layer_address.h" #include "srsran/support/srsran_assert.h" #include -#include using namespace srsran; -transport_layer_address& transport_layer_address::from_string(const std::string& ip_str) +transport_layer_address transport_layer_address::create_from_string(const std::string& ip_str) { - struct addrinfo* results; + ::addrinfo* results; + native_type addr; - int err = getaddrinfo(ip_str.c_str(), nullptr, nullptr, &results); - srsran_assert(err == 0, "Getaddrinfo error: {} - {}", ip_str, gai_strerror(err)); + int err = ::getaddrinfo(ip_str.c_str(), nullptr, nullptr, &results); + srsran_assert(err == 0, "Getaddrinfo error: {} - {}", ip_str, ::gai_strerror(err)); - // store address - memcpy(&addr, results->ai_addr, results->ai_addrlen); + // Store address. + std::memcpy(&addr, results->ai_addr, results->ai_addrlen); - freeaddrinfo(results); + ::freeaddrinfo(results); - return *this; + return transport_layer_address(addr); } -transport_layer_address& transport_layer_address::from_bitstring(std::string bit_str) +transport_layer_address transport_layer_address::create_from_bitstring(const std::string& bit_str) { srsran_assert(bit_str.length() < 160, "Combined IPv4 and IPv6 addresses are currently not supported"); - std::string ip_str; - - if (bit_str.length() == 32) { // see see TS 38.414: 32 bits in case of IPv4 address according to IETF RFC 791 - ip_str = fmt::format("{}.{}.{}.{}", - std::stoi(bit_str.substr(0, 8), nullptr, 2), - std::stoi(bit_str.substr(8, 8), nullptr, 2), - std::stoi(bit_str.substr(16, 8), nullptr, 2), - std::stoi(bit_str.substr(24, 8), nullptr, 2)); - } else if (bit_str.length() == - 128) { // see see TS 38.414: 128 bits in case of IPv6 address according to IETF RFC 8200 - ip_str = fmt::format("{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", - std::stoi(bit_str.substr(0, 16), nullptr, 2), - std::stoi(bit_str.substr(16, 16), nullptr, 2), - std::stoi(bit_str.substr(32, 16), nullptr, 2), - std::stoi(bit_str.substr(48, 16), nullptr, 2), - std::stoi(bit_str.substr(64, 16), nullptr, 2), - std::stoi(bit_str.substr(80, 16), nullptr, 2), - std::stoi(bit_str.substr(96, 16), nullptr, 2), - std::stoi(bit_str.substr(112, 16), nullptr, 2)); - - } else { // see see TS 38.414: 160 bits if both IPv4 and IPv6 addresses are signalled, in which case the IPv4 - // address is contained in the first 32 bits - // TODO: Support 160 bit transport layer addresses + // See TS 38.414: 32 bits in case of IPv4 address according to IETF RFC 791. + if (bit_str.length() == 32) { + std::string ip_str = fmt::format("{}.{}.{}.{}", + std::stoi(bit_str.substr(0, 8), nullptr, 2), + std::stoi(bit_str.substr(8, 8), nullptr, 2), + std::stoi(bit_str.substr(16, 8), nullptr, 2), + std::stoi(bit_str.substr(24, 8), nullptr, 2)); + return create_from_string(ip_str); } - return from_string(ip_str); + // See TS 38.414: 128 bits in case of IPv6 address according to IETF RFC 8200. + if (bit_str.length() == 128) { + std::string ip_str = fmt::format("{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", + std::stoi(bit_str.substr(0, 16), nullptr, 2), + std::stoi(bit_str.substr(16, 16), nullptr, 2), + std::stoi(bit_str.substr(32, 16), nullptr, 2), + std::stoi(bit_str.substr(48, 16), nullptr, 2), + std::stoi(bit_str.substr(64, 16), nullptr, 2), + std::stoi(bit_str.substr(80, 16), nullptr, 2), + std::stoi(bit_str.substr(96, 16), nullptr, 2), + std::stoi(bit_str.substr(112, 16), nullptr, 2)); + return create_from_string(ip_str); + } + + // See TS 38.414: 160 bits if both IPv4 and IPv6 addresses are signalled, in which case the IPv4 address is contained + // in the first 32 bits. + // TODO: Support 160 bit transport layer addresses + + return create_from_string(""); } std::string transport_layer_address::to_bitstring() const { - std::string bitstr; - - char ip_addr[NI_MAXHOST]; - getnameinfo((sockaddr*)&addr, sizeof(addr), ip_addr, NI_MAXHOST, nullptr, 0, NI_NUMERICHOST); - - if (((sockaddr*)&addr)->sa_family == AF_INET) { - std::string ip_str = ip_addr; - std::string delim = "."; - - auto start = 0U; - auto end = ip_str.find(delim); + char ip_addr[NI_MAXHOST]; + const auto* saddr = reinterpret_cast(&addr); + ::getnameinfo(saddr, sizeof(addr), ip_addr, sizeof(ip_addr), nullptr, 0, NI_NUMERICHOST); + std::string ip_str = ip_addr; + + if (saddr->sa_family == AF_INET) { + std::string bitstr; + char delim = '.'; + auto start = 0U; + auto end = ip_str.find(delim); while (end != std::string::npos) { - bitstr = bitstr + fmt::format("{:08b}", std::stoul(ip_str.substr(start, end - start))); - start = end + delim.length(); - end = ip_str.find(delim, start); + bitstr += fmt::format("{:08b}", std::stoul(ip_str.substr(start, end - start))); + start = end + sizeof(delim); + end = ip_str.find(delim, start); } - bitstr = bitstr + fmt::format("{:08b}", std::stoul(ip_str.substr(start, end))); - } else { - std::string ip_str = ip_addr; - std::string delim = ":"; + bitstr += fmt::format("{:08b}", std::stoul(ip_str.substr(start, end))); - auto start = 0U; - auto end = ip_str.find(delim); - while (end != std::string::npos) { - bitstr = bitstr + fmt::format("{:016b}", std::stoul(ip_str.substr(start, end - start), nullptr, 16)); - start = end + delim.length(); - end = ip_str.find(delim, start); - } - bitstr = bitstr + fmt::format("{:016b}", std::stoul(ip_str.substr(start, end), nullptr, 16)); + return bitstr; + } + + std::string bitstr; + char delim = ':'; + auto start = 0U; + auto end = ip_str.find(delim); + while (end != std::string::npos) { + bitstr += fmt::format("{:016b}", std::stoul(ip_str.substr(start, end - start), nullptr, 16)); + start = end + sizeof(delim); + end = ip_str.find(delim, start); } + bitstr += fmt::format("{:016b}", std::stoul(ip_str.substr(start, end), nullptr, 16)); return bitstr; } bool transport_layer_address::operator==(const transport_layer_address& other) const { - for (long unsigned int i = 0; i < sizeof(((sockaddr*)&addr)->sa_data); i++) { - if (((sockaddr*)&addr)->sa_data[i] != ((sockaddr*)&other)->sa_data[i]) { + const auto* saddr = reinterpret_cast(&addr); + for (unsigned i = 0; i != sizeof(saddr->sa_data); ++i) { + if (saddr->sa_data[i] != reinterpret_cast(&other)->sa_data[i]) { return false; } } @@ -123,10 +127,12 @@ bool transport_layer_address::operator==(const transport_layer_address& other) c bool transport_layer_address::operator<(const transport_layer_address& other) const { - for (long unsigned int i = 0; i < sizeof(((sockaddr*)&addr)->sa_data); i++) { - if (((sockaddr*)&addr)->sa_data[i] < ((sockaddr*)&other)->sa_data[i]) { + const auto* saddr = reinterpret_cast(&addr); + for (unsigned i = 0; i != sizeof(saddr->sa_data); ++i) { + if (saddr->sa_data[i] < reinterpret_cast(&other)->sa_data[i]) { return true; } } + return false; } diff --git a/lib/support/unique_thread.cpp b/lib/support/unique_thread.cpp index 16cd149ce2..558bddcec6 100644 --- a/lib/support/unique_thread.cpp +++ b/lib/support/unique_thread.cpp @@ -21,7 +21,6 @@ */ #include "srsran/support/unique_thread.h" -#include "srsran/support/sysinfo.h" #include "fmt/ostream.h" #include #include @@ -29,64 +28,6 @@ using namespace srsran; -namespace { - -struct cpu_description { - cpu_set_t cpuset; - size_t nof_cores; - size_t max_cpu_id; -}; - -/// \brief Compute the CPU set of the caller thread. -cpu_description compute_machine_desc() -{ - // Clean-up cgroups possibly left from a previous run. - cleanup_cgroups(); - - cpu_description desc; - cpu_set_t& cpuset = desc.cpuset; - - // Compute the CPU bitmask. - CPU_ZERO(&cpuset); - if (sched_getaffinity(0, sizeof(cpuset), &cpuset) != 0) { - printf("Warning: Could not get CPU affinity of main thread. Cause: %s\n", strerror(errno)); - // In failure case, we set all CPUs as available. - unsigned nof_cores = std::max(1U, std::thread::hardware_concurrency()); - for (size_t i = 0; i < nof_cores; ++i) { - CPU_SET(i, &cpuset); - } - } - - // Compute the number of cores. - desc.nof_cores = std::max(1, CPU_COUNT(&cpuset)); - - // Compute max CPU id. - for (unsigned i = 0; i < CPU_SETSIZE; ++i) { - if (CPU_ISSET(i, &cpuset)) { - desc.max_cpu_id = i; - } - } - - return desc; -} - -// Obtain CPU description at the start of the application. This value is affected by commands or tools like taskset, -// which limit the number of cores available to the application. However, frameworks (e.g. DPDK) that affect the -// affinities of the main thread in the main() function will not affect this value. -const cpu_description host_desc = compute_machine_desc(); - -} // namespace - -size_t srsran::compute_host_nof_hardware_threads() -{ - return host_desc.nof_cores; -} - -size_t srsran::get_host_max_cpu_id() -{ - return host_desc.max_cpu_id; -} - /// Sets thread OS scheduling real-time priority. static bool thread_set_param(pthread_t t, os_thread_realtime_priority prio) { @@ -190,8 +131,9 @@ const os_sched_affinity_bitmask& os_sched_affinity_bitmask::available_cpus() { static os_sched_affinity_bitmask available_cpus_mask = []() { os_sched_affinity_bitmask bitmask; + cpu_set_t cpuset = cpu_architecture_info::get().get_available_cpuset(); for (size_t i = 0; i < bitmask.size(); ++i) { - if (CPU_ISSET(i, &host_desc.cpuset)) { + if (CPU_ISSET(i, &cpuset)) { bitmask.cpu_bitset.set(i); } } diff --git a/tests/benchmarks/du_high/du_high_benchmark.cpp b/tests/benchmarks/du_high/du_high_benchmark.cpp index 3869bd1cce..11e38ed559 100644 --- a/tests/benchmarks/du_high/du_high_benchmark.cpp +++ b/tests/benchmarks/du_high/du_high_benchmark.cpp @@ -41,8 +41,8 @@ #include "lib/du_high/du_high_executor_strategies.h" #include "lib/du_high/du_high_impl.h" +#include "lib/mac/mac_ul/ul_bsr.h" #include "tests/unittests/f1ap/du/f1ap_du_test_helpers.h" -#include "tests/unittests/mac/mac_test_helpers.h" #include "srsran/asn1/f1ap/common.h" #include "srsran/du/du_cell_config_helpers.h" #include "srsran/du_high/du_high_configuration.h" @@ -309,7 +309,8 @@ class cu_up_simulator : public f1u_du_gateway const up_transport_layer_info& dl_tnl, const up_transport_layer_info& ul_tnl, srs_du::f1u_rx_sdu_notifier& du_rx, - timer_factory timers) override + timer_factory timers, + task_executor& ue_executor) override { du_notif_list.push_back(&du_rx); bearer_list.emplace_back(); @@ -548,16 +549,16 @@ class du_high_bench report_fatal_error_if_not(bsr_mac_subpdu.append(lbsr_buff_sz), "Failed to allocate PDU"); // Instantiate a DU-high object. - cfg.gnb_du_id = 1; - cfg.gnb_du_name = fmt::format("srsgnb{}", cfg.gnb_du_id); - cfg.du_bind_addr = fmt::format("127.0.0.{}", cfg.gnb_du_id); - cfg.exec_mapper = &workers.du_high_exec_mapper; - cfg.f1c_client = &sim_cu_cp; - cfg.f1u_gw = &sim_cu_up; - cfg.phy_adapter = &sim_phy; - cfg.timers = &timers; - cfg.cells = {config_helpers::make_default_du_cell_config(params)}; - cfg.sched_cfg = config_helpers::make_default_scheduler_expert_config(); + cfg.gnb_du_id = 1; + cfg.gnb_du_name = fmt::format("srsgnb{}", cfg.gnb_du_id); + cfg.du_bind_addr = transport_layer_address::create_from_string(fmt::format("127.0.0.{}", cfg.gnb_du_id)); + cfg.exec_mapper = &workers.du_high_exec_mapper; + cfg.f1c_client = &sim_cu_cp; + cfg.f1u_gw = &sim_cu_up; + cfg.phy_adapter = &sim_phy; + cfg.timers = &timers; + cfg.cells = {config_helpers::make_default_du_cell_config(params)}; + cfg.sched_cfg = config_helpers::make_default_scheduler_expert_config(); cfg.sched_cfg.ue.pdsch_nof_rbs = {1, max_nof_rbs_per_dl_grant}; cfg.mac_cfg = mac_expert_config{.configs = {{10000, 10000, 10000}}}; cfg.qos = config_helpers::make_default_du_qos_config_list(1000); @@ -746,7 +747,10 @@ class du_high_bench } if (i == nof_dl_pdus_per_slot - 1 and last_dl_pdu_size != 0) { // If it is last DL PDU. - pdu_copy.resize(last_dl_pdu_size); + if (!pdu_copy.resize(last_dl_pdu_size)) { + test_logger.warning("Unable to resize PDU to {} bytes", last_dl_pdu_size); + return; + } } du_notif->on_new_sdu(pdcp_tx_pdu{.buf = std::move(pdu_copy), .pdcp_sn = pdcp_sn_list[bearer_idx]}); } diff --git a/tests/benchmarks/ofh/ofh_compression_benchmark.cpp b/tests/benchmarks/ofh/ofh_compression_benchmark.cpp index 3071f8d8f4..ca2abda96a 100644 --- a/tests/benchmarks/ofh/ofh_compression_benchmark.cpp +++ b/tests/benchmarks/ofh/ofh_compression_benchmark.cpp @@ -45,7 +45,7 @@ static void usage(const char* prog) { fmt::print("Usage: {} [-R repetitions] [-T compression type] [-F factory type] [-s silent]\n", prog); fmt::print("\t-R Repetitions [Default {}]\n", nof_repetitions); - fmt::print("\t-T Type of compression [{'none', 'bfp'}, default is {}]\n", method); + fmt::print("\t-T Type of compression [{{'none', 'bfp'}}, default is {}]\n", method); fmt::print("\t-F Select compression factory [Default {}]\n", impl_type); fmt::print("\t-B Channel bandwidth [Default {}]\n", bw); fmt::print("\t-C Subcarrier spacing. [Default {}]\n", to_string(scs)); diff --git a/tests/benchmarks/pdcp/pdcp_rx_benchmark.cpp b/tests/benchmarks/pdcp/pdcp_rx_benchmark.cpp index 09fe3d43f6..6bf4b9c4aa 100644 --- a/tests/benchmarks/pdcp/pdcp_rx_benchmark.cpp +++ b/tests/benchmarks/pdcp/pdcp_rx_benchmark.cpp @@ -74,31 +74,45 @@ struct bench_params { bool print_timing_info = false; }; -static void usage(const char* prog, const bench_params& params, int algo) +struct app_params { + int algo = -1; + std::string log_level = "error"; + std::string log_filename = "stdout"; +}; + +static void usage(const char* prog, const bench_params& params, const app_params& app) { fmt::print("Usage: {} [-R repetitions] [-s silent]\n", prog); - fmt::print("\t-a Security algorithm to use [Default {}, valid {{-1,0,1,2,3}}]\n", algo); + fmt::print("\t-a Security algorithm to use [Default {}, valid {{-1,0,1,2,3}}]\n", app.algo); fmt::print("\t-R Repetitions [Default {}]\n", params.nof_repetitions); + fmt::print("\t-l Log level to use [Default {}, valid {{error, warning, info, debug}}]\n", app.log_level); + fmt::print("\t-f Log filename to use [Default {}]\n", app.log_filename); fmt::print("\t-h Show this message\n"); } -static void parse_args(int argc, char** argv, bench_params& params, int& algo) +static void parse_args(int argc, char** argv, bench_params& params, app_params& app) { int opt = 0; - while ((opt = getopt(argc, argv, "a:R:th")) != -1) { + while ((opt = getopt(argc, argv, "a:R:l:f:th")) != -1) { switch (opt) { case 'R': params.nof_repetitions = std::strtol(optarg, nullptr, 10); break; case 'a': - algo = std::strtol(optarg, nullptr, 10); + app.algo = std::strtol(optarg, nullptr, 10); break; case 't': params.print_timing_info = true; break; + case 'l': + app.log_level = std::string(optarg); + break; + case 'f': + app.log_filename = std::string(optarg); + break; case 'h': default: - usage(argv[0], params, algo); + usage(argv[0], params, app); exit(0); } } @@ -264,20 +278,27 @@ int run_benchmark(bench_params params, int algo) int main(int argc, char** argv) { - srslog::init(); - srslog::fetch_basic_logger("PDCP").set_level(srslog::basic_levels::error); - - int algo = -1; bench_params params{}; - parse_args(argc, argv, params, algo); + app_params app_params{}; + parse_args(argc, argv, params, app_params); + + srslog::init(); + srslog::sink* log_sink = (app_params.log_filename == "stdout") ? srslog::create_stdout_sink() + : srslog::create_file_sink(app_params.log_filename); + if (log_sink == nullptr) { + return -1; + } + srslog::set_default_sink(*log_sink); + srslog::fetch_basic_logger("PDCP").set_level(srslog::str_to_basic_level(app_params.log_level)); - if (algo != -1 && algo != 0 && algo != 1 && algo != 2 && algo != 3) { + if (app_params.algo != -1 && app_params.algo != 0 && app_params.algo != 1 && app_params.algo != 2 && + app_params.algo != 3) { fmt::print("Unsupported algorithm. Use -1, 0, 1, 2 or 3.\n"); return -1; } - if (algo != -1) { - run_benchmark(params, algo); + if (app_params.algo != -1) { + run_benchmark(params, app_params.algo); } else { for (unsigned i = 0; i < 4; i++) { run_benchmark(params, i); diff --git a/tests/benchmarks/pdcp/pdcp_tx_benchmark.cpp b/tests/benchmarks/pdcp/pdcp_tx_benchmark.cpp index 3b24a4efbf..c6d0a95e75 100644 --- a/tests/benchmarks/pdcp/pdcp_tx_benchmark.cpp +++ b/tests/benchmarks/pdcp/pdcp_tx_benchmark.cpp @@ -50,32 +50,46 @@ struct bench_params { bool print_timing_info = false; }; -static void usage(const char* prog, const bench_params& params, int algo) +struct app_params { + int algo = -1; + std::string log_level = "error"; + std::string log_filename = "stdout"; +}; + +static void usage(const char* prog, const bench_params& params, const app_params& app) { fmt::print("Usage: {} [-R repetitions] [-t timing information]\n", prog); - fmt::print("\t-a Security algorithm to use [Default {}, valid {{-1,0,1,2,3}}]\n", algo); + fmt::print("\t-a Security algorithm to use [Default {}, valid {{-1,0,1,2,3}}]\n", app.algo); fmt::print("\t-t Print timing information [Default {}]\n", params.print_timing_info); fmt::print("\t-R Repetitions [Default {}]\n", params.nof_repetitions); + fmt::print("\t-l Log level to use [Default {}, valid {{error, warning, info, debug}}]\n", app.log_level); + fmt::print("\t-f Log filename to use [Default {}]\n", app.log_filename); fmt::print("\t-h Show this message\n"); } -static void parse_args(int argc, char** argv, bench_params& params, int& algo) +static void parse_args(int argc, char** argv, bench_params& params, app_params& app) { int opt = 0; - while ((opt = getopt(argc, argv, "a:R:th")) != -1) { + while ((opt = getopt(argc, argv, "a:R:l:f:th")) != -1) { switch (opt) { case 'R': params.nof_repetitions = std::strtol(optarg, nullptr, 10); break; case 'a': - algo = std::strtol(optarg, nullptr, 10); + app.algo = std::strtol(optarg, nullptr, 10); break; case 't': params.print_timing_info = true; break; + case 'l': + app.log_level = std::string(optarg); + break; + case 'f': + app.log_filename = std::string(optarg); + break; case 'h': default: - usage(argv[0], params, algo); + usage(argv[0], params, app); exit(0); } } @@ -125,9 +139,6 @@ void benchmark_pdcp_tx(bench_params params, // Create test frame pdcp_tx_gen_frame frame = {}; - auto& logger = srslog::fetch_basic_logger("PDCP"); - logger.set_level(srslog::str_to_basic_level("warning")); - // Create PDCP entities std::unique_ptr pdcp_tx = std::make_unique(0, drb_id_t::drb1, config, frame, frame, timer_factory{timers, worker}); @@ -172,8 +183,8 @@ int run_benchmark(bench_params params, int algo) } fmt::print("------ Benchmarcking: NIA{} NEA{} ------\n", algo, algo); - security::integrity_algorithm int_algo = static_cast(algo); - security::ciphering_algorithm ciph_algo = static_cast(algo); + auto int_algo = static_cast(algo); + auto ciph_algo = static_cast(algo); if (algo == 0) { benchmark_pdcp_tx(params, @@ -192,15 +203,22 @@ int run_benchmark(bench_params params, int algo) int main(int argc, char** argv) { + bench_params params{}; + app_params app_params{}; + parse_args(argc, argv, params, app_params); + srslog::init(); - srslog::fetch_basic_logger("PDCP").set_level(srslog::basic_levels::error); - int algo = -1; - bench_params params{}; - parse_args(argc, argv, params, algo); + srslog::sink* log_sink = (app_params.log_filename == "stdout") ? srslog::create_stdout_sink() + : srslog::create_file_sink(app_params.log_filename); + if (log_sink == nullptr) { + return -1; + } + srslog::set_default_sink(*log_sink); + srslog::fetch_basic_logger("PDCP").set_level(srslog::str_to_basic_level(app_params.log_level)); - if (algo != -1) { - run_benchmark(params, algo); + if (app_params.algo != -1) { + run_benchmark(params, app_params.algo); } else { for (unsigned i = 0; i < 4; i++) { run_benchmark(params, i); diff --git a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_decoder_hwacc_benchmark.cpp b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_decoder_hwacc_benchmark.cpp index 98e49fd1e6..af45128f12 100644 --- a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_decoder_hwacc_benchmark.cpp +++ b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_decoder_hwacc_benchmark.cpp @@ -218,7 +218,7 @@ static std::shared_ptr create_hw_accelera // Interfacing to a shared external HARQ buffer context repository. unsigned nof_cbs = MAX_NOF_SEGMENTS; - unsigned acc100_ext_harq_buff_size = bbdev_accelerator->get_harq_buff_size().value(); + uint64_t acc100_ext_harq_buff_size = bbdev_accelerator->get_harq_buff_size_bytes(); std::shared_ptr harq_buffer_context = create_ext_harq_buffer_context_repository(nof_cbs, acc100_ext_harq_buff_size, test_harq); TESTASSERT(harq_buffer_context); diff --git a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp index 48b20fd4b4..c6b5e6ab2a 100644 --- a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp +++ b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp @@ -501,7 +501,7 @@ static std::shared_ptr create_hw_accelera // Interfacing to a shared external HARQ buffer context repository. unsigned nof_cbs = MAX_NOF_SEGMENTS; - unsigned acc100_ext_harq_buff_size = bbdev_accelerator->get_harq_buff_size().value(); + uint64_t acc100_ext_harq_buff_size = bbdev_accelerator->get_harq_buff_size_bytes(); std::shared_ptr harq_buffer_context = create_ext_harq_buffer_context_repository(nof_cbs, acc100_ext_harq_buff_size, false); TESTASSERT(harq_buffer_context); diff --git a/tests/benchmarks/rlc/rlc_am_rx_benchmark.cpp b/tests/benchmarks/rlc/rlc_am_rx_benchmark.cpp index 2bdc672da1..b14b5778d6 100644 --- a/tests/benchmarks/rlc/rlc_am_rx_benchmark.cpp +++ b/tests/benchmarks/rlc/rlc_am_rx_benchmark.cpp @@ -81,6 +81,8 @@ class rlc_rx_am_test_frame : public rlc_rx_upper_layer_data_notifier, struct bench_params { unsigned nof_repetitions = 10000; + unsigned sdu_size = 1500; + unsigned pdu_size = 1550; }; enum class rx_order { @@ -94,17 +96,25 @@ static void usage(const char* prog, const bench_params& params) { fmt::print("Usage: {} [-R repetitions] [-s silent]\n", prog); fmt::print("\t-R Repetitions [Default {}]\n", params.nof_repetitions); + fmt::print("\t-s SDU size [Default {}]\n", params.sdu_size); + fmt::print("\t-p PDU size [Default {}]\n", params.pdu_size); fmt::print("\t-h Show this message\n"); } static void parse_args(int argc, char** argv, bench_params& params) { int opt = 0; - while ((opt = getopt(argc, argv, "R:h")) != -1) { + while ((opt = getopt(argc, argv, "R:s:p:h")) != -1) { switch (opt) { case 'R': params.nof_repetitions = std::strtol(optarg, nullptr, 10); break; + case 's': + params.sdu_size = std::strtol(optarg, nullptr, 10); + break; + case 'p': + params.pdu_size = std::strtol(optarg, nullptr, 10); + break; case 'h': default: usage(argv[0], params); @@ -160,7 +170,7 @@ std::vector generate_pdus(bench_params params, rx_order order) // Prepare SDU list for benchmark std::vector sdu_list = {}; int num_sdus = params.nof_repetitions + 1; // +1 to expire t_reassembly on setup - int num_bytes = 1500; + int num_bytes = params.sdu_size; for (int i = 0; i < num_sdus; i++) { byte_buffer sdu_buf = {}; for (int j = 0; j < num_bytes; ++j) { @@ -169,6 +179,8 @@ std::vector generate_pdus(bench_params params, rx_order order) sdu_list.push_back(std::move(sdu_buf)); } + int num_pdus = 0; + int pdu_size = params.pdu_size; for (int i = 0; i < num_sdus; i++) { rlc_sdu sdu; byte_buffer pdcp_hdr_buf = {0x80, 0x00, 0x16}; @@ -177,11 +189,14 @@ std::vector generate_pdus(bench_params params, rx_order order) sdu.buf = std::move(pdcp_hdr_buf); report_error_if_not(sdu.buf.append(std::move(sdu_buf)), "Failed to allocate SDU"); rlc_tx->handle_sdu(std::move(sdu)); - std::vector pdu_buf; - pdu_buf.resize(1550); - size_t pdu_len = rlc_tx->pull_pdu(pdu_buf); - pdu_buf.resize(pdu_len); - pdus.push_back(byte_buffer{pdu_buf}); + while (rlc_tx->get_buffer_state() > 0) { + std::vector pdu_buf; + pdu_buf.resize(pdu_size); + size_t pdu_len = rlc_tx->pull_pdu(pdu_buf); + pdu_buf.resize(pdu_len); + pdus.emplace_back(pdu_buf); + num_pdus++; + } } // shuffle PDUs according to requested order @@ -195,14 +210,14 @@ std::vector generate_pdus(bench_params params, rx_order order) std::reverse(pdus.begin(), pdus.end()); break; case rx_order::even_odd: - std::vector sdu_list_mod; - for (int i = 0; i < num_sdus; i += 2) { - sdu_list_mod.push_back(std::move(sdu_list[i])); + std::vector pdus_mod; + for (int i = 0; i < num_pdus; i += 2) { + pdus_mod.push_back(std::move(pdus[i])); } - for (int i = 1; i < num_sdus; i += 2) { - sdu_list_mod.push_back(std::move(sdu_list[i])); + for (int i = 1; i < num_pdus; i += 2) { + pdus_mod.push_back(std::move(pdus[i])); } - sdu_list = std::move(sdu_list_mod); + pdus = std::move(pdus_mod); break; } diff --git a/tests/e2e/tests/attach_detach.py b/tests/e2e/tests/attach_detach.py index 90533e2c7b..78005196ae 100644 --- a/tests/e2e/tests/attach_detach.py +++ b/tests/e2e/tests/attach_detach.py @@ -56,14 +56,14 @@ ), ) @mark.parametrize( - "band, common_scs, bandwidth, always_download_artifacts", + "band, common_scs, bandwidth", ( - param(3, 15, 50, True, id="band:%s-scs:%s-bandwidth:%s-artifacts:%s"), - param(41, 30, 50, False, id="band:%s-scs:%s-bandwidth:%s-artifacts:%s"), + param(3, 15, 50, id="band:%s-scs:%s-bandwidth:%s"), + param(41, 30, 50, id="band:%s-scs:%s-bandwidth:%s"), ), ) @mark.zmq -@mark.flaky(reruns=3, only_rerun=["failed to start"]) +@mark.flaky(reruns=3, only_rerun=["failed to start", "IPerf Data Invalid"]) # pylint: disable=too-many-arguments def test_zmq( retina_manager: RetinaTestManager, @@ -74,7 +74,6 @@ def test_zmq( band: int, common_scs: int, bandwidth: int, - always_download_artifacts: bool, protocol: IPerfProto, direction: IPerfDir, ): @@ -98,7 +97,7 @@ def test_zmq( direction=direction, global_timing_advance=0, time_alignment_calibration=0, - always_download_artifacts=always_download_artifacts, + always_download_artifacts=False, ) diff --git a/tests/e2e/tests/iperf.py b/tests/e2e/tests/iperf.py index 48b9244624..0c76ae4fdd 100644 --- a/tests/e2e/tests/iperf.py +++ b/tests/e2e/tests/iperf.py @@ -360,6 +360,7 @@ def test_android_hp( (param(41, 30, 20, id="band:%s-scs:%s-bandwidth:%s"),), ) @mark.zmq_4x4_mimo +@mark.flaky(reruns=2, only_rerun=["failed to start", "Attach timeout reached", "5GC crashed"]) # pylint: disable=too-many-arguments def test_zmq_4x4_mimo( retina_manager: RetinaTestManager, @@ -481,14 +482,16 @@ def test_zmq_smoke( param(3, 15, 5, MEDIUM_BITRATE, False, id=ZMQ_ID), param(3, 15, 10, MEDIUM_BITRATE, False, id=ZMQ_ID), param(3, 15, 20, MEDIUM_BITRATE, False, id=ZMQ_ID), - param(3, 15, 50, MEDIUM_BITRATE, True, id=ZMQ_ID), + param(3, 15, 50, MEDIUM_BITRATE, False, id=ZMQ_ID), param(41, 30, 10, MEDIUM_BITRATE, False, id=ZMQ_ID), param(41, 30, 20, MEDIUM_BITRATE, False, id=ZMQ_ID), - param(41, 30, 50, MEDIUM_BITRATE, True, id=ZMQ_ID), + param(41, 30, 50, MEDIUM_BITRATE, False, id=ZMQ_ID), ), ) @mark.zmq -@mark.flaky(reruns=1, only_rerun=["failed to start", "iperf did not achieve the expected data rate"]) +@mark.flaky( + reruns=2, only_rerun=["failed to start", "Attach timeout reached", "iperf did not achieve the expected data rate"] +) # pylint: disable=too-many-arguments def test_zmq( retina_manager: RetinaTestManager, @@ -526,7 +529,7 @@ def test_zmq( time_alignment_calibration=0, always_download_artifacts=always_download_artifacts, bitrate_threshold=0, - gnb_post_cmd="log --hex_max_size=32", + gnb_post_cmd="log --hex_max_size=32 cu_cp --inactivity_timer=600", ) diff --git a/tests/e2e/tests/ping.py b/tests/e2e/tests/ping.py index c02f8a442f..903890c284 100644 --- a/tests/e2e/tests/ping.py +++ b/tests/e2e/tests/ping.py @@ -152,7 +152,7 @@ def test_android_hp( ), ) @mark.zmq -@mark.flaky(reruns=2, only_rerun=["Some packages got lost"]) +@mark.flaky(reruns=2, only_rerun=["failed to start", "Attach timeout reached", "Some packages got lost"]) # pylint: disable=too-many-arguments def test_zmq( retina_manager: RetinaTestManager, @@ -180,6 +180,7 @@ def test_zmq( sample_rate=None, # default from testbed global_timing_advance=0, time_alignment_calibration=0, + post_command="cu_cp --inactivity_timer=600", ) @@ -350,6 +351,7 @@ def _ping( global_timing_advance=global_timing_advance, time_alignment_calibration=time_alignment_calibration, gtpu_enable=True, + log_ip_level="debug", ) configure_artifacts( retina_data=retina_data, diff --git a/tests/e2e/tests/reestablishment.py b/tests/e2e/tests/reestablishment.py index e6f4e54fe9..68e84129e7 100644 --- a/tests/e2e/tests/reestablishment.py +++ b/tests/e2e/tests/reestablishment.py @@ -23,7 +23,7 @@ """ import logging import time -from typing import Optional, Sequence, Tuple, Union +from typing import Optional, Sequence, Union from pytest import mark from retina.client.manager import RetinaTestManager @@ -45,12 +45,12 @@ ), ) @mark.zmq -@mark.flaky(reruns=5, only_rerun=["failed to start", "StatusCode.ABORTED"]) +@mark.flaky(reruns=2, only_rerun=["failed to start", "Attach timeout reached", "StatusCode.ABORTED"]) # pylint: disable=too-many-arguments def test_zmq_reestablishment( retina_manager: RetinaTestManager, retina_data: RetinaTestData, - ue_4: Tuple[UEStub, ...], + ue: UEStub, # pylint: disable=invalid-name fivegc: FiveGCStub, gnb: GNBStub, band: int, @@ -69,7 +69,7 @@ def test_zmq_reestablishment( _ping_and_reestablishment_multi_ues( retina_manager=retina_manager, retina_data=retina_data, - ue_array=ue_4, + ue_array=(ue,), gnb=gnb, fivegc=fivegc, band=band, diff --git a/tests/e2e/tests/steps/configuration.py b/tests/e2e/tests/steps/configuration.py index cb315ef80a..9b6afa8922 100644 --- a/tests/e2e/tests/steps/configuration.py +++ b/tests/e2e/tests/steps/configuration.py @@ -45,6 +45,7 @@ def configure_test_parameters( gtpu_enable: Optional[bool] = None, common_search_space_enable: bool = False, prach_config_index: int = -1, + log_ip_level="", ): """ Configure test parameters @@ -59,6 +60,7 @@ def configure_test_parameters( "ssb_scs": common_scs, "bandwidth": bandwidth, "global_timing_advance": global_timing_advance, + "log_ip_level": log_ip_level, }, }, "gnb": { diff --git a/tests/e2e/tests/steps/stub.py b/tests/e2e/tests/steps/stub.py index 14f64636d3..9c10a7cca5 100644 --- a/tests/e2e/tests/steps/stub.py +++ b/tests/e2e/tests/steps/stub.py @@ -55,7 +55,7 @@ UE_STARTUP_TIMEOUT: int = RF_MAX_TIMEOUT GNB_STARTUP_TIMEOUT: int = 5 # GNB delay (we wait x seconds and check it's still alive). UE later and has a big timeout FIVEGC_STARTUP_TIMEOUT: int = RF_MAX_TIMEOUT -ATTACH_TIMEOUT: int = 5 * 60 +ATTACH_TIMEOUT: int = 1 * 60 # pylint: disable=too-many-arguments,too-many-locals diff --git a/tests/e2e/tests/test_mode.py b/tests/e2e/tests/test_mode.py index e1bdfc789b..a3b83131d2 100644 --- a/tests/e2e/tests/test_mode.py +++ b/tests/e2e/tests/test_mode.py @@ -43,13 +43,13 @@ @mark.parametrize( "extra_config, nof_ant", ( - param("test_mode test_ue --rnti 0x44 --cqi 15 --ri 1", 1, id="Test UE 1x1 Rank 1"), - param("test_mode test_ue --rnti 0x44 --cqi 15 --ri 1", 2, id="Test UE 2x2 Rank 1"), - param("test_mode test_ue --rnti 0x44 --cqi 15 --ri 2", 2, id="Test UE 2x2 Rank 2"), - param("test_mode test_ue --rnti 0x44 --cqi 15 --ri 1", 4, id="Test UE 4x4 Rank 1"), - param("test_mode test_ue --rnti 0x44 --cqi 15 --ri 2", 4, id="Test UE 4x4 Rank 2"), - param("test_mode test_ue --rnti 0x44 --cqi 15 --ri 3", 4, id="Test UE 4x4 Rank 3"), - param("test_mode test_ue --rnti 0x44 --cqi 15 --ri 4", 4, id="Test UE 4x4 Rank 4"), + param("test_mode test_ue --ri 1", 1, id="Test UE 1x1 Rank 1"), + param("test_mode test_ue --ri 1", 2, id="Test UE 2x2 Rank 1"), + param("test_mode test_ue --ri 2", 2, id="Test UE 2x2 Rank 2"), + param("test_mode test_ue --ri 1", 4, id="Test UE 4x4 Rank 1"), + param("test_mode test_ue --ri 2", 4, id="Test UE 4x4 Rank 2"), + param("test_mode test_ue --ri 3", 4, id="Test UE 4x4 Rank 3"), + param("test_mode test_ue --ri 4", 4, id="Test UE 4x4 Rank 4"), ), ) @mark.test_mode diff --git a/tests/e2e/tests/test_mode/config_ue.yml b/tests/e2e/tests/test_mode/config_ue.yml index 997ccc6087..b3df54b05b 100644 --- a/tests/e2e/tests/test_mode/config_ue.yml +++ b/tests/e2e/tests/test_mode/config_ue.yml @@ -19,7 +19,7 @@ # cu_cp: - ue_context_setup_timeout_s: 5 + pdu_session_setup_timeout: 5 inactivity_timer: 3 rrc: force_reestablishment_fallback: false @@ -61,10 +61,12 @@ cell_cfg: nof_dl_symbols: 7 nof_dl_slots: 7 nof_ul_slots: 2 + # nof_antennas_dl: 1 - Set in gnb call. Please check it in agent.log + # nof_antennas_ul: 1 - Set in gnb call. Please check it in agent.log test_mode: test_ue: - rnti: 0x4655 - ri: 2 + # ri: 1 - Set in gnb call. Please check it in agent.log + rnti: 0x44 cqi: 15 nof_ues: 2 diff --git a/tests/e2e/tests/viavi/config.yml b/tests/e2e/tests/viavi/config.yml index 6c607b16ca..602f1acfcd 100644 --- a/tests/e2e/tests/viavi/config.yml +++ b/tests/e2e/tests/viavi/config.yml @@ -19,7 +19,7 @@ # cu_cp: - ue_context_setup_timeout_s: 5 + pdu_session_setup_timeout: 5 inactivity_timer: 5 rrc: force_reestablishment_fallback: true diff --git a/tests/integrationtests/du_high/du_high_test.cpp b/tests/integrationtests/du_high/du_high_test.cpp index 934c406858..b3577976c4 100644 --- a/tests/integrationtests/du_high/du_high_test.cpp +++ b/tests/integrationtests/du_high/du_high_test.cpp @@ -23,7 +23,6 @@ /// \file /// \brief Tests that check the setup/teardown, addition/removal of UEs in the DU-high class. -#include "lib/f1ap/common/f1ap_asn1_packer.h" #include "tests/integrationtests/du_high/test_utils/du_high_test_bench.h" #include "tests/test_doubles/f1ap/f1ap_test_message_validators.h" #include "tests/test_doubles/f1ap/f1ap_test_messages.h" diff --git a/tests/integrationtests/du_high/test_utils/du_high_test_bench.cpp b/tests/integrationtests/du_high/test_utils/du_high_test_bench.cpp index 207b2d3c26..e55a150626 100644 --- a/tests/integrationtests/du_high/test_utils/du_high_test_bench.cpp +++ b/tests/integrationtests/du_high/test_utils/du_high_test_bench.cpp @@ -174,7 +174,7 @@ du_high_test_bench::du_high_test_bench() : cfg.timers = &timers; cfg.gnb_du_id = 0; cfg.gnb_du_name = "srsdu"; - cfg.du_bind_addr = {"127.0.0.1"}; + cfg.du_bind_addr = transport_layer_address::create_from_string("127.0.0.1"); cfg.cells = {config_helpers::make_default_du_cell_config()}; cfg.qos = config_helpers::make_default_du_qos_config_list(0); cfg.sched_cfg = config_helpers::make_default_scheduler_expert_config(); diff --git a/tests/integrationtests/du_high_cu/du_high_cu_test_simulator.cpp b/tests/integrationtests/du_high_cu/du_high_cu_test_simulator.cpp index 300daa6e73..7a0bbe0b4f 100644 --- a/tests/integrationtests/du_high_cu/du_high_cu_test_simulator.cpp +++ b/tests/integrationtests/du_high_cu/du_high_cu_test_simulator.cpp @@ -169,18 +169,18 @@ void du_high_cu_test_simulator::start_dus() srs_du::du_high_configuration& du_hi_cfg = du_ctxt.du_high_cfg; du_hi_cfg.gnb_du_name = fmt::format("srsgnb{}", du_idx + 1); du_hi_cfg.gnb_du_id = du_idx + 1; - du_hi_cfg.du_bind_addr = {fmt::format("127.0.0.{}", du_idx + 1)}; - du_hi_cfg.exec_mapper = workers.du_hi_exec_mappers[du_idx].get(); - du_hi_cfg.f1c_client = &f1c_gw; - du_hi_cfg.f1u_gw = nullptr; - du_hi_cfg.phy_adapter = &du_ctxt.phy; - du_hi_cfg.timers = &timers; - du_hi_cfg.sched_ue_metrics_notifier = &du_ctxt.ue_metrics_notifier; - du_hi_cfg.cells = cfg.dus[du_idx]; - du_hi_cfg.sched_cfg = config_helpers::make_default_scheduler_expert_config(); - du_hi_cfg.mac_p = &du_ctxt.mac_pcap; - du_hi_cfg.rlc_p = &du_ctxt.rlc_pcap; - du_ctxt.du_high_inst = make_du_high(du_hi_cfg); + du_hi_cfg.du_bind_addr = transport_layer_address::create_from_string(fmt::format("127.0.0.{}", du_idx + 1)); + du_hi_cfg.exec_mapper = workers.du_hi_exec_mappers[du_idx].get(); + du_hi_cfg.f1c_client = &f1c_gw; + du_hi_cfg.f1u_gw = nullptr; + du_hi_cfg.phy_adapter = &du_ctxt.phy; + du_hi_cfg.timers = &timers; + du_hi_cfg.sched_ue_metrics_notifier = &du_ctxt.ue_metrics_notifier; + du_hi_cfg.cells = cfg.dus[du_idx]; + du_hi_cfg.sched_cfg = config_helpers::make_default_scheduler_expert_config(); + du_hi_cfg.mac_p = &du_ctxt.mac_pcap; + du_hi_cfg.rlc_p = &du_ctxt.rlc_pcap; + du_ctxt.du_high_inst = make_du_high(du_hi_cfg); du_ctxt.du_high_inst->start(); } diff --git a/tests/integrationtests/ofh/ofh_integration_test.cpp b/tests/integrationtests/ofh/ofh_integration_test.cpp index b72c636003..ba79b9b98b 100644 --- a/tests/integrationtests/ofh/ofh_integration_test.cpp +++ b/tests/integrationtests/ofh/ofh_integration_test.cpp @@ -51,9 +51,16 @@ using namespace std::chrono_literals; /// Random generator. static std::mt19937 rgen(0); -/// Static test parameters. -static const du_tx_window_timing_parameters tx_window_timing_params{470us, 258us, 300us, 285us, 350us, 50us}; -static const du_rx_window_timing_parameters rx_window_timing_params{150us, 25us}; +/// Transmission window parameters expressed in symbols, given the 30kHz scs. +unsigned T1a_max_cp_dl = 13; // 470us. +unsigned T1a_min_cp_dl = 8; // 258us. +unsigned T1a_max_cp_ul = 8; // 300us. +unsigned T1a_min_cp_ul = 8; // 285us. +unsigned T1a_max_up = 9; // 350us. +unsigned T1a_min_up = 2; // 50us. +/// Reception window parameters expressed in symbols, given the 30kHz scs. +unsigned Ta4_min = 1; // 150us. +unsigned Ta4_max = 5; // 25us. static const tdd_ul_dl_pattern tdd_pattern_7d2u{10, 7, 0, 2, 0}; static const tdd_ul_dl_pattern tdd_pattern_6d3u{10, 6, 0, 3, 0}; @@ -753,7 +760,9 @@ class test_du_emulator void start() { slot_point slot(0, to_numerology_value(test_params.scs)); - slot_duration_us = std::chrono::microseconds(1000 * SUBFRAME_DURATION_MSEC / slot.nof_slots_per_subframe()); + slot_duration_us = std::chrono::microseconds(1000 * SUBFRAME_DURATION_MSEC / slot.nof_slots_per_subframe()); + symbol_duration_us = std::chrono::microseconds(static_cast( + std::ceil(1e3 / (get_nsymb_per_slot(cyclic_prefix::NORMAL) * get_nof_slots_per_subframe(test_params.scs))))); if (!executor.execute([this]() { run_test(); })) { report_fatal_error("Failed to start DU emulator"); } @@ -794,7 +803,7 @@ class test_du_emulator slot_val = (++slot).to_uint(); } // Leave time fo the uplink slots to be processed. - auto proc_time = processing_delay_slots * slot_duration_us + tx_window_timing_params.T1a_max_cp_ul + 100ms; + auto proc_time = processing_delay_slots * slot_duration_us + (T1a_max_cp_ul * symbol_duration_us) + 100ms; std::this_thread::sleep_for(proc_time); test_finished.store(true, std::memory_order_relaxed); } @@ -808,6 +817,7 @@ class test_du_emulator const unsigned nof_prb; std::chrono::microseconds slot_duration_us; + std::chrono::microseconds symbol_duration_us; std::atomic test_finished{false}; }; @@ -1045,6 +1055,9 @@ static void configure_ofh_sector(ru_ofh_sector_configuration& sector_cfg) // Default IQ data scaling to be applied prior to downlink data compression. const float iq_scaling = 0.9f; + std::chrono::duration symbol_duration( + (1e6 / (get_nsymb_per_slot(cyclic_prefix::NORMAL) * get_nof_slots_per_subframe(test_params.scs)))); + sector_cfg.interface = "lo"; sector_cfg.mac_src_address = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; sector_cfg.mac_dst_address = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -1055,9 +1068,10 @@ static void configure_ofh_sector(ru_ofh_sector_configuration& sector_cfg) sector_cfg.cp = cyclic_prefix::NORMAL; sector_cfg.is_prach_control_plane_enabled = test_params.is_prach_control_plane_enabled; sector_cfg.ignore_ecpri_payload_size_field = test_params.ignore_ecpri_payload_size_field; - sector_cfg.tx_window_timing_params = tx_window_timing_params; - sector_cfg.rx_window_timing_params = rx_window_timing_params; - sector_cfg.is_downlink_broadcast_enabled = test_params.is_downlink_broadcast_enabled; + sector_cfg.tx_window_timing_params = { + T1a_max_cp_dl, T1a_min_cp_dl, T1a_max_cp_ul, T1a_min_cp_ul, T1a_max_up, T1a_min_up}; + sector_cfg.rx_window_timing_params = {Ta4_min, Ta4_max}; + sector_cfg.is_downlink_broadcast_enabled = test_params.is_downlink_broadcast_enabled; // Configure compression ru_compression_params dl_ul_compression_params{to_compression_type(test_params.data_compr_method), diff --git a/tests/integrationtests/phy/upper/channel_processors/pxsch_chain_test.cpp b/tests/integrationtests/phy/upper/channel_processors/pxsch_chain_test.cpp index c38e37a134..bbb93dcdfa 100644 --- a/tests/integrationtests/phy/upper/channel_processors/pxsch_chain_test.cpp +++ b/tests/integrationtests/phy/upper/channel_processors/pxsch_chain_test.cpp @@ -218,6 +218,7 @@ TEST_P(PxschChainFixture, Ideal) dlsch_params.dmrs_symbol_mask = dmrs_symbol_mask; dlsch_params.nof_cdm_groups_without_data = 2; dlsch_params.nof_layers = nof_layers; + dlsch_params.contains_dc = true; dlsch_information info = get_dlsch_information(dlsch_params); ASSERT_LT(info.get_effective_code_rate(), 0.95); diff --git a/tests/integrationtests/rlc/rlc_stress_test.cpp b/tests/integrationtests/rlc/rlc_stress_test.cpp index 7901cb6d69..36a1198ccf 100644 --- a/tests/integrationtests/rlc/rlc_stress_test.cpp +++ b/tests/integrationtests/rlc/rlc_stress_test.cpp @@ -68,7 +68,9 @@ stress_stack::stress_stack(const stress_test_args& args_, uint32_t id, rb_id_t r pdcp_msg.tx_upper_cn = rrc.get(); pdcp_msg.rx_upper_dn = traffic_sink.get(); pdcp_msg.rx_upper_cn = rrc.get(); - pdcp_msg.timers = timer_factory{timers, *ue_executor}; + pdcp_msg.ue_dl_timer_factory = timer_factory{timers, *ue_executor}; + pdcp_msg.ue_ul_timer_factory = timer_factory{timers, *ue_executor}; + pdcp_msg.ue_ctrl_timer_factory = timer_factory{timers, *ue_executor}; pdcp = create_pdcp_entity(pdcp_msg); traffic_source->set_pdcp_tx_upper(&pdcp->get_tx_upper_data_interface()); f1ap->set_pdcp_rx_lower(&pdcp->get_rx_lower_interface()); diff --git a/tests/integrationtests/rlc/rlc_stress_test_args.h b/tests/integrationtests/rlc/rlc_stress_test_args.h index ce9a0664e0..ab500ef09e 100644 --- a/tests/integrationtests/rlc/rlc_stress_test_args.h +++ b/tests/integrationtests/rlc/rlc_stress_test_args.h @@ -126,7 +126,7 @@ inline bool parse_args(stress_test_args& args, int argc, char* argv[]) " -d, --pdu_drop_rate Set rate at which RLC PDUs are dropped.\n" " -c, --pdu_cut_rate Set rate at which RLC PDUs are chopped in length.\n" " -D, --pdu_duplicate_rate Set rate at which RLC PDUs are dropped.\n" - " -l, --nof_ttis Set number of TTIs to emulate.\n" + " -t, --nof_ttis Set number of TTIs to emulate.\n" " -l, --log_filename Set log filename. Use 'stdout' to print to console.\n" " --pdcp_sn_size Set PDCP SN size.\n" " --pdcp_t_reordering Set PDCP t-Reordering timeout (ms).\n" diff --git a/tests/integrationtests/rlc/rlc_stress_test_mac.cpp b/tests/integrationtests/rlc/rlc_stress_test_mac.cpp index c0bd977900..9294a31f0c 100644 --- a/tests/integrationtests/rlc/rlc_stress_test_mac.cpp +++ b/tests/integrationtests/rlc/rlc_stress_test_mac.cpp @@ -42,15 +42,19 @@ std::vector mac_dummy::run_tx_tti(uint32_t tti) // Request data to transmit if (bsr.load(std::memory_order_relaxed) > 0) { - unsigned nwritten = rlc_tx_lower->pull_pdu(tx_pdu); - byte_buffer_chain pdu = byte_buffer_slice{span(tx_pdu.data(), nwritten)}; + unsigned nwritten = rlc_tx_lower->pull_pdu(tx_pdu); + expected pdu = + byte_buffer_chain::create(byte_buffer_slice{span(tx_pdu.data(), nwritten)}); + if (!pdu) { + report_fatal_error_if_not(pdu, "Failed to create PDU buffer"); + } logger.log_debug("Pulled PDU. PDU size={}, MAC opportunity={}, buffer_state={}", - pdu.length(), + pdu.value().length(), opp_size, bsr.load(std::memory_order_relaxed)); // Push PDU in the list - if (pdu.length() > 0) { - pdu_list.push_back(std::move(pdu)); + if (pdu.value().length() > 0) { + pdu_list.push_back(std::move(pdu.value())); } } else { logger.log_debug("Did not pull a PDU. No data to TX."); diff --git a/tests/test_doubles/f1ap/f1ap_test_messages.cpp b/tests/test_doubles/f1ap/f1ap_test_messages.cpp index 1090fca646..86e2158d0b 100644 --- a/tests/test_doubles/f1ap/f1ap_test_messages.cpp +++ b/tests/test_doubles/f1ap/f1ap_test_messages.cpp @@ -51,8 +51,8 @@ static asn1::f1ap::drbs_to_be_setup_item_s generate_drb_am_setup_item(drb_id_t d drb_info.snssai.sd.from_string("0027db"); drb.rlc_mode.value = rlc_mode_opts::rlc_am; drb.ul_up_tnl_info_to_be_setup_list.resize(1); - auto& gtp_tun = drb.ul_up_tnl_info_to_be_setup_list[0].ul_up_tnl_info.set_gtp_tunnel(); - transport_layer_address addr{"127.0.0.1"}; + auto& gtp_tun = drb.ul_up_tnl_info_to_be_setup_list[0].ul_up_tnl_info.set_gtp_tunnel(); + auto addr = transport_layer_address::create_from_string("127.0.0.1"); gtp_tun.transport_layer_address.from_string(addr.to_bitstring()); gtp_tun.gtp_teid.from_number(1); diff --git a/tests/test_doubles/f1u/dummy_f1u_du_gateway.h b/tests/test_doubles/f1u/dummy_f1u_du_gateway.h index ca6f97ab7f..bd28efc58c 100644 --- a/tests/test_doubles/f1u/dummy_f1u_du_gateway.h +++ b/tests/test_doubles/f1u/dummy_f1u_du_gateway.h @@ -57,7 +57,8 @@ class cu_up_simulator : public f1u_du_gateway const up_transport_layer_info& dl_tnl, const up_transport_layer_info& ul_tnl, srs_du::f1u_rx_sdu_notifier& du_rx, - timer_factory timers) override + timer_factory timers, + task_executor& ue_executor) override { du_notif = &du_rx; return &bearer; @@ -67,4 +68,4 @@ class cu_up_simulator : public f1u_du_gateway }; } // namespace srs_du -} // namespace srsran \ No newline at end of file +} // namespace srsran diff --git a/tests/unittests/adt/CMakeLists.txt b/tests/unittests/adt/CMakeLists.txt index 9c581a7817..e123ab9454 100644 --- a/tests/unittests/adt/CMakeLists.txt +++ b/tests/unittests/adt/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable(byte_buffer_test byte_buffer_chain_test.cpp) target_link_libraries(byte_buffer_test srslog srsran_support gtest gtest_main) add_test(byte_buffer_test byte_buffer_test) +set_tests_properties(byte_buffer_test PROPERTIES LABELS "tsan") gtest_discover_tests(byte_buffer_test) add_executable(ring_buffer_test ring_buffer_test.cpp blocking_queue_test.cpp concurrent_queue_test.cpp) diff --git a/tests/unittests/adt/byte_buffer_chain_test.cpp b/tests/unittests/adt/byte_buffer_chain_test.cpp index f7c9731531..b07a5149fe 100644 --- a/tests/unittests/adt/byte_buffer_chain_test.cpp +++ b/tests/unittests/adt/byte_buffer_chain_test.cpp @@ -64,11 +64,11 @@ TEST_F(byte_buffer_chain_test, empty_container_in_valid_state) ASSERT_EQ(chain.length(), 0U); ASSERT_EQ(chain.begin(), chain.end()); - chain.append(byte_buffer{}); + ASSERT_TRUE(chain.append(byte_buffer{})); ASSERT_EQ(chain.length(), 0U); ASSERT_EQ(chain.begin(), chain.end()); - chain.prepend(byte_buffer{}); + ASSERT_TRUE(chain.prepend(byte_buffer{})); ASSERT_EQ(chain.length(), 0U); ASSERT_EQ(chain.begin(), chain.end()); ASSERT_TRUE(chain.slices().empty()); @@ -82,8 +82,8 @@ TEST_F(byte_buffer_chain_test, append_byte_buffer) byte_buffer other_buffer2{6, 7, 8}; byte_buffer buf_concat = other_buffer.deep_copy(); ASSERT_TRUE(buf_concat.append(other_buffer2.deep_copy())); - buf.append(other_buffer.copy()); - buf.append(other_buffer2.copy()); + ASSERT_TRUE(buf.append(other_buffer.copy())); + ASSERT_TRUE(buf.append(other_buffer2.copy())); // Test length/empty methods. ASSERT_FALSE(buf.empty()); @@ -121,7 +121,7 @@ TEST_F(byte_buffer_chain_test, prepend_buffer) ASSERT_TRUE(buf.empty()); // Set header using a span of bytes. - buf.prepend(buf2.deep_copy()); + ASSERT_TRUE(buf.prepend(buf2.deep_copy())); ASSERT_FALSE(buf.empty()); ASSERT_EQ(3, buf.length()); ASSERT_EQ(buf, vec); @@ -137,14 +137,14 @@ TEST_F(byte_buffer_chain_test, prepend_buffer) // Set header avoiding ref-count increment and avoiding deep copy. buf.clear(); - buf.prepend(std::move(buf2)); + ASSERT_TRUE(buf.prepend(std::move(buf2))); ASSERT_EQ(buf, vec); ASSERT_TRUE(buf2.empty()); // Set header by ref-count increment, avoiding deep copy. buf.clear(); buf2 = vec; - buf.prepend(buf2.copy()); + ASSERT_TRUE(buf.prepend(buf2.copy())); ASSERT_EQ(buf, vec); *buf2.begin() = 5; ASSERT_NE(buf, vec); @@ -152,7 +152,7 @@ TEST_F(byte_buffer_chain_test, prepend_buffer) // Set header by deep copy. buf.clear(); buf2 = vec; - buf.prepend(buf2.deep_copy()); + ASSERT_TRUE(buf.prepend(buf2.deep_copy())); ASSERT_EQ(buf, vec); *buf2.begin() = 5; ASSERT_EQ(buf, vec); @@ -165,9 +165,9 @@ TEST_F(byte_buffer_chain_test, prepend_header_and_append_payload) byte_buffer header_bytes = {1, 2, 3}; byte_buffer payload = {4, 5, 6}; - buf.prepend(header_bytes.deep_copy()); + ASSERT_TRUE(buf.prepend(header_bytes.deep_copy())); ASSERT_EQ(header_bytes.length(), buf.length()); - buf.append(payload.copy()); + ASSERT_TRUE(buf.append(payload.copy())); ASSERT_EQ(header_bytes.length() + payload.length(), buf.length()); // Test comparison. @@ -204,9 +204,9 @@ TEST_F(byte_buffer_chain_test, payload_lifetime) all_bytes.insert(all_bytes.end(), payload1.begin(), payload1.end()); all_bytes.insert(all_bytes.end(), payload2.begin(), payload2.end()); - buf.prepend(header_bytes.copy()); - buf.append(std::move(payload1)); - buf.append(payload2.copy()); + ASSERT_TRUE(buf.prepend(header_bytes.copy())); + ASSERT_TRUE(buf.append(std::move(payload1))); + ASSERT_TRUE(buf.append(payload2.copy())); } // Note: header and payload went out of scope, but that shouldnt affect the rlc buffer content. @@ -221,8 +221,8 @@ TEST_F(byte_buffer_chain_test, slice_chain_formatter) ASSERT_TRUE(pdu2.append(bytes)); byte_buffer_chain chain; - chain.append(byte_buffer_slice{std::move(pdu), 3, 2}); - chain.append(byte_buffer_slice{std::move(pdu2), 0, 2}); + ASSERT_TRUE(chain.append(byte_buffer_slice{std::move(pdu), 3, 2})); + ASSERT_TRUE(chain.append(byte_buffer_slice{std::move(pdu2), 0, 2})); for (auto& b : chain) { ASSERT_TRUE(b > 0); diff --git a/tests/unittests/adt/byte_buffer_test.cpp b/tests/unittests/adt/byte_buffer_test.cpp index ff2718676a..1e5f8a604c 100644 --- a/tests/unittests/adt/byte_buffer_test.cpp +++ b/tests/unittests/adt/byte_buffer_test.cpp @@ -180,7 +180,7 @@ TEST_F(byte_buffer_tester, empty_byte_buffer_in_valid_state) ASSERT_EQ(pdu, std::list{}) << "Comparison with empty non-span type failed"; ASSERT_EQ(pdu.segments().begin(), pdu.segments().end()); ASSERT_TRUE(pdu.is_contiguous()); - pdu.resize(0); + ASSERT_TRUE(pdu.resize(0)); ASSERT_EQ_LEN(pdu, 0); pdu.clear(); ASSERT_EQ_LEN(pdu, 0); @@ -289,12 +289,12 @@ TEST_P(two_vector_size_param_test, prepend) byte_buffer pdu; // prepend in empty byte_buffer. - pdu.prepend(bytes1); + ASSERT_TRUE(pdu.prepend(bytes1)); ASSERT_EQ(pdu.length(), bytes1.size()); ASSERT_EQ(pdu, bytes1); // prepend in non-empty byte_buffer. - pdu.prepend(bytes2); + ASSERT_TRUE(pdu.prepend(bytes2)); ASSERT_EQ(pdu.length(), bytes1.size() + bytes2.size()); ASSERT_EQ(pdu, concat_vec(bytes2, bytes1)); } @@ -420,7 +420,7 @@ TEST_P(three_vector_size_param_test, shallow_copy_prepend_and_append) byte_buffer pdu2 = pdu.copy(); ASSERT_EQ_BUFFER(pdu2, pdu); ASSERT_EQ_BUFFER(pdu2, bytes1); - pdu2.prepend(bytes2); + ASSERT_TRUE(pdu2.prepend(bytes2)); ASSERT_EQ(pdu2, pdu); ASSERT_TRUE(pdu2.append(bytes3)); ASSERT_EQ(pdu2, pdu); @@ -480,7 +480,7 @@ TEST_F(byte_buffer_tester, prepend_and_trim_tail) ASSERT_TRUE(sdu.append(pdu.begin() + prefix_len, pdu.end())); std::array hdr_buf; std::copy(pdu.begin(), pdu.begin() + prefix_len, hdr_buf.begin()); - sdu.prepend(hdr_buf); + ASSERT_TRUE(sdu.prepend(hdr_buf)); ASSERT_EQ(sdu.length(), pdu_len); ASSERT_EQ(std::distance(sdu.begin(), sdu.end()), pdu_len); @@ -497,7 +497,7 @@ TEST_P(three_vector_size_param_test, shallow_copy_prepend_and_append_keeps_valid byte_buffer pdu{bytes1}; byte_buffer pdu2{pdu.copy()}; - pdu.prepend(bytes2); + ASSERT_TRUE(pdu.prepend(bytes2)); ASSERT_TRUE(pdu.append(bytes3)); ASSERT_EQ(pdu, concat_vec(concat_vec(bytes2, bytes1), bytes3)); @@ -772,14 +772,14 @@ TEST_F(byte_buffer_tester, append_rvalue_byte_buffer) // Chain small vector to empty buffer byte_buffer pdu2(small_vec); ASSERT_EQ(pdu2, small_vec); - pdu.prepend(std::move(pdu2)); + ASSERT_TRUE(pdu.prepend(std::move(pdu2))); ASSERT_FALSE(pdu.empty()); ASSERT_EQ(pdu, small_vec); ASSERT_TRUE(pdu2.empty()); // Chain byte_buffer before another non-empty byte_buffer. ASSERT_TRUE(pdu2.append(big_vec)); - pdu.prepend(std::move(pdu2)); + ASSERT_TRUE(pdu.prepend(std::move(pdu2))); ASSERT_TRUE(pdu2.empty()); ASSERT_EQ_LEN(pdu, big_vec.size() + small_vec.size()); ASSERT_EQ(pdu, bytes_concat); @@ -864,6 +864,38 @@ TEST_P(byte_buffer_stress_tester, concurrent_alloc_dealloc_test) } } +TEST_F(byte_buffer_tester, concurrent_alloc_dealloc_test) +{ + const unsigned max_buffer_size = memory_block_size * 16; + std::vector randbytes = test_rgen::random_vector(max_buffer_size); + std::vector allocated_buffers; + + // Deplete the pool + while (true) { + byte_buffer pdu = byte_buffer{span{randbytes.data(), 10U}}; + if (pdu.empty()) { + break; + } + allocated_buffers.push_back(std::move(pdu)); + } + + // Pool is still empty. + byte_buffer pdu = byte_buffer{span{randbytes.data(), test_rgen::uniform_int(1U, max_buffer_size)}}; + ASSERT_TRUE(pdu.empty()); + + // Test if a span can be added to a byte_buffer with heap as fallback allocator. + size_t sz = test_rgen::uniform_int(1U, max_buffer_size); + pdu = byte_buffer{byte_buffer::fallback_allocation_tag{}, span{randbytes.data(), sz}}; + ASSERT_EQ(pdu.length(), sz); + span expected_bytes{randbytes.data(), sz}; + ASSERT_EQ(pdu, expected_bytes); + + // Test if a byte_buffer can be added to a byte_buffer with heap as fallback allocator. + pdu = byte_buffer{byte_buffer::fallback_allocation_tag{}, allocated_buffers.front()}; + ASSERT_EQ(pdu.length(), allocated_buffers.front().length()); + ASSERT_EQ(pdu, allocated_buffers.front()); +} + INSTANTIATE_TEST_SUITE_P(byte_buffer_test, one_vector_size_param_test, ::testing::Values(small_vec_size, large_vec_size, random_vec_size())); diff --git a/tests/unittests/asn1/CMakeLists.txt b/tests/unittests/asn1/CMakeLists.txt index 4f1f7b45fa..bf08ad7d31 100644 --- a/tests/unittests/asn1/CMakeLists.txt +++ b/tests/unittests/asn1/CMakeLists.txt @@ -18,6 +18,8 @@ # and at http://www.gnu.org/licenses/. # +include_directories(../../..) + set_directory_properties(PROPERTIES LABELS "asn1") add_executable(asn1_utils_test asn1_utils_test.cpp) @@ -46,3 +48,7 @@ gtest_discover_tests(asn1_f1ap_test) add_executable(asn1_ngap_test asn1_ngap_test.cpp) target_link_libraries(asn1_ngap_test ngap_asn1 srslog srsran_support srsran_pcap gtest gtest_main) gtest_discover_tests(asn1_ngap_test) + +add_executable(asn1_cause_conversion_test asn1_cause_conversion_test.cpp) +target_link_libraries(asn1_cause_conversion_test ngap_asn1 f1ap_asn1 e1ap_asn1 srslog srsran_ran srsran_support gtest gtest_main) +gtest_discover_tests(asn1_cause_conversion_test) diff --git a/tests/unittests/asn1/asn1_cause_conversion_test.cpp b/tests/unittests/asn1/asn1_cause_conversion_test.cpp new file mode 100644 index 0000000000..6918bd585c --- /dev/null +++ b/tests/unittests/asn1/asn1_cause_conversion_test.cpp @@ -0,0 +1,424 @@ +/* + * + * 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 "lib/e1ap/common/e1ap_asn1_converters.h" +#include "lib/f1ap/cu_cp/f1ap_asn1_converters.h" +#include "lib/ngap/ngap_asn1_converters.h" +#include "srsran/ran/cause/e1ap_cause_converters.h" +#include "srsran/ran/cause/f1ap_cause_converters.h" +#include "srsran/ran/cause/ngap_cause_converters.h" +#include + +using namespace srsran; +using namespace srs_cu_cp; + +class asn1_cause_conversion_test : public ::testing::Test +{ +public: + ~asn1_cause_conversion_test() override + { + // flush logger after each test + srslog::flush(); + } + +protected: + asn1_cause_conversion_test() + { + srslog::fetch_basic_logger("ASN1").set_level(srslog::basic_levels::debug); + srslog::fetch_basic_logger("ASN1").set_hex_dump_max_size(-1); + + test_logger.set_level(srslog::basic_levels::debug); + test_logger.set_hex_dump_max_size(-1); + + // Start the log backend. + srslog::init(); + } + + srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); +}; + +// test conversion from ngap asn1 +TEST_F(asn1_cause_conversion_test, when_ngap_cause_received_then_conversion_to_common_cause_successful) +{ + asn1::ngap::cause_c asn1_cause; + ngap_cause_t ngap_cause; + + asn1_cause.set_radio_network() = asn1::ngap::cause_radio_network_opts::unspecified; + ngap_cause = asn1_to_cause(asn1_cause); + ASSERT_TRUE(variant_holds_alternative(ngap_cause)); + ASSERT_EQ(variant_get(ngap_cause), ngap_cause_radio_network_t::unspecified); + + asn1_cause.set_radio_network() = asn1::ngap::cause_radio_network_opts::cell_not_available; + ngap_cause = asn1_to_cause(asn1_cause); + ASSERT_TRUE(variant_holds_alternative(ngap_cause)); + ASSERT_EQ(variant_get(ngap_cause), ngap_cause_radio_network_t::cell_not_available); + + asn1_cause.set_radio_network() = asn1::ngap::cause_radio_network_opts::up_confidentiality_protection_not_possible; + ngap_cause = asn1_to_cause(asn1_cause); + ASSERT_TRUE(variant_holds_alternative(ngap_cause)); + ASSERT_EQ(variant_get(ngap_cause), + ngap_cause_radio_network_t::up_confidentiality_protection_not_possible); + + asn1_cause.set_radio_network() = asn1::ngap::cause_radio_network_opts::ho_target_not_allowed; + ngap_cause = asn1_to_cause(asn1_cause); + ASSERT_TRUE(variant_holds_alternative(ngap_cause)); + ASSERT_EQ(variant_get(ngap_cause), ngap_cause_radio_network_t::ho_target_not_allowed); + + asn1_cause.set_radio_network() = asn1::ngap::cause_radio_network_opts::user_inactivity; + ngap_cause = asn1_to_cause(asn1_cause); + ASSERT_TRUE(variant_holds_alternative(ngap_cause)); + ASSERT_EQ(variant_get(ngap_cause), ngap_cause_radio_network_t::user_inactivity); + + asn1_cause.set_transport() = asn1::ngap::cause_transport_opts::unspecified; + ngap_cause = asn1_to_cause(asn1_cause); + ASSERT_TRUE(variant_holds_alternative(ngap_cause)); + ASSERT_EQ(variant_get(ngap_cause), ngap_cause_transport_t::unspecified); + + asn1_cause.set_nas() = asn1::ngap::cause_nas_opts::deregister; + ngap_cause = asn1_to_cause(asn1_cause); + ASSERT_TRUE(variant_holds_alternative(ngap_cause)); + ASSERT_EQ(variant_get(ngap_cause), cause_nas_t::deregister); + + asn1_cause.set_protocol() = asn1::ngap::cause_protocol_opts::msg_not_compatible_with_receiver_state; + ngap_cause = asn1_to_cause(asn1_cause); + ASSERT_TRUE(variant_holds_alternative(ngap_cause)); + ASSERT_EQ(variant_get(ngap_cause), cause_protocol_t::msg_not_compatible_with_receiver_state); + + asn1_cause.set_misc() = asn1::ngap::cause_misc_opts::unspecified; + ngap_cause = asn1_to_cause(asn1_cause); + ASSERT_TRUE(variant_holds_alternative(ngap_cause)); + ASSERT_EQ(variant_get(ngap_cause), ngap_cause_misc_t::unspecified); + + asn1_cause.set_misc() = asn1::ngap::cause_misc_opts::unknown_plmn_or_sn_pn; + ngap_cause = asn1_to_cause(asn1_cause); + ASSERT_TRUE(variant_holds_alternative(ngap_cause)); + ASSERT_EQ(variant_get(ngap_cause), ngap_cause_misc_t::unknown_plmn_or_sn_pn); +} + +// test conversion to ngap asn1 +TEST_F(asn1_cause_conversion_test, when_common_cause_received_then_conversion_to_ngap_cause_successful) +{ + ngap_cause_t cause; + asn1::ngap::cause_c ngap_cause; + + cause = ngap_cause_radio_network_t::unspecified; + ngap_cause = cause_to_asn1(cause); + ASSERT_EQ(ngap_cause.type(), asn1::ngap::cause_c::types::radio_network); + ASSERT_EQ(ngap_cause.radio_network().value, asn1::ngap::cause_radio_network_opts::unspecified); + + cause = ngap_cause_radio_network_t::unknown_local_ue_ngap_id; + ngap_cause = cause_to_asn1(cause); + ASSERT_EQ(ngap_cause.type(), asn1::ngap::cause_c::types::radio_network); + ASSERT_EQ(ngap_cause.radio_network().value, asn1::ngap::cause_radio_network_opts::unknown_local_ue_ngap_id); + + cause = ngap_cause_radio_network_t::ue_in_rrc_inactive_state_not_reachable; + ngap_cause = cause_to_asn1(cause); + ASSERT_EQ(ngap_cause.type(), asn1::ngap::cause_c::types::radio_network); + ASSERT_EQ(ngap_cause.radio_network().value, + asn1::ngap::cause_radio_network_opts::ue_in_rrc_inactive_state_not_reachable); + + cause = srsran::e1ap_to_ngap_cause(e1ap_cause_t{e1ap_cause_radio_network_t::encryption_algorithms_not_supported}); + ngap_cause = cause_to_asn1(cause); + ASSERT_EQ(ngap_cause.type(), asn1::ngap::cause_c::types::radio_network); + ASSERT_EQ(ngap_cause.radio_network().value, + asn1::ngap::cause_radio_network_opts::encryption_and_or_integrity_protection_algorithms_not_supported); + + cause = f1ap_to_ngap_cause(f1ap_cause_radio_network_t::rl_fail_rlc); + ngap_cause = cause_to_asn1(cause); + ASSERT_EQ(ngap_cause.type(), asn1::ngap::cause_c::types::radio_network); + ASSERT_EQ(ngap_cause.radio_network().value, asn1::ngap::cause_radio_network_opts::radio_conn_with_ue_lost); + + cause = ngap_cause_transport_t::unspecified; + ngap_cause = cause_to_asn1(cause); + ASSERT_EQ(ngap_cause.type(), asn1::ngap::cause_c::types::transport); + ASSERT_EQ(ngap_cause.transport().value, asn1::ngap::cause_transport_opts::unspecified); + + cause = cause_nas_t::deregister; + ngap_cause = cause_to_asn1(cause); + ASSERT_EQ(ngap_cause.type(), asn1::ngap::cause_c::types::nas); + ASSERT_EQ(ngap_cause.nas().value, asn1::ngap::cause_nas_opts::deregister); + + cause = cause_protocol_t::msg_not_compatible_with_receiver_state; + ngap_cause = cause_to_asn1(cause); + ASSERT_EQ(ngap_cause.type(), asn1::ngap::cause_c::types::protocol); + ASSERT_EQ(ngap_cause.protocol().value, asn1::ngap::cause_protocol_opts::msg_not_compatible_with_receiver_state); + + cause = ngap_cause_misc_t::unspecified; + ngap_cause = cause_to_asn1(cause); + ASSERT_EQ(ngap_cause.type(), asn1::ngap::cause_c::types::misc); + ASSERT_EQ(ngap_cause.misc().value, asn1::ngap::cause_misc_opts::unspecified); + + cause = ngap_cause_misc_t::unknown_plmn_or_sn_pn; + ngap_cause = cause_to_asn1(cause); + ASSERT_EQ(ngap_cause.type(), asn1::ngap::cause_c::types::misc); + ASSERT_EQ(ngap_cause.misc().value, asn1::ngap::cause_misc_opts::unknown_plmn_or_sn_pn); +} + +// test conversion from f1ap asn1 +TEST_F(asn1_cause_conversion_test, when_f1ap_cause_received_then_conversion_to_common_cause_successful) +{ + asn1::f1ap::cause_c f1ap_cause; + f1ap_cause_t cause; + + f1ap_cause.set_radio_network() = asn1::f1ap::cause_radio_network_opts::unspecified; + cause = asn1_to_cause(f1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), f1ap_cause_radio_network_t::unspecified); + + f1ap_cause.set_radio_network() = asn1::f1ap::cause_radio_network_opts::cell_not_available; + cause = asn1_to_cause(f1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), f1ap_cause_radio_network_t::cell_not_available); + + f1ap_cause.set_radio_network() = asn1::f1ap::cause_radio_network_opts::normal_release; + cause = asn1_to_cause(f1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), f1ap_cause_radio_network_t::normal_release); + + f1ap_cause.set_radio_network() = asn1::f1ap::cause_radio_network_opts::gnb_cu_cell_capacity_exceeded; + cause = asn1_to_cause(f1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), f1ap_cause_radio_network_t::gnb_cu_cell_capacity_exceeded); + + f1ap_cause.set_radio_network() = asn1::f1ap::cause_radio_network_opts::rl_fail_others; + cause = asn1_to_cause(f1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), f1ap_cause_radio_network_t::rl_fail_others); + + f1ap_cause.set_transport() = asn1::f1ap::cause_transport_opts::unspecified; + cause = asn1_to_cause(f1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), f1ap_cause_transport_t::unspecified); + + f1ap_cause.set_transport() = asn1::f1ap::cause_transport_opts::unknown_tnl_address_for_iab; + cause = asn1_to_cause(f1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), f1ap_cause_transport_t::unknown_tnl_address_for_iab); + + f1ap_cause.set_transport() = asn1::f1ap::cause_transport_opts::unknown_up_tnl_info_for_iab; + cause = asn1_to_cause(f1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), f1ap_cause_transport_t::unknown_up_tnl_info_for_iab); + + f1ap_cause.set_protocol() = asn1::f1ap::cause_protocol_opts::semantic_error; + cause = asn1_to_cause(f1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), cause_protocol_t::semantic_error); + + f1ap_cause.set_misc() = asn1::f1ap::cause_misc_opts::hardware_fail; + cause = asn1_to_cause(f1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), cause_misc_t::hardware_fail); + + f1ap_cause.set_misc() = asn1::f1ap::cause_misc_opts::unspecified; + cause = asn1_to_cause(f1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), cause_misc_t::unspecified); +} + +// test conversion to f1ap asn1 +TEST_F(asn1_cause_conversion_test, when_common_cause_received_then_conversion_to_f1ap_cause_successful) +{ + f1ap_cause_t cause; + asn1::f1ap::cause_c f1ap_cause; + + cause = f1ap_cause_radio_network_t::unspecified; + f1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(f1ap_cause.type(), asn1::f1ap::cause_c::types::radio_network); + ASSERT_EQ(f1ap_cause.radio_network().value, asn1::f1ap::cause_radio_network_opts::unspecified); + + cause = f1ap_cause_radio_network_t::cell_not_available; + f1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(f1ap_cause.type(), asn1::f1ap::cause_c::types::radio_network); + ASSERT_EQ(f1ap_cause.radio_network().value, asn1::f1ap::cause_radio_network_opts::cell_not_available); + + cause = f1ap_cause_radio_network_t::normal_release; + f1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(f1ap_cause.type(), asn1::f1ap::cause_c::types::radio_network); + ASSERT_EQ(f1ap_cause.radio_network().value, asn1::f1ap::cause_radio_network_opts::normal_release); + + cause = f1ap_cause_radio_network_t::gnb_cu_cell_capacity_exceeded; + f1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(f1ap_cause.type(), asn1::f1ap::cause_c::types::radio_network); + ASSERT_EQ(f1ap_cause.radio_network().value, asn1::f1ap::cause_radio_network_opts::gnb_cu_cell_capacity_exceeded); + + cause = f1ap_cause_radio_network_t::rl_fail_others; + f1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(f1ap_cause.type(), asn1::f1ap::cause_c::types::radio_network); + ASSERT_EQ(f1ap_cause.radio_network().value, asn1::f1ap::cause_radio_network_opts::rl_fail_others); + + cause = f1ap_cause_transport_t::unspecified; + f1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(f1ap_cause.type(), asn1::f1ap::cause_c::types::transport); + ASSERT_EQ(f1ap_cause.transport().value, asn1::f1ap::cause_transport_opts::unspecified); + + cause = f1ap_cause_transport_t::unknown_tnl_address_for_iab; + f1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(f1ap_cause.type(), asn1::f1ap::cause_c::types::transport); + ASSERT_EQ(f1ap_cause.transport().value, asn1::f1ap::cause_transport_opts::unknown_tnl_address_for_iab); + + cause = f1ap_cause_transport_t::unknown_up_tnl_info_for_iab; + f1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(f1ap_cause.type(), asn1::f1ap::cause_c::types::transport); + ASSERT_EQ(f1ap_cause.transport().value, asn1::f1ap::cause_transport_opts::unknown_up_tnl_info_for_iab); + + cause = srsran::ngap_to_f1ap_cause(ngap_cause_t{cause_nas_t::normal_release}); + f1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(f1ap_cause.type(), asn1::ngap::cause_c::types::radio_network); + ASSERT_EQ(f1ap_cause.radio_network().value, asn1::f1ap::cause_radio_network_opts::normal_release); + + cause = cause_protocol_t::semantic_error; + f1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(f1ap_cause.type(), asn1::f1ap::cause_c::types::protocol); + ASSERT_EQ(f1ap_cause.protocol().value, asn1::f1ap::cause_protocol_opts::semantic_error); + + cause = cause_misc_t::hardware_fail; + f1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(f1ap_cause.type(), asn1::f1ap::cause_c::types::misc); + ASSERT_EQ(f1ap_cause.misc().value, asn1::f1ap::cause_misc_opts::hardware_fail); + + cause = cause_misc_t::unspecified; + f1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(f1ap_cause.type(), asn1::f1ap::cause_c::types::misc); + ASSERT_EQ(f1ap_cause.misc().value, asn1::f1ap::cause_misc_opts::unspecified); +} + +// test conversion from e1ap asn1 +TEST_F(asn1_cause_conversion_test, when_e1ap_cause_received_then_conversion_to_common_cause_successful) +{ + asn1::e1ap::cause_c e1ap_cause; + e1ap_cause_t cause; + + e1ap_cause.set_radio_network() = asn1::e1ap::cause_radio_network_opts::unspecified; + cause = asn1_to_cause(e1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), e1ap_cause_radio_network_t::unspecified); + + e1ap_cause.set_radio_network() = asn1::e1ap::cause_radio_network_opts::unknown_or_inconsistent_pair_of_ue_e1ap_id; + cause = asn1_to_cause(e1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), + e1ap_cause_radio_network_t::unknown_or_inconsistent_pair_of_ue_e1ap_id); + + e1ap_cause.set_radio_network() = asn1::e1ap::cause_radio_network_opts::normal_release; + cause = asn1_to_cause(e1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), e1ap_cause_radio_network_t::normal_release); + + e1ap_cause.set_radio_network() = asn1::e1ap::cause_radio_network_opts::pdcp_cfg_not_supported; + cause = asn1_to_cause(e1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), e1ap_cause_radio_network_t::pdcp_cfg_not_supported); + + e1ap_cause.set_radio_network() = asn1::e1ap::cause_radio_network_opts::report_characteristic_empty; + cause = asn1_to_cause(e1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), e1ap_cause_radio_network_t::report_characteristic_empty); + + e1ap_cause.set_transport() = asn1::e1ap::cause_transport_opts::unspecified; + cause = asn1_to_cause(e1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), e1ap_cause_transport_t::unspecified); + + e1ap_cause.set_transport() = asn1::e1ap::cause_transport_opts::unknown_tnl_address_for_iab; + cause = asn1_to_cause(e1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), e1ap_cause_transport_t::unknown_tnl_address_for_iab); + + e1ap_cause.set_protocol() = asn1::e1ap::cause_protocol_opts::semantic_error; + cause = asn1_to_cause(e1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), cause_protocol_t::semantic_error); + + e1ap_cause.set_misc() = asn1::e1ap::cause_misc_opts::hardware_fail; + cause = asn1_to_cause(e1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), cause_misc_t::hardware_fail); + + e1ap_cause.set_misc() = asn1::e1ap::cause_misc_opts::unspecified; + cause = asn1_to_cause(e1ap_cause); + ASSERT_TRUE(variant_holds_alternative(cause)); + ASSERT_EQ(variant_get(cause), cause_misc_t::unspecified); +} + +// test conversion to e1ap asn1 +TEST_F(asn1_cause_conversion_test, when_common_cause_received_then_conversion_to_e1ap_cause_successful) +{ + e1ap_cause_t cause; + asn1::e1ap::cause_c e1ap_cause; + + cause = e1ap_cause_radio_network_t::unspecified; + e1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(e1ap_cause.type(), asn1::e1ap::cause_c::types::radio_network); + ASSERT_EQ(e1ap_cause.radio_network().value, asn1::e1ap::cause_radio_network_opts::unspecified); + + cause = e1ap_cause_radio_network_t::unknown_or_inconsistent_pair_of_ue_e1ap_id; + e1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(e1ap_cause.type(), asn1::e1ap::cause_c::types::radio_network); + ASSERT_EQ(e1ap_cause.radio_network().value, + asn1::e1ap::cause_radio_network_opts::unknown_or_inconsistent_pair_of_ue_e1ap_id); + + cause = e1ap_cause_radio_network_t::normal_release; + e1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(e1ap_cause.type(), asn1::e1ap::cause_c::types::radio_network); + ASSERT_EQ(e1ap_cause.radio_network().value, asn1::e1ap::cause_radio_network_opts::normal_release); + + cause = e1ap_cause_radio_network_t::pdcp_cfg_not_supported; + e1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(e1ap_cause.type(), asn1::e1ap::cause_c::types::radio_network); + ASSERT_EQ(e1ap_cause.radio_network().value, asn1::e1ap::cause_radio_network_opts::pdcp_cfg_not_supported); + + cause = e1ap_cause_radio_network_t::report_characteristic_empty; + e1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(e1ap_cause.type(), asn1::e1ap::cause_c::types::radio_network); + ASSERT_EQ(e1ap_cause.radio_network().value, asn1::e1ap::cause_radio_network_opts::report_characteristic_empty); + + cause = e1ap_cause_transport_t::unspecified; + e1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(e1ap_cause.type(), asn1::e1ap::cause_c::types::transport); + ASSERT_EQ(e1ap_cause.transport().value, asn1::e1ap::cause_transport_opts::unspecified); + + cause = e1ap_cause_transport_t::unknown_tnl_address_for_iab; + e1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(e1ap_cause.type(), asn1::e1ap::cause_c::types::transport); + ASSERT_EQ(e1ap_cause.transport().value, asn1::e1ap::cause_transport_opts::unknown_tnl_address_for_iab); + + cause = srsran::ngap_to_e1ap_cause(ngap_cause_t{cause_nas_t::normal_release}); + e1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(e1ap_cause.type(), asn1::ngap::cause_c::types::radio_network); + ASSERT_EQ(e1ap_cause.radio_network().value, asn1::e1ap::cause_radio_network_opts::normal_release); + + cause = cause_protocol_t::semantic_error; + e1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(e1ap_cause.type(), asn1::e1ap::cause_c::types::protocol); + ASSERT_EQ(e1ap_cause.protocol().value, asn1::e1ap::cause_protocol_opts::semantic_error); + + cause = cause_misc_t::hardware_fail; + e1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(e1ap_cause.type(), asn1::e1ap::cause_c::types::misc); + ASSERT_EQ(e1ap_cause.misc().value, asn1::e1ap::cause_misc_opts::hardware_fail); + + cause = cause_misc_t::unspecified; + e1ap_cause = cause_to_asn1(cause); + ASSERT_EQ(e1ap_cause.type(), asn1::e1ap::cause_c::types::misc); + ASSERT_EQ(e1ap_cause.misc().value, asn1::e1ap::cause_misc_opts::unspecified); +} diff --git a/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test.cpp b/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test.cpp index a0e9da3005..44e33449d3 100644 --- a/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test.cpp +++ b/tests/unittests/cu_cp/cell_meas_manager/cell_meas_manager_test.cpp @@ -65,8 +65,9 @@ TEST_F(cell_meas_manager_test, when_empty_config_is_used_then_no_neighbor_cells_ { create_empty_manager(); + ue_index_t ue_index = ue_index_t::min; nr_cell_id_t cid = 0; - optional meas_cfg = manager->get_measurement_config(cid); + optional meas_cfg = manager->get_measurement_config(ue_index, cid); // Make sure meas_cfg is empty. verify_empty_meas_cfg(meas_cfg); @@ -76,8 +77,9 @@ TEST_F(cell_meas_manager_test, when_serving_cell_not_found_no_neighbor_cells_are { create_default_manager(); + ue_index_t ue_index = ue_index_t::min; nr_cell_id_t cid = 5; - optional meas_cfg = manager->get_measurement_config(cid); + optional meas_cfg = manager->get_measurement_config(ue_index, cid); // Make sure meas_cfg is empty. verify_empty_meas_cfg(meas_cfg); @@ -87,9 +89,11 @@ TEST_F(cell_meas_manager_test, when_serving_cell_found_then_neighbor_cells_are_a { create_default_manager(); + ue_index_t ue_index = ue_index_t::min; + meas_obj_id_t meas_obj_id = meas_obj_id_t::min; for (unsigned cid = 0; cid < 2; ++cid) { - optional meas_cfg = manager->get_measurement_config(cid); + optional meas_cfg = manager->get_measurement_config(ue_index, cid); check_default_meas_cfg(meas_cfg, meas_obj_id); meas_obj_id = uint_to_meas_obj_id(meas_obj_id_to_uint(meas_obj_id) + 2); } @@ -99,7 +103,8 @@ TEST_F(cell_meas_manager_test, when_inexisting_cell_config_is_updated_then_confi { create_default_manager(); - const nr_cell_id_t nci = 1; + ue_index_t ue_index = ue_index_t::min; + const nr_cell_id_t nci = 1; // get current config optional cell_cfg = manager->get_cell_config(nci); @@ -113,7 +118,7 @@ TEST_F(cell_meas_manager_test, when_inexisting_cell_config_is_updated_then_confi cell_cfg_val.serving_cell_cfg.ssb_scs.emplace() = subcarrier_spacing::kHz30; // Make sure meas_cfg is created. - optional meas_cfg = manager->get_measurement_config(nci); + optional meas_cfg = manager->get_measurement_config(ue_index, nci); check_default_meas_cfg(meas_cfg, meas_obj_id_t::min); } @@ -121,7 +126,8 @@ TEST_F(cell_meas_manager_test, when_incomplete_cell_config_is_updated_then_valid { create_default_manager(); - const nr_cell_id_t nci = 1; + ue_index_t ue_index = ue_index_t::min; + const nr_cell_id_t nci = 1; // get current config optional cell_cfg = manager->get_cell_config(nci); @@ -134,7 +140,7 @@ TEST_F(cell_meas_manager_test, when_incomplete_cell_config_is_updated_then_valid cell_cfg_val.serving_cell_cfg.ssb_scs.emplace() = subcarrier_spacing::kHz30; // Make sure meas_cfg is created. - optional meas_cfg = manager->get_measurement_config(nci); + optional meas_cfg = manager->get_measurement_config(ue_index, nci); check_default_meas_cfg(meas_cfg, meas_obj_id_t::min); } @@ -143,8 +149,9 @@ TEST_F(cell_meas_manager_test, when_empty_cell_config_is_used_then_meas_cfg_is_n // Create a manager without ncells and without report config. create_manager_without_ncells_and_periodic_report(); + ue_index_t ue_index = ue_index_t::min; nr_cell_id_t cid = 0; - optional meas_cfg = manager->get_measurement_config(cid); + optional meas_cfg = manager->get_measurement_config(ue_index, cid); // Make sure meas_cfg is empty. verify_empty_meas_cfg(meas_cfg); @@ -154,15 +161,16 @@ TEST_F(cell_meas_manager_test, when_old_meas_config_is_provided_old_ids_are_remo { create_default_manager(); + ue_index_t ue_index = ue_index_t::min; const nr_cell_id_t initial_nci = 0; // Make sure meas_cfg is created (no previous meas config provided) - optional initial_meas_cfg = manager->get_measurement_config(initial_nci); + optional initial_meas_cfg = manager->get_measurement_config(ue_index, initial_nci); check_default_meas_cfg(initial_meas_cfg, meas_obj_id_t::min); verify_meas_cfg(initial_meas_cfg); const nr_cell_id_t target_nci = 1; - optional target_meas_cfg = manager->get_measurement_config(target_nci, initial_meas_cfg); + optional target_meas_cfg = manager->get_measurement_config(ue_index, target_nci, initial_meas_cfg); // Make sure initial IDs are release again. ASSERT_EQ(target_meas_cfg.value().meas_obj_to_rem_list.at(0), diff --git a/tests/unittests/cu_cp/du_processor_test_messages.cpp b/tests/unittests/cu_cp/du_processor_test_messages.cpp index 456b648f7b..09410ac03d 100644 --- a/tests/unittests/cu_cp/du_processor_test_messages.cpp +++ b/tests/unittests/cu_cp/du_processor_test_messages.cpp @@ -109,7 +109,7 @@ cu_cp_ue_context_release_command srsran::srs_cu_cp::generate_ue_context_release_ { cu_cp_ue_context_release_command ue_context_release_command = {}; ue_context_release_command.ue_index = ue_index; - ue_context_release_command.cause = cause_radio_network_t::unspecified; + ue_context_release_command.cause = ngap_cause_radio_network_t::unspecified; return ue_context_release_command; } @@ -126,16 +126,17 @@ srsran::srs_cu_cp::generate_pdu_session_resource_setup(unsigned num_pdu_sessions cu_cp_pdu_session_res_setup_item item; item.pdu_session_id = pdu_session_id; - item.pdu_session_nas_pdu.resize(2); + bool ret = item.pdu_session_nas_pdu.resize(2); + (void)ret; item.pdu_session_nas_pdu[0] = 0xaa; item.pdu_session_nas_pdu[1] = 0xbb; item.s_nssai.sst = 1; item.pdu_session_aggregate_maximum_bit_rate_dl = 100; item.pdu_session_aggregate_maximum_bit_rate_ul = 100; - item.ul_ngu_up_tnl_info = {transport_layer_address{"127.0.0.1"}, int_to_gtpu_teid(0x1)}; - item.pdu_session_type = "ipv4"; - item.security_ind = {}; + item.ul_ngu_up_tnl_info = {transport_layer_address::create_from_string("127.0.0.1"), int_to_gtpu_teid(0x1)}; + item.pdu_session_type = "ipv4"; + item.security_ind = {}; for (unsigned k = 0; k < num_qos_flows; ++k) { qos_flow_setup_request_item qos_item; @@ -166,8 +167,9 @@ cu_cp_pdu_session_resource_release_command srsran::srs_cu_cp::generate_pdu_sessi cmd.ue_index = uint_to_ue_index(0); cu_cp_pdu_session_res_to_release_item_rel_cmd pdu_session_res_to_release_item_rel_cmd; - pdu_session_res_to_release_item_rel_cmd.pdu_session_id = pdu_session_id; - pdu_session_res_to_release_item_rel_cmd.pdu_session_res_release_cmd_transfer.cause = cause_nas_t::unspecified; + pdu_session_res_to_release_item_rel_cmd.pdu_session_id = pdu_session_id; + pdu_session_res_to_release_item_rel_cmd.pdu_session_res_release_cmd_transfer.cause = + ngap_cause_radio_network_t::unspecified; cmd.pdu_session_res_to_release_list_rel_cmd.emplace(pdu_session_id, pdu_session_res_to_release_item_rel_cmd); @@ -217,7 +219,7 @@ srsran::srs_cu_cp::generate_pdu_session_resource_modification_with_qos_flow_remo // Add item to remove inexisting QoS flow. cu_cp_qos_flow_with_cause_item release_item; release_item.qos_flow_id = flow_id; - release_item.cause = cause_radio_network_t::unspecified; + release_item.cause = ngap_cause_radio_network_t::unspecified; transfer.qos_flow_to_release_list.emplace(release_item.qos_flow_id, release_item); modify_item.transfer = transfer; diff --git a/tests/unittests/cu_cp/test_helpers.h b/tests/unittests/cu_cp/test_helpers.h index 2925a1dd5e..53a9a3e912 100644 --- a/tests/unittests/cu_cp/test_helpers.h +++ b/tests/unittests/cu_cp/test_helpers.h @@ -212,7 +212,7 @@ struct dummy_du_processor_e1ap_control_notifier : public du_processor_e1ap_contr // add one UP transport item e1ap_up_params_item up_item; up_item.cell_group_id = 0; - up_item.up_tnl_info = {transport_layer_address{"127.0.0.1"}, int_to_gtpu_teid(0x1)}; + up_item.up_tnl_info = {transport_layer_address::create_from_string("127.0.0.1"), int_to_gtpu_teid(0x1)}; drb_item.ul_up_transport_params.push_back(up_item); res_setup_item.drb_setup_list_ng_ran.emplace(drb_id, drb_item); diff --git a/tests/unittests/cu_up/cu_up_test_helpers.h b/tests/unittests/cu_up/cu_up_test_helpers.h index 9b55f7ce5a..09f513baa9 100644 --- a/tests/unittests/cu_up/cu_up_test_helpers.h +++ b/tests/unittests/cu_up/cu_up_test_helpers.h @@ -250,7 +250,9 @@ class dummy_f1u_gateway final : public f1u_cu_up_gateway const up_transport_layer_info& ul_up_tnl_info, srs_cu_up::f1u_rx_delivery_notifier& cu_delivery, srs_cu_up::f1u_rx_sdu_notifier& cu_rx, - timer_factory timers) override + task_executor& ul_exec, + timer_factory ue_dl_timer_factory, + unique_timer& ue_inactivity_timer) override { created_ul_teid_list.push_back(ul_up_tnl_info.gtp_teid); bearer.connect_f1u_rx_sdu_notifier(cu_rx); diff --git a/tests/unittests/cu_up/pdu_session_manager_test.cpp b/tests/unittests/cu_up/pdu_session_manager_test.cpp index 650523a777..cecfc32aac 100644 --- a/tests/unittests/cu_up/pdu_session_manager_test.cpp +++ b/tests/unittests/cu_up/pdu_session_manager_test.cpp @@ -214,11 +214,11 @@ TEST_F(pdu_session_manager_test, drb_create_with_one_qfi_which_is_already_mapped EXPECT_TRUE(mod_result.success); ASSERT_EQ(mod_result.drb_setup_results.size(), 1); EXPECT_FALSE(mod_result.drb_setup_results[0].success); - EXPECT_EQ(mod_result.drb_setup_results[0].cause, cause_t{cause_radio_network_t::unspecified}); + EXPECT_EQ(mod_result.drb_setup_results[0].cause, e1ap_cause_t{e1ap_cause_radio_network_t::unspecified}); ASSERT_EQ(mod_result.drb_setup_results[0].qos_flow_results.size(), 1); EXPECT_FALSE(mod_result.drb_setup_results[0].qos_flow_results[0].success); EXPECT_EQ(mod_result.drb_setup_results[0].qos_flow_results[0].cause, - cause_t{cause_radio_network_t::multiple_qos_flow_id_instances}); + e1ap_cause_t{e1ap_cause_radio_network_t::multiple_qos_flow_id_instances}); // validate pdu session is not disconnected from GTP-U gateway EXPECT_EQ(pdu_session_mng->get_nof_pdu_sessions(), 1); @@ -251,7 +251,7 @@ TEST_F(pdu_session_manager_test, drb_create_with_unknown_five_qi) ASSERT_EQ(setup_result.drb_setup_results.size(), 1); EXPECT_FALSE(setup_result.drb_setup_results[0].success); - EXPECT_EQ(setup_result.drb_setup_results[0].cause, cause_t{cause_radio_network_t::not_supported_5qi_value}); + EXPECT_EQ(setup_result.drb_setup_results[0].cause, e1ap_cause_t{e1ap_cause_radio_network_t::not_supported_5qi_value}); ASSERT_EQ(setup_result.drb_setup_results[0].qos_flow_results.size(), 0); } @@ -302,13 +302,14 @@ TEST_F(pdu_session_manager_test, drb_create_with_two_qfi_of_which_one_is_already EXPECT_TRUE(mod_result.success); ASSERT_EQ(mod_result.drb_setup_results.size(), 1); EXPECT_TRUE(mod_result.drb_setup_results[0].success); // success, since at least one QFI mapping was valid - EXPECT_EQ(mod_result.drb_setup_results[0].cause, cause_t{cause_radio_network_t::unspecified}); + EXPECT_EQ(mod_result.drb_setup_results[0].cause, e1ap_cause_t{e1ap_cause_radio_network_t::unspecified}); ASSERT_EQ(mod_result.drb_setup_results[0].qos_flow_results.size(), 2); EXPECT_FALSE(mod_result.drb_setup_results[0].qos_flow_results[0].success); // the first was invalid EXPECT_EQ(mod_result.drb_setup_results[0].qos_flow_results[0].cause, - cause_t{cause_radio_network_t::multiple_qos_flow_id_instances}); + e1ap_cause_t{e1ap_cause_radio_network_t::multiple_qos_flow_id_instances}); EXPECT_TRUE(mod_result.drb_setup_results[0].qos_flow_results[1].success); // the second was valid - EXPECT_EQ(mod_result.drb_setup_results[0].qos_flow_results[1].cause, cause_t{cause_radio_network_t::unspecified}); + EXPECT_EQ(mod_result.drb_setup_results[0].qos_flow_results[1].cause, + e1ap_cause_t{e1ap_cause_radio_network_t::unspecified}); // validate pdu session is not disconnected from GTP-U gateway EXPECT_EQ(pdu_session_mng->get_nof_pdu_sessions(), 1); diff --git a/tests/unittests/cu_up/pdu_session_manager_test.h b/tests/unittests/cu_up/pdu_session_manager_test.h index a036aecd4a..1232bc7407 100644 --- a/tests/unittests/cu_up/pdu_session_manager_test.h +++ b/tests/unittests/cu_up/pdu_session_manager_test.h @@ -62,11 +62,15 @@ class pdu_session_manager_test : public ::testing::Test logger, ue_inactivity_timer, timers_factory, + timers_factory, + timers_factory, *f1u_gw, *f1u_allocator, *gtpu_tx_notifier, *gtpu_rx_demux, teid_worker, + teid_worker, + teid_worker, gtpu_pcap); } @@ -106,9 +110,11 @@ generate_pdu_session_res_to_setup_item(pdu_session_id_t psi, drb_id_t drb_id, qo pdu_session_setup_item.security_ind.confidentiality_protection_ind = confidentiality_protection_indication_t::not_needed; pdu_session_setup_item.pdu_session_res_dl_ambr = 330000000; - pdu_session_setup_item.ng_ul_up_tnl_info.tp_address.from_bitstring("01111111000000000000000000000001"); + pdu_session_setup_item.ng_ul_up_tnl_info.tp_address = + transport_layer_address::create_from_bitstring("01111111000000000000000000000001"); pdu_session_setup_item.ng_ul_up_tnl_info.gtp_teid = int_to_gtpu_teid(0x12345678); - pdu_session_setup_item.ng_ul_up_tnl_info = {transport_layer_address{"0.0.0.0"}, int_to_gtpu_teid(0)}; + pdu_session_setup_item.ng_ul_up_tnl_info = {transport_layer_address::create_from_string("0.0.0.0"), + int_to_gtpu_teid(0)}; e1ap_drb_to_setup_item_ng_ran drb_to_setup_item; drb_to_setup_item.drb_id = drb_id; diff --git a/tests/unittests/du_manager/du_manager_test_helpers.cpp b/tests/unittests/du_manager/du_manager_test_helpers.cpp index 54ab1891a0..5995640f49 100644 --- a/tests/unittests/du_manager/du_manager_test_helpers.cpp +++ b/tests/unittests/du_manager/du_manager_test_helpers.cpp @@ -21,6 +21,7 @@ */ #include "du_manager_test_helpers.h" +#include "srsran/du/du_cell_config_helpers.h" #include "srsran/mac/config/mac_cell_group_config_factory.h" #include "srsran/mac/config/mac_config_helpers.h" #include "srsran/rlc/rlc_srb_config_factory.h" @@ -102,7 +103,7 @@ srsran::srs_du::create_f1ap_ue_context_update_request(du_ue_index_t req.drbs_to_setup.back().five_qi = uint_to_five_qi(9); req.drbs_to_setup.back().uluptnl_info_list.resize(1); req.drbs_to_setup.back().uluptnl_info_list[0].gtp_teid = int_to_gtpu_teid(0); - req.drbs_to_setup.back().uluptnl_info_list[0].tp_address = transport_layer_address{"127.0.0.1"}; + req.drbs_to_setup.back().uluptnl_info_list[0].tp_address = transport_layer_address::create_from_string("127.0.0.1"); } return req; @@ -114,7 +115,7 @@ du_manager_test_bench::du_manager_test_bench(span cells) : du_mng_exec(worker), ue_exec_mapper(worker), cell_exec_mapper(worker), - params{{"srsgnb", 1, 1, {"127.0.0.1"}, du_cells}, + params{{"srsgnb", 1, 1, transport_layer_address::create_from_string("127.0.0.1"), du_cells}, {timers, du_mng_exec, ue_exec_mapper, cell_exec_mapper}, {f1ap, f1ap}, {f1u_gw}, diff --git a/tests/unittests/du_manager/du_manager_test_helpers.h b/tests/unittests/du_manager/du_manager_test_helpers.h index 2f38abfcbe..cd0def86ad 100644 --- a/tests/unittests/du_manager/du_manager_test_helpers.h +++ b/tests/unittests/du_manager/du_manager_test_helpers.h @@ -22,9 +22,9 @@ #pragma once -#include "lib/du_manager/du_ue/du_ue_manager_repository.h" -#include "srsran/du/du_cell_config_helpers.h" +#include "lib/du_manager/ran_resource_management/du_ran_resource_manager.h" #include "srsran/du_manager/du_manager_params.h" +#include "srsran/gtpu/gtpu_teid_pool.h" #include "srsran/support/async/async_test_utils.h" #include "srsran/support/executors/manual_task_worker.h" #include @@ -70,7 +70,6 @@ class dummy_cell_executor_mapper : public du_high_cell_executor_mapper explicit dummy_cell_executor_mapper(task_executor& exec_) : exec(exec_) {} task_executor& executor(du_cell_index_t cell_index) override { return exec; } task_executor& slot_ind_executor(du_cell_index_t cell_index) override { return exec; } - task_executor& error_ind_executor(du_cell_index_t cell_index) override { return exec; } task_executor& exec; }; @@ -224,7 +223,8 @@ class f1u_gateway_dummy : public f1u_du_gateway const up_transport_layer_info& dl_tnl_info, const up_transport_layer_info& ul_tnl_info, srs_du::f1u_rx_sdu_notifier& du_rx, - timer_factory timers) override + timer_factory timers, + task_executor& ue_executor) override { if (next_bearer_is_created and f1u_bearers.count(dl_tnl_info) == 0) { f1u_bearers.insert(std::make_pair(dl_tnl_info, std::map{})); diff --git a/tests/unittests/du_manager/du_ue/du_bearer_test.cpp b/tests/unittests/du_manager/du_ue/du_bearer_test.cpp index c178817fbd..fce79d82bd 100644 --- a/tests/unittests/du_manager/du_ue/du_bearer_test.cpp +++ b/tests/unittests/du_manager/du_ue/du_bearer_test.cpp @@ -21,6 +21,7 @@ */ #include "lib/du_manager/du_ue/du_bearer.h" +#include "lib/du_manager/du_ue/du_ue_bearer_manager.h" #include "tests/unittests/du_manager/du_manager_test_helpers.h" #include "srsran/du/du_cell_config_helpers.h" #include "srsran/support/test_utils.h" @@ -50,7 +51,7 @@ std::unique_ptr create_dummy_drb(drb_id_t drb_id, lcid_t lcid) static dummy_rlc_rlf_notifier rlf_notifier; std::array ul_tnls = { - up_transport_layer_info{transport_layer_address{"127.0.0.1"}, gtpu_teid_t{0}}}; + up_transport_layer_info{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{0}}}; return create_drb(to_du_ue_index(0), to_du_cell_index(0), drb_id, 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 5c0779fbe1..d22966bfc8 100644 --- a/tests/unittests/du_manager/du_ue/ue_manager_test.cpp +++ b/tests/unittests/du_manager/du_ue/ue_manager_test.cpp @@ -108,7 +108,7 @@ class du_ue_manager_tester : public ::testing::Test null_rlc_pcap rlc_pcap; dummy_ue_resource_configurator_factory cell_res_alloc; - du_manager_params params{{"srsgnb", 1, 1, {"127.0.0.1"}, cells}, + du_manager_params params{{"srsgnb", 1, 1, transport_layer_address::create_from_string("127.0.0.1"), cells}, {timers, worker, ue_execs, cell_execs}, {f1ap_dummy, f1ap_dummy}, {f1u_dummy}, diff --git a/tests/unittests/du_manager/procedures/du_manager_procedure_test_helpers.h b/tests/unittests/du_manager/procedures/du_manager_procedure_test_helpers.h index 8e3692599e..fec69e1144 100644 --- a/tests/unittests/du_manager/procedures/du_manager_procedure_test_helpers.h +++ b/tests/unittests/du_manager/procedures/du_manager_procedure_test_helpers.h @@ -23,7 +23,8 @@ #pragma once #include "../du_manager_test_helpers.h" -#include "srsran/mac/config/mac_cell_group_config_factory.h" +#include "lib/du_manager/du_ue/du_ue.h" +#include "lib/du_manager/du_ue/du_ue_manager_repository.h" #include "srsran/support/async/fifo_async_task_scheduler.h" namespace srsran { @@ -50,10 +51,11 @@ class du_ue_dummy : public du_ue, public mac_ue_radio_link_notifier, public rlc_ ue_notifiers_disconnected = true; return launch_no_op_task(); } - void schedule_async_task(async_task task) override { ue_ctrl_loop->schedule(std::move(task)); } - void handle_rlf_detection(rlf_cause cause) override {} - void handle_crnti_ce_detection() override {} - void stop_drb_traffic() override {} + async_task handle_activity_stop_request() override { return launch_no_op_task(); } + void schedule_async_task(async_task task) override { ue_ctrl_loop->schedule(std::move(task)); } + void handle_rlf_detection(rlf_cause cause) override {} + void handle_crnti_ce_detection() override {} + void stop_drb_traffic() override {} mac_ue_radio_link_notifier& get_mac_rlf_notifier() override { return *this; } void on_rlf_detected() override {} void on_crnti_ce_received() override {} @@ -126,4 +128,4 @@ class du_manager_proc_tester : public du_manager_test_bench }; } // namespace srs_du -} // namespace srsran \ No newline at end of file +} // namespace srsran diff --git a/tests/unittests/du_manager/procedures/du_ue_ric_configuration_procedure_test.cpp b/tests/unittests/du_manager/procedures/du_ue_ric_configuration_procedure_test.cpp index 27bf56675f..2f8029707d 100644 --- a/tests/unittests/du_manager/procedures/du_ue_ric_configuration_procedure_test.cpp +++ b/tests/unittests/du_manager/procedures/du_ue_ric_configuration_procedure_test.cpp @@ -22,6 +22,7 @@ #include "du_manager_procedure_test_helpers.h" #include "lib/du_manager/procedures/du_ue_ric_configuration_procedure.h" +#include "srsran/du/du_cell_config_helpers.h" #include "srsran/support/test_utils.h" #include @@ -110,4 +111,4 @@ TEST_F(du_ue_ric_config_tester, mac.wait_ue_reconf.result = mac_ue_reconfiguration_response{test_ue->ue_index, true}; mac.wait_ue_reconf.ready_ev.set(); ASSERT_TRUE(proc_result().has_value()) << "The procedure should have finished after receiving MAC response"; -} \ No newline at end of file +} diff --git a/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp b/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp index baa46d7086..148d452cd1 100644 --- a/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp +++ b/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp @@ -105,7 +105,7 @@ e1ap_bearer_context_setup_request srsran::srs_cu_cp::generate_bearer_context_set res_to_setup_item.security_ind.integrity_protection_ind = integrity_protection_indication_t::not_needed; res_to_setup_item.security_ind.confidentiality_protection_ind = confidentiality_protection_indication_t::not_needed; res_to_setup_item.pdu_session_res_dl_ambr = 330000000; - res_to_setup_item.ng_ul_up_tnl_info = {transport_layer_address{"0.0.0.0"}, int_to_gtpu_teid(0)}; + res_to_setup_item.ng_ul_up_tnl_info = {transport_layer_address::create_from_string("0.0.0.0"), int_to_gtpu_teid(0)}; e1ap_drb_to_setup_item_ng_ran drb_to_setup_item; drb_to_setup_item.drb_id = uint_to_drb_id(1); @@ -271,7 +271,7 @@ e1ap_bearer_context_release_command srsran::srs_cu_cp::generate_bearer_context_r { e1ap_bearer_context_release_command command; command.ue_index = ue_index; - command.cause = cause_radio_network_t::unspecified; + command.cause = e1ap_cause_radio_network_t::unspecified; return command; } diff --git a/tests/unittests/e1ap/common/test_helpers.h b/tests/unittests/e1ap/common/test_helpers.h index 6aa0843091..572ad1b984 100644 --- a/tests/unittests/e1ap/common/test_helpers.h +++ b/tests/unittests/e1ap/common/test_helpers.h @@ -87,7 +87,7 @@ class dummy_e1ap_cu_up_notifier : public srs_cu_up::e1ap_cu_up_notifier e1ap_pdu_session_resource_setup_modification_item response_setup_item; response_setup_item.pdu_session_id = request_setup_item.pdu_session_id; response_setup_item.ng_dl_up_tnl_info.gtp_teid = int_to_gtpu_teid(1); - response_setup_item.ng_dl_up_tnl_info.tp_address = transport_layer_address{"127.0.0.1"}; + response_setup_item.ng_dl_up_tnl_info.tp_address = transport_layer_address::create_from_string("127.0.0.1"); for (const auto& request_drb_item : request_setup_item.drb_to_setup_list_ng_ran) { e1ap_drb_setup_item_ng_ran response_drb_item; diff --git a/tests/unittests/e1ap/cu_cp/e1ap_cu_cp_setup_procedure_test.cpp b/tests/unittests/e1ap/cu_cp/e1ap_cu_cp_setup_procedure_test.cpp index 236528339f..3393e662bb 100644 --- a/tests/unittests/e1ap/cu_cp/e1ap_cu_cp_setup_procedure_test.cpp +++ b/tests/unittests/e1ap/cu_cp/e1ap_cu_cp_setup_procedure_test.cpp @@ -76,7 +76,7 @@ TEST_F(e1ap_cu_cp_test, when_received_cu_up_e1_setup_request_invalid_then_reject test_logger.info("TEST: Transmit CuUpE1SetupFailure message..."); cu_up_e1_setup_response msg = {}; msg.success = false; - msg.cause = cause_radio_network_t::unspecified; + msg.cause = e1ap_cause_radio_network_t::unspecified; e1ap->handle_cu_up_e1_setup_response(msg); // Check the generated PDU is indeed the E1 Setup failure diff --git a/tests/unittests/e2/common/e2_test_helpers.h b/tests/unittests/e2/common/e2_test_helpers.h index 4ae7af9a8d..bad15f676e 100644 --- a/tests/unittests/e2/common/e2_test_helpers.h +++ b/tests/unittests/e2/common/e2_test_helpers.h @@ -183,7 +183,8 @@ inline e2_message generate_ric_control_request_style2_action6(srslog::basic_logg logger.error("Failed to pack E2SM RC control header\n"); } - ric_control_request->ri_cctrl_hdr.value.resize(ctrl_hdr_buff.length()); + bool ret = ric_control_request->ri_cctrl_hdr.value.resize(ctrl_hdr_buff.length()); + (void)ret; std::copy(ctrl_hdr_buff.begin(), ctrl_hdr_buff.end(), ric_control_request->ri_cctrl_hdr.value.begin()); asn1::e2sm_rc::e2_sm_rc_ctrl_msg_s ctrl_msg; @@ -214,7 +215,8 @@ inline e2_message generate_ric_control_request_style2_action6(srslog::basic_logg logger.error("Failed to pack E2SM RC control message\n"); } - ric_control_request->ri_cctrl_msg.value.resize(ctrl_msg_buff.length()); + ret = ric_control_request->ri_cctrl_msg.value.resize(ctrl_msg_buff.length()); + (void)ret; std::copy(ctrl_msg_buff.begin(), ctrl_msg_buff.end(), ric_control_request->ri_cctrl_msg.value.begin()); return e2_msg; } @@ -253,7 +255,8 @@ inline e2_message generate_ric_control_request(srslog::basic_logger& logger, logger.error("Failed to pack E2SM RC control header\n"); } - ric_control_request->ri_cctrl_hdr.value.resize(ctrl_hdr_buff.length()); + bool ret = ric_control_request->ri_cctrl_hdr.value.resize(ctrl_hdr_buff.length()); + (void)ret; std::copy(ctrl_hdr_buff.begin(), ctrl_hdr_buff.end(), ric_control_request->ri_cctrl_hdr.value.begin()); asn1::e2sm_rc::e2_sm_rc_ctrl_msg_s ctrl_msg; @@ -277,7 +280,8 @@ inline e2_message generate_ric_control_request(srslog::basic_logger& logger, logger.error("Failed to pack E2SM RC control message\n"); } - ric_control_request->ri_cctrl_msg.value.resize(ctrl_msg_buff.length()); + ret = ric_control_request->ri_cctrl_msg.value.resize(ctrl_msg_buff.length()); + (void)ret; std::copy(ctrl_msg_buff.begin(), ctrl_msg_buff.end(), ric_control_request->ri_cctrl_msg.value.begin()); return e2_msg; } @@ -393,7 +397,7 @@ class dummy_e2sm_kpm_du_meas_provider : public e2sm_kpm_meas_provider } } else { if (meas_values_int.size()) { - meas_record_item.integer() = meas_values_int[0]; + meas_record_item.set_integer() = meas_values_int[0]; } else { meas_record_item.set_integer() = 1; } @@ -481,12 +485,13 @@ class dummy_e2sm_kpm_du_meas_provider : public e2sm_kpm_meas_provider return false; }; - std::vector supported_metrics = {"CQI", "RSRP", "RSRQ", "DRB.UEThpDl", "DRB.RlcSduDelayDl"}; - std::vector presence = {1}; - std::vector cond_satisfied = {1}; - std::vector meas_values_float = {0.15625}; - std::vector meas_values_int = {1}; - std::vector ue_ids = {0}; + std::vector supported_metrics = + {"CQI", "RSRP", "RSRQ", "DRB.UEThpDl", "DRB.UEThpUl", "DRB.RlcSduDelayDl"}; + std::vector presence = {1}; + std::vector cond_satisfied = {1}; + std::vector meas_values_float = {0.15625}; + std::vector meas_values_int = {1}; + std::vector ue_ids = {0}; }; class dummy_e2_subscription_mngr : public e2_subscription_manager @@ -613,7 +618,8 @@ inline asn1::e2ap::ri_caction_to_be_setup_item_s generate_e2sm_kpm_ric_action(e2 return {}; } - ric_action.ric_action_definition.resize(buf.length()); + bool ret = ric_action.ric_action_definition.resize(buf.length()); + (void)ret; std::copy(buf.begin(), buf.end(), ric_action.ric_action_definition.begin()); return ric_action; } @@ -636,7 +642,8 @@ inline e2_message generate_e2sm_kpm_subscription_request(asn1::e2ap::ri_caction_ return {}; } - ric_subscript_reqs->ricsubscription_details->ric_event_trigger_definition.resize(buf.length()); + bool ret = ric_subscript_reqs->ricsubscription_details->ric_event_trigger_definition.resize(buf.length()); + (void)ret; std::copy(buf.begin(), buf.end(), ric_subscript_reqs->ricsubscription_details->ric_event_trigger_definition.begin()); e2_message e2_msg; @@ -662,9 +669,11 @@ inline e2_message generate_e2_ind_msg(byte_buffer& ind_hdr_bytes, byte_buffer& i e2_ind.indication->ri_ccall_process_id_present = false; // put RIC indication content into message - e2_ind.indication->ri_cind_msg.value.resize(ind_msg_bytes.length()); + bool ret = e2_ind.indication->ri_cind_msg.value.resize(ind_msg_bytes.length()); + (void)ret; std::copy(ind_msg_bytes.begin(), ind_msg_bytes.end(), e2_ind.indication->ri_cind_msg.value.begin()); - e2_ind.indication->ri_cind_hdr.value.resize(ind_hdr_bytes.length()); + ret = e2_ind.indication->ri_cind_hdr.value.resize(ind_hdr_bytes.length()); + (void)ret; std::copy(ind_hdr_bytes.begin(), ind_hdr_bytes.end(), e2_ind.indication->ri_cind_hdr.value.begin()); e2_message e2_msg; @@ -785,7 +794,7 @@ class dummy_du_configurator : public du_configurator }; /// Fixture class for E2AP -class e2_test_base : public ::testing::Test +class e2_base { protected: void tick() @@ -818,6 +827,12 @@ class e2_test_base : public ::testing::Test srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); }; +class e2_test_base : public e2_base, public ::testing::Test +{}; + +class e2_test_base_with_pcap : public e2_base, public testing::TestWithParam +{}; + class e2_test : public e2_test_base { void SetUp() override diff --git a/tests/unittests/e2/e2sm_kpm_test.cpp b/tests/unittests/e2/e2sm_kpm_test.cpp index c868891009..8807617a02 100644 --- a/tests/unittests/e2/e2sm_kpm_test.cpp +++ b/tests/unittests/e2/e2sm_kpm_test.cpp @@ -27,28 +27,63 @@ #include "srsran/support/async/async_test_utils.h" #include "srsran/support/executors/task_worker.h" #include "srsran/support/test_utils.h" +#include #include using namespace srsran; -#define PCAP_OUTPUT 0 +// Helper global variables to pass pcap_writer to all tests. +bool g_enable_pcap = false; +dlt_pcap* g_pcap = nullptr; -class e2sm_kpm_indication : public e2_test_base +class e2_entity_test_with_pcap : public e2_test_base_with_pcap { -public: -#if PCAP_OUTPUT - void save_msg_pcap(const byte_buffer& last_pdu) +protected: + dlt_pcap* external_pcap_writer; + + void SetUp() override { - if (not g_pcap->is_write_enabled()) { - return; + external_pcap_writer = GetParam(); + + srslog::fetch_basic_logger("TEST").set_level(srslog::basic_levels::debug); + srslog::init(); + + cfg = srsran::config_helpers::make_default_e2ap_config(); + cfg.e2sm_kpm_enabled = true; + + gw = std::make_unique(); + pcap = std::make_unique(); + if (external_pcap_writer) { + packer = std::make_unique(*gw, *e2, *external_pcap_writer); + } else { + packer = std::make_unique(*gw, *e2, *pcap); } - g_pcap->push_pdu(last_pdu.copy()); - usleep(200); + e2_client = std::make_unique(*packer); + du_metrics = std::make_unique(); + f1ap_ue_id_mapper = std::make_unique(); + factory = timer_factory{timers, task_worker}; + rc_param_configurator = std::make_unique(); + e2 = create_e2_entity( + cfg, e2_client.get(), *du_metrics, *f1ap_ue_id_mapper, *rc_param_configurator, factory, task_worker); } -#endif -private: + + void TearDown() override + { + // flush logger after each test + srslog::flush(); + pcap->close(); + } +}; + +class e2sm_kpm_indication : public e2_test_base_with_pcap +{ +protected: + dlt_pcap* external_pcap_writer; + void SetUp() override { + external_pcap_writer = GetParam(); + srslog::fetch_basic_logger("TEST").set_level(srslog::basic_levels::debug); srslog::init(); @@ -61,22 +96,19 @@ class e2sm_kpm_indication : public e2_test_base e2sm_kpm_iface = std::make_unique(test_logger, *e2sm_kpm_packer, *du_meas_provider); gw = std::make_unique(); pcap = std::make_unique(); - packer = std::make_unique(*gw, *e2, *pcap); -#if PCAP_OUTPUT - g_pcap = std::make_unique(155, "E2AP", "/tmp/e2sm_kpm_test.pcap", pcap_exec); -#endif + if (external_pcap_writer) { + packer = std::make_unique(*gw, *e2, *external_pcap_writer); + } else { + packer = std::make_unique(*gw, *e2, *pcap); + } } + void TearDown() override { // Flush logger after each test. srslog::flush(); pcap->close(); } -#if PCAP_OUTPUT - srsran::task_worker worker{"pcap_worker", 1024}; - task_worker_executor pcap_exec{worker}; - std::unique_ptr g_pcap; -#endif }; void get_presence_starting_with_cond_satisfied(const std::vector& presence, @@ -134,13 +166,34 @@ std::vector get_reported_ues(const std::vector>& return reported_ues; } -TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style1) +// E2 Setup Request is needed for Wireshark to correctly decode the subsequent Subscription Requests +TEST_P(e2_entity_test_with_pcap, e2sm_kpm_generates_ran_func_desc) +{ + dummy_e2_pdu_notifier* dummy_msg_notifier = e2_client->get_e2_msg_notifier(); + // We need this test to generate E2 Setup Request, so Wireshark can decode the following RIC indication messages. + test_logger.info("Launch e2 setup request procedure with task worker..."); + e2->start(); + + // Need to send setup response, so the transaction can be completed. + unsigned transaction_id = get_transaction_id(dummy_msg_notifier->last_e2_msg.pdu).value(); + e2_message e2_setup_response = generate_e2_setup_response(transaction_id); + e2_setup_response.pdu.successful_outcome() + .value.e2setup_resp() + ->ra_nfunctions_accepted.value[0] + ->ra_nfunction_id_item() + .ran_function_id = e2sm_kpm_asn1_packer::ran_func_id; + test_logger.info("Injecting E2SetupResponse"); + e2->handle_message(e2_setup_response); +} + +TEST_P(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style1) { // Measurement values in 5 time slot. - std::vector meas_values = {0.15625, 0.15625, 0.15625, 0.15625, 0.15625}; - uint32_t nof_meas_data = meas_values.size(); - uint32_t nof_metrics = 1; - uint32_t nof_records = nof_metrics; + std::vector meas_real_values = {0.15625, 0.15625, 0.15625, 0.15625, 0.15625}; + std::vector meas_int_values = {1, 2, 3, 4, 5}; + uint32_t nof_meas_data = meas_real_values.size(); + uint32_t nof_metrics = 2; + uint32_t nof_records = nof_metrics; // Define E2SM_KPM action format 1. e2_sm_kpm_action_definition_s action_def; @@ -161,16 +214,17 @@ TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style1) asn1::e2ap::ri_caction_to_be_setup_item_s ric_action = generate_e2sm_kpm_ric_action(action_def); ASSERT_FALSE(e2sm_kpm_iface->action_supported(ric_action)); - action_def_f1.meas_info_list[0].meas_type.set_meas_name().from_string( - "DRB.RlcSduDelayDl"); // Change to a valid metric. + action_def_f1.meas_info_list[0].meas_type.set_meas_name().from_string("DRB.UEThpUl"); // Change to a valid metric. + + action_def_f1.meas_info_list.push_back(meas_info_item); // Add a second metric. + action_def_f1.meas_info_list[1].meas_type.set_meas_name().from_string("DRB.RlcSduDelayDl"); ric_action = generate_e2sm_kpm_ric_action(action_def); -#if PCAP_OUTPUT - // Save E2 Subscription Request. - e2_message e2_subscript_req = generate_e2sm_kpm_subscription_request(ric_action); - packer->handle_message(e2_subscript_req); - save_msg_pcap(gw->last_pdu); -#endif + if (g_enable_pcap) { + // Save E2 Subscription Request. + e2_message e2_subscript_req = generate_e2sm_kpm_subscription_request(ric_action); + packer->handle_message(e2_subscript_req); + } ASSERT_TRUE(e2sm_kpm_iface->action_supported(ric_action)); auto report_service = e2sm_kpm_iface->get_e2sm_report_service(ric_action.ric_action_definition); @@ -181,7 +235,8 @@ TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style1) for (unsigned i = 0; i < nof_meas_data; ++i) { // Push dummy metric measurements. - du_meas_provider->push_measurements_float({1}, {1}, {meas_values[i]}); + du_meas_provider->push_measurements_int({1}, {1}, {meas_int_values[i]}); + du_meas_provider->push_measurements_float({1}, {1}, {meas_real_values[i]}); // Trigger measurement collection. report_service->collect_measurements(); } @@ -202,18 +257,19 @@ TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style1) TESTASSERT_EQ(nof_meas_data, ric_ind_msg.ind_msg_formats.ind_msg_format1().meas_data.size()); for (unsigned i = 0; i < nof_meas_data; ++i) { TESTASSERT_EQ(nof_records, ric_ind_msg.ind_msg_formats.ind_msg_format1().meas_data[i].meas_record.size()); - TESTASSERT_EQ(meas_values[i], - ric_ind_msg.ind_msg_formats.ind_msg_format1().meas_data[i].meas_record[0].real().value); + TESTASSERT_EQ(meas_int_values[i], + ric_ind_msg.ind_msg_formats.ind_msg_format1().meas_data[i].meas_record[0].integer()); + TESTASSERT_EQ(meas_real_values[i], + ric_ind_msg.ind_msg_formats.ind_msg_format1().meas_data[i].meas_record[1].real().value); } -#if PCAP_OUTPUT - e2_message e2_msg = generate_e2_ind_msg(ind_hdr_bytes, ind_msg_bytes); - packer->handle_message(e2_msg); - save_msg_pcap(gw->last_pdu); -#endif + if (g_enable_pcap) { + e2_message e2_msg = generate_e2_ind_msg(ind_hdr_bytes, ind_msg_bytes); + packer->handle_message(e2_msg); + } } -TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style2) +TEST_P(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style2) { std::vector ue_ids = {31}; du_meas_provider->set_ue_ids(ue_ids); @@ -256,12 +312,11 @@ TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style2) action_def_f2.subscript_info.meas_info_list[0].meas_type.set_meas_name().from_string("DRB.UEThpDl"); ric_action = generate_e2sm_kpm_ric_action(action_def); -#if PCAP_OUTPUT - // Save E2 Subscription Request. - e2_message e2_subscript_req = generate_e2sm_kpm_subscription_request(ric_action); - packer->handle_message(e2_subscript_req); - save_msg_pcap(gw->last_pdu); -#endif + if (g_enable_pcap) { + // Save E2 Subscription Request. + e2_message e2_subscript_req = generate_e2sm_kpm_subscription_request(ric_action); + packer->handle_message(e2_subscript_req); + } ASSERT_TRUE(e2sm_kpm_iface->action_supported(ric_action)); auto report_service = e2sm_kpm_iface->get_e2sm_report_service(ric_action.ric_action_definition); @@ -308,14 +363,13 @@ TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style2) } } -#if PCAP_OUTPUT - e2_message e2_msg = generate_e2_ind_msg(ind_hdr_bytes, ind_msg_bytes); - packer->handle_message(e2_msg); - save_msg_pcap(gw->last_pdu); -#endif + if (g_enable_pcap) { + e2_message e2_msg = generate_e2_ind_msg(ind_hdr_bytes, ind_msg_bytes); + packer->handle_message(e2_msg); + } } -TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style3) +TEST_P(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style3) { std::vector ue_ids = {32, 129, 2, 15, 8}; du_meas_provider->set_ue_ids(ue_ids); @@ -406,12 +460,11 @@ TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style3) action_def_f3.meas_cond_list[0].meas_type.set_meas_name().from_string("DRB.UEThpDl"); // Change to a valid metric. ric_action = generate_e2sm_kpm_ric_action(action_def); -#if PCAP_OUTPUT - // Save E2 Subscription Request. - e2_message e2_subscript_req = generate_e2sm_kpm_subscription_request(ric_action); - packer->handle_message(e2_subscript_req); - save_msg_pcap(gw->last_pdu); -#endif + if (g_enable_pcap) { + // Save E2 Subscription Request. + e2_message e2_subscript_req = generate_e2sm_kpm_subscription_request(ric_action); + packer->handle_message(e2_subscript_req); + } ASSERT_TRUE(e2sm_kpm_iface->action_supported(ric_action)); auto report_service = e2sm_kpm_iface->get_e2sm_report_service(ric_action.ric_action_definition); @@ -467,14 +520,13 @@ TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style3) } } -#if PCAP_OUTPUT - e2_message e2_msg = generate_e2_ind_msg(ind_hdr_bytes, ind_msg_bytes); - packer->handle_message(e2_msg); - save_msg_pcap(gw->last_pdu); -#endif + if (g_enable_pcap) { + e2_message e2_msg = generate_e2_ind_msg(ind_hdr_bytes, ind_msg_bytes); + packer->handle_message(e2_msg); + } } -TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style4) +TEST_P(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style4) { std::vector ue_ids = {23, 3, 14, 2, 9}; du_meas_provider->set_ue_ids(ue_ids); @@ -553,12 +605,11 @@ TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style4) subscription_info.meas_info_list[0].meas_type.set_meas_name().from_string("DRB.UEThpDl"); // change to a valid metric ric_action = generate_e2sm_kpm_ric_action(action_def); -#if PCAP_OUTPUT - // Save E2 Subscription Request. - e2_message e2_subscript_req = generate_e2sm_kpm_subscription_request(ric_action); - packer->handle_message(e2_subscript_req); - save_msg_pcap(gw->last_pdu); -#endif + if (g_enable_pcap) { + // Save E2 Subscription Request. + e2_message e2_subscript_req = generate_e2sm_kpm_subscription_request(ric_action); + packer->handle_message(e2_subscript_req); + } ASSERT_TRUE(e2sm_kpm_iface->action_supported(ric_action)); auto report_service = e2sm_kpm_iface->get_e2sm_report_service(ric_action.ric_action_definition); @@ -613,14 +664,13 @@ TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style4) } } -#if PCAP_OUTPUT - e2_message e2_msg = generate_e2_ind_msg(ind_hdr_bytes, ind_msg_bytes); - packer->handle_message(e2_msg); - save_msg_pcap(gw->last_pdu); -#endif + if (g_enable_pcap) { + e2_message e2_msg = generate_e2_ind_msg(ind_hdr_bytes, ind_msg_bytes); + packer->handle_message(e2_msg); + } } -TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style5) +TEST_P(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style5) { std::vector ue_ids = {2, 81, 22, 5, 18}; du_meas_provider->set_ue_ids(ue_ids); @@ -680,12 +730,11 @@ TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style5) subscript_info.meas_info_list[0].meas_type.set_meas_name().from_string("DRB.UEThpDl"); // Change to a valid metric. ric_action = generate_e2sm_kpm_ric_action(action_def); -#if PCAP_OUTPUT - // Save E2 Subscription Request. - e2_message e2_subscript_req = generate_e2sm_kpm_subscription_request(ric_action); - packer->handle_message(e2_subscript_req); - save_msg_pcap(gw->last_pdu); -#endif + if (g_enable_pcap) { + // Save E2 Subscription Request. + e2_message e2_subscript_req = generate_e2sm_kpm_subscription_request(ric_action); + packer->handle_message(e2_subscript_req); + } ASSERT_TRUE(e2sm_kpm_iface->action_supported(ric_action)); auto report_service = e2sm_kpm_iface->get_e2sm_report_service(ric_action.ric_action_definition); @@ -740,9 +789,39 @@ TEST_F(e2sm_kpm_indication, e2sm_kpm_generates_ric_indication_style5) } } -#if PCAP_OUTPUT - e2_message e2_msg = generate_e2_ind_msg(ind_hdr_bytes, ind_msg_bytes); - packer->handle_message(e2_msg); - save_msg_pcap(gw->last_pdu); -#endif + if (g_enable_pcap) { + e2_message e2_msg = generate_e2_ind_msg(ind_hdr_bytes, ind_msg_bytes); + packer->handle_message(e2_msg); + } +} + +INSTANTIATE_TEST_SUITE_P(e2sm_kpm_tests, e2_entity_test_with_pcap, testing::Values(g_pcap)); +INSTANTIATE_TEST_SUITE_P(e2sm_kpm_tests, e2sm_kpm_indication, testing::Values(g_pcap)); + +int main(int argc, char** argv) +{ + // Check for '--enable_pcap' cmd line argument, do not use getopt as it interferes with gtest. + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + if (arg == "--enable_pcap") { + g_enable_pcap = true; + } + } + + srslog::init(); + + std::unique_ptr pcap_exec; + std::unique_ptr pcap_worker; + std::unique_ptr common_pcap_writer; + + if (g_enable_pcap) { + pcap_worker = std::make_unique("pcap_worker", 128); + pcap_exec = std::make_unique(*pcap_worker); + common_pcap_writer = create_e2ap_pcap("/tmp/e2sm_kpm_test.pcap", *pcap_exec); + g_pcap = common_pcap_writer.get(); + } + + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; } diff --git a/tests/unittests/f1ap/common/f1ap_asn1_helpers_test.cpp b/tests/unittests/f1ap/common/f1ap_asn1_helpers_test.cpp index 76efbbe4e4..d3c6b0ce17 100644 --- a/tests/unittests/f1ap/common/f1ap_asn1_helpers_test.cpp +++ b/tests/unittests/f1ap/common/f1ap_asn1_helpers_test.cpp @@ -112,7 +112,7 @@ static uint32_t generate_gtp_teid() TEST(f1ap_asn1_helpers_test, test_up_transport_layer_converter) { - up_transport_layer_info up_tp_layer_info = {transport_layer_address{create_random_ipv4_string()}, + up_transport_layer_info up_tp_layer_info = {transport_layer_address::create_from_string(create_random_ipv4_string()), int_to_gtpu_teid(0x1)}; asn1::f1ap::up_transport_layer_info_c asn1_transport_layer_info; @@ -126,7 +126,7 @@ TEST(f1ap_asn1_helpers_test, test_up_transport_layer_converter) TEST(transport_layer_address_test, ipv6_transport_layer_address_to_asn1) { - up_transport_layer_info up_tp_layer_info = {transport_layer_address{create_random_ipv6_string()}, + up_transport_layer_info up_tp_layer_info = {transport_layer_address::create_from_string(create_random_ipv6_string()), int_to_gtpu_teid(0x1)}; asn1::f1ap::up_transport_layer_info_c asn1_transport_layer_info; diff --git a/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp b/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp index 9912d71726..a98cdcc5e8 100644 --- a/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp +++ b/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp @@ -348,7 +348,8 @@ f1ap_ue_context_modification_request srsran::srs_cu_cp::generate_ue_context_modi drbs_to_be_setup_mod_item.qos_info.flows_mapped_to_drb_list.emplace(uint_to_qos_flow_id(1), flows_mapped_to_drb_item); // ul up tnl info to be setup list - up_transport_layer_info ul_up_tnl_info_item = {transport_layer_address{"127.0.0.1"}, int_to_gtpu_teid(1)}; + up_transport_layer_info ul_up_tnl_info_item = {transport_layer_address::create_from_string("127.0.0.1"), + int_to_gtpu_teid(1)}; drbs_to_be_setup_mod_item.ul_up_tnl_info_to_be_setup_list.push_back(ul_up_tnl_info_item); // rlc mode diff --git a/tests/unittests/f1ap/cu_cp/f1ap_cu_ue_context_release_procedure_test.cpp b/tests/unittests/f1ap/cu_cp/f1ap_cu_ue_context_release_procedure_test.cpp index 55de9aa1ce..3253181cef 100644 --- a/tests/unittests/f1ap/cu_cp/f1ap_cu_ue_context_release_procedure_test.cpp +++ b/tests/unittests/f1ap/cu_cp/f1ap_cu_ue_context_release_procedure_test.cpp @@ -41,7 +41,7 @@ TEST_F(f1ap_cu_test, when_ue_release_command_received_then_procedure_succeeds) test_logger.info("Starting UE Context Release procedure"); f1ap_ue_context_release_command f1ap_ue_ctxt_rel_cmd_msg; f1ap_ue_ctxt_rel_cmd_msg.ue_index = ue_index_t::min; - f1ap_ue_ctxt_rel_cmd_msg.cause = cause_radio_network_t::unspecified; + f1ap_ue_ctxt_rel_cmd_msg.cause = f1ap_cause_radio_network_t::unspecified; // launch F1 UE context release procedure async_task t = f1ap->handle_ue_context_release_command(f1ap_ue_ctxt_rel_cmd_msg); diff --git a/tests/unittests/f1ap/du/f1ap_du_test_helpers.cpp b/tests/unittests/f1ap/du/f1ap_du_test_helpers.cpp index 3e6300aa94..efe7c85a5c 100644 --- a/tests/unittests/f1ap/du/f1ap_du_test_helpers.cpp +++ b/tests/unittests/f1ap/du/f1ap_du_test_helpers.cpp @@ -41,7 +41,7 @@ f1_setup_request_message srsran::srs_du::generate_f1_setup_request_message() ran_params.gnb_du_name = "srsgnb"; ran_params.gnb_du_id = 1; ran_params.rrc_version = 1; - ran_params.du_bind_addr = {"127.0.0.1"}; + ran_params.du_bind_addr = transport_layer_address::create_from_string("127.0.0.1"); du_cell_config cell = config_helpers::make_default_du_cell_config(); ran_params.cells = {cell}; fill_f1_setup_request(request_msg, ran_params); @@ -70,8 +70,8 @@ asn1::f1ap::drbs_to_be_setup_item_s srsran::srs_du::generate_drb_am_setup_item(d drb_info.snssai.sd.from_string("0027db"); drb.rlc_mode.value = rlc_mode_opts::rlc_am; drb.ul_up_tnl_info_to_be_setup_list.resize(1); - auto& gtp_tun = drb.ul_up_tnl_info_to_be_setup_list[0].ul_up_tnl_info.set_gtp_tunnel(); - transport_layer_address addr{"127.0.0.1"}; + auto& gtp_tun = drb.ul_up_tnl_info_to_be_setup_list[0].ul_up_tnl_info.set_gtp_tunnel(); + auto addr = transport_layer_address::create_from_string("127.0.0.1"); gtp_tun.transport_layer_address.from_string(addr.to_bitstring()); gtp_tun.gtp_teid.from_number(1); @@ -130,8 +130,8 @@ asn1::f1ap::drbs_to_be_setup_mod_item_s srsran::srs_du::generate_drb_am_mod_item drb_info.snssai.sd.from_string("0027db"); drb.rlc_mode.value = rlc_mode_opts::rlc_am; drb.ul_up_tnl_info_to_be_setup_list.resize(1); - auto& gtp_tun = drb.ul_up_tnl_info_to_be_setup_list[0].ul_up_tnl_info.set_gtp_tunnel(); - transport_layer_address addr{"127.0.0.1"}; + auto& gtp_tun = drb.ul_up_tnl_info_to_be_setup_list[0].ul_up_tnl_info.set_gtp_tunnel(); + auto addr = transport_layer_address::create_from_string("127.0.0.1"); gtp_tun.transport_layer_address.from_string(addr.to_bitstring()); gtp_tun.gtp_teid.from_number(1); return drb; diff --git a/tests/unittests/f1ap/du/f1ap_du_test_helpers.h b/tests/unittests/f1ap/du/f1ap_du_test_helpers.h index def1e2511f..c20f687607 100644 --- a/tests/unittests/f1ap/du/f1ap_du_test_helpers.h +++ b/tests/unittests/f1ap/du/f1ap_du_test_helpers.h @@ -116,6 +116,8 @@ class dummy_f1ap_du_configurator : public f1ap_du_configurator }); } + async_task request_ue_drb_deactivation(du_ue_index_t ue_index) override { return launch_no_op_task(); } + void notify_reestablishment_of_old_ue(du_ue_index_t new_ue_index, du_ue_index_t old_ue_index) override {} /// \brief Retrieve task scheduler specific to a given UE. 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 17a3bfcfb7..b5a39943fb 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 @@ -49,7 +49,7 @@ class f1ap_du_ue_context_modification_test : public f1ap_du_test drb.drb_id = drb_id; drb.dluptnl_info_list.resize(1); drb.dluptnl_info_list[0].gtp_teid = int_to_gtpu_teid(test_rgen::uniform_int()); - drb.dluptnl_info_list[0].tp_address = transport_layer_address{"127.0.0.1"}; + drb.dluptnl_info_list[0].tp_address = transport_layer_address::create_from_string("127.0.0.1"); } this->f1ap_du_cfg_handler.next_ue_context_update_response.du_to_cu_rrc_container = {0x1, 0x2, 0x3}; 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 508c8f6740..9438f81bf0 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 @@ -84,7 +84,8 @@ class f1ap_du_ue_context_setup_test : public f1ap_du_test du_to_f1_resp.drbs_setup[i].drb_id = uint_to_drb_id(drb_id); du_to_f1_resp.drbs_setup[i].lcid = uint_to_lcid((uint8_t)LCID_MIN_DRB + drb_id); du_to_f1_resp.drbs_setup[i].dluptnl_info_list.resize(1); - du_to_f1_resp.drbs_setup[i].dluptnl_info_list[0] = up_transport_layer_info{{"127.0.0.1"}, int_to_gtpu_teid(1)}; + du_to_f1_resp.drbs_setup[i].dluptnl_info_list[0] = + up_transport_layer_info{transport_layer_address::create_from_string("127.0.0.1"), int_to_gtpu_teid(1)}; } } @@ -257,4 +258,4 @@ TEST_F(f1ap_du_test, f1ap_handles_precanned_ue_context_setup_request_correctly) // F1AP sends RRC Container present in UE CONTEXT SETUP REQUEST via SRB1. ASSERT_EQ(test_ues[ue_index].f1c_bearers[1].rx_sdu_notifier.last_pdu, ue_ctxt_setup_req.pdu.init_msg().value.ue_context_setup_request()->rrc_container); -} \ No newline at end of file +} diff --git a/tests/unittests/f1u/common/f1u_connector_test.cpp b/tests/unittests/f1u/common/f1u_connector_test.cpp index f29b892929..8808ae8f68 100644 --- a/tests/unittests/f1u/common/f1u_connector_test.cpp +++ b/tests/unittests/f1u/common/f1u_connector_test.cpp @@ -88,6 +88,8 @@ class f1u_connector_test : public ::testing::Test timers = timer_factory{timer_mng, ue_worker}; + ue_inactivity_timer = timers.create_timer(); + // set F1-U bearer config config.t_notify = 10; } @@ -101,6 +103,7 @@ class f1u_connector_test : public ::testing::Test timer_manager timer_mng; manual_task_worker ue_worker{128}; timer_factory timers; + unique_timer ue_inactivity_timer; srs_du::f1u_config config; std::unique_ptr f1u_conn; @@ -123,18 +126,19 @@ TEST_F(f1u_connector_test, attach_detach_cu_up_f1u_to_du_f1u) f1u_cu_up_gateway* cu_gw = f1u_conn->get_f1u_cu_up_gateway(); srs_du::f1u_du_gateway* du_gw = f1u_conn->get_f1u_du_gateway(); - up_transport_layer_info ul_tnl{{"127.0.0.1"}, gtpu_teid_t{1}}; - up_transport_layer_info dl_tnl{{"127.0.0.2"}, gtpu_teid_t{2}}; + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; // Create CU TX notifier adapter dummy_f1u_cu_up_rx_sdu_notifier cu_rx; dummy_f1u_cu_up_rx_delivery_notifier cu_delivery; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, ul_tnl, cu_delivery, cu_rx, timers); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, ul_tnl, cu_delivery, cu_rx, ue_worker, timers, ue_inactivity_timer); // Create DU TX notifier adapter and RX handler dummy_f1u_du_rx_sdu_notifier du_rx; - srs_du::f1u_bearer* du_bearer = du_gw->create_du_bearer(0, drb_id_t::drb1, config, dl_tnl, ul_tnl, du_rx, timers); + srs_du::f1u_bearer* du_bearer = + du_gw->create_du_bearer(0, drb_id_t::drb1, config, dl_tnl, ul_tnl, du_rx, timers, ue_worker); // Create CU RX handler and attach it to the DU TX cu_gw->attach_dl_teid(ul_tnl, dl_tnl); @@ -173,18 +177,19 @@ TEST_F(f1u_connector_test, detach_du_f1u_first) f1u_cu_up_gateway* cu_gw = f1u_conn->get_f1u_cu_up_gateway(); srs_du::f1u_du_gateway* du_gw = f1u_conn->get_f1u_du_gateway(); - up_transport_layer_info ul_tnl{{"127.0.0.1"}, gtpu_teid_t{1}}; - up_transport_layer_info dl_tnl{{"127.0.0.2"}, gtpu_teid_t{2}}; + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; // Create CU TX notifier adapter dummy_f1u_cu_up_rx_sdu_notifier cu_rx; dummy_f1u_cu_up_rx_delivery_notifier cu_delivery; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, ul_tnl, cu_delivery, cu_rx, timers); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, ul_tnl, cu_delivery, cu_rx, ue_worker, timers, ue_inactivity_timer); // Create DU TX notifier adapter and RX handler dummy_f1u_du_rx_sdu_notifier du_rx; - srs_du::f1u_bearer* du_bearer = du_gw->create_du_bearer(0, drb_id_t::drb1, config, dl_tnl, ul_tnl, du_rx, timers); + srs_du::f1u_bearer* du_bearer = + du_gw->create_du_bearer(0, drb_id_t::drb1, config, dl_tnl, ul_tnl, du_rx, timers, ue_worker); // Create CU RX handler and attach it to the DU TX cu_gw->attach_dl_teid(ul_tnl, dl_tnl); @@ -223,19 +228,20 @@ TEST_F(f1u_connector_test, update_du_f1u) f1u_cu_up_gateway* cu_gw = f1u_conn->get_f1u_cu_up_gateway(); srs_du::f1u_du_gateway* du_gw = f1u_conn->get_f1u_du_gateway(); - up_transport_layer_info ul_tnl{{"127.0.0.1"}, gtpu_teid_t{1}}; - up_transport_layer_info dl_tnl1{{"127.0.0.2"}, gtpu_teid_t{2}}; - up_transport_layer_info dl_tnl2{{"127.0.0.3"}, gtpu_teid_t{2}}; + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl1{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; + up_transport_layer_info dl_tnl2{transport_layer_address::create_from_string("127.0.0.3"), gtpu_teid_t{2}}; // Create CU TX notifier adapter dummy_f1u_cu_up_rx_sdu_notifier cu_rx; dummy_f1u_cu_up_rx_delivery_notifier cu_delivery; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, ul_tnl, cu_delivery, cu_rx, timers); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, ul_tnl, cu_delivery, cu_rx, ue_worker, timers, ue_inactivity_timer); // Create DU TX notifier adapter and RX handler dummy_f1u_du_rx_sdu_notifier du_rx1; - srs_du::f1u_bearer* du_bearer1 = du_gw->create_du_bearer(0, drb_id_t::drb1, config, dl_tnl1, ul_tnl, du_rx1, timers); + srs_du::f1u_bearer* du_bearer1 = + du_gw->create_du_bearer(0, drb_id_t::drb1, config, dl_tnl1, ul_tnl, du_rx1, timers, ue_worker); // Create CU RX handler and attach it to the DU TX cu_gw->attach_dl_teid(ul_tnl, dl_tnl1); @@ -263,7 +269,8 @@ TEST_F(f1u_connector_test, update_du_f1u) // Attach new DU bearer dummy_f1u_du_rx_sdu_notifier du_rx2; - srs_du::f1u_bearer* du_bearer2 = du_gw->create_du_bearer(0, drb_id_t::drb1, config, dl_tnl2, ul_tnl, du_rx2, timers); + srs_du::f1u_bearer* du_bearer2 = + du_gw->create_du_bearer(0, drb_id_t::drb1, config, dl_tnl2, ul_tnl, du_rx2, timers, ue_worker); // Attach new DL TEID cu_gw->attach_dl_teid(ul_tnl, dl_tnl2); diff --git a/tests/unittests/f1u/cu_up/f1u_cu_up_bearer_test.cpp b/tests/unittests/f1u/cu_up/f1u_cu_up_bearer_test.cpp index 7595a73a61..7392689ae3 100644 --- a/tests/unittests/f1u/cu_up/f1u_cu_up_bearer_test.cpp +++ b/tests/unittests/f1u/cu_up/f1u_cu_up_bearer_test.cpp @@ -82,6 +82,8 @@ class f1u_trx_test class f1u_cu_up_test : public ::testing::Test, public f1u_trx_test { protected: + const unsigned inactivity_time_ms = 100; + void SetUp() override { // init test's logger @@ -94,16 +96,26 @@ class f1u_cu_up_test : public ::testing::Test, public f1u_trx_test // create tester and testee logger.info("Creating F1-U bearer"); - tester = std::make_unique(); - drb_id_t drb_id = drb_id_t::drb1; - f1u = std::make_unique(0, - drb_id, - up_transport_layer_info{{"127.0.0.1"}, gtpu_teid_t{ul_teid_next.value()++}}, - *tester, - *tester, - *tester, - timer_factory{timers, ue_worker}, - *tester); + tester = std::make_unique(); + drb_id_t drb_id = drb_id_t::drb1; + ue_inactivity_timer = ue_timer_factory.create_timer(); + ue_inactivity_timer.set(std::chrono::milliseconds(inactivity_time_ms), [this](timer_id_t) { + // Text + ue_inactivity_triggered = true; + }); + ue_inactivity_timer.run(); + f1u = std::make_unique( + 0, + drb_id, + up_transport_layer_info(transport_layer_address::create_from_string("127.0.0.1"), + gtpu_teid_t{ul_teid_next.value()++}), + *tester, + *tester, + *tester, + ue_timer_factory, + ue_inactivity_timer, + ue_worker, + *tester); } void TearDown() override @@ -112,22 +124,29 @@ class f1u_cu_up_test : public ::testing::Test, public f1u_trx_test srslog::flush(); } - void tick() + void tick(unsigned ms = 1) { - timers.tick(); - ue_worker.run_pending_tasks(); + for (unsigned t = 0; t < ms; t++) { + ue_timer_manager.tick(); + ue_worker.run_pending_tasks(); + } } srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false); - timer_manager timers; manual_task_worker ue_worker{128}; + timer_manager ue_timer_manager; + timer_factory ue_timer_factory{ue_timer_manager, ue_worker}; + unique_timer ue_inactivity_timer; std::unique_ptr tester; std::unique_ptr f1u; gtpu_teid_t ul_teid_next{1234}; + + bool ue_inactivity_triggered = false; }; TEST_F(f1u_cu_up_test, create_and_delete) { + EXPECT_FALSE(ue_inactivity_triggered); EXPECT_TRUE(tester->tx_msg_list.empty()); EXPECT_TRUE(tester->highest_transmitted_pdcp_sn_list.empty()); EXPECT_TRUE(tester->highest_delivered_pdcp_sn_list.empty()); @@ -214,6 +233,9 @@ TEST_F(f1u_cu_up_test, tx_discard) TEST_F(f1u_cu_up_test, tx_pdcp_pdus) { + tick(inactivity_time_ms - 1); + EXPECT_FALSE(ue_inactivity_triggered); + constexpr uint32_t pdu_size = 10; constexpr uint32_t pdcp_sn = 123; @@ -248,10 +270,17 @@ TEST_F(f1u_cu_up_test, tx_pdcp_pdus) tester->tx_msg_list.pop_front(); EXPECT_TRUE(tester->tx_msg_list.empty()); + + // DL PDUs do not restart inactivity timer (only their transmit notif), hence one more tick shall expire the timer + tick(inactivity_time_ms); + EXPECT_TRUE(ue_inactivity_triggered); } TEST_F(f1u_cu_up_test, rx_pdcp_pdus) { + tick(inactivity_time_ms - 1); + EXPECT_FALSE(ue_inactivity_triggered); + constexpr uint32_t pdu_size = 10; constexpr uint32_t pdcp_sn = 123; @@ -260,11 +289,19 @@ TEST_F(f1u_cu_up_test, rx_pdcp_pdus) msg1.t_pdu = byte_buffer_chain{rx_pdcp_pdu1.deep_copy()}; f1u->handle_pdu(std::move(msg1)); + // UL PDUs restart inactivity timer, hence further ticks shall not expire the timer + tick(inactivity_time_ms - 1); + EXPECT_FALSE(ue_inactivity_triggered); + byte_buffer rx_pdcp_pdu2 = create_sdu_byte_buffer(pdu_size, pdcp_sn + 1); nru_ul_message msg2; msg2.t_pdu = byte_buffer_chain{rx_pdcp_pdu2.deep_copy()}; f1u->handle_pdu(std::move(msg2)); + // UL PDUs restart inactivity timer, hence further ticks shall not expire the timer + tick(inactivity_time_ms - 1); + EXPECT_FALSE(ue_inactivity_triggered); + EXPECT_TRUE(tester->tx_msg_list.empty()); EXPECT_TRUE(tester->highest_transmitted_pdcp_sn_list.empty()); EXPECT_TRUE(tester->highest_delivered_pdcp_sn_list.empty()); @@ -280,10 +317,17 @@ TEST_F(f1u_cu_up_test, rx_pdcp_pdus) tester->rx_sdu_list.pop_front(); EXPECT_TRUE(tester->rx_sdu_list.empty()); + + // One more tick shall finally expire the inactivity timer + tick(inactivity_time_ms); + EXPECT_TRUE(ue_inactivity_triggered); } TEST_F(f1u_cu_up_test, rx_transmit_notification) { + tick(inactivity_time_ms - 1); + EXPECT_FALSE(ue_inactivity_triggered); + constexpr uint32_t highest_pdcp_sn = 123; nru_dl_data_delivery_status status1 = {}; @@ -292,12 +336,20 @@ TEST_F(f1u_cu_up_test, rx_transmit_notification) msg1.data_delivery_status = std::move(status1); f1u->handle_pdu(std::move(msg1)); + // Transmit notifications restart inactivity timer, hence further ticks shall not expire the timer + tick(inactivity_time_ms - 1); + EXPECT_FALSE(ue_inactivity_triggered); + nru_dl_data_delivery_status status2 = {}; status2.highest_transmitted_pdcp_sn = highest_pdcp_sn + 1; nru_ul_message msg2 = {}; msg2.data_delivery_status = std::move(status2); f1u->handle_pdu(std::move(msg2)); + // Transmit notifications restart inactivity timer, hence further ticks shall not expire the timer + tick(inactivity_time_ms - 1); + EXPECT_FALSE(ue_inactivity_triggered); + EXPECT_TRUE(tester->tx_msg_list.empty()); EXPECT_TRUE(tester->rx_sdu_list.empty()); EXPECT_TRUE(tester->highest_delivered_pdcp_sn_list.empty()); @@ -315,10 +367,17 @@ TEST_F(f1u_cu_up_test, rx_transmit_notification) tester->highest_transmitted_pdcp_sn_list.pop_front(); EXPECT_TRUE(tester->highest_transmitted_pdcp_sn_list.empty()); + + // One more tick shall finally expire the inactivity timer + tick(inactivity_time_ms); + EXPECT_TRUE(ue_inactivity_triggered); } TEST_F(f1u_cu_up_test, rx_delivery_notification) { + tick(inactivity_time_ms - 1); + EXPECT_FALSE(ue_inactivity_triggered); + constexpr uint32_t highest_pdcp_sn = 123; nru_dl_data_delivery_status status1 = {}; @@ -350,4 +409,8 @@ TEST_F(f1u_cu_up_test, rx_delivery_notification) tester->highest_delivered_pdcp_sn_list.pop_front(); EXPECT_TRUE(tester->highest_delivered_pdcp_sn_list.empty()); + + // Delivery notifications do not restart inactivity timer, hence one more tick shall expire the timer + tick(inactivity_time_ms); + EXPECT_TRUE(ue_inactivity_triggered); } diff --git a/tests/unittests/f1u/du/f1u_du_bearer_test.cpp b/tests/unittests/f1u/du/f1u_du_bearer_test.cpp index d379d4eff7..64827e8a38 100644 --- a/tests/unittests/f1u/du/f1u_du_bearer_test.cpp +++ b/tests/unittests/f1u/du/f1u_du_bearer_test.cpp @@ -81,13 +81,16 @@ class f1u_du_test : public ::testing::Test, public f1u_trx_test f1u_config config = {}; config.t_notify = f1u_ul_notif_time_ms; drb_id_t drb_id = drb_id_t::drb1; - f1u = std::make_unique(0, - drb_id, - up_transport_layer_info{{"127.0.0.1"}, gtpu_teid_t{dl_teid_next.value()++}}, - config, - *tester, - *tester, - timer_factory{timers, ue_worker}); + f1u = std::make_unique( + 0, + drb_id, + up_transport_layer_info{transport_layer_address::create_from_string("127.0.0.1"), + gtpu_teid_t{dl_teid_next.value()++}}, + config, + *tester, + *tester, + timer_factory{timers, ue_worker}, + ue_worker); } void TearDown() override diff --git a/tests/unittests/gtpu/gtpu_tunnel_ngu_test.cpp b/tests/unittests/gtpu/gtpu_tunnel_ngu_test.cpp index 1ea87ca286..6a40451a96 100644 --- a/tests/unittests/gtpu/gtpu_tunnel_ngu_test.cpp +++ b/tests/unittests/gtpu/gtpu_tunnel_ngu_test.cpp @@ -130,7 +130,7 @@ TEST_F(gtpu_tunnel_ngu_test, entity_creation) msg.gtpu_pcap = &dummy_pcap; msg.rx_lower = >pu_rx; msg.tx_upper = >pu_tx; - msg.timers = timers; + msg.ue_dl_timer_factory = timers; gtpu = create_gtpu_tunnel_ngu(msg); ASSERT_NE(gtpu, nullptr); @@ -148,7 +148,7 @@ TEST_F(gtpu_tunnel_ngu_test, rx_sdu) msg.gtpu_pcap = &dummy_pcap; msg.rx_lower = >pu_rx; msg.tx_upper = >pu_tx; - msg.timers = timers; + msg.ue_dl_timer_factory = timers; gtpu = create_gtpu_tunnel_ngu(msg); sockaddr_storage orig_addr = {}; @@ -176,7 +176,7 @@ TEST_F(gtpu_tunnel_ngu_test, tx_pdu) msg.gtpu_pcap = &dummy_pcap; msg.rx_lower = >pu_rx; msg.tx_upper = >pu_tx; - msg.timers = timers; + msg.ue_dl_timer_factory = timers; gtpu = create_gtpu_tunnel_ngu(msg); byte_buffer sdu{gtpu_ping_sdu}; diff --git a/tests/unittests/mac/dl_sch_pdu_assembler_test.cpp b/tests/unittests/mac/dl_sch_pdu_assembler_test.cpp index 78c0c96f8d..18a9f84f4b 100644 --- a/tests/unittests/mac/dl_sch_pdu_assembler_test.cpp +++ b/tests/unittests/mac/dl_sch_pdu_assembler_test.cpp @@ -20,11 +20,13 @@ * */ +#include "lib/mac/mac_dl/cell_dl_harq_buffer_pool.h" #include "lib/mac/mac_dl/dl_sch_pdu_assembler.h" #include "mac_test_helpers.h" #include "srsran/mac/config/mac_config_helpers.h" #include "srsran/ran/pdsch/pdsch_constants.h" #include "srsran/support/bit_encoding.h" +#include "srsran/support/executors/manual_task_worker.h" #include "srsran/support/test_utils.h" #include @@ -152,7 +154,11 @@ class dummy_dl_bearer : public mac_sdu_tx_builder class mac_dl_sch_assembler_tester : public testing::Test { public: - mac_dl_sch_assembler_tester() : ue_mng(rnti_table), dl_bearers(2), dl_sch_enc(ue_mng) + mac_dl_sch_assembler_tester() : + ue_mng(rnti_table), + dl_bearers(2), + harqs(MAX_NOF_PRBS, pdsch_constants::CODEWORD_MAX_NOF_LAYERS, task_worker), + dl_sch_enc(ue_mng, harqs) { srslog::fetch_basic_logger("MAC", true).set_level(srslog::basic_levels::debug); srslog::init(); @@ -171,6 +177,8 @@ class mac_dl_sch_assembler_tester : public testing::Test rnti_table.add_ue(req.crnti, req.ue_index); + harqs.allocate_ue_buffers(req.ue_index, MAX_NOF_HARQS); + mac_dl_ue_context u{req}; ue_mng.add_ue(std::move(u)); } @@ -183,6 +191,8 @@ class mac_dl_sch_assembler_tester : public testing::Test du_rnti_table rnti_table; mac_dl_ue_manager ue_mng; std::vector dl_bearers; + manual_task_worker task_worker{16}; + cell_dl_harq_buffer_pool harqs; dl_sch_pdu_assembler dl_sch_enc; }; diff --git a/tests/unittests/mac/mac_cell_processor_test.cpp b/tests/unittests/mac/mac_cell_processor_test.cpp index 19669210db..de772653d5 100644 --- a/tests/unittests/mac/mac_cell_processor_test.cpp +++ b/tests/unittests/mac/mac_cell_processor_test.cpp @@ -45,7 +45,6 @@ class mac_cell_processor_tester : public ::testing::TestWithParam task_worker, task_worker, task_worker, - task_worker, pcap) { } diff --git a/tests/unittests/mac/mac_ctrl_test_dummies.h b/tests/unittests/mac/mac_ctrl_test_dummies.h index 9dedcf7172..ed2b9aaf0b 100644 --- a/tests/unittests/mac/mac_ctrl_test_dummies.h +++ b/tests/unittests/mac/mac_ctrl_test_dummies.h @@ -116,7 +116,6 @@ class dummy_dl_executor_mapper : public du_high_cell_executor_mapper task_executor& executor(du_cell_index_t cell_index) override { return *execs[cell_index % execs.size()]; } task_executor& slot_ind_executor(du_cell_index_t cell_index) override { return *execs[cell_index % execs.size()]; } - task_executor& error_ind_executor(du_cell_index_t cell_index) override { return *execs[cell_index % execs.size()]; } std::vector execs; }; diff --git a/tests/unittests/ngap/ngap_nas_message_test.cpp b/tests/unittests/ngap/ngap_nas_message_test.cpp index d933d84ed6..0a83e7da7c 100644 --- a/tests/unittests/ngap/ngap_nas_message_test.cpp +++ b/tests/unittests/ngap/ngap_nas_message_test.cpp @@ -22,7 +22,7 @@ #include "ngap_test_helpers.h" #include "srsran/asn1/ngap/ngap_pdu_contents.h" -#include "srsran/ran/cause.h" +#include "srsran/ran/cause/ngap_cause.h" #include "srsran/support/async/async_test_utils.h" #include "srsran/support/test_utils.h" #include @@ -95,14 +95,14 @@ TEST_F(ngap_nas_message_routine_test, when_initial_context_setup_request_is_not_ ASSERT_EQ(ngap->get_nof_ues(), 1); // tick timers - // Status: NGAP does not receive new Initial Context Setup Request until ue_context_setup_timer has ended. - for (unsigned msec_elapsed = 0; msec_elapsed < cfg.ue_context_setup_timeout.count() * 1000; ++msec_elapsed) { + // Status: NGAP does not receive new Initial Context Setup Request until pdu_session_setup_timer has ended. + for (unsigned msec_elapsed = 0; msec_elapsed < cfg.pdu_session_setup_timeout.count() * 1000; ++msec_elapsed) { this->tick(); } // check that UE release was requested ASSERT_NE(du_processor_notifier->last_command.ue_index, ue_index_t::invalid); - ASSERT_EQ(du_processor_notifier->last_command.cause, cause_t{cause_nas_t::unspecified}); + ASSERT_EQ(du_processor_notifier->last_command.cause, ngap_cause_t{ngap_cause_radio_network_t::unspecified}); } /// Test DL NAS transport handling diff --git a/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp b/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp index 64da15c7f9..88bfe2efb3 100644 --- a/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp +++ b/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp @@ -107,6 +107,33 @@ class ngap_pdu_session_resource_setup_procedure_test : public ngap_test } }; +/// Test missing PDU Session Resource Setup Request +TEST_F(ngap_pdu_session_resource_setup_procedure_test, + when_pdu_session_resource_setup_request_is_not_received_then_ue_release_is_requested) +{ + ASSERT_EQ(ngap->get_nof_ues(), 0); + + // Test preamble + this->start_procedure(); + + // check that initial context setup request was received to the AMF and that UE object has been created + ASSERT_EQ(msg_notifier.last_ngap_msgs.back().pdu.type().value, + asn1::ngap::ngap_pdu_c::types_opts::successful_outcome); + ASSERT_EQ(msg_notifier.last_ngap_msgs.back().pdu.successful_outcome().value.type(), + asn1::ngap::ngap_elem_procs_o::successful_outcome_c::types_opts::init_context_setup_resp); + ASSERT_EQ(ngap->get_nof_ues(), 1); + + // tick timers + // Status: NGAP does not receive new PDU Session Resource Setup Request until pdu_session_setup_timer has ended. + for (unsigned msec_elapsed = 0; msec_elapsed < cfg.pdu_session_setup_timeout.count() * 1000; ++msec_elapsed) { + this->tick(); + } + + // check that UE release was requested + ASSERT_EQ(msg_notifier.last_ngap_msgs.back().pdu.init_msg().value.type(), + asn1::ngap::ngap_elem_procs_o::init_msg_c::types_opts::ue_context_release_request); +} + /// Test valid PDU Session Resource Setup Request TEST_F(ngap_pdu_session_resource_setup_procedure_test, when_valid_pdu_session_resource_setup_request_received_then_pdu_session_setup_succeeds) diff --git a/tests/unittests/ngap/ngap_test_helpers.cpp b/tests/unittests/ngap/ngap_test_helpers.cpp index 2d91d73c58..4db179cc4d 100644 --- a/tests/unittests/ngap/ngap_test_helpers.cpp +++ b/tests/unittests/ngap/ngap_test_helpers.cpp @@ -46,7 +46,7 @@ ngap_test::ngap_test() : ngap_ue_task_scheduler(timers, ctrl_worker) s_nssai_t slice_cfg; slice_cfg.sst = 1; cfg.slice_configurations.push_back(slice_cfg); - cfg.ue_context_setup_timeout = std::chrono::seconds(2); + cfg.pdu_session_setup_timeout = std::chrono::seconds(2); ngap = create_ngap( cfg, ngap_ue_creation_notifier, cu_cp_paging_notifier, ngap_ue_task_scheduler, ue_mng, msg_notifier, ctrl_worker); diff --git a/tests/unittests/ngap/ngap_test_messages.cpp b/tests/unittests/ngap/ngap_test_messages.cpp index 500ef4e507..2c2a351100 100644 --- a/tests/unittests/ngap/ngap_test_messages.cpp +++ b/tests/unittests/ngap/ngap_test_messages.cpp @@ -159,7 +159,8 @@ cu_cp_initial_ue_message srsran::srs_cu_cp::generate_initial_ue_message(ue_index { cu_cp_initial_ue_message msg = {}; msg.ue_index = ue_index; - msg.nas_pdu.resize(nas_pdu_len); + bool ret = msg.nas_pdu.resize(nas_pdu_len); + (void)ret; msg.establishment_cause = static_cast(rrc_establishment_cause_opts::mo_sig); msg.user_location_info.nr_cgi.plmn_hex = "00f110"; msg.user_location_info.nr_cgi.nci = 6576; @@ -181,7 +182,8 @@ ngap_message srsran::srs_cu_cp::generate_downlink_nas_transport_message(amf_ue_i dl_nas_transport_msg->amf_ue_ngap_id = amf_ue_id_to_uint(amf_ue_id); dl_nas_transport_msg->ran_ue_ngap_id = ran_ue_id_to_uint(ran_ue_id); if (nas_pdu.empty()) { - dl_nas_transport_msg->nas_pdu.resize(nas_pdu_len); + bool ret = dl_nas_transport_msg->nas_pdu.resize(nas_pdu_len); + (void)ret; } else { dl_nas_transport_msg->nas_pdu = nas_pdu.copy(); } @@ -193,7 +195,8 @@ cu_cp_ul_nas_transport srsran::srs_cu_cp::generate_ul_nas_transport_message(ue_i { cu_cp_ul_nas_transport ul_nas_transport = {}; ul_nas_transport.ue_index = ue_index; - ul_nas_transport.nas_pdu.resize(nas_pdu_len); + bool ret = ul_nas_transport.nas_pdu.resize(nas_pdu_len); + (void)ret; ul_nas_transport.user_location_info.nr_cgi.plmn_hex = "00f110"; ul_nas_transport.user_location_info.nr_cgi.nci = 6576; ul_nas_transport.user_location_info.tai.plmn_id = "00f110"; @@ -212,7 +215,8 @@ ngap_message srsran::srs_cu_cp::generate_uplink_nas_transport_message(amf_ue_id_ auto& ul_nas_transport_msg = ul_nas_transport.pdu.init_msg().value.ul_nas_transport(); ul_nas_transport_msg->amf_ue_ngap_id = amf_ue_id_to_uint(amf_ue_id); ul_nas_transport_msg->ran_ue_ngap_id = ran_ue_id_to_uint(ran_ue_id); - ul_nas_transport_msg->nas_pdu.resize(nas_pdu_len); + bool ret = ul_nas_transport_msg->nas_pdu.resize(nas_pdu_len); + (void)ret; auto& user_loc_info_nr = ul_nas_transport_msg->user_location_info.set_user_location_info_nr(); user_loc_info_nr.nr_cgi.plmn_id.from_string("00f110"); @@ -463,7 +467,8 @@ srsran::srs_cu_cp::generate_cu_cp_pdu_session_resource_setup_response(pdu_sessio auto& dlqos_flow_per_tnl_info = pdu_session_setup_response_item.pdu_session_resource_setup_response_transfer.dlqos_flow_per_tnl_info; - dlqos_flow_per_tnl_info.up_tp_layer_info = {transport_layer_address{"0.0.0.0"}, int_to_gtpu_teid(0)}; + dlqos_flow_per_tnl_info.up_tp_layer_info = {transport_layer_address::create_from_string("0.0.0.0"), + int_to_gtpu_teid(0)}; cu_cp_associated_qos_flow assoc_qos_flow; assoc_qos_flow.qos_flow_id = uint_to_qos_flow_id(1); dlqos_flow_per_tnl_info.associated_qos_flow_list.emplace(uint_to_qos_flow_id(1), assoc_qos_flow); @@ -628,7 +633,8 @@ srsran::srs_cu_cp::generate_cu_cp_pdu_session_resource_modify_response(pdu_sessi pdu_session_modify_response_item.pdu_session_id = pdu_session_id; cu_cp_qos_flow_per_tnl_information qos_flow_per_tnl_info; - qos_flow_per_tnl_info.up_tp_layer_info = {transport_layer_address{"127.0.0.1"}, int_to_gtpu_teid(1)}; + qos_flow_per_tnl_info.up_tp_layer_info = {transport_layer_address::create_from_string("127.0.0.1"), + int_to_gtpu_teid(1)}; cu_cp_associated_qos_flow assoc_qos_flow; assoc_qos_flow.qos_flow_id = qos_flow_id; @@ -881,4 +887,4 @@ ngap_message srsran::srs_cu_cp::generate_valid_handover_command(amf_ue_id_t amf_ ho_cmd->target_to_source_transparent_container = pack_into_pdu(transparent_container); return ngap_msg; -} \ No newline at end of file +} diff --git a/tests/unittests/ngap/ngap_ue_context_management_procedure_test.cpp b/tests/unittests/ngap/ngap_ue_context_management_procedure_test.cpp index b83a2aaf47..25355b593a 100644 --- a/tests/unittests/ngap/ngap_ue_context_management_procedure_test.cpp +++ b/tests/unittests/ngap/ngap_ue_context_management_procedure_test.cpp @@ -211,6 +211,26 @@ TEST_F(ngap_ue_context_management_procedure_test, ASSERT_TRUE(was_pdu_session_resource_setup_unsuccessful()); } +/// Test successful UE context release +TEST_F( + ngap_ue_context_management_procedure_test, + when_ue_context_release_command_as_first_message_from_core_received_then_ue_is_released_and_release_complete_is_sent) +{ + // Test preamble + ue_index_t ue_index = create_ue(); + auto& ue = test_ues.at(ue_index); + + ASSERT_TRUE(was_ue_added()); + + // Inject UE Context Release Command + ngap_message ue_context_release_cmd = + generate_valid_ue_context_release_command_with_ue_ngap_id_pair(amf_ue_id_t(1), ue.ran_ue_id.value()); + ngap->handle_message(ue_context_release_cmd); + + ASSERT_TRUE(was_ue_context_release_complete_sent()); + ASSERT_TRUE(was_ue_removed()); +} + /// Test successful UE context release TEST_F(ngap_ue_context_management_procedure_test, when_ue_context_release_command_with_amf_ue_ngap_id_received_then_ue_is_released_and_release_complete_is_sent) diff --git a/tests/unittests/ngap/ngap_validators_test.cpp b/tests/unittests/ngap/ngap_validators_test.cpp index 10c26a3aee..7f83218ee0 100644 --- a/tests/unittests/ngap/ngap_validators_test.cpp +++ b/tests/unittests/ngap/ngap_validators_test.cpp @@ -108,7 +108,7 @@ class ngap_validator_test : public ngap_test asn1::ngap::pdu_session_res_setup_request_transfer_s asn1_setup_req_transfer; asn1_setup_req_transfer->ul_ngu_up_tnl_info.set_gtp_tunnel(); - transport_layer_address addr{"127.0.0.1"}; + auto addr = transport_layer_address::create_from_string("127.0.0.1"); asn1_setup_req_transfer->ul_ngu_up_tnl_info.gtp_tunnel().transport_layer_address.from_string(addr.to_bitstring()); asn1_setup_req_transfer->ul_ngu_up_tnl_info.gtp_tunnel().gtp_teid.from_number(1); diff --git a/tests/unittests/ngap/test_helpers.h b/tests/unittests/ngap/test_helpers.h index cca17eb595..f9b1fa2d6d 100644 --- a/tests/unittests/ngap/test_helpers.h +++ b/tests/unittests/ngap/test_helpers.h @@ -209,7 +209,7 @@ class dummy_ngap_du_processor_notifier : public ngap_du_processor_control_notifi if (last_request.pdu_session_res_setup_items.empty()) { cu_cp_pdu_session_res_setup_failed_item failed_item; failed_item.pdu_session_id = uint_to_pdu_session_id(1); - failed_item.unsuccessful_transfer.cause = cause_radio_network_t::unspecified; + failed_item.unsuccessful_transfer.cause = ngap_cause_radio_network_t::unspecified; res.pdu_session_res_failed_to_setup_items.emplace(failed_item.pdu_session_id, failed_item); } else { res = generate_cu_cp_pdu_session_resource_setup_response(uint_to_pdu_session_id(1)); diff --git a/tests/unittests/ofh/ethernet/ethernet_frame_pool_test.cpp b/tests/unittests/ofh/ethernet/ethernet_frame_pool_test.cpp index 35492965af..a582a57bcf 100644 --- a/tests/unittests/ofh/ethernet/ethernet_frame_pool_test.cpp +++ b/tests/unittests/ofh/ethernet/ethernet_frame_pool_test.cpp @@ -495,9 +495,15 @@ TEST_P(EthFramePoolFixture, clearing_full_pool_should_allow_adding_more_data) } pool.push_frame_buffers(ctx, frame_buffers); } - // Increase slot by pool size, clear stale buffers in the pool and try to get buffers again. + // Increase slot by pool size, clear stale buffers in the pool. auto wrapped_slot = slot + pool_size_slots; pool.clear_downlink_slot(wrapped_slot, logger); + + // Verify the pool is empty in the given slot. + auto rd_buffers = pool.read_frame_buffers(ctx); + ASSERT_TRUE(rd_buffers.empty()) << "No buffers are expected to be read from the pool after clearing it"; + + // Try to get buffers again and make sure they are available. for (unsigned i = 0; i != nof_requested_buffers; ++i) { span frame_buffers = pool.get_frame_buffers(ctx); ASSERT_TRUE(!frame_buffers.empty()) << "Non-empty span of buffers expected"; @@ -523,8 +529,14 @@ TEST_P(EthFramePoolFixture, clearing_full_pool_should_allow_adding_more_data) } pool.push_frame_buffers(ctx, frame_buffers); } - // Clear full slot in the pool and try to get buffers again. + // Clear full slot in the pool. pool.clear_uplink_slot(wrapped_slot, logger); + + // Verify the pool is empty in the given slot. + rd_buffers = pool.read_frame_buffers(ctx); + ASSERT_TRUE(rd_buffers.empty()) << "No buffers are expected to be read from the pool after clearing it"; + + // Try to get buffers again and make sure they are available. for (unsigned i = 0; i != nof_requested_buffers; ++i) { span frame_buffers = pool.get_frame_buffers(ctx); ASSERT_TRUE(!frame_buffers.empty()) << "Non-empty span of buffers expected"; diff --git a/tests/unittests/ofh/receiver/ofh_rx_window_checker_test.cpp b/tests/unittests/ofh/receiver/ofh_rx_window_checker_test.cpp index 2b9c6f30f7..515d443f4a 100644 --- a/tests/unittests/ofh/receiver/ofh_rx_window_checker_test.cpp +++ b/tests/unittests/ofh/receiver/ofh_rx_window_checker_test.cpp @@ -29,14 +29,15 @@ using namespace std::chrono_literals; TEST(ofh_rx_window_checker, on_time_packet_counts_one_packet) { - du_rx_window_timing_parameters time_params = {std::chrono::microseconds(300), std::chrono::microseconds(50)}; - unsigned nof_symbols_per_slot = 14; - subcarrier_spacing scs = subcarrier_spacing::kHz30; - - rx_window_checker rx_window( - srslog::fetch_basic_logger("TEST"), - time_params, + unsigned nof_symbols_per_slot = 14; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + std::chrono::duration symbol_duration( std::chrono::duration(1e6 / (nof_symbols_per_slot * get_nof_slots_per_subframe(scs)))); + auto& logger = srslog::fetch_basic_logger("TEST"); + logger.set_level(srslog::basic_levels::info); + + // Create window checker with timing parameters corresponding to Ta4_min=50us, Ta4_max=300us. + rx_window_checker rx_window(logger, {2, 9}, symbol_duration); // Create the OTA notification. slot_symbol_point ota_slot({1, 1, 1, 1}, 7, 14); @@ -51,15 +52,17 @@ TEST(ofh_rx_window_checker, on_time_packet_counts_one_packet) TEST(ofh_rx_window_checker, packet_on_the_window_start_count_as_valid) { - du_rx_window_timing_parameters time_params = {std::chrono::microseconds(300), std::chrono::microseconds(50)}; - unsigned nof_symbols_per_slot = 14; - subcarrier_spacing scs = subcarrier_spacing::kHz30; + unsigned nof_symbols_per_slot = 14; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + auto& logger = srslog::fetch_basic_logger("TEST"); + logger.set_level(srslog::basic_levels::info); - rx_window_checker rx_window( - srslog::fetch_basic_logger("TEST"), - time_params, + std::chrono::duration symbol_duration( std::chrono::duration(1e6 / (nof_symbols_per_slot * get_nof_slots_per_subframe(scs)))); + // Create window checker with timing parameters corresponding to Ta4_min=50us, Ta4_max=300us. + rx_window_checker rx_window(logger, {2, 9}, symbol_duration); + // Create the OTA notification. slot_symbol_point ota_slot({1, 1, 1, 1}, 7, 14); rx_window.on_new_symbol(ota_slot); @@ -73,15 +76,17 @@ TEST(ofh_rx_window_checker, packet_on_the_window_start_count_as_valid) TEST(ofh_rx_window_checker, packet_on_the_window_end_count_as_valid) { - du_rx_window_timing_parameters time_params = {std::chrono::microseconds(510), std::chrono::microseconds(50)}; - unsigned nof_symbols_per_slot = 14; - subcarrier_spacing scs = subcarrier_spacing::kHz30; + unsigned nof_symbols_per_slot = 14; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + auto& logger = srslog::fetch_basic_logger("TEST"); + logger.set_level(srslog::basic_levels::info); - rx_window_checker rx_window( - srslog::fetch_basic_logger("TEST"), - time_params, + std::chrono::duration symbol_duration( std::chrono::duration(1e6 / (nof_symbols_per_slot * get_nof_slots_per_subframe(scs)))); + // Create window checker with timing parameters corresponding to Ta4_min=50us, Ta4_max=510us. + rx_window_checker rx_window(logger, {2, 15}, symbol_duration); + // Create the OTA notification. slot_symbol_point ota_slot({1, 1, 1, 1}, 7, 14); rx_window.on_new_symbol(ota_slot); @@ -95,15 +100,17 @@ TEST(ofh_rx_window_checker, packet_on_the_window_end_count_as_valid) TEST(ofh_rx_window_checker, early_packet_counts_one_packet) { - du_rx_window_timing_parameters time_params = {std::chrono::microseconds(300), std::chrono::microseconds(80)}; - unsigned nof_symbols_per_slot = 14; - subcarrier_spacing scs = subcarrier_spacing::kHz30; + unsigned nof_symbols_per_slot = 14; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + auto& logger = srslog::fetch_basic_logger("TEST"); + logger.set_level(srslog::basic_levels::info); - rx_window_checker rx_window( - srslog::fetch_basic_logger("TEST"), - time_params, + std::chrono::duration symbol_duration( std::chrono::duration(1e6 / (nof_symbols_per_slot * get_nof_slots_per_subframe(scs)))); + // Create window checker with timing parameters corresponding to Ta4_min=80us, Ta4_max=300us. + rx_window_checker rx_window(logger, {3, 9}, symbol_duration); + // Create the OTA notification. slot_symbol_point ota_slot({1, 1, 1, 1}, 7, 14); rx_window.on_new_symbol(ota_slot); @@ -117,15 +124,17 @@ TEST(ofh_rx_window_checker, early_packet_counts_one_packet) TEST(ofh_rx_window_checker, late_packet_counts_one_packet) { - du_rx_window_timing_parameters time_params = {std::chrono::microseconds(300), std::chrono::microseconds(50)}; - unsigned nof_symbols_per_slot = 14; - subcarrier_spacing scs = subcarrier_spacing::kHz30; + unsigned nof_symbols_per_slot = 14; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + auto& logger = srslog::fetch_basic_logger("TEST"); + logger.set_level(srslog::basic_levels::info); - rx_window_checker rx_window( - srslog::fetch_basic_logger("TEST"), - time_params, + std::chrono::duration symbol_duration( std::chrono::duration(1e6 / (nof_symbols_per_slot * get_nof_slots_per_subframe(scs)))); + // Create window checker with timing parameters corresponding to Ta4_min=50us, Ta4_max=300us. + rx_window_checker rx_window(logger, {2, 9}, symbol_duration); + // Create the OTA notification. slot_symbol_point ota_slot({1, 1, 1, 1}, 7, 14); rx_window.on_new_symbol(ota_slot); @@ -139,15 +148,17 @@ TEST(ofh_rx_window_checker, late_packet_counts_one_packet) TEST(ofh_rx_window_checker, window_change_slot_works) { - du_rx_window_timing_parameters time_params = {std::chrono::microseconds(300), std::chrono::microseconds(50)}; - unsigned nof_symbols_per_slot = 14; - subcarrier_spacing scs = subcarrier_spacing::kHz30; + unsigned nof_symbols_per_slot = 14; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + auto& logger = srslog::fetch_basic_logger("TEST"); + logger.set_level(srslog::basic_levels::info); - rx_window_checker rx_window( - srslog::fetch_basic_logger("TEST"), - time_params, + std::chrono::duration symbol_duration( std::chrono::duration(1e6 / (nof_symbols_per_slot * get_nof_slots_per_subframe(scs)))); + // Create window checker with timing parameters corresponding to Ta4_min=50us, Ta4_max=300us. + rx_window_checker rx_window(logger, {2, 9}, symbol_duration); + // Create the OTA notification. slot_symbol_point ota_slot({1, 1, 1, 0}, 1, 14); rx_window.on_new_symbol(ota_slot); @@ -161,15 +172,17 @@ TEST(ofh_rx_window_checker, window_change_slot_works) TEST(ofh_rx_window_checker, window_change_sfn_works) { - du_rx_window_timing_parameters time_params = {std::chrono::microseconds(300), std::chrono::microseconds(50)}; - unsigned nof_symbols_per_slot = 14; - subcarrier_spacing scs = subcarrier_spacing::kHz30; + unsigned nof_symbols_per_slot = 14; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + auto& logger = srslog::fetch_basic_logger("TEST"); + logger.set_level(srslog::basic_levels::info); - rx_window_checker rx_window( - srslog::fetch_basic_logger("TEST"), - time_params, + std::chrono::duration symbol_duration( std::chrono::duration(1e6 / (nof_symbols_per_slot * get_nof_slots_per_subframe(scs)))); + // Create window checker with timing parameters corresponding to Ta4_min=50us, Ta4_max=300us. + rx_window_checker rx_window(logger, {2, 9}, symbol_duration); + // Create the OTA notification. slot_symbol_point ota_slot({1, 1, 0, 0}, 1, 14); rx_window.on_new_symbol(ota_slot); @@ -183,15 +196,17 @@ TEST(ofh_rx_window_checker, window_change_sfn_works) TEST(ofh_rx_window_checker, window_change_sfn_byte_works) { - du_rx_window_timing_parameters time_params = {std::chrono::microseconds(300), std::chrono::microseconds(50)}; - unsigned nof_symbols_per_slot = 14; - subcarrier_spacing scs = subcarrier_spacing::kHz30; + unsigned nof_symbols_per_slot = 14; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + auto& logger = srslog::fetch_basic_logger("TEST"); + logger.set_level(srslog::basic_levels::info); - rx_window_checker rx_window( - srslog::fetch_basic_logger("TEST"), - time_params, + std::chrono::duration symbol_duration( std::chrono::duration(1e6 / (nof_symbols_per_slot * get_nof_slots_per_subframe(scs)))); + // Create window checker with timing parameters corresponding to Ta4_min=50us, Ta4_max=300us. + rx_window_checker rx_window(logger, {2, 9}, symbol_duration); + // Create the OTA notification. slot_symbol_point ota_slot({1, 0, 0, 0}, 1, 14); rx_window.on_new_symbol(ota_slot); @@ -205,15 +220,17 @@ TEST(ofh_rx_window_checker, window_change_sfn_byte_works) TEST(ofh_rx_window_checker, window_change_sfn_byte_and_message_is_in_sfn_0) { - du_rx_window_timing_parameters time_params = {std::chrono::microseconds(300), std::chrono::microseconds(50)}; - unsigned nof_symbols_per_slot = 14; - subcarrier_spacing scs = subcarrier_spacing::kHz30; + unsigned nof_symbols_per_slot = 14; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + auto& logger = srslog::fetch_basic_logger("TEST"); + logger.set_level(srslog::basic_levels::info); - rx_window_checker rx_window( - srslog::fetch_basic_logger("TEST"), - time_params, + std::chrono::duration symbol_duration( std::chrono::duration(1e6 / (nof_symbols_per_slot * get_nof_slots_per_subframe(scs)))); + // Create window checker with timing parameters corresponding to Ta4_min=50us, Ta4_max=300us. + rx_window_checker rx_window(logger, {2, 9}, symbol_duration); + // Create the OTA notification. slot_symbol_point ota_slot({1, 0, 0, 0}, 3, 14); rx_window.on_new_symbol(ota_slot); diff --git a/tests/unittests/ofh/transmitter/ofh_downlink_handler_impl_test.cpp b/tests/unittests/ofh/transmitter/ofh_downlink_handler_impl_test.cpp index 50f966aa93..a2032b6150 100644 --- a/tests/unittests/ofh/transmitter/ofh_downlink_handler_impl_test.cpp +++ b/tests/unittests/ofh/transmitter/ofh_downlink_handler_impl_test.cpp @@ -72,13 +72,11 @@ static downlink_handler_impl_config generate_default_config() config.cp = cyclic_prefix::NORMAL; config.scs = subcarrier_spacing::kHz30; config.dl_processing_time = std::chrono::milliseconds(400); - config.tx_timing_params = {std::chrono::milliseconds(500), - std::chrono::milliseconds(200), - std::chrono::milliseconds(300), - std::chrono::milliseconds(150), - std::chrono::milliseconds(250), - std::chrono::milliseconds(100)}; - + // Transmission timing parameters corresponding to: + // T1a_max_cp_dl=500us, T1a_min_cp_dl=200us, + // T1a_max_cp_ul=300us, T1a_min_cp_ul=150us, + // T1a_max_up=250us, T1a_min_up=100us. + config.tx_timing_params = {13, 6, 8, 5, 6, 3}; return config; } diff --git a/tests/unittests/phy/upper/channel_processors/pdsch_processor_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pdsch_processor_vectortest.cpp index 90a91848c0..b573edc0d4 100644 --- a/tests/unittests/phy/upper/channel_processors/pdsch_processor_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdsch_processor_vectortest.cpp @@ -231,7 +231,7 @@ class PdschProcessorFixture : public ::testing::TestWithParam create_hw_accelera // Interfacing to a shared external HARQ buffer context repository. unsigned nof_cbs = MAX_NOF_SEGMENTS; - unsigned acc100_ext_harq_buff_size = bbdev_accelerator->get_harq_buff_size().value(); + uint64_t acc100_ext_harq_buff_size = bbdev_accelerator->get_harq_buff_size_bytes(); std::shared_ptr harq_buffer_context = create_ext_harq_buffer_context_repository(nof_cbs, acc100_ext_harq_buff_size, test_harq); TESTASSERT(harq_buffer_context); diff --git a/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_unittest.cpp b/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_unittest.cpp index e87393bb07..8c51b240f5 100644 --- a/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_unittest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_unittest.cpp @@ -352,6 +352,7 @@ TEST_P(PuschProcessorFixture, PuschProcessorUnittest) ulsch_config.dmrs_symbol_mask = pdu.dmrs_symbol_mask; ulsch_config.nof_cdm_groups_without_data = pdu.nof_cdm_groups_without_data; ulsch_config.nof_layers = pdu.nof_tx_layers; + ulsch_config.contains_dc = false; ulsch_information ulsch_info = get_ulsch_information(ulsch_config); // Calculate the number of LLR. diff --git a/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_vectortest.cpp index 5f3bc86412..41c8f0a2ff 100644 --- a/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_vectortest.cpp @@ -135,7 +135,7 @@ class PuschProcessorFixture : public ::testing::TestWithParamget_harq_buff_size().value(); + uint64_t acc100_ext_harq_buff_size = bbdev_accelerator->get_harq_buff_size_bytes(); std::shared_ptr harq_buffer_context = create_ext_harq_buffer_context_repository(nof_cbs, acc100_ext_harq_buff_size, false); if (!harq_buffer_context) { diff --git a/tests/unittests/ran/pusch/pusch_tpmi_select_test.cpp b/tests/unittests/ran/pusch/pusch_tpmi_select_test.cpp index 9d38f86d9a..2e70ddaa88 100644 --- a/tests/unittests/ran/pusch/pusch_tpmi_select_test.cpp +++ b/tests/unittests/ran/pusch/pusch_tpmi_select_test.cpp @@ -121,7 +121,12 @@ TEST_P(PuschTpmiSelectFixture, VectorTest) // Get combined parameter. const test_case_t& test_case = GetParam(); - if ((test_case.channel_matrix.get_nof_rx_ports() > 1) || (test_case.channel_matrix.get_nof_tx_ports() > 2)) { + unsigned nof_tx_ports = test_case.channel_matrix.get_nof_tx_ports(); + unsigned nof_rx_ports = test_case.channel_matrix.get_nof_rx_ports(); + unsigned max_nof_layers = std::min(nof_tx_ports, nof_rx_ports); + + // Only one layer is currently supported. + if (max_nof_layers > 1) { GTEST_SKIP(); } diff --git a/tests/unittests/ran/pusch/pusch_tpmi_select_test_data.h b/tests/unittests/ran/pusch/pusch_tpmi_select_test_data.h index 143aa46216..33fa15c9ad 100644 --- a/tests/unittests/ran/pusch/pusch_tpmi_select_test_data.h +++ b/tests/unittests/ran/pusch/pusch_tpmi_select_test_data.h @@ -22,7 +22,7 @@ #pragma once -// This file was generated using the following MATLAB class on 15-02-2024 (seed 0): +// This file was generated using the following MATLAB class on 20-02-2024 (seed 0): // + "srsTPMISelectUnittest.m" #include "srsran/ran/pusch/pusch_tpmi_select.h" diff --git a/tests/unittests/rlc/rlc_um_test.cpp b/tests/unittests/rlc/rlc_um_test.cpp index 41b41ad540..6ae85b44a4 100644 --- a/tests/unittests/rlc/rlc_um_test.cpp +++ b/tests/unittests/rlc/rlc_um_test.cpp @@ -206,7 +206,7 @@ class rlc_um_test : public ::testing::Test, public ::testing::WithParamInterface if (i != hold_back_pdu) { byte_buffer pdu; for (const byte_buffer_slice& slice : pdu_bufs[i].slices()) { - pdu.append(slice); + EXPECT_TRUE(pdu.append(slice)); } rlc2_rx_lower->handle_pdu(std::move(pdu)); } @@ -219,7 +219,7 @@ class rlc_um_test : public ::testing::Test, public ::testing::WithParamInterface { byte_buffer pdu; for (const byte_buffer_slice& slice : pdu_bufs[hold_back_pdu].slices()) { - pdu.append(slice); + EXPECT_TRUE(pdu.append(slice)); } rlc2_rx_lower->handle_pdu(std::move(pdu)); } @@ -376,7 +376,7 @@ TEST_P(rlc_um_test, tx_without_segmentation) for (uint32_t i = 0; i < num_pdus; i++) { byte_buffer pdu; for (const byte_buffer_slice& slice : pdu_bufs[i].slices()) { - pdu.append(slice); + EXPECT_TRUE(pdu.append(slice)); } rlc2_rx_lower->handle_pdu(std::move(pdu)); } @@ -456,7 +456,7 @@ TEST_P(rlc_um_test, tx_with_segmentation) for (uint32_t i = 0; i < num_pdus; i++) { byte_buffer pdu; for (const byte_buffer_slice& slice : pdu_bufs[i].slices()) { - pdu.append(slice); + EXPECT_TRUE(pdu.append(slice)); } rlc2_rx_lower->handle_pdu(std::move(pdu)); } @@ -740,7 +740,7 @@ TEST_P(rlc_um_test, tx_with_segmentation_reverse_rx) for (uint32_t i = 0; i < num_pdus; i++) { byte_buffer pdu; for (const byte_buffer_slice& slice : pdu_bufs[num_pdus - i - 1].slices()) { - pdu.append(slice); + EXPECT_TRUE(pdu.append(slice)); } rlc2_rx_lower->handle_pdu(std::move(pdu)); } @@ -810,7 +810,7 @@ TEST_P(rlc_um_test, tx_multiple_SDUs_with_segmentation) if (i != 1 && i != 6) { byte_buffer pdu; for (const byte_buffer_slice& slice : pdu_bufs[i].slices()) { - pdu.append(slice); + EXPECT_TRUE(pdu.append(slice)); } rlc2_rx_lower->handle_pdu(std::move(pdu)); } @@ -820,14 +820,14 @@ TEST_P(rlc_um_test, tx_multiple_SDUs_with_segmentation) { byte_buffer pdu; for (const byte_buffer_slice& slice : pdu_bufs[6].slices()) { - pdu.append(slice); + EXPECT_TRUE(pdu.append(slice)); } rlc2_rx_lower->handle_pdu(std::move(pdu)); } { byte_buffer pdu; for (const byte_buffer_slice& slice : pdu_bufs[1].slices()) { - pdu.append(slice); + EXPECT_TRUE(pdu.append(slice)); } rlc2_rx_lower->handle_pdu(std::move(pdu)); } @@ -908,7 +908,7 @@ TEST_P(rlc_um_test, reassembly_window_wrap_around) byte_buffer rx_pdu; for (const byte_buffer_slice& slice : tx_pdu.slices()) { - rx_pdu.append(slice); + EXPECT_TRUE(rx_pdu.append(slice)); } rlc2_rx_lower->handle_pdu(std::move(rx_pdu)); @@ -972,7 +972,7 @@ TEST_P(rlc_um_test, lost_PDU_outside_reassembly_window) if (num_pdus != 10 && num_pdus != 21) { byte_buffer rx_pdu; for (const byte_buffer_slice& slice : tx_pdu.slices()) { - rx_pdu.append(slice); + EXPECT_TRUE(rx_pdu.append(slice)); } rlc2_rx_lower->handle_pdu(std::move(rx_pdu)); } else { @@ -1062,7 +1062,7 @@ TEST_P(rlc_um_test, lost_segment_outside_reassembly_window) if (i != 2) { byte_buffer pdu; for (const byte_buffer_slice& slice : pdu_bufs[i].slices()) { - pdu.append(slice); + EXPECT_TRUE(pdu.append(slice)); } rlc2_rx_lower->handle_pdu(std::move(pdu)); } @@ -1148,7 +1148,7 @@ TEST_P(rlc_um_test, out_of_order_segments_across_SDUs) for (uint32_t i = 0; i < num_pdus; i++) { byte_buffer pdu; for (const byte_buffer_slice& slice : pdu_bufs[order[i]].slices()) { - pdu.append(slice); + EXPECT_TRUE(pdu.append(slice)); } rlc2_rx_lower->handle_pdu(std::move(pdu)); } diff --git a/tests/unittests/rrc/rrc_ue_capability_transfer_proc_test.cpp b/tests/unittests/rrc/rrc_ue_capability_transfer_proc_test.cpp index 0999aa8529..e597dcbb5e 100644 --- a/tests/unittests/rrc/rrc_ue_capability_transfer_proc_test.cpp +++ b/tests/unittests/rrc/rrc_ue_capability_transfer_proc_test.cpp @@ -22,10 +22,7 @@ #include "rrc_ue_test_helpers.h" #include "rrc_ue_test_messages.h" -#include "srsran/adt/byte_buffer.h" -#include "srsran/rrc/rrc_du_factory.h" -#include "srsran/support/async/fifo_async_task_scheduler.h" -#include "srsran/support/test_utils.h" +#include "srsran/support/async/async_test_utils.h" #include using namespace srsran; diff --git a/tests/unittests/rrc/rrc_ue_reconfig_proc_test.cpp b/tests/unittests/rrc/rrc_ue_reconfig_proc_test.cpp index 3c8d6f2103..5e0b367b14 100644 --- a/tests/unittests/rrc/rrc_ue_reconfig_proc_test.cpp +++ b/tests/unittests/rrc/rrc_ue_reconfig_proc_test.cpp @@ -22,9 +22,7 @@ #include "rrc_ue_test_helpers.h" #include "rrc_ue_test_messages.h" -#include "srsran/adt/byte_buffer.h" -#include "srsran/support/async/fifo_async_task_scheduler.h" -#include "srsran/support/test_utils.h" +#include "srsran/support/async/async_test_utils.h" #include using namespace srsran; diff --git a/tests/unittests/rrc/rrc_ue_setup_proc_test.cpp b/tests/unittests/rrc/rrc_ue_setup_proc_test.cpp index 91d3b244e9..65fda62bfa 100644 --- a/tests/unittests/rrc/rrc_ue_setup_proc_test.cpp +++ b/tests/unittests/rrc/rrc_ue_setup_proc_test.cpp @@ -106,3 +106,23 @@ TEST_F(rrc_ue_setup, when_setup_complete_received_initial_ue_message_sent) check_initial_ue_message_sent(); } + +/// Test the correct handling of corrupted RRC setup complete message +TEST_F(rrc_ue_setup, when_integrity_failure_detected_then_ue_deleted) +{ + receive_setup_request(); + + // check if the RRC setup message was generated + ASSERT_EQ(get_srb0_pdu_type(), asn1::rrc_nr::dl_ccch_msg_type_c::c1_c_::types::rrc_setup); + + // check if SRB1 was created + check_srb1_exists(); + + receive_corrupted_setup_complete(); + + // tick timer until RRC setup complete timer fires + tick_timer(); + + // verify that RRC requested UE context release + check_ue_release_requested(); +} diff --git a/tests/unittests/rrc/rrc_ue_smc_proc_test.cpp b/tests/unittests/rrc/rrc_ue_smc_proc_test.cpp index f1365c47d9..7f0c0bd672 100644 --- a/tests/unittests/rrc/rrc_ue_smc_proc_test.cpp +++ b/tests/unittests/rrc/rrc_ue_smc_proc_test.cpp @@ -22,10 +22,7 @@ #include "rrc_ue_test_helpers.h" #include "rrc_ue_test_messages.h" -#include "srsran/adt/byte_buffer.h" -#include "srsran/rrc/rrc_du_factory.h" -#include "srsran/support/async/fifo_async_task_scheduler.h" -#include "srsran/support/test_utils.h" +#include "srsran/support/async/async_test_utils.h" #include using namespace srsran; diff --git a/tests/unittests/rrc/rrc_ue_test_helpers.h b/tests/unittests/rrc/rrc_ue_test_helpers.h index 42d08689e2..1173bd83e7 100644 --- a/tests/unittests/rrc/rrc_ue_test_helpers.h +++ b/tests/unittests/rrc/rrc_ue_test_helpers.h @@ -30,7 +30,6 @@ #include "srsran/ran/subcarrier_spacing.h" #include "srsran/rrc/rrc_config.h" #include "srsran/rrc/rrc_du.h" -#include "srsran/support/async/async_test_utils.h" #include "srsran/support/executors/manual_task_worker.h" #include @@ -82,7 +81,8 @@ class rrc_ue_test_helper rrc_ue_creation_message rrc_ue_create_msg{}; rrc_ue_create_msg.ue_index = ALLOCATED_UE_INDEX; rrc_ue_create_msg.c_rnti = to_rnti(0x1234); - rrc_ue_create_msg.du_to_cu_container.resize(1); + bool ret = rrc_ue_create_msg.du_to_cu_container.resize(1); + (void)ret; rrc_ue_create_msg.f1ap_pdu_notifier = &rrc_ue_f1ap_notifier; rrc_ue_create_msg.rrc_ue_cu_cp_notifier = &rrc_ue_cu_cp_notifier; rrc_ue_create_msg.measurement_notifier = &rrc_ue_cu_cp_notifier; @@ -144,7 +144,7 @@ class rrc_ue_test_helper return dl_ccch.msg.c1().type(); } - srb_id_t get_last_srb() + srb_id_t get_last_srb() const { // generated PDU must not be empty EXPECT_GT(rrc_ue_f1ap_notifier.last_rrc_pdu.length(), 0); @@ -249,6 +249,12 @@ class rrc_ue_test_helper rrc_ue->get_ul_dcch_pdu_handler().handle_ul_dcch_pdu(srb_id_t::srb1, byte_buffer{rrc_setup_complete_pdu}); } + void receive_corrupted_setup_complete() + { + // inject corrupted RRC setup complete + rrc_ue->get_ul_dcch_pdu_handler().handle_ul_dcch_pdu(srb_id_t::srb1, byte_buffer{corrupted_rrc_setup_complete_pdu}); + } + void send_dl_info_transfer(byte_buffer nas_pdu) { // inject RRC setup complete @@ -295,7 +301,7 @@ class rrc_ue_test_helper void check_smc_pdu() { ASSERT_EQ(rrc_ue_f1ap_notifier.last_rrc_pdu, byte_buffer{rrc_smc_pdu}); } - void check_initial_ue_message_sent() { ASSERT_TRUE(rrc_ue_ngap_notifier.initial_ue_msg_received); } + void check_initial_ue_message_sent() const { ASSERT_TRUE(rrc_ue_ngap_notifier.initial_ue_msg_received); } void check_rrc_ue_enquiry_pdu(uint8_t transaction_id) { @@ -331,7 +337,7 @@ class rrc_ue_test_helper rrc_ue_cu_cp_notifier.add_ue_context(reest_context); } - void check_meas_results(const rrc_meas_results& meas_results) + static void check_meas_results(const rrc_meas_results& meas_results) { ASSERT_EQ(meas_results.meas_id, uint_to_meas_id(1)); ASSERT_EQ(meas_results.meas_result_serving_mo_list.size(), 1); @@ -434,6 +440,16 @@ class rrc_ue_test_helper 0x00, 0x05, 0x20, 0x2f, 0x89, 0x90, 0x00, 0x00, 0x11, 0x70, 0x7f, 0x07, 0x0c, 0x04, 0x01, 0x98, 0x0b, 0x01, 0x80, 0x10, 0x17, 0x40, 0x00, 0x09, 0x05, 0x30, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00}; + // UL-DCCH with corrupted RRC setup complete message + std::array corrupted_rrc_setup_complete_pdu = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xf1, 0x00, 0xc0, 0x47, 0xe0, 0x04, 0x13, + 0x90, 0x00, 0xbf, 0x20, 0x2f, 0x89, 0x98, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0xf2, 0xe0, 0x4f, 0x07, 0x0f, 0x07, + 0x07, 0x10, 0x05, 0xe5, 0xe0, 0x04, 0x13, 0x90, 0x00, 0xbf, 0x20, 0x2f, 0x89, 0x98, 0x00, 0x04, 0x10, 0x00, 0x00, + 0x00, 0xf1, 0x00, 0x10, 0x32, 0x01, 0x4f, 0x07, 0x0f, 0x07, 0x02, 0xf1, 0xb0, 0x80, 0x10, 0x02, 0x7d, 0xb0, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x10, 0x1b, 0x66, 0x90, 0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x05, 0x20, 0x2f, 0x89, 0x90, 0x00, 0x00, 0x11, 0x70, 0x7f, 0x07, 0x0c, 0x04, 0x01, 0x98, 0x0b, 0x01, 0x80, + 0x10, 0x17, 0x40, 0x00, 0x09, 0x05, 0x30, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00}; + // DL-DCCH with RRC security mode command std::array rrc_smc_pdu = {0x0, 0x0, 0x22, 0x08, 0x10, 0xec, 0xf0, 0xef, 0x74}; diff --git a/tests/unittests/rrc/test_helpers.h b/tests/unittests/rrc/test_helpers.h index efc3fb5730..eb1fe33b96 100644 --- a/tests/unittests/rrc/test_helpers.h +++ b/tests/unittests/rrc/test_helpers.h @@ -145,7 +145,8 @@ class dummy_rrc_ue_cu_cp_adapter : public rrc_ue_context_update_notifier, public void on_ue_removal_required(ue_index_t ue_index) override { logger.info("ue={}: Requested a UE removal", ue_index); } - optional on_measurement_config_request(nr_cell_id_t nci, + optional on_measurement_config_request(ue_index_t ue_index, + nr_cell_id_t nci, optional current_meas_config = {}) override { optional meas_cfg; diff --git a/tests/unittests/scheduler/multiple_ue_sched_test.cpp b/tests/unittests/scheduler/multiple_ue_sched_test.cpp index 12d8b45b1c..d143bd42e6 100644 --- a/tests/unittests/scheduler/multiple_ue_sched_test.cpp +++ b/tests/unittests/scheduler/multiple_ue_sched_test.cpp @@ -241,9 +241,9 @@ class scheduler_impl_tester variant_get( ue_creation_req.cfg.cells.value()[0].serv_cell_cfg.csi_meas_cfg->csi_report_cfg_list[0].report_cfg_type) .report_slot_period); - if (params.tdd_ul_dl_cfg_common.has_value()) { + if (bench->cell_cfg.tdd_cfg_common.has_value()) { optional slot_offset = - find_next_tdd_full_ul_slot(params.tdd_ul_dl_cfg_common.value(), last_csi_report_offset + 1); + find_next_tdd_full_ul_slot(bench->cell_cfg.tdd_cfg_common.value(), last_csi_report_offset + 1); srsran_assert(slot_offset.has_value(), "Unable to find a valid CSI report slot offset UE={}", ue_index); srsran_assert(slot_offset.value() < csi_report_period_slots, "Unable to find a valid CSI report slot offset UE={}", @@ -578,7 +578,7 @@ struct multiple_ue_test_params { class multiple_ue_sched_tester : public scheduler_impl_tester, public ::testing::TestWithParam { public: - multiple_ue_sched_tester() : params{GetParam()} {}; + multiple_ue_sched_tester() : params{GetParam()} {} protected: multiple_ue_test_params params; diff --git a/tests/unittests/scheduler/scheduler_ue_fallback_mode_test.cpp b/tests/unittests/scheduler/scheduler_ue_fallback_mode_test.cpp index dddb868919..efbb2e9326 100644 --- a/tests/unittests/scheduler/scheduler_ue_fallback_mode_test.cpp +++ b/tests/unittests/scheduler/scheduler_ue_fallback_mode_test.cpp @@ -226,6 +226,34 @@ TEST_P(scheduler_con_res_msg4_test, while_ue_is_in_fallback_then_common_pucch_is ASSERT_FALSE(pucch_ptr->resources.second_hop_prbs.empty()) << "For common PUCCH resources, second hop is used"; } +TEST_P(scheduler_con_res_msg4_test, while_ue_is_in_fallback_then_common_ss_is_used) +{ + const static unsigned msg4_size = 128; + + // Enqueue ConRes CE + Msg4. + this->sched->handle_dl_mac_ce_indication(dl_mac_ce_indication{ue_index, lcid_dl_sch_t::UE_CON_RES_ID}); + this->push_dl_buffer_state(dl_buffer_state_indication_message{this->ue_index, params.msg4_lcid, msg4_size}); + + // Wait for ConRes + Msg4 PDCCH to be scheduled. + ASSERT_TRUE(this->run_slot_until([this]() { return find_ue_dl_pdcch(rnti) != nullptr; })); + + const pdcch_dl_information& dl_pdcch = *find_ue_dl_pdcch(rnti); + bool is_common_ss_used = false; + const search_space_configuration* ss_used = nullptr; + for (const search_space_configuration& ss : + cell_cfg_list.front().dl_cfg_common.init_dl_bwp.pdcch_common.search_spaces) { + if (dl_pdcch.ctx.context.ss_id == ss.get_id()) { + is_common_ss_used = true; + ss_used = &ss; + break; + } + } + ASSERT_TRUE(is_common_ss_used) << "UE in fallback should use common SS"; + // PDCCH monitoring must be active in this slot. + ASSERT_TRUE(ss_used != nullptr and pdcch_helper::is_pdcch_monitoring_active(next_slot, *ss_used)) + << fmt::format("Common SS id={} is not monitored at slot={}", ss_used->get_id(), next_slot.slot_index()); +} + INSTANTIATE_TEST_SUITE_P(scheduler_con_res_msg4_test, scheduler_con_res_msg4_test, ::testing::Values(conres_test_params{LCID_SRB0, duplex_mode::FDD}, diff --git a/tests/unittests/scheduler/support/mcs_tbs_calculator_test.cpp b/tests/unittests/scheduler/support/mcs_tbs_calculator_test.cpp index 2930cde879..a29ad28ac8 100644 --- a/tests/unittests/scheduler/support/mcs_tbs_calculator_test.cpp +++ b/tests/unittests/scheduler/support/mcs_tbs_calculator_test.cpp @@ -74,7 +74,7 @@ TEST_P(dl_mcs_tbs_calculator_test_bench, test_values) // Run test function. optional test = - compute_dl_mcs_tbs(pdsch_cfg, ue_cell_cfg, sch_mcs_index(test_entry.max_mcs), test_entry.nof_prbs); + compute_dl_mcs_tbs(pdsch_cfg, ue_cell_cfg, sch_mcs_index(test_entry.max_mcs), test_entry.nof_prbs, false); ASSERT_TRUE(test.has_value()); ASSERT_EQ(GetParam().final_mcs, test.value().mcs); @@ -125,7 +125,7 @@ TEST_P(ul_mcs_tbs_prbs_calculator_test_bench, test_values) // Run test function. optional test = - compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_entry.max_mcs), test_entry.nof_prbs); + compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_entry.max_mcs), test_entry.nof_prbs, false); ASSERT_TRUE(test.has_value()); ASSERT_EQ(GetParam().final_mcs, test.value().mcs); @@ -178,7 +178,7 @@ TEST_P(ul_mcs_tbs_prbs_calculator_dci_0_1_test_bench, test_values_with_uci) // Run test function. optional test = - compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_entry.max_mcs), test_entry.nof_prbs); + compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_entry.max_mcs), test_entry.nof_prbs, false); ASSERT_TRUE(test.has_value()); ASSERT_EQ(GetParam().final_mcs, test.value().mcs); @@ -235,7 +235,7 @@ TEST_F(ul_mcs_tbs_prbs_calculator_low_mcs_test_bench, test_values_with_uci) // Run test function. optional test = - compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_1_prb.max_mcs), test_1_prb.nof_prbs); + compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_1_prb.max_mcs), test_1_prb.nof_prbs, false); ASSERT_TRUE(test.has_value()); ASSERT_EQ(test_1_prb.final_mcs, test.value().mcs); @@ -245,7 +245,7 @@ TEST_F(ul_mcs_tbs_prbs_calculator_low_mcs_test_bench, test_values_with_uci) test_1_prb.max_mcs = 4; // Run test function. - test = compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_1_prb.max_mcs), test_1_prb.nof_prbs); + test = compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_1_prb.max_mcs), test_1_prb.nof_prbs, false); ASSERT_FALSE(test.has_value()); @@ -253,7 +253,7 @@ TEST_F(ul_mcs_tbs_prbs_calculator_low_mcs_test_bench, test_values_with_uci) mcs_test_entry test_2_prb{.final_mcs = 4, .tbs_bytes = 19, .max_mcs = 4, .nof_prbs = 2}; // Run test function. - test = compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_2_prb.max_mcs), test_2_prb.nof_prbs); + test = compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_2_prb.max_mcs), test_2_prb.nof_prbs, false); ASSERT_TRUE(test.has_value()); ASSERT_EQ(test_2_prb.final_mcs, test.value().mcs); ASSERT_EQ(test_2_prb.tbs_bytes, test.value().tbs); @@ -262,7 +262,8 @@ TEST_F(ul_mcs_tbs_prbs_calculator_low_mcs_test_bench, test_values_with_uci) mcs_test_entry test_2_prb_mcs_0{.final_mcs = 0, .tbs_bytes = 7, .max_mcs = 0, .nof_prbs = 2}; // Run test function. - test = compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_2_prb_mcs_0.max_mcs), test_2_prb_mcs_0.nof_prbs); + test = compute_ul_mcs_tbs( + pusch_cfg, ue_cell_cfg, sch_mcs_index(test_2_prb_mcs_0.max_mcs), test_2_prb_mcs_0.nof_prbs, false); ASSERT_TRUE(test.has_value()); ASSERT_EQ(test_2_prb_mcs_0.final_mcs, test.value().mcs); ASSERT_EQ(test_2_prb_mcs_0.tbs_bytes, test.value().tbs); @@ -298,7 +299,7 @@ TEST_F(ul_mcs_tbs_prbs_calculator_with_harq_ack, test_values_with_2_harq_bits) // Run test function. optional test = - compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_1_prb.max_mcs), test_1_prb.nof_prbs); + compute_ul_mcs_tbs(pusch_cfg, ue_cell_cfg, sch_mcs_index(test_1_prb.max_mcs), test_1_prb.nof_prbs, false); ASSERT_TRUE(test.has_value()); ASSERT_EQ(test_1_prb.final_mcs, test.value().mcs); diff --git a/tests/unittests/scheduler/test_utils/dummy_test_components.h b/tests/unittests/scheduler/test_utils/dummy_test_components.h index 3bb1365063..d9086db0f9 100644 --- a/tests/unittests/scheduler/test_utils/dummy_test_components.h +++ b/tests/unittests/scheduler/test_utils/dummy_test_components.h @@ -144,6 +144,8 @@ class dummy_uci_allocator : public uci_allocator { return 0; } + + bool has_uci_harq_on_common_pucch_res(rnti_t crnti, slot_point sl_tx) override { return false; } }; class sched_cfg_dummy_notifier : public sched_configuration_notifier diff --git a/tests/unittests/scheduler/uci_and_pucch/uci_scheduling_test.cpp b/tests/unittests/scheduler/uci_and_pucch/uci_scheduling_test.cpp index 0b48a25ccf..9d48d41469 100644 --- a/tests/unittests/scheduler/uci_and_pucch/uci_scheduling_test.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/uci_scheduling_test.cpp @@ -118,7 +118,7 @@ TEST_P(uci_sr_scheduler_tester, test_different_periods) // Randomize initial slot, as the UCI scheduler will be called only after the UE is added. const unsigned starting_slot = test_rgen::uniform_int(0, 1000U); for (unsigned sl_cnt = starting_slot; sl_cnt < starting_slot + NOF_SLOTS_TO_TEST; ++sl_cnt) { - t_bench.uci_sched.run_slot(t_bench.res_grid, t_bench.sl_tx); + t_bench.uci_sched.run_slot(t_bench.res_grid); if ((t_bench.sl_tx - sr_offset).to_uint() % sr_periodicity_to_slot(sr_period) == 0) { ASSERT_EQ(1, t_bench.res_grid[0].result.ul.pucchs.size()); // The scheduler allocates: @@ -231,7 +231,7 @@ TEST_P(uci_csi_scheduler_tester, test_different_periods) // Randomize initial slot, as the UCI scheduler will be called only after the UE is added. const unsigned starting_slot = test_rgen::uniform_int(0, 1000U); for (unsigned sl_cnt = starting_slot; sl_cnt < starting_slot + NOF_SLOTS_TO_TEST; ++sl_cnt) { - t_bench.uci_sched.run_slot(t_bench.res_grid, t_bench.sl_tx); + t_bench.uci_sched.run_slot(t_bench.res_grid); if ((t_bench.sl_tx - csi_offset).to_uint() % csi_report_periodicity_to_uint(csi_period) == 0) { ASSERT_EQ(1, t_bench.res_grid[0].result.ul.pucchs.size()); // The scheduler allocates: diff --git a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp index b3f60be0c3..3e77202f46 100644 --- a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp @@ -195,6 +195,7 @@ test_bench::test_bench(const test_bench_params& params, ue_ded_cfgs.push_back(std::make_unique(ue_req.ue_index, ue_req.crnti, cell_cfg_list, ue_req.cfg)); ues.add_ue( std::make_unique(ue_creation_command{*ue_ded_cfgs.back(), ue_req.starts_in_fallback, harq_timeout_handler})); + uci_sched.add_ue(ues[ue_req.ue_index].get_pcell().cfg()); last_allocated_rnti = ue_req.crnti; last_allocated_ue_idx = main_ue_idx; slot_indication(sl_tx); diff --git a/tests/unittests/sdap/sdap_rx_test.cpp b/tests/unittests/sdap/sdap_rx_test.cpp index 671167dc12..b4ee1ad8b7 100644 --- a/tests/unittests/sdap/sdap_rx_test.cpp +++ b/tests/unittests/sdap/sdap_rx_test.cpp @@ -22,7 +22,6 @@ #include "lib/sdap/sdap_entity_rx_impl.h" #include "srsran/sdap/sdap.h" -#include "srsran/support/executors/manual_task_worker.h" #include #include @@ -59,10 +58,7 @@ class sdap_rx_test : public ::testing::Test tester = std::make_unique(); // Create SDAP RX entity - ue_inactivity_timer = timers.create_timer(); - ue_inactivity_timer.set(std::chrono::milliseconds(10000), [](timer_id_t) {}); - sdap = std::make_unique( - 7, pdu_session_id_t::min, qos_flow_id_t::min, drb_id_t::drb1, ue_inactivity_timer, *tester); + sdap = std::make_unique(7, pdu_session_id_t::min, qos_flow_id_t::min, drb_id_t::drb1, *tester); } void TearDown() override @@ -72,10 +68,6 @@ class sdap_rx_test : public ::testing::Test } srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false); - manual_task_worker worker{64}; - timer_manager timers_manager; - timer_factory timers{timers_manager, worker}; - unique_timer ue_inactivity_timer; std::unique_ptr tester; std::unique_ptr sdap; }; diff --git a/tests/unittests/sdap/sdap_test.h b/tests/unittests/sdap/sdap_test.h index c92305809b..1c5f2ace4c 100644 --- a/tests/unittests/sdap/sdap_test.h +++ b/tests/unittests/sdap/sdap_test.h @@ -22,7 +22,6 @@ #include "lib/sdap/sdap_entity_impl.h" #include "srsran/sdap/sdap.h" -#include "srsran/support/executors/manual_task_worker.h" #include #include @@ -74,9 +73,7 @@ class sdap_test : public ::testing::Test dl_sink2 = std::make_unique(); // Create SDAP TX entity - ue_inactivity_timer = timers.create_timer(); - ue_inactivity_timer.set(std::chrono::milliseconds(10000), [](timer_id_t) {}); - sdap = std::make_unique(7, pdu_session_id_t::min, ue_inactivity_timer, *ul_sink); + sdap = std::make_unique(7, pdu_session_id_t::min, *ul_sink); } void TearDown() override @@ -86,10 +83,6 @@ class sdap_test : public ::testing::Test } srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false); - manual_task_worker worker{64}; - timer_manager timers_manager; - timer_factory timers{timers_manager, worker}; - unique_timer ue_inactivity_timer; std::unique_ptr ul_sink; std::unique_ptr dl_sink1; std::unique_ptr dl_sink2; diff --git a/tests/unittests/sdap/sdap_tx_test.cpp b/tests/unittests/sdap/sdap_tx_test.cpp index 9def9d9b4a..abdd047f60 100644 --- a/tests/unittests/sdap/sdap_tx_test.cpp +++ b/tests/unittests/sdap/sdap_tx_test.cpp @@ -22,7 +22,6 @@ #include "lib/sdap/sdap_entity_tx_impl.h" #include "srsran/sdap/sdap.h" -#include "srsran/support/executors/manual_task_worker.h" #include #include @@ -59,10 +58,7 @@ class sdap_tx_test : public ::testing::Test tester = std::make_unique(); // Create SDAP TX entity - ue_inactivity_timer = timers.create_timer(); - ue_inactivity_timer.set(std::chrono::milliseconds(10000), [](timer_id_t) {}); - sdap = std::make_unique( - 7, pdu_session_id_t::min, qos_flow_id_t::min, drb_id_t::drb1, ue_inactivity_timer, *tester); + sdap = std::make_unique(7, pdu_session_id_t::min, qos_flow_id_t::min, drb_id_t::drb1, *tester); } void TearDown() override @@ -72,10 +68,6 @@ class sdap_tx_test : public ::testing::Test } srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false); - manual_task_worker worker{64}; - timer_manager timers_manager; - timer_factory timers{timers_manager, worker}; - unique_timer ue_inactivity_timer; std::unique_ptr tester; std::unique_ptr sdap; }; diff --git a/tests/unittests/support/network/transport_layer_address_test.cpp b/tests/unittests/support/network/transport_layer_address_test.cpp index ab3618165f..5f5f250411 100644 --- a/tests/unittests/support/network/transport_layer_address_test.cpp +++ b/tests/unittests/support/network/transport_layer_address_test.cpp @@ -55,42 +55,45 @@ TEST(transport_layer_address_test, empty_address) TEST(transport_layer_address_test, conversion_to_ipv4_string) { - std::string ipv4_str = create_random_ipv4_string(); - transport_layer_address addr{ipv4_str}; + std::string ipv4_str = create_random_ipv4_string(); + auto addr = transport_layer_address::create_from_string(ipv4_str); ASSERT_EQ(addr.to_string(), ipv4_str); ASSERT_EQ(fmt::format("{}", addr), ipv4_str); } TEST(transport_layer_address_test, conversion_to_ipv6_string) { - std::string ipv6_str = create_random_ipv6_string(); - transport_layer_address addr{ipv6_str}; + std::string ipv6_str = create_random_ipv6_string(); + auto addr = transport_layer_address::create_from_string(ipv6_str); ASSERT_EQ(addr.to_string(), ipv6_str); ASSERT_EQ(fmt::format("{}", addr), ipv6_str); } TEST(transport_layer_address_test, ipv4_address_comparison) { - std::string ipv4_str1 = create_random_ipv4_string(); - std::string ipv4_str2 = create_random_ipv4_string(); - transport_layer_address addr1{ipv4_str1}, addr2{ipv4_str2}; + std::string ipv4_str1 = create_random_ipv4_string(); + std::string ipv4_str2 = create_random_ipv4_string(); + auto addr1 = transport_layer_address::create_from_string(ipv4_str1); + auto addr2 = transport_layer_address::create_from_string(ipv4_str2); ASSERT_EQ(addr1, ipv4_str1); ASSERT_EQ(addr2, ipv4_str2); } TEST(transport_layer_address_test, ipv6_address_comparison) { - std::string ipv6_str1 = create_random_ipv6_string(); - std::string ipv6_str2 = create_random_ipv6_string(); - transport_layer_address addr1{ipv6_str1}, addr2{ipv6_str2}; + std::string ipv6_str1 = create_random_ipv6_string(); + std::string ipv6_str2 = create_random_ipv6_string(); + auto addr1 = transport_layer_address::create_from_string(ipv6_str1); + auto addr2 = transport_layer_address::create_from_string(ipv6_str2); ASSERT_EQ(addr1, ipv6_str1); ASSERT_EQ(addr2, ipv6_str2); } TEST(transport_layer_address_test, ipv4_is_always_different_from_ipv6) { - std::string ipv4_str = create_random_ipv4_string(); - std::string ipv6_str = create_random_ipv6_string(); - transport_layer_address addr1{ipv4_str}, addr2{ipv6_str}; + std::string ipv4_str = create_random_ipv4_string(); + std::string ipv6_str = create_random_ipv6_string(); + auto addr1 = transport_layer_address::create_from_string(ipv4_str); + auto addr2 = transport_layer_address::create_from_string(ipv6_str); ASSERT_NE(addr1, addr2); }