diff --git a/.clang-tidy b/.clang-tidy
index 26f2b38730..5af5cd29f0 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,49 +1,56 @@
---
-# All Clang-Tidy Checks allowed, except:
-# - forbidden vararg
-# - forbidden magic numbers
-# - forbidden namespace "using"
-# - forbidden array->pointer decay
-# - init of static memory may cause an exception (cert-err58)
-# - forbidden implicit conversion from pointer/int to bool
-# - recommended auto
-# - remove llvm-specific checks (header guard style, usage of llvm namespace, restriction of libc includes, etc.)
-# Naming conventions set to snake_case
-Checks: '*,-fuchsia-*,
- -cppcoreguidelines-pro-type-vararg,-hicpp-vararg,
- -cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,
- -cppcoreguidelines-pro-bounds-array-to-pointer-decay,-hicpp-no-array-decay,
- -cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-type-cstyle-cast,
- -cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-static-cast-downcast,
- -cppcoreguidelines-macro-usage,
- -cppcoreguidelines-avoid-const-or-ref-data-members,
- -cppcoreguidelines-non-private-member-variables-in-classes,
- -cppcoreguidelines-special-member-functions,
- -cppcoreguidelines-avoid-do-while,
- -modernize-use-using,-modernize-use-trailing-return-type,
- -modernize-use-auto,-hicpp-use-auto,
- -llvmlibc-callee-namespace,-llvmlibc-implementation-in-namespace,-llvmlibc-restrict-system-libc-headers,
- -llvm-header-guard,-llvmlibc-inline-function-decl,
- -bugprone-easily-swappable-parameters,
- -google-runtime-references,-google-readability-casting,-google-build-using-namespace,
- -google-readability-avoid-underscore-in-googletest-name,
- google-default-arguments,-cppcoreguidelines-pro-bounds-pointer-arithmetic,
- -cert-err58-cpp,
- -altera-unroll-loops,-altera-id-dependent-backward-branch,
- -readability-function-cognitive-complexity,-readability-isolate-declaration,
- -misc-non-private-member-variables-in-classes,-altera-struct-pack-align,-readability-uppercase-literal-suffix,
+Checks: '-*,
+ bugprone-copy-constructor-init,
+ bugprone-forward-declaration-namespace,
+ bugprone-inaccurate-erase,
+ bugprone-move-forwarding-reference,
+ bugprone-parent-virtual-call,
+ bugprone-reserved-identifier,
+ bugprone-suspicious-memset-usage,
+ bugprone-suspicious-semicolon,
+ bugprone-undefined-memory-manipulation,
+ bugprone-use-after-move,
+ cppcoreguidelines-macro-usage,
+ cppcoreguidelines-missing-std-forward,
+ cppcoreguidelines-pro-type-cstyle-cast,
+ cppcoreguidelines-slicing,
+ cppcoreguidelines-virtual-class-destructor,
+ llvm-namespace-comment,
+ misc-*,
+ -misc-const-correctness,
+ -misc-no-recursion,
+ -misc-unused-parameters,
-misc-use-anonymous-namespace,
- -hicpp-special-member-functions,
+ -misc-non-private-member-variables-in-classes,
+ modernize-*,
+ -modernize-avoid-c-arrays,
+ -modernize-use-trailing-return-type,
+ performance-*,
+ readability-*,
+ -readability-avoid-unconditional-preprocessor-if,
+ -readability-function-cognitive-complexity,
+ -readability-function-size,
-readability-identifier-length,
- readability-identifier-naming'
-HeaderFilterRegex: ''
-AnalyzeTemporaryDtors: false
+ -readability-implicit-bool-conversion,
+ -readability-magic-numbers,
+ -readability-static-accessed-through-instance,
+ -readability-uppercase-literal-suffix'
CheckOptions:
- - key: readability-identifier-naming.NamespaceCase
- value: lower_case
- key: readability-identifier-naming.ClassCase
value: lower_case
+ - key: readability-identifier-naming.EnumCase
+ value: lower_case
+ - key: readability-identifier-naming.FunctionCase
+ value: lower_case
+ - key: readability-identifier-naming.MemberCase
+ value: lower_case
+ - key: readability-identifier-naming.NamespaceCase
+ value: lower_case
- key: readability-identifier-naming.StructCase
value: lower_case
- key: readability-identifier-naming.VariableCase
value: lower_case
+ - key: readability-identifier-naming.StaticConstantCase
+ value: aNy_CasE
+ - key: readability-identifier-naming.GlobalConstantCase
+ value: aNy_CasE
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6eb427bbe1..edcb351398 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -25,19 +25,19 @@
include:
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/setup/all.yml
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/features/all.yml
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/tools/python.yml
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/tools/test_reporter.yml
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/tools/tagger.yml
- local: .gitlab/ci/builders/version.yml
- local: .gitlab/ci/build.yml
@@ -140,7 +140,7 @@ full-code-format:
- static-analyzer.sh -i /tmp/codechecker_skip --analyzers ${ANALYZER} ${ANALYZER_ARGS} $CI_PROJECT_DIR
after_script:
- mv codechecker_html codechecker-${ANALYZER}-html
- artifacts:
+ artifacts: &codechecker_artifacts
reports:
codequality: code-quality-report.json
paths:
@@ -159,7 +159,7 @@ clang-tidy:
allow_failure: false
variables:
ANALYZER: clang-tidy
- ANALYZER_ARGS: --tidy-config .clang-tidy
+ ANALYZER_ARGS: --analyzer-config clang-tidy:take-config-from-directory=true --tidy-config .clang-tidy
ARTIFACT_EXTRA_PATH: "/index.html"
cppcheck:
@@ -170,6 +170,9 @@ cppcheck:
variables:
ANALYZER: cppcheck
ANALYZER_ARGS: --cppcheck-max-template-recursion 10
+ artifacts:
+ <<: *codechecker_artifacts
+ expire_in: 1 day
clangsa:
extends: .codechecker
@@ -203,12 +206,12 @@ clangsa:
cov-build --dir cov-int make -j${KUBERNETES_CPU_REQUEST}
tar czvf srsgnb.tgz cov-int
ver=$(git rev-parse HEAD)
-
+ - |
curl --form token=$COV_TOKEN \
- --form email=nils@srs.io \
+ --form email=${COVERITY_EMAIL} \
--form file=@srsgnb.tgz \
--form version=$ver \
- --form description="srsRAN Project dev build" \
+ --form description="${DESCRIPTION}" \
https://scan.coverity.com/builds?project=${PROJECT_NAME}
coverity-dev:
@@ -280,11 +283,6 @@ unit coverage:
coverage_report: summary
when: always # Even if previous stages/required jobs fail
allow_failure: true
- - if: $CI_DESCRIPTION =~ /Nightly/
- variables:
- coverage_report: full
- when: always # Even if previous stages/required jobs fail
- allow_failure: true
before_script:
- PACKAGE_URL=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/coverage/${CI_COMMIT_BRANCH}${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME}/coverage_history.tar.gz
# Download coverage history from the registry
@@ -329,10 +327,11 @@ unit coverage:
- pip3 install --verbose retina-reporter --index-url https://__token__:$CODEBOT_TOKEN@gitlab.com/api/v4/projects/44296988/packages/pypi/simple
- retina-reporter --input build_time_metrics.json --bucket ci --db-config token=$INFLUXDB_TOKEN org=$INFLUXDB_ORG url=$INFLUXDB_URL
coverage: /^\s*Line coverage:\s*\d+.\d+\%/
- artifacts:
+ artifacts: &unit_coverage_artifacts
paths:
- coverage_html
expire_in: 10 minutes
+ retry: 2
needs:
- job: smoke release cached
optional: true
@@ -344,29 +343,41 @@ unit coverage:
optional: true
artifacts: true
+unit coverage dev:
+ extends: unit coverage
+ rules:
+ - if: $CI_DESCRIPTION =~ /Nightly/
+ variables:
+ coverage_report: full
+ when: always # Even if previous stages/required jobs fail
+ allow_failure: true
+ artifacts:
+ <<: *unit_coverage_artifacts
+ expire_in: 1 day
+
pages:
stage: documentation
rules:
- if: $CI_DESCRIPTION == "Nightly"
when: always # Even if previous stages/required jobs fail
allow_failure: true
- image: ${GITLAB_REGISTRY_URI}/${CI_TOOLS_REPO}/doxygen:2.0.0
+ image: ${GITLAB_REGISTRY_URI}/${CI_TOOLS_REPO}/doxygen:1.9.8-1.2023.7
script:
- mkdir public
- mv coverage_html public/
- mv codechecker*html public/
- mv docs/index.html public/index.html
- .gitlab/ci/builders/install_dependencies.sh
+ - apt-get update && apt-get install -y --no-install-recommends rsync
- |
rm -Rf build
mkdir build
cd build || exit
- cmake -G Ninja ..
- ninja doxygen
- mv ./docs/html ./public
+ cmake ..
+ make -j $(nproc) doxygen
cd ..
- - mv build/public/index.html build/public/index_doxygen.html
- - find build/public/ -name '*.*' -exec mv {} public/ \;
+ - mkdir public/doxygen
+ - rsync -a build/docs/html/ public/doxygen/
after_script:
- |
if [ $CI_JOB_STATUS = "failed" ]; then
@@ -375,32 +386,11 @@ pages:
fi
- mv docs/*.png public/
- sed -i 's/commit_hash/'$CI_COMMIT_SHA'/' public/index.html
- - sed -i 's/zmq_job_id/'$( cat ./zmq/job.env )'/' public/index.html
- - sed -i 's/zmq_asan_job_id/'$( cat ./zmq-asan/job.env )'/' public/index.html
- - sed -i 's/zmq_valgrind_job_id/'$( cat ./zmq-valgrind/job.env )'/' public/index.html
- - sed -i 's/rf_job_id/'$( cat ./rf/job.env )'/' public/index.html
- - sed -i 's/rf_tsan_job_id/'$( cat ./rf-tsan/job.env )'/' public/index.html
- - sed -i 's/rf_asan_job_id/'$( cat ./rf-asan/job.env )'/' public/index.html
- - sed -i 's/rf_b200_config_job_id/'$( cat ./rf-b200-config/job.env )'/' public/index.html
needs:
- - job: unit coverage
+ - job: unit coverage dev
artifacts: true
- job: cppcheck
artifacts: true
- - job: zmq
- artifacts: true
- - job: zmq-asan
- artifacts: true
- - job: zmq-valgrind
- artifacts: true
- - job: rf
- artifacts: true
- - job: rf-tsan
- artifacts: true
- - job: rf-asan
- artifacts: true
- - job: rf-b200-config
- artifacts: true
artifacts:
paths:
- public
diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml
index 258008218c..d166df8d2b 100644
--- a/.gitlab/ci/build.yml
+++ b/.gitlab/ci/build.yml
@@ -20,7 +20,7 @@
include:
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/setup/all.yml
- project: softwareradiosystems/ci/srsran_project_packaging
ref: "1"
@@ -293,7 +293,7 @@ variables:
rm -Rf build_time_metrics.json
fi
timeout: 4h
- artifacts:
+ artifacts: &build_artifacts
when: always
reports:
coverage_report:
@@ -552,6 +552,9 @@ smoke release update cache:
cache:
- !reference [.fetch_src_cache, cache]
- *cache_build_set
+ artifacts:
+ <<: *build_artifacts
+ expire_in: 1 day
smoke tsan update cache:
extends: .smoke tsan
@@ -566,6 +569,9 @@ smoke tsan update cache:
cache:
- !reference [.fetch_src_cache, cache]
- *cache_build_set
+ artifacts:
+ <<: *build_artifacts
+ expire_in: 1 day
smoke rhel update cache:
extends: .smoke rhel
@@ -607,6 +613,9 @@ basic rel with deb info:
ENABLE_AVX512: "False"
SAVE_ARTIFACTS: "True"
tags: ["${AMD64_AVX2_TAG}"]
+ artifacts:
+ <<: *build_artifacts
+ expire_in: 1 day
basic asan:
extends: .build_and_unit
@@ -625,6 +634,9 @@ basic asan:
ENABLE_AVX512: "False"
SAVE_ARTIFACTS: "True"
tags: ["${AMD64_AVX2_TAG}"]
+ artifacts:
+ <<: *build_artifacts
+ expire_in: 1 day
basic valgrind:
extends: .build_and_unit
@@ -642,6 +654,9 @@ basic valgrind:
TEST_MODE: valgrind
SAVE_ARTIFACTS: "True"
tags: ["${AMD64_TAG}"]
+ artifacts:
+ <<: *build_artifacts
+ expire_in: 1 day
# Packaging
diff --git a/.gitlab/ci/builders.yml b/.gitlab/ci/builders.yml
index 7ab034af86..605b4151e7 100644
--- a/.gitlab/ci/builders.yml
+++ b/.gitlab/ci/builders.yml
@@ -1,15 +1,15 @@
include:
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/setup/default.yml
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/setup/workflow.yml
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/tools/docker.yml
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/setup/versions.yml
- local: .gitlab/ci/builders/version.yml
- local: .gitlab/ci/src_cache.yml
diff --git a/.gitlab/ci/e2e.yml b/.gitlab/ci/e2e.yml
index 3e05f91a0d..bea8bde808 100644
--- a/.gitlab/ci/e2e.yml
+++ b/.gitlab/ci/e2e.yml
@@ -20,7 +20,7 @@
include:
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/setup/all.yml
variables:
@@ -61,6 +61,10 @@ variables:
.txrx-lib: &txrx-lib
- job: "build trx driver"
+ optional: true
+ artifacts: true
+ - job: "build amariue zmq driver"
+ optional: true
artifacts: true
load retina variables:
@@ -79,7 +83,7 @@ load retina variables:
.prepare_test:
variables:
- KUBECONFIG_VAR_NAME: "RETINA_HIGH_PERFORMANCE_LAB_KUBECONFIG"
+ KUBECONFIG_VAR_NAME: "RETINA_NAMESPACE_KUBECONFIG"
before_script:
- |
eval K_PATH="\$$KUBECONFIG_VAR_NAME"
@@ -99,7 +103,7 @@ load retina variables:
variables:
ARTIFACT_COMPRESSION_LEVEL: "slowest"
KUBERNETES_EPHEMERAL_STORAGE_REQUEST: "5G"
- KUBECONFIG_VAR_NAME: "RETINA_HIGH_PERFORMANCE_LAB_KUBECONFIG"
+ KUBECONFIG_VAR_NAME: "RETINA_NAMESPACE_KUBECONFIG"
GROUP: zmq
tags:
- on-prem-amd64
@@ -119,7 +123,7 @@ load retina variables:
- |
cd tests/e2e
export LOGNAME=ci_${GROUP}_${GITLAB_USER_LOGIN}
- - retina-launcher --retina-request="${CI_PROJECT_DIR}/.gitlab/ci/e2e/retina_request_${TESTBED}.yml" --log-folder=./log --html=./log/report.html --self-contained-html --junitxml=out.xml ${PYTEST_ARGS} -k "${KEYWORDS}" -m "${MARKERS}" --register-parameter ue.all.log_level=$E2E_LOG_LEVEL gnb.all.log_level=$E2E_LOG_LEVEL ${RETINA_ARGS}
+ - retina-launcher --retina-request="${CI_PROJECT_DIR}/.gitlab/ci/e2e/retina_request_${TESTBED}.yml" --log-folder=./log --html=./log/report.html --self-contained-html --junitxml=out.xml ${PYTEST_ARGS} --reruns 3 --only-rerun ErrorReportedByAgent --only-rerun ValidationError -k "${KEYWORDS}" -m "${MARKERS}" --register-parameter ue.all.log_level=$E2E_LOG_LEVEL gnb.all.log_level=$E2E_LOG_LEVEL ${RETINA_ARGS}
after_script:
- |
echo "*******************************************************************************************************************************"
@@ -277,10 +281,17 @@ rf:
KEYWORDS: "not iperf"
E2E_LOG_LEVEL: "info"
-rf-iperf:
+rf-iperf-udp:
extends: .rf
variables:
- KEYWORDS: "iperf"
+ KEYWORDS: "iperf and udp"
+ E2E_LOG_LEVEL: "info"
+
+rf-iperf-tcp:
+ extends: .rf
+ variables:
+ KEYWORDS: "iperf and tcp"
+ E2E_LOG_LEVEL: "info"
rf-tsan:
extends: .rf
@@ -324,29 +335,32 @@ android:
TESTBED: "android_b200"
MARKERS: "android"
E2E_LOG_LEVEL: "warning"
- KUBECONFIG_VAR_NAME: "RETINA_HIGH_PERFORMANCE_LAB_KUBECONFIG"
+ KUBECONFIG_VAR_NAME: "RETINA_NAMESPACE_KUBECONFIG"
needs:
- job: "basic rel with deb info"
artifacts: true
- *retina-needs
-android-hp:
+android-x300:
extends: .e2e-run
rules:
- if: $CI_DESCRIPTION =~ /Nightly/
- allow_failure: true
variables:
GROUP: "rf"
TESTBED: "android_x300"
MARKERS: "android_hp"
E2E_LOG_LEVEL: "info"
- KUBECONFIG_VAR_NAME: "RETINA_HIGH_PERFORMANCE_LAB_KUBECONFIG"
- PYTEST_ARGS: --graph-url $INFLUXDB_URL@$INFLUXDB_ORG:$INFLUXDB_TOKEN --graph-bucket e2e
+ KUBECONFIG_VAR_NAME: "RETINA_NAMESPACE_KUBECONFIG"
needs:
- job: "basic rel with deb info"
artifacts: true
- *retina-needs
+android-n300:
+ extends: android-x300
+ variables:
+ TESTBED: "android_n300"
+
################################################################################
# Garbage collector
################################################################################
diff --git a/.gitlab/ci/e2e/.env b/.gitlab/ci/e2e/.env
index fc4e85ead5..cfcf3eaf7e 100644
--- a/.gitlab/ci/e2e/.env
+++ b/.gitlab/ci/e2e/.env
@@ -1,5 +1,5 @@
RETINA_REGISTRY_PREFIX=registry.gitlab.com/softwareradiosystems/ci/retina
-RETINA_VERSION=0.20.1
+RETINA_VERSION=0.30.5
AMARISOFT_VERSION=2023-03-17
SRSUE_VERSION=23.04.01
OPEN5GS_VERSION=2.5.6
diff --git a/.gitlab/ci/e2e/retina_request_android_n300.yml b/.gitlab/ci/e2e/retina_request_android_n300.yml
index 6eb29fd077..c14c247699 100644
--- a/.gitlab/ci/e2e/retina_request_android_n300.yml
+++ b/.gitlab/ci/e2e/retina_request_android_n300.yml
@@ -40,10 +40,10 @@
requests: "3G"
limit: "3G"
mode: grpc
+ taints: ["retina"]
resources:
- type: sdr
model: n3xx
- nof_ant: 2
shared_files:
- local_path: ../../build/apps/gnb/gnb
remote_path: /usr/local/bin
diff --git a/.gitlab/ci/e2e/retina_request_android_x300.yml b/.gitlab/ci/e2e/retina_request_android_x300.yml
index e9ac64cee2..ef418a77b8 100644
--- a/.gitlab/ci/e2e/retina_request_android_x300.yml
+++ b/.gitlab/ci/e2e/retina_request_android_x300.yml
@@ -12,8 +12,8 @@
requirements:
arch: amd64
cpu:
- requests: 4
- limits: 4
+ requests: 1
+ limits: 1
memory:
requests: "2G"
limits: "2G"
@@ -31,19 +31,19 @@
requirements:
arch: amd64
cpu:
- requests: 10
- limits: 10
+ requests: 11
+ limits: 11
memory:
- requests: "20G"
- limits: "20G"
+ requests: "12G"
+ limits: "12G"
ephemeral-storage:
requests: "3G"
limit: "3G"
mode: grpc
+ taints: ["retina"]
resources:
- type: sdr
model: x300
- nof_ant: 2
shared_files:
- local_path: ../../build/apps/gnb/gnb
remote_path: /usr/local/bin
diff --git a/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml b/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml
index d7eee3022f..aac8509dd0 100644
--- a/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml
+++ b/.gitlab/ci/e2e/retina_request_zmq_4x4_mimo.yml
@@ -15,7 +15,6 @@
requests: 1
memory:
requests: "8G"
- limits: "8G"
ephemeral-storage:
requests: "3G"
limit: "3G"
@@ -38,8 +37,7 @@
cpu:
requests: 1
memory:
- requests: "3G"
- limits: "3G"
+ requests: "8G"
ephemeral-storage:
requests: "3G"
limit: "3G"
@@ -60,7 +58,6 @@
requests: 1
memory:
requests: "4G"
- limits: "4G"
ephemeral-storage:
requests: "3G"
limit: "3G"
diff --git a/.gitlab/ci/e2e/retina_request_zmq_srsue.yml b/.gitlab/ci/e2e/retina_request_zmq_srsue.yml
index f4d4f887d9..a680661e21 100644
--- a/.gitlab/ci/e2e/retina_request_zmq_srsue.yml
+++ b/.gitlab/ci/e2e/retina_request_zmq_srsue.yml
@@ -15,7 +15,6 @@
requests: 1
memory:
requests: "8G"
- limits: "8G"
ephemeral-storage:
requests: "3G"
limit: "3G"
@@ -30,7 +29,6 @@
requests: 1
memory:
requests: "8G"
- limits: "8G"
ephemeral-storage:
requests: "3G"
limit: "3G"
@@ -50,7 +48,6 @@
requests: 1
memory:
requests: "4G"
- limits: "4G"
ephemeral-storage:
requests: "3G"
limit: "3G"
diff --git a/.gitlab/ci/release.yml b/.gitlab/ci/release.yml
index a261ce5998..d96aca6bb1 100644
--- a/.gitlab/ci/release.yml
+++ b/.gitlab/ci/release.yml
@@ -8,10 +8,10 @@
include:
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/setup/all.yml
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/features/all.yml
stages:
@@ -62,10 +62,18 @@ coverity-agpl:
stage: private
rules:
- if: $ON_TAG
+ variables:
+ GIT_STRATEGY: none
+ PRIVATE_BRANCH: agpl_main
before_script:
- export PROJECT_NAME="srsRAN_5G_agpl"
- export DESCRIPTION="srsRAN Project AGPL build"
- export COV_TOKEN="${COVERITY_TOKEN_AGPL}"
+ # Download agpl branch
+ - git config --global user.name "${CODEBOT_USERNAME}"
+ - git config --global user.email "${CODEBOT_LONG_USERNAME}@noreply.gitlab.com"
+ - git clone --depth 1 --branch $PRIVATE_BRANCH https://${CODEBOT_USERNAME}:${CODEBOT_TOKEN}@gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}.git /${CI_PROJECT_NAME}
+ - cd /${CI_PROJECT_NAME}
needs:
- job: update agpl main
optional: false
diff --git a/.gitlab/ci/trx.yml b/.gitlab/ci/trx.yml
index a0eb914269..d2b77dc106 100644
--- a/.gitlab/ci/trx.yml
+++ b/.gitlab/ci/trx.yml
@@ -8,7 +8,7 @@
include:
- project: softwareradiosystems/ci/tools
- ref: "12"
+ ref: "13"
file: .gitlab/ci-shared/setup/all.yml
- local: .gitlab/ci/build.yml
@@ -24,7 +24,6 @@ build trx driver:
timeout: 30 min
rules:
- if: $ON_MR
- - if: $CI_DESCRIPTION =~ /Nightly/
- if: $ON_WEB
retry: 2
script:
@@ -73,7 +72,15 @@ build trx driver:
mv ${CI_PROJECT_DIR}/build/utils/trx_srsran/libtrx_srsran.so ${CI_PROJECT_DIR}/build_trx_srsran/
after_script: []
- artifacts:
+ artifacts: &trx_artifacts
paths:
- build_trx_srsran/libtrx_srsran.so
expire_in: 10 minutes
+
+build amariue zmq driver:
+ extends: build trx driver
+ rules:
+ - if: $CI_DESCRIPTION =~ /Nightly/
+ artifacts:
+ <<: *trx_artifacts
+ expire_in: 1 day
diff --git a/COPYRIGHT b/COPYRIGHT
index 3fa1fd41fc..88619c81c4 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -50,6 +50,16 @@ Copyright: 2015-2017 Michael Park
License: Boost Software License, Version 1.0
+Files: external/rigtorp/MPMCQueue.hpp
+Copyright: 2018 Erik Rigtorp
+License: MIT
+
+
+Files: external/rigtorp/SPSCQueue.hpp
+Copyright: 2018 Erik Rigtorp
+License: MIT
+
+
License: MIT
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
diff --git a/README.md b/README.md
index 9c8f25cfc1..c16e86cc03 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@ srsRAN Project
==============
[![Build Status](https://github.com/srsran/srsRAN_Project/actions/workflows/ccpp.yml/badge.svg?branch=main)](https://github.com/srsran/srsRAN_Project/actions/workflows/ccpp.yml)
-[![CodeQL](https://github.com/srsran/srsRAN_Project/actions/workflows/codeql.yml/badge.svg?branch=main)](https://github.com/srsran/srsRAN_Project/actions/workflows/codeql.yml)
+[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/7868/badge)](https://www.bestpractices.dev/projects/7868)
The srsRAN Project is a complete 5G RAN solution, featuring an ORAN-native CU/DU developed by [SRS](http://www.srs.io).
@@ -34,33 +34,33 @@ You can install the build tools and mandatory requirements for some example dist
Ubuntu 22.04
-
```bash
sudo apt-get install cmake make gcc g++ pkg-config libfftw3-dev libmbedtls-dev libsctp-dev libyaml-cpp-dev libgtest-dev
```
+
Fedora
-
```bash
sudo yum install cmake make gcc gcc-c++ fftw-devel lksctp-tools-devel yaml-cpp-devel mbedtls-devel gtest-devel
```
+
Arch Linux
-
```bash
sudo pacman -S cmake make base-devel fftw mbedtls yaml-cpp lksctp-tools gtest
```
+
The srsRAN Project supports split-8 and split-7.2 fronthaul. Split-8 fronthaul is supported via UHD for USRP devices:
* UHD:
* See UHD documentation for installation instructions.
-
+
Build Instructions
------------------
diff --git a/apps/examples/du/phy_factory.cpp b/apps/examples/du/phy_factory.cpp
index f7f72ecd5f..0ff0eea74b 100644
--- a/apps/examples/du/phy_factory.cpp
+++ b/apps/examples/du/phy_factory.cpp
@@ -74,6 +74,8 @@ std::unique_ptr srsran::create_upper_phy(const upper_phy_params&
upper_config.nof_slots_ul_rg = ul_pipeline_depth * nof_slots_per_subframe;
upper_config.nof_ul_processors = upper_config.nof_slots_ul_rg;
upper_config.max_ul_thread_concurrency = 4;
+ upper_config.max_pusch_concurrency = 1;
+ upper_config.nof_pusch_decoder_threads = 1;
upper_config.nof_prach_buffer = prach_pipeline_depth * nof_slots_per_subframe;
upper_config.max_nof_td_prach_occasions = 1;
upper_config.max_nof_fd_prach_occasions = 1;
@@ -96,6 +98,7 @@ std::unique_ptr srsran::create_upper_phy(const upper_phy_params&
upper_config.rg_gateway = rg_gateway;
upper_config.pucch_executor = ul_executor;
upper_config.pusch_executor = ul_executor;
+ upper_config.pusch_decoder_executor = nullptr;
upper_config.prach_executor = ul_executor;
upper_config.rx_symbol_request_notifier = rx_symbol_request_notifier;
upper_config.crc_calculator_type = "auto";
diff --git a/apps/gnb/CMakeLists.txt b/apps/gnb/CMakeLists.txt
index 88b95538b6..e0ebd56cd7 100644
--- a/apps/gnb/CMakeLists.txt
+++ b/apps/gnb/CMakeLists.txt
@@ -27,6 +27,7 @@ add_executable(gnb
gnb_e2_metric_connector_manager.cpp
gnb_du_factory.cpp
helpers/metrics_plotter_stdout.cpp
+ helpers/metrics_plotter_json.cpp
helpers/gnb_console_helper.cpp
helpers/metrics_hub.cpp
adapters/e1ap_gateway_local_connector.cpp
diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp
index b2c8007200..955b1cb476 100644
--- a/apps/gnb/gnb.cpp
+++ b/apps/gnb/gnb.cpp
@@ -313,7 +313,9 @@ int main(int argc, char** argv)
e2ap_logger.set_hex_dump_max_size(gnb_cfg.log_cfg.hex_max_size);
if (not gnb_cfg.log_cfg.tracing_filename.empty()) {
+ gnb_logger.info("Opening event tracer...");
open_trace_file(gnb_cfg.log_cfg.tracing_filename);
+ gnb_logger.info("Event tracer opened successfully");
}
// Log build info
@@ -336,30 +338,38 @@ int main(int argc, char** argv)
check_drm_kms_polling(gnb_logger);
// Set layer-specific pcap options.
- std::unique_ptr ngap_p = std::make_unique(PCAP_NGAP_DLT, "NGAP");
+ const auto& low_prio_cpu_mask = gnb_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg.mask;
+
+ std::unique_ptr ngap_p = std::make_unique(PCAP_NGAP_DLT, "NGAP", low_prio_cpu_mask);
if (gnb_cfg.pcap_cfg.ngap.enabled) {
- ngap_p->open(gnb_cfg.pcap_cfg.ngap.filename.c_str());
+ ngap_p->open(gnb_cfg.pcap_cfg.ngap.filename);
}
- std::unique_ptr e1ap_p = std::make_unique(PCAP_E1AP_DLT, "E1AP");
+ std::unique_ptr e1ap_p = std::make_unique(PCAP_E1AP_DLT, "E1AP", low_prio_cpu_mask);
if (gnb_cfg.pcap_cfg.e1ap.enabled) {
- e1ap_p->open(gnb_cfg.pcap_cfg.e1ap.filename.c_str());
+ e1ap_p->open(gnb_cfg.pcap_cfg.e1ap.filename);
}
- std::unique_ptr f1ap_p = std::make_unique(PCAP_F1AP_DLT, "F1AP");
+ std::unique_ptr f1ap_p = std::make_unique(PCAP_F1AP_DLT, "F1AP", low_prio_cpu_mask);
if (gnb_cfg.pcap_cfg.f1ap.enabled) {
- f1ap_p->open(gnb_cfg.pcap_cfg.f1ap.filename.c_str());
+ f1ap_p->open(gnb_cfg.pcap_cfg.f1ap.filename);
}
- std::unique_ptr e2ap_p = std::make_unique(PCAP_E2AP_DLT, "E2AP");
+ std::unique_ptr e2ap_p = std::make_unique(PCAP_E2AP_DLT, "E2AP", low_prio_cpu_mask);
if (gnb_cfg.pcap_cfg.e2ap.enabled) {
- e2ap_p->open(gnb_cfg.pcap_cfg.e2ap.filename.c_str());
+ e2ap_p->open(gnb_cfg.pcap_cfg.e2ap.filename);
}
- std::unique_ptr gtpu_p = std::make_unique(PCAP_GTPU_DLT, "GTPU");
+ std::unique_ptr gtpu_p = std::make_unique(PCAP_GTPU_DLT, "GTPU", low_prio_cpu_mask);
if (gnb_cfg.pcap_cfg.gtpu.enabled) {
gtpu_p->open(gnb_cfg.pcap_cfg.gtpu.filename);
}
- std::unique_ptr mac_p = std::make_unique();
+ std::unique_ptr mac_p = std::make_unique(low_prio_cpu_mask);
if (gnb_cfg.pcap_cfg.mac.enabled) {
- mac_p->open(gnb_cfg.pcap_cfg.mac.filename.c_str());
+ if (gnb_cfg.pcap_cfg.mac.type == "dlt") {
+ mac_p->open(gnb_cfg.pcap_cfg.mac.filename, mac_pcap_type::dlt);
+ } else if (gnb_cfg.pcap_cfg.mac.type == "udp") {
+ mac_p->open(gnb_cfg.pcap_cfg.mac.filename, mac_pcap_type::udp);
+ } else {
+ report_error("Invalid type for MAC PCAP. type={}\n", gnb_cfg.pcap_cfg.mac.type);
+ }
}
worker_manager workers{gnb_cfg};
@@ -381,10 +391,17 @@ int main(int argc, char** argv)
std::unique_ptr f1u_conn = std::make_unique();
// Create IO broker.
- std::unique_ptr epoll_broker = create_io_broker(io_broker_type::epoll);
+ io_broker_config io_broker_cfg(low_prio_cpu_mask);
+ std::unique_ptr epoll_broker = create_io_broker(io_broker_type::epoll, io_broker_cfg);
+
+ // Set up the JSON log channel used by metrics.
+ srslog::sink& json_sink =
+ srslog::fetch_file_sink(gnb_cfg.metrics_cfg.json_filename, 0, false, srslog::create_json_formatter());
+ srslog::log_channel& json_channel = srslog::fetch_log_channel("JSON_channel", json_sink, {});
+ json_channel.set_enabled(gnb_cfg.metrics_cfg.enable_json_metrics);
// Create console helper object for commands and metrics printing.
- gnb_console_helper console(*epoll_broker);
+ gnb_console_helper console(*epoll_broker, json_channel);
console.on_app_starting();
std::unique_ptr hub = std::make_unique(*workers.metrics_hub_exec);
@@ -445,18 +462,20 @@ int main(int argc, char** argv)
}
// Create CU-UP config.
- srsran::srs_cu_up::cu_up_configuration cu_up_cfg;
- cu_up_cfg.cu_up_executor = workers.cu_up_exec;
- cu_up_cfg.cu_up_e2_exec = workers.cu_up_e2_exec;
- cu_up_cfg.gtpu_pdu_executor = workers.gtpu_pdu_exec;
- cu_up_cfg.e1ap.e1ap_conn_client = &e1ap_gw;
- cu_up_cfg.f1u_gateway = f1u_conn->get_f1u_cu_up_gateway();
- cu_up_cfg.epoll_broker = epoll_broker.get();
- cu_up_cfg.gtpu_pcap = gtpu_p.get();
- cu_up_cfg.timers = cu_timers;
- cu_up_cfg.net_cfg.n3_bind_addr = gnb_cfg.amf_cfg.bind_addr; // TODO: rename variable to core addr
+ srsran::srs_cu_up::cu_up_configuration cu_up_cfg = generate_cu_up_config(gnb_cfg);
+ cu_up_cfg.cu_up_executor = workers.cu_up_exec;
+ cu_up_cfg.cu_up_e2_exec = workers.cu_up_e2_exec;
+ cu_up_cfg.gtpu_pdu_executor = workers.gtpu_pdu_exec;
+ cu_up_cfg.e1ap.e1ap_conn_client = &e1ap_gw;
+ cu_up_cfg.f1u_gateway = f1u_conn->get_f1u_cu_up_gateway();
+ cu_up_cfg.epoll_broker = epoll_broker.get();
+ cu_up_cfg.gtpu_pcap = gtpu_p.get();
+ cu_up_cfg.timers = cu_timers;
+ cu_up_cfg.net_cfg.n3_bind_addr = gnb_cfg.amf_cfg.bind_addr; // TODO: rename variable to core addr
+ cu_up_cfg.net_cfg.n3_rx_max_mmsg = gnb_cfg.amf_cfg.udp_rx_max_msgs;
cu_up_cfg.net_cfg.f1u_bind_addr =
gnb_cfg.amf_cfg.bind_addr; // FIXME: check if this can be removed for co-located case
+
// create and start CU-UP
std::unique_ptr cu_up_obj = create_cu_up(cu_up_cfg);
cu_up_obj->start();
@@ -560,6 +579,12 @@ int main(int argc, char** argv)
srslog::flush();
+ if (not gnb_cfg.log_cfg.tracing_filename.empty()) {
+ gnb_logger.info("Closing event tracer...");
+ close_trace_file();
+ gnb_logger.info("Event tracer closed successfully");
+ }
+
return 0;
}
diff --git a/apps/gnb/gnb_appconfig.h b/apps/gnb/gnb_appconfig.h
index 0b7c681a92..2c85c849ce 100644
--- a/apps/gnb/gnb_appconfig.h
+++ b/apps/gnb/gnb_appconfig.h
@@ -22,6 +22,7 @@
#pragma once
+#include "gnb_os_sched_affinity_manager.h"
#include "srsran/adt/optional.h"
#include "srsran/adt/variant.h"
#include "srsran/ran/band_helper.h"
@@ -37,6 +38,8 @@
#include "srsran/ran/pusch/pusch_mcs.h"
#include "srsran/ran/rnti.h"
#include "srsran/ran/s_nssai.h"
+#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/unique_thread.h"
#include
@@ -63,6 +66,11 @@ struct prach_appconfig {
/// with the PUCCH, the user should leave some guardband between the PUCCH CRBs and the PRACH PRBs.
/// Possible values: {0,...,MAX_NOF_PRB - 1}.
optional prach_frequency_start;
+ /// Max number of RA preamble transmissions performed before declaring a failure. Values {3, 4, 5, 6, 7, 8, 10, 20,
+ /// 50, 100, 200}.
+ uint8_t preamble_trans_max = 7;
+ /// Power ramping steps for PRACH. Values {0, 2, 4, 6}.
+ uint8_t power_ramping_step_db = 4;
};
/// TDD pattern configuration. See TS 38.331, \c TDD-UL-DL-Pattern.
@@ -72,7 +80,7 @@ struct tdd_ul_dl_pattern_appconfig {
/// Values: {0,...,maxNrofSlots=80}.
unsigned nof_dl_slots = 6;
/// Values: {0,...,maxNrofSymbols-1=13}.
- unsigned nof_dl_symbols = 0;
+ unsigned nof_dl_symbols = 8;
/// Values: {0,...,maxNrofSlots=80}.
unsigned nof_ul_slots = 3;
/// Values: {0,...,maxNrofSymbols-1=13}.
@@ -156,12 +164,14 @@ struct pdsch_appconfig {
std::vector rv_sequence = {0, 2, 3, 1};
/// MCS table to use for PDSCH
pdsch_mcs_table mcs_table = pdsch_mcs_table::qam64;
- /// Number of antenna ports. If empty, the \c nof_ports is derived from the number of DL antennas.
- optional nof_ports;
/// Minimum number of RBs for Resource Allocation of UE PDSCHs.
unsigned min_rb_size = 1;
/// Maximum number of RBs for Resource Allocation of UE PDSCHs.
unsigned max_rb_size = MAX_NOF_PRBS;
+ /// Maximum number of PDSCH grants per slot.
+ unsigned max_pdschs_per_slot = MAX_PDSCH_PDUS_PER_SLOT;
+ /// Maximum number of DL or UL PDCCH allocation attempts per slot.
+ unsigned max_pdcch_alloc_attempts_per_slot = std::max(MAX_DL_PDCCH_PDUS_PER_SLOT, MAX_UL_PDCCH_PDUS_PER_SLOT);
/// CQI offset increment used in outer loop link adaptation (OLLA) algorithm. If set to zero, OLLA is disabled.
float olla_cqi_inc{0.001};
/// DL Target BLER to be achieved with OLLA.
@@ -174,6 +184,12 @@ struct pdsch_appconfig {
/// The numerology of the active DL BWP is used as a reference to determine the number of subcarriers.
/// The DC offset value 0 corresponds to the center of the SCS-Carrier for the numerology of the active DL BWP.
optional dc_offset;
+ /// Link Adaptation (LA) threshold for drop in CQI of the first HARQ transmission above which HARQ retransmissions are
+ /// cancelled.
+ uint8_t harq_la_cqi_drop_threshold{3};
+ /// Link Adaptation (LA) threshold for drop in nof. layers of the first HARQ transmission above which HARQ
+ /// retransmission is cancelled.
+ uint8_t harq_la_ri_drop_threshold{1};
};
/// PUSCH application configuration.
@@ -216,6 +232,8 @@ struct pusch_appconfig {
/// Minimum k2 value (distance in slots between UL PDCCH and PUSCH) that the gNB can use. Values: {1, ..., 32}.
unsigned min_k2 = 4;
+ /// Maximum number of PUSCH grants per slot.
+ unsigned max_puschs_per_slot = MAX_PUSCH_PDUS_PER_SLOT;
/// \brief Direct Current (DC) offset, in number of subcarriers, used in PUSCH.
///
/// The numerology of the active UL BWP is used as a reference to determine the number of subcarriers.
@@ -315,6 +333,51 @@ struct ssb_appconfig {
ssb_pss_to_sss_epre pss_to_sss_epre = ssb_pss_to_sss_epre::dB_0;
};
+/// Configuration of SIBs and SI-message scheduling.
+struct sib_appconfig {
+ struct si_sched_info_config {
+ /// List of SIB indexes (sib2 => value 2 in list, sib3 => value 3 in list, ...) included in this SI message. The
+ /// list has at most 32 elements.
+ std::vector sib_mapping_info;
+ /// Periodicity of the SI-message in radio frames. Values: {8, 16, 32, 64, 128, 256, 512}.
+ unsigned si_period_rf = 32;
+ };
+
+ struct sib_ue_timers_and_constants {
+ /// t300
+ /// Values (in ms): {100, 200, 300, 400, 600, 1000, 1500, 2000}
+ unsigned t300 = 1000;
+ /// t301
+ /// Values (in ms): {100, 200, 300, 400, 600, 1000, 1500, 2000}
+ unsigned t301 = 1000;
+ /// t310
+ /// Values (in ms): {0, 50, 100, 200, 500, 1000, 2000}
+ unsigned t310 = 1000;
+ /// n310
+ /// Values: {1, 2, 3, 4, 6, 8, 10, 20}
+ unsigned n310 = 1;
+ /// t311
+ /// Values (in ms): {1000, 3000, 5000, 10000, 15000, 20000, 30000}
+ unsigned t311 = 30000;
+ /// n311
+ /// Values: {1, 2, 3, 4, 5, 6, 8, 10}
+ unsigned n311 = 1;
+ /// t319
+ /// Values (in ms): {100, 200, 300, 400, 600, 1000, 1500, 2000}
+ unsigned t319 = 1000;
+ };
+
+ /// The length of the SI scheduling window, in slots. It is always shorter or equal to the period of the SI message.
+ /// Values: {5, 10, 20, 40, 80, 160, 320, 640, 1280}.
+ unsigned si_window_len_slots = 160;
+ /// List of SI-messages and associated scheduling information.
+ std::vector si_sched_info;
+ /// UE timers and constants parameters
+ sib_ue_timers_and_constants ue_timers_and_constants;
+ /// Parameters of the SIB19.
+ sib19_info sib19;
+};
+
struct csi_appconfig {
/// \brief \c CSI-RS period in milliseconds. Limited by TS38.214, clause 5.1.6.1.1. Values: {10, 20, 40, 80}.
unsigned csi_rs_period_msec = 20;
@@ -396,6 +459,8 @@ struct base_cell_appconfig {
int q_qual_min = -20;
/// SSB parameters.
ssb_appconfig ssb_cfg;
+ /// SIB parameters.
+ sib_appconfig sib_cfg;
/// UL common configuration parameters.
ul_common_appconfig ul_common_cfg;
/// PDCCH configuration.
@@ -453,6 +518,7 @@ struct rlc_tx_am_appconfig {
uint32_t max_retx_thresh; ///< Max retx threshold
int32_t poll_pdu; ///< Insert poll bit after this many PDUs
int32_t poll_byte; ///< Insert poll bit after this much data (bytes)
+ uint32_t max_window = 0; ///< Custom parameter to limit the maximum window size for memory reasons. 0 means no limit.
};
/// RLC UM RX configuration
@@ -520,6 +586,7 @@ struct amf_appconfig {
int sctp_rto_max = 500;
int sctp_init_max_attempts = 3;
int sctp_max_init_timeo = 500;
+ int udp_rx_max_msgs = 256;
bool no_core = false;
};
@@ -583,8 +650,8 @@ struct mobility_appconfig {
/// \brief RRC specific configuration parameters.
struct rrc_appconfig {
bool force_reestablishment_fallback = false;
- unsigned rrc_procedure_timeout_ms = 360; ///< Timeout for RRC procedures (default SRB maxRetxThreshold *
- ///< t-PollRetransmit = 8 * 45ms = 360ms, see TS 38.331 Sec 9.2.1).
+ unsigned rrc_procedure_timeout_ms = 720; ///< Timeout for RRC procedures (2 * default SRB maxRetxThreshold *
+ ///< t-PollRetransmit = 2 * 8 * 45ms = 720ms, see TS 38.331 Sec 9.2.1).
};
/// \brief Security configuration parameters.
@@ -594,7 +661,8 @@ struct security_appconfig {
};
struct cu_cp_appconfig {
- int inactivity_timer = 7200; // in seconds
+ int inactivity_timer = 7200; // in seconds
+ int ue_context_setup_timeout_s = 2; // in seconds
mobility_appconfig mobility_config;
rrc_appconfig rrc_config;
security_appconfig security_config;
@@ -650,13 +718,19 @@ struct pcap_appconfig {
} gtpu;
struct {
std::string filename = "/tmp/gnb_mac.pcap";
+ std::string type = "udp";
bool enabled = false;
} mac;
};
/// Metrics report configuration.
struct metrics_appconfig {
- unsigned rlc_report_period = 1000; // RLC report period in ms
+ unsigned rlc_report_period = 1000; // RLC report period in ms
+ unsigned cu_cp_statistics_report_period = 1; // Statistics report period in seconds
+ unsigned cu_up_statistics_report_period = 1; // Statistics report period in seconds
+ /// JSON metrics reporting.
+ bool enable_json_metrics = false;
+ std::string json_filename = "/tmp/gnb_metrics.json";
};
/// Lower physical layer thread profiles.
@@ -680,21 +754,6 @@ struct expert_upper_phy_appconfig {
/// demanding cell configurations, such as using large bandwidths or higher order MIMO. Higher values also increase
/// the round trip latency of the radio link.
unsigned max_processing_delay_slots = 2U;
- /// \brief PDSCH processor type.
- ///
- /// Use of there options:
- /// - \c automatic: selects \c lite implementation if \c nof_pdsch_threads is one, otherwise \c concurrent, or
- /// - \c generic: for using unoptimized PDSCH processing, or
- /// - \c concurrent: for using a processor that processes code blocks in parallel, or
- /// - \c lite: for using a memory optimized processor.
- std::string pdsch_processor_type = "auto";
- /// Number of threads for encoding PDSCH concurrently. Only used if \c pdsch_processor_type is set to \c concurrent.
- unsigned nof_pdsch_threads = 1;
- /// Number of threads for processing PUSCH and PUCCH. It is set to 4 by default unless the available hardware
- /// concurrency is limited, in which case the most suitable number of threads between one and three will be selected.
- unsigned nof_ul_threads = std::min(4U, std::max(std::thread::hardware_concurrency(), 4U) - 3U);
- /// Number of threads for processing PDSCH, PDCCH, NZP CSI-RS and SSB. It is set to 1 by default.
- unsigned nof_dl_threads = 1;
/// Number of PUSCH LDPC decoder iterations.
unsigned pusch_decoder_max_iterations = 6;
/// Set to true to enable the PUSCH LDPC decoder early stop.
@@ -735,20 +794,6 @@ struct test_mode_appconfig {
/// Expert SDR Radio Unit configuration.
struct ru_sdr_expert_appconfig {
- ru_sdr_expert_appconfig()
- {
- // Set the lower PHY thread profile according to the number of CPU cores.
- if (srsran::compute_host_nof_hardware_threads() >= 8U) {
- lphy_executor_profile = lower_phy_thread_profile::quad;
- } else {
- lphy_executor_profile = lower_phy_thread_profile::dual;
- }
- }
-
- /// \brief Lower physical layer thread profile.
- ///
- /// If not configured, a default value is selected based on the number of available CPU cores.
- lower_phy_thread_profile lphy_executor_profile;
/// System time-based throttling. See \ref lower_phy_configuration::system_time_throttling for more information.
float lphy_dl_throttling = 0.0F;
};
@@ -821,7 +866,7 @@ struct ru_ofh_base_cell_appconfig {
/// Ta4 minimum parameter for uplink User-Plane in microseconds.
unsigned Ta4_min = 85U;
/// Enables the Control-Plane PRACH message signalling.
- bool is_prach_control_plane_enabled = false;
+ bool is_prach_control_plane_enabled = true;
/// \brief Downlink broadcast flag.
///
/// If enabled, broadcasts the contents of a single antenna port to all downlink RU eAxCs.
@@ -831,15 +876,15 @@ struct ru_ofh_base_cell_appconfig {
/// Uplink compression method.
std::string compression_method_ul = "bfp";
/// Uplink compression bitwidth.
- unsigned compresion_bitwidth_ul = 9;
+ unsigned compression_bitwidth_ul = 9;
/// Downlink compression method.
std::string compression_method_dl = "bfp";
/// Downlink compression bitwidth.
- unsigned compresion_bitwidth_dl = 9;
+ unsigned compression_bitwidth_dl = 9;
/// PRACH compression method.
std::string compression_method_prach = "bfp";
/// PRACH compression bitwidth.
- unsigned compresion_bitwidth_prach = 9;
+ unsigned compression_bitwidth_prach = 9;
/// Downlink static compression header flag.
bool is_downlink_static_comp_hdr_enabled = true;
/// Uplink static compression header flag.
@@ -878,8 +923,6 @@ struct ru_ofh_appconfig {
unsigned dl_processing_time = 400U;
/// Base cell configuration for the Radio Unit.
ru_ofh_base_cell_appconfig base_cell_cfg;
- /// Enables the parallelization of the downlink.
- bool is_downlink_parallelized = true;
/// Individual Open Fronthaul cells configurations.
std::vector cells = {{}};
};
@@ -889,14 +932,84 @@ struct buffer_pool_appconfig {
std::size_t segment_size = 1024;
};
+/// CPU affinities configuration for the gNB app.
+struct cpu_affinities_appconfig {
+ /// L1 uplink CPU affinity mask.
+ gnb_os_sched_affinity_config l1_ul_cpu_cfg;
+ /// L1 downlink workers CPU affinity mask.
+ gnb_os_sched_affinity_config l1_dl_cpu_cfg;
+ /// L2 workers CPU affinity mask.
+ gnb_os_sched_affinity_config l2_cell_cpu_cfg;
+ /// Radio Unit workers CPU affinity mask.
+ gnb_os_sched_affinity_config ru_cpu_cfg;
+ /// Low priority workers CPU affinity mask.
+ gnb_os_sched_affinity_config low_priority_cpu_cfg;
+};
+
+/// Upper PHY thread configuration for the gNB.
+struct upper_phy_threads_appconfig {
+ /// \brief PDSCH processor type.
+ ///
+ /// Use of there options:
+ /// - \c automatic: selects \c lite implementation if \c nof_pdsch_threads is one, otherwise \c concurrent, or
+ /// - \c generic: for using unoptimized PDSCH processing, or
+ /// - \c concurrent: for using a processor that processes code blocks in parallel, or
+ /// - \c lite: for using a memory optimized processor.
+ std::string pdsch_processor_type = "auto";
+ /// Number of threads for encoding PDSCH concurrently. Only used if \c pdsch_processor_type is set to \c concurrent.
+ unsigned nof_pdsch_threads = 1;
+ /// \brief Number of threads for concurrent PUSCH decoding.
+ ///
+ /// If the number of PUSCH decoder threads is greater than zero, the PUSCH decoder will enqueue received soft bits and
+ /// process them asynchronously. Otherwise, PUSCH decoding will be performed synchronously.
+ ///
+ /// In non-real-time operations (e.g., when using ZeroMQ), setting this parameter to a non-zero value can potentially
+ /// introduce delays in uplink HARQ feedback.
+ unsigned nof_pusch_decoder_threads = 0;
+ /// Number of threads for processing PUSCH and PUCCH.
+ unsigned nof_ul_threads = 1;
+ /// Number of threads for processing PDSCH, PDCCH, NZP CSI-RS and SSB. It is set to 1 by default.
+ unsigned nof_dl_threads = 1;
+};
+
+/// Lower PHY thread configuration fo the gNB.
+struct lower_phy_threads_appconfig {
+ lower_phy_threads_appconfig()
+ {
+ // Set the lower PHY thread profile according to the number of CPU cores.
+ if (srsran::compute_host_nof_hardware_threads() >= 8U) {
+ execution_profile = lower_phy_thread_profile::quad;
+ } else {
+ execution_profile = lower_phy_thread_profile::dual;
+ }
+ }
+ /// \brief Lower physical layer thread profile.
+ ///
+ /// If not configured, a default value is selected based on the number of available CPU cores.
+ lower_phy_thread_profile execution_profile;
+};
+
+/// Open Fronthaul thread configuration for the gNB.
+struct ofh_threads_appconfig {
+ bool is_downlink_parallelized = true;
+};
+
+/// Expert threads configuration of the gNB app.
+struct expert_threads_appconfig {
+ /// Upper PHY thread configuration of the gNB app.
+ upper_phy_threads_appconfig upper_threads;
+ /// Lower PHY thread configuration of the gNB app.
+ lower_phy_threads_appconfig lower_threads;
+ /// Open Fronthaul thread configuration of the gNB app.
+ ofh_threads_appconfig ofh_threads;
+};
+
/// Expert configuration of the gNB app.
-struct expert_appconfig {
- /// Enables usage of affinity profile tuned for higher performance.
- bool enable_tuned_affinity_profile = false;
- /// Number of threads per physical CPU.
- unsigned nof_threads_per_cpu = 2;
- /// Number of CPU cores reserved for non-priority tasks.
- unsigned nof_cores_for_non_prio_workers = 4;
+struct expert_execution_appconfig {
+ /// CPU affinities of the gNB app.
+ cpu_affinities_appconfig affinities;
+ /// Expert thread configuration of the gNB app.
+ expert_threads_appconfig threads;
};
/// HAL configuration of the gNB app.
@@ -956,7 +1069,7 @@ struct gnb_appconfig {
buffer_pool_appconfig buffer_pool_config;
/// \brief Expert configuration.
- expert_appconfig expert_config;
+ expert_execution_appconfig expert_execution_cfg;
/// \brief HAL configuration.
optional hal_config;
diff --git a/apps/gnb/gnb_appconfig_cli11_schema.cpp b/apps/gnb/gnb_appconfig_cli11_schema.cpp
index 062d1468da..c4b14af1c9 100644
--- a/apps/gnb/gnb_appconfig_cli11_schema.cpp
+++ b/apps/gnb/gnb_appconfig_cli11_schema.cpp
@@ -161,6 +161,7 @@ static void configure_cli11_pcap_args(CLI::App& app, pcap_appconfig& pcap_params
app.add_option("--f1ap_filename", pcap_params.f1ap.filename, "F1AP PCAP file output path")->capture_default_str();
app.add_option("--f1ap_enable", pcap_params.f1ap.enabled, "Enable F1AP packet capture")->always_capture_default();
app.add_option("--mac_filename", pcap_params.mac.filename, "MAC PCAP file output path")->capture_default_str();
+ app.add_option("--mac_type", pcap_params.mac.type, "MAC PCAP pcap type (DLT or UDP)")->capture_default_str();
app.add_option("--mac_enable", pcap_params.mac.enabled, "Enable MAC packet capture")->always_capture_default();
app.add_option("--e2ap_filename", pcap_params.e2ap.filename, "E2AP PCAP file output path")->capture_default_str();
app.add_option("--e2ap_enable", pcap_params.e2ap.enabled, "Enable E2AP packet capture")->always_capture_default();
@@ -172,6 +173,21 @@ static void configure_cli11_metrics_args(CLI::App& app, metrics_appconfig& metri
{
app.add_option("--rlc_report_period", metrics_params.rlc_report_period, "RLC metrics report period")
->capture_default_str();
+
+ app.add_option("--cu_cp_statistics_report_period",
+ metrics_params.cu_cp_statistics_report_period,
+ "CU-CP statistics report period in seconds. Set this value to 0 to disable this feature")
+ ->capture_default_str();
+
+ app.add_option("--cu_up_statistics_report_period",
+ metrics_params.cu_up_statistics_report_period,
+ "CU-UP statistics report period in seconds. Set this value to 0 to disable this feature")
+ ->capture_default_str();
+
+ app.add_option("--enable_json_metrics", metrics_params.enable_json_metrics, "Enable JSON metrics reporting")
+ ->always_capture_default();
+ app.add_option("--json_metrics_filename", metrics_params.json_filename, "JSON metrics output path")
+ ->capture_default_str();
}
static void configure_cli11_slicing_args(CLI::App& app, s_nssai_t& slice_params)
@@ -193,6 +209,7 @@ static void configure_cli11_amf_args(CLI::App& app, amf_appconfig& amf_params)
app.add_option("--sctp_rto_max", amf_params.sctp_rto_max, "SCTP RTO max");
app.add_option("--sctp_init_max_attempts", amf_params.sctp_init_max_attempts, "SCTP init max attempts");
app.add_option("--sctp_max_init_timeo", amf_params.sctp_max_init_timeo, "SCTP max init timeout");
+ app.add_option("--udp_max_rx_msgs", amf_params.udp_rx_max_msgs, "Maximum amount of messages RX in a single syscall");
app.add_option("--no_core", amf_params.no_core, "Allow gNB to run without a core");
}
@@ -355,6 +372,12 @@ 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. If the value is reached, the UE will be released")
+ ->capture_default_str();
+
CLI::App* mobility_subcmd = app.add_subcommand("mobility", "Mobility configuration");
configure_cli11_mobility_args(*mobility_subcmd, cu_cp_params.mobility_config);
@@ -367,13 +390,6 @@ static void configure_cli11_cu_cp_args(CLI::App& app, cu_cp_appconfig& cu_cp_par
static void configure_cli11_expert_phy_args(CLI::App& app, expert_upper_phy_appconfig& expert_phy_params)
{
- auto pdsch_processor_check = [](const std::string& value) -> std::string {
- if ((value == "auto") || (value == "generic") || (value == "concurrent") || (value == "lite")) {
- return {};
- }
- return "Invalid PDSCH processor type. Accepted values [auto,generic,concurrent,lite]";
- };
-
auto pusch_sinr_method_check = [](const std::string& value) -> std::string {
if ((value == "channel_estimator") || (value == "post_equalization") || (value == "evm")) {
return {};
@@ -386,21 +402,6 @@ static void configure_cli11_expert_phy_args(CLI::App& app, expert_upper_phy_appc
"Maximum allowed DL processing delay in slots.")
->capture_default_str()
->check(CLI::Range(1, 30));
- app.add_option("--pdsch_processor_type",
- expert_phy_params.pdsch_processor_type,
- "PDSCH processor type: auto, generic, concurrent and lite.")
- ->capture_default_str()
- ->check(pdsch_processor_check);
- app.add_option("--nof_pdsch_threads", expert_phy_params.nof_pdsch_threads, "Number of threads to encode PDSCH.")
- ->capture_default_str()
- ->check(CLI::Number);
- app.add_option("--nof_ul_threads", expert_phy_params.nof_ul_threads, "Number of upper PHY threads to process uplink.")
- ->capture_default_str()
- ->check(CLI::Number);
- app.add_option(
- "--nof_dl_threads", expert_phy_params.nof_dl_threads, "Number of upper PHY threads to process downlink.")
- ->capture_default_str()
- ->check(CLI::Number);
app.add_option("--pusch_dec_max_iterations",
expert_phy_params.pusch_decoder_max_iterations,
"Maximum number of PUSCH LDPC decoder iterations")
@@ -523,17 +524,22 @@ static void configure_cli11_pdsch_args(CLI::App& app, pdsch_appconfig& pdsch_par
"MCS table to use PDSCH")
->default_str("qam64")
->check(CLI::IsMember({"qam64", "qam256"}, CLI::ignore_case));
- app.add_option("--nof_ports",
- pdsch_params.nof_ports,
- "Number of ports for PDSCH. By default it is set to be equal to number of DL antennas")
- ->capture_default_str()
- ->check(CLI::IsMember({1, 2, 4}));
app.add_option("--min_rb_size", pdsch_params.min_rb_size, "Minimum RB size for UE PDSCH resource allocation")
->capture_default_str()
->check(CLI::Range(1U, (unsigned)MAX_NOF_PRBS));
app.add_option("--max_rb_size", pdsch_params.max_rb_size, "Maximum RB size for UE PDSCH resource allocation")
->capture_default_str()
->check(CLI::Range(1U, (unsigned)MAX_NOF_PRBS));
+ app.add_option("--max_pdschs_per_slot",
+ pdsch_params.max_pdschs_per_slot,
+ "Maximum number of PDSCH grants per slot, including SIB, RAR, Paging and UE data grants.")
+ ->capture_default_str()
+ ->check(CLI::Range(1U, (unsigned)MAX_PDSCH_PDUS_PER_SLOT));
+ app.add_option("--max_alloc_attempts",
+ pdsch_params.max_pdcch_alloc_attempts_per_slot,
+ "Maximum number of DL or UL PDCCH grant allocation attempts per slot before scheduler skips the slot")
+ ->capture_default_str()
+ ->check(CLI::Range(1U, (unsigned)std::max(MAX_DL_PDCCH_PDUS_PER_SLOT, MAX_UL_PDCCH_PDUS_PER_SLOT)));
app.add_option("--olla_cqi_inc_step",
pdsch_params.olla_cqi_inc,
"Outer-loop link adaptation (OLLA) increment value. The value 0 means that OLLA is disabled")
@@ -569,6 +575,18 @@ static void configure_cli11_pdsch_args(CLI::App& app, pdsch_appconfig& pdsch_par
->capture_default_str()
->check(CLI::Range(static_cast(dc_offset_t::min), static_cast(dc_offset_t::max)) |
CLI::IsMember({"outside", "undetermined", "center"}));
+ app.add_option("--harq_la_cqi_drop_threshold",
+ pdsch_params.harq_la_cqi_drop_threshold,
+ "Link Adaptation (LA) threshold for drop in CQI of the first HARQ transmission above which HARQ "
+ "retransmissions are cancelled. Set this value to 0 to disable this feature")
+ ->capture_default_str()
+ ->check(CLI::Range(0, 15));
+ app.add_option("--harq_la_ri_drop_threshold",
+ pdsch_params.harq_la_ri_drop_threshold,
+ "Link Adaptation (LA) threshold for drop in nof. layers of the first HARQ transmission above which "
+ "HARQ retransmission is cancelled. Set this value to 0 to disable this feature")
+ ->capture_default_str()
+ ->check(CLI::Range(0, 4));
}
static void configure_cli11_pusch_args(CLI::App& app, pusch_appconfig& pusch_params)
@@ -633,6 +651,9 @@ static void configure_cli11_pusch_args(CLI::App& app, pusch_appconfig& pusch_par
return "";
});
+ app.add_option("--max_puschs_per_slot", pusch_params.max_puschs_per_slot, "Maximum number of PUSCH grants per slot")
+ ->capture_default_str()
+ ->check(CLI::Range(1U, (unsigned)MAX_PUSCH_PDUS_PER_SLOT));
app.add_option("--b_offset_ack_idx_1", pusch_params.b_offset_ack_idx_1, "betaOffsetACK-Index1 part of UCI-OnPUSCH")
->capture_default_str()
->check(CLI::Range(0, 31));
@@ -826,6 +847,99 @@ static void configure_cli11_ssb_args(CLI::App& app, ssb_appconfig& ssb_params)
->check(CLI::IsMember({0, 3}));
}
+static void configure_cli11_si_sched_info(CLI::App& app, sib_appconfig::si_sched_info_config& si_sched_info)
+{
+ app.add_option("--si_period", si_sched_info.si_period_rf, "SI message scheduling period in radio frames")
+ ->capture_default_str()
+ ->check(CLI::IsMember({8, 16, 32, 64, 128, 256, 512}));
+ app.add_option("--sib_mapping",
+ si_sched_info.sib_mapping_info,
+ "Mapping of SIB types to SI-messages. SIB numbers should not be repeated")
+ ->capture_default_str()
+ ->check(CLI::IsMember({19}));
+}
+
+static void configure_cli11_sib19_args(CLI::App& app, sib19_info& sib19)
+{
+ app.add_option("--distance_thres", sib19.distance_thres, "Distance threshold for SIB19")
+ ->capture_default_str()
+ ->check(CLI::Range(0, 255));
+ // TODO: Add remaining parameters.
+}
+
+static void configure_cli11_sib_args(CLI::App& app, sib_appconfig& sib_params)
+{
+ app.add_option(
+ "--si_window_length",
+ sib_params.si_window_len_slots,
+ "The length of the SI scheduling window, in slots. It must be always shorter or equal to the period of "
+ "the SI message.")
+ ->capture_default_str()
+ ->check(CLI::IsMember({5, 10, 20, 40, 80, 160, 320, 640, 1280}));
+
+ CLI::App* sib19_subcmd = app.add_subcommand("sib19", "Content of SIB19");
+ configure_cli11_sib19_args(*sib19_subcmd, sib_params.sib19);
+
+ // SI message scheduling parameters.
+ app.add_option_function>(
+ "--si_sched_info",
+ [&sib_params](const std::vector& values) {
+ sib_params.si_sched_info.resize(values.size());
+
+ for (unsigned i = 0, e = values.size(); i != e; ++i) {
+ CLI::App subapp("SI-message scheduling information");
+ subapp.config_formatter(create_yaml_config_parser());
+ subapp.allow_config_extras(CLI::config_extras_mode::error);
+ configure_cli11_si_sched_info(subapp, sib_params.si_sched_info[i]);
+ std::istringstream ss(values[i]);
+ subapp.parse_from_stream(ss);
+ }
+ },
+ "Configures the scheduling for each of the SI-messages broadcast by the gNB");
+
+ app.add_option("--t300",
+ sib_params.ue_timers_and_constants.t300,
+ "RRC Connection Establishment timer in ms. The timer starts upon transmission of RRCSetupRequest.")
+ ->capture_default_str()
+ ->check(CLI::IsMember({100, 200, 300, 400, 600, 1000, 1500, 2000}));
+ app.add_option("--t301",
+ sib_params.ue_timers_and_constants.t301,
+ "RRC Connection Re-establishment timer in ms. The timer starts upon transmission of "
+ "RRCReestablishmentRequest.")
+ ->capture_default_str()
+ ->check(CLI::IsMember({100, 200, 300, 400, 600, 1000, 1500, 2000}));
+ app.add_option("--t310",
+ sib_params.ue_timers_and_constants.t310,
+ "Out-of-sync timer in ms. The timer starts upon detecting physical layer problems for the SpCell i.e. "
+ "upon receiving N310 consecutive out-of-sync indications from lower layers.")
+ ->capture_default_str()
+ ->check(CLI::IsMember({0, 50, 100, 200, 500, 1000, 2000}));
+ app.add_option("--n310",
+ sib_params.ue_timers_and_constants.n310,
+ "Out-of-sync counter. The counter is increased upon reception of \"out-of-sync\" from lower layer "
+ "while the timer T310 is stopped. Starts the timer T310, when configured value is reached.")
+ ->capture_default_str()
+ ->check(CLI::IsMember({1, 2, 3, 4, 6, 8, 10, 20}));
+ app.add_option("--t311",
+ sib_params.ue_timers_and_constants.t311,
+ "RRC Connection Re-establishment procedure timer in ms. The timer starts upon initiating the RRC "
+ "connection re-establishment procedure.")
+ ->capture_default_str()
+ ->check(CLI::IsMember({1000, 3000, 5000, 10000, 15000, 20000, 30000}));
+ app.add_option("--n311",
+ sib_params.ue_timers_and_constants.n311,
+ "In-sync counter. The counter is increased upon reception of the \"in-sync\" from lower layer while "
+ "the timer T310 is running. Stops the timer T310, when configured value is reached.")
+ ->capture_default_str()
+ ->check(CLI::IsMember({1, 2, 3, 4, 5, 6, 8, 10}));
+ app.add_option("--t319",
+ sib_params.ue_timers_and_constants.t319,
+ "RRC Connection Resume timer in ms. The timer starts upon transmission of RRCResumeRequest "
+ "or RRCResumeRequest1.")
+ ->capture_default_str()
+ ->check(CLI::IsMember({100, 200, 300, 400, 600, 1000, 1500, 2000}));
+}
+
static void configure_cli11_prach_args(CLI::App& app, prach_appconfig& prach_params)
{
app.add_option("--prach_config_index",
@@ -868,6 +982,14 @@ static void configure_cli11_prach_args(CLI::App& app, prach_appconfig& prach_par
return "";
});
+ app.add_option("--preamble_trans_max",
+ prach_params.preamble_trans_max,
+ "Max number of RA preamble transmissions performed before declaring a failure")
+ ->capture_default_str()
+ ->check(CLI::IsMember({3, 4, 5, 6, 7, 8, 10, 20, 50, 100, 200}));
+ app.add_option("--power_ramping_step_db", prach_params.power_ramping_step_db, "Power ramping steps for PRACH")
+ ->capture_default_str()
+ ->check(CLI::IsMember({0, 2, 4, 6}));
}
static void configure_cli11_amplitude_control_args(CLI::App& app, amplitude_control_appconfig& amplitude_params)
@@ -1105,6 +1227,10 @@ static void configure_cli11_common_cell_args(CLI::App& app, base_cell_appconfig&
CLI::App* ssb_subcmd = app.add_subcommand("ssb", "SSB parameters");
configure_cli11_ssb_args(*ssb_subcmd, cell_params.ssb_cfg);
+ // SIB configuration.
+ CLI::App* sib_subcmd = app.add_subcommand("sib", "SIB configuration parameters");
+ configure_cli11_sib_args(*sib_subcmd, cell_params.sib_cfg);
+
// UL common configuration.
CLI::App* ul_common_subcmd = app.add_subcommand("ul_common", "UL common parameters");
configure_cli11_ul_common_args(*ul_common_subcmd, cell_params.ul_common_cfg);
@@ -1178,6 +1304,12 @@ static void configure_cli11_rlc_am_args(CLI::App& app, rlc_am_appconfig& rlc_am_
->capture_default_str();
rlc_tx_am_subcmd->add_option("--poll-pdu", rlc_am_params.tx.poll_pdu, "RLC AM TX PollPdu")->capture_default_str();
rlc_tx_am_subcmd->add_option("--poll-byte", rlc_am_params.tx.poll_byte, "RLC AM TX PollByte")->capture_default_str();
+ rlc_tx_am_subcmd
+ ->add_option("--max_window",
+ rlc_am_params.tx.max_window,
+ "Non-standard parameter that limits the tx window size. Can be used for limiting memory usage with "
+ "large windows. 0 means no limits other than the SN size (i.e. 2^[sn_size-1]).")
+ ->capture_default_str();
CLI::App* rlc_rx_am_subcmd = app.add_subcommand("rx", "AM RX parameters");
rlc_rx_am_subcmd->add_option("--sn", rlc_am_params.rx.sn_field_length, "RLC AM RX SN")->capture_default_str();
rlc_rx_am_subcmd->add_option("--t-reassembly", rlc_am_params.rx.t_reassembly, "RLC AM RX t-Reassembly")
@@ -1319,26 +1451,6 @@ static void configure_cli11_ru_sdr_expert_args(CLI::App& app, ru_sdr_expert_appc
config.lphy_dl_throttling,
"Throttles the lower PHY DL baseband generation. The range is (0, 1). Set it to zero to disable it.")
->capture_default_str();
-
- app.add_option_function(
- "--low_phy_thread_profile",
- [&config](const std::string& value) {
- if (value == "single") {
- config.lphy_executor_profile = lower_phy_thread_profile::single;
- } else if (value == "dual") {
- config.lphy_executor_profile = lower_phy_thread_profile::dual;
- } else if (value == "quad") {
- config.lphy_executor_profile = lower_phy_thread_profile::quad;
- }
- },
- "Lower physical layer executor profile [single, dual, quad].")
- ->check([](const std::string& value) -> std::string {
- if ((value == "single") || (value == "dual") || (value == "quad")) {
- return "";
- }
-
- return "Invalid executor profile. Valid profiles are: single, dual and quad.";
- });
}
static void configure_cli11_ru_sdr_args(CLI::App& app, ru_sdr_appconfig& config)
@@ -1492,19 +1604,19 @@ static void configure_cli11_ru_ofh_base_cell_args(CLI::App& app, ru_ofh_base_cel
app.add_option("--compr_method_ul", config.compression_method_ul, "Uplink compression method")
->capture_default_str()
->check(compression_method_check);
- app.add_option("--compr_bitwidth_ul", config.compresion_bitwidth_ul, "Uplink compression bit width")
+ app.add_option("--compr_bitwidth_ul", config.compression_bitwidth_ul, "Uplink compression bit width")
->capture_default_str()
->check(CLI::Range(1, 16));
app.add_option("--compr_method_dl", config.compression_method_dl, "Downlink compression method")
->capture_default_str()
->check(compression_method_check);
- app.add_option("--compr_bitwidth_dl", config.compresion_bitwidth_dl, "Downlink compression bit width")
+ app.add_option("--compr_bitwidth_dl", config.compression_bitwidth_dl, "Downlink compression bit width")
->capture_default_str()
->check(CLI::Range(1, 16));
app.add_option("--compr_method_prach", config.compression_method_prach, "PRACH compression method")
->capture_default_str()
->check(compression_method_check);
- app.add_option("--compr_bitwidth_prach", config.compresion_bitwidth_prach, "PRACH compression bit width")
+ app.add_option("--compr_bitwidth_prach", config.compression_bitwidth_prach, "PRACH compression bit width")
->capture_default_str()
->check(CLI::Range(1, 16));
app.add_option("--enable_ul_static_compr_hdr",
@@ -1556,9 +1668,6 @@ static void configure_cli11_ru_ofh_args(CLI::App& app, ru_ofh_appconfig& config)
{
app.add_option("--gps_alpha", config.gps_Alpha, "GPS Alpha")->capture_default_str()->check(CLI::Range(0.0, 1.2288e7));
app.add_option("--gps_beta", config.gps_Beta, "GPS Beta")->capture_default_str()->check(CLI::Range(-32768, 32767));
- app.add_option(
- "--enable_dl_parallelization", config.is_downlink_parallelized, "Open Fronthaul downlink parallelization flag")
- ->capture_default_str();
// Common cell parameters.
configure_cli11_ru_ofh_base_cell_args(app, config.base_cell_cfg);
@@ -1584,19 +1693,7 @@ static void configure_cli11_ru_ofh_args(CLI::App& app, ru_ofh_appconfig& config)
"Sets the cell configuration on a per cell basis, overwriting the default configuration defined by cell_cfg");
}
-static void parse_ru_ofh_config(CLI::App& app, ru_ofh_appconfig& ofh_cfg)
-{
- CLI::App* ru_ofh_subcmd = app.add_subcommand("ru_ofh", "Open Fronthaul Radio Unit configuration")->configurable();
- configure_cli11_ru_ofh_args(*ru_ofh_subcmd, ofh_cfg);
-}
-
-static void parse_ru_sdr_config(CLI::App& app, ru_sdr_appconfig& sdr_cfg)
-{
- CLI::App* ru_sdr_subcmd = app.add_subcommand("ru_sdr", "SDR Radio Unit configuration")->configurable();
- configure_cli11_ru_sdr_args(*ru_sdr_subcmd, sdr_cfg);
-}
-
-static void parse_buffer_pool_config(CLI::App& app, buffer_pool_appconfig& config)
+static void configure_cli11_buffer_pool_args(CLI::App& app, buffer_pool_appconfig& config)
{
app.add_option("--nof_segments", config.nof_segments, "Number of segments allocated by the buffer pool")
->capture_default_str();
@@ -1604,27 +1701,258 @@ static void parse_buffer_pool_config(CLI::App& app, buffer_pool_appconfig& confi
->capture_default_str();
}
-static void parse_hal_config(CLI::App& app, optional& config)
+static void configure_cli11_hal_args(CLI::App& app, optional& config)
{
config.emplace();
app.add_option("--eal_args", config->eal_args, "EAL configuration parameters used to initialize DPDK");
}
-static void parse_expert_config(CLI::App& app, expert_appconfig& config)
+static error_type is_valid_cpu_index(unsigned cpu_idx)
{
- app.add_option("--enable_tuned_affinity_profile",
- config.enable_tuned_affinity_profile,
- "Enable usage of tuned affinity profile")
- ->capture_default_str();
- app.add_option("--number_of_threads_per_cpu", config.nof_threads_per_cpu, "Number of threads per physical CPU")
+ unsigned nof_cpus = compute_host_nof_hardware_threads();
+ if (cpu_idx >= nof_cpus) {
+ return fmt::format("Invalid CPU core selected '{}'. Valid range is [{}-{}]", cpu_idx, 0, nof_cpus - 1);
+ }
+
+ return default_success_t();
+}
+
+static expected parse_one_cpu(const std::string& value)
+{
+ expected result = parse_int(value);
+
+ if (result.is_error()) {
+ return fmt::format("Could not parse '{}' string as a CPU index", value);
+ }
+
+ error_type validation_result = is_valid_cpu_index(result.value());
+ if (validation_result.is_error()) {
+ return validation_result.error();
+ }
+
+ return result.value();
+}
+
+static expected, std::string> parse_cpu_range(const std::string& value)
+{
+ std::vector range;
+ std::stringstream ss(value);
+ while (ss.good()) {
+ std::string str;
+ getline(ss, str, '-');
+ auto parse_result = parse_one_cpu(str);
+ if (parse_result.is_error()) {
+ return fmt::format("{}. Could not parse '{}' as a range", parse_result.error(), value);
+ }
+
+ range.push_back(parse_result.value());
+ }
+
+ // A range is defined by two numbers.
+ if (range.size() != 2) {
+ return fmt::format("Could not parse '{}' as a range", value);
+ }
+
+ if (range[1] <= range[0]) {
+ return fmt::format("Invalid CPU core range detected [{}-{}]", range[0], range[1]);
+ }
+
+ return interval(range[0], range[1]);
+}
+
+static void configure_cli11_affinity_args(CLI::App& app, cpu_affinities_appconfig& config)
+{
+ auto parsing_fcn = [](os_sched_affinity_bitmask& mask, const std::string& value, const std::string& property_name) {
+ std::stringstream ss(value);
+
+ while (ss.good()) {
+ std::string str;
+ getline(ss, str, ',');
+ if (str.find('-') != std::string::npos) {
+ auto range = parse_cpu_range(str);
+ if (range.is_error()) {
+ report_error("{} in the '{}' property", range.error(), property_name);
+ }
+
+ // Add 1 to the stop value as the fill method excludes the end position.
+ mask.fill(range.value().start(), range.value().stop() + 1);
+ } else {
+ auto cpu_idx = parse_one_cpu(str);
+ if (cpu_idx.is_error()) {
+ report_error("{} in the '{}' property", cpu_idx.error(), property_name);
+ }
+
+ mask.set(cpu_idx.value());
+ }
+ }
+ };
+
+ app.add_option_function(
+ "--l1_dl_cpus",
+ [&config, &parsing_fcn](const std::string& value) {
+ parsing_fcn(config.l1_dl_cpu_cfg.mask, value, "l1_dl_cpus");
+ },
+ "CPU cores assigned to L1 downlink tasks");
+
+ app.add_option_function(
+ "--l1_ul_cpus",
+ [&config, &parsing_fcn](const std::string& value) {
+ parsing_fcn(config.l1_ul_cpu_cfg.mask, value, "l1_ul_cpus");
+ },
+ "CPU cores assigned to L1 uplink tasks");
+
+ app.add_option_function(
+ "--l2_cell_cpus",
+ [&config, &parsing_fcn](const std::string& value) {
+ parsing_fcn(config.l2_cell_cpu_cfg.mask, value, "l2_cell_cpus");
+ },
+ "CPU cores assigned to L2 cell tasks");
+
+ app.add_option_function(
+ "--low_priority_cpus",
+ [&config, &parsing_fcn](const std::string& value) {
+ parsing_fcn(config.low_priority_cpu_cfg.mask, value, "low_priority_cpus");
+ },
+ "CPU cores assigned to low priority tasks");
+
+ app.add_option_function(
+ "--ru_cpus",
+ [&config, &parsing_fcn](const std::string& value) { parsing_fcn(config.ru_cpu_cfg.mask, value, "ru_cpus"); },
+ "Number of CPUs used for the Radio Unit tasks");
+
+ app.add_option_function(
+ "--l1_dl_pinning",
+ [&config](const std::string& value) {
+ config.l1_dl_cpu_cfg.pinning_policy = to_affinity_mask_policy(value);
+ if (config.l1_dl_cpu_cfg.pinning_policy == gnb_sched_affinity_mask_policy::last) {
+ report_error("Incorrect value={} used in {} property", value, "l1_dl_pinning");
+ }
+ },
+ "Policy used for assigning CPU cores to L1 downlink tasks");
+
+ app.add_option_function(
+ "--l1_ul_pinning",
+ [&config](const std::string& value) {
+ config.l1_ul_cpu_cfg.pinning_policy = to_affinity_mask_policy(value);
+ if (config.l1_ul_cpu_cfg.pinning_policy == gnb_sched_affinity_mask_policy::last) {
+ report_error("Incorrect value={} used in {} property", value, "l1_ul_pinning");
+ }
+ },
+ "Policy used for assigning CPU cores to L1 uplink tasks");
+
+ app.add_option_function(
+ "--l2_cell_pinning",
+ [&config](const std::string& value) {
+ config.l2_cell_cpu_cfg.pinning_policy = to_affinity_mask_policy(value);
+ if (config.l2_cell_cpu_cfg.pinning_policy == gnb_sched_affinity_mask_policy::last) {
+ report_error("Incorrect value={} used in {} property", value, "l2_cell_pinning");
+ }
+ },
+ "Policy used for assigning CPU cores to L2 cell tasks");
+
+ app.add_option_function(
+ "--low_priority_pinning",
+ [&config](const std::string& value) {
+ config.low_priority_cpu_cfg.pinning_policy = to_affinity_mask_policy(value);
+ if (config.low_priority_cpu_cfg.pinning_policy == gnb_sched_affinity_mask_policy::last) {
+ report_error("Incorrect value={} used in {} property", value, "low_priority_pinning");
+ }
+ },
+ "Policy used for assigning CPU cores to low priority tasks");
+
+ app.add_option_function(
+ "--ru_pinning",
+ [&config](const std::string& value) {
+ config.ru_cpu_cfg.pinning_policy = to_affinity_mask_policy(value);
+ if (config.ru_cpu_cfg.pinning_policy == gnb_sched_affinity_mask_policy::last) {
+ report_error("Incorrect value={} used in {} property", value, "ru_pinning");
+ }
+ },
+ "Policy used for assigning CPU cores to the Radio Unit tasks");
+}
+
+static void configure_cli11_upper_phy_threads_args(CLI::App& app, upper_phy_threads_appconfig& config)
+{
+ auto pdsch_processor_check = [](const std::string& value) -> std::string {
+ if ((value == "auto") || (value == "generic") || (value == "concurrent") || (value == "lite")) {
+ return {};
+ }
+ return "Invalid PDSCH processor type. Accepted values [auto,generic,concurrent,lite]";
+ };
+
+ app.add_option("--pdsch_processor_type",
+ config.pdsch_processor_type,
+ "PDSCH processor type: auto, generic, concurrent and lite.")
->capture_default_str()
- ->check(CLI::Range(1, 2));
- app.add_option("--number_of_reserved_cores",
- config.nof_cores_for_non_prio_workers,
- "Number of CPU cores reserved for non-priority tasks")
+ ->check(pdsch_processor_check);
+ app.add_option("--nof_pdsch_threads", config.nof_pdsch_threads, "Number of threads to encode PDSCH.")
->capture_default_str()
- ->check(CLI::Range(0, 1024));
+ ->check(CLI::Number);
+ app.add_option("--nof_pusch_decoder_threads", config.nof_pusch_decoder_threads, "Number of threads to decode PUSCH.")
+ ->capture_default_str()
+ ->check(CLI::Number);
+ app.add_option("--nof_ul_threads", config.nof_ul_threads, "Number of upper PHY threads to process uplink.")
+ ->capture_default_str()
+ ->check(CLI::Number);
+ app.add_option("--nof_dl_threads", config.nof_dl_threads, "Number of upper PHY threads to process downlink.")
+ ->capture_default_str()
+ ->check(CLI::Number);
+}
+
+static void configure_cli11_lower_phy_threads_args(CLI::App& app, lower_phy_threads_appconfig& config)
+{
+ app.add_option_function(
+ "--execution_profile",
+ [&config](const std::string& value) {
+ if (value == "single") {
+ config.execution_profile = lower_phy_thread_profile::single;
+ } else if (value == "dual") {
+ config.execution_profile = lower_phy_thread_profile::dual;
+ } else if (value == "quad") {
+ config.execution_profile = lower_phy_thread_profile::quad;
+ }
+ },
+ "Lower physical layer executor profile [single, dual, quad].")
+ ->check([](const std::string& value) -> std::string {
+ if ((value == "single") || (value == "dual") || (value == "quad")) {
+ return "";
+ }
+
+ return "Invalid executor profile. Valid profiles are: single, dual and quad.";
+ });
+}
+
+static void configure_cli11_ofh_threads_args(CLI::App& app, ofh_threads_appconfig& config)
+{
+ app.add_option(
+ "--enable_dl_parallelization", config.is_downlink_parallelized, "Open Fronthaul downlink parallelization flag")
+ ->capture_default_str();
+}
+
+static void configure_cli11_expert_execution_args(CLI::App& app, expert_execution_appconfig& config)
+{
+ // Affinity section.
+ CLI::App* affinity_subcmd = app.add_subcommand("affinities", "CPU affinities")->configurable();
+ configure_cli11_affinity_args(*affinity_subcmd, config.affinities);
+
+ // Threads section.
+ CLI::App* threads_subcmd = app.add_subcommand("threads", "Threads configuration")->configurable();
+
+ // Upper PHY threads.
+ CLI::App* upper_phy_threads_subcmd =
+ threads_subcmd->add_subcommand("upper_phy", "Upper PHY thread configuration")->configurable();
+ configure_cli11_upper_phy_threads_args(*upper_phy_threads_subcmd, config.threads.upper_threads);
+
+ // Lower PHY threads.
+ CLI::App* lower_phy_threads_subcmd =
+ threads_subcmd->add_subcommand("lower_phy", "Lower PHY thread configuration")->configurable();
+ configure_cli11_lower_phy_threads_args(*lower_phy_threads_subcmd, config.threads.lower_threads);
+
+ // OFH threads.
+ CLI::App* ofh_threads_subcmd =
+ threads_subcmd->add_subcommand("ofh", "Open Fronthaul thread configuration")->configurable();
+ configure_cli11_ofh_threads_args(*ofh_threads_subcmd, config.threads.ofh_threads);
}
static void manage_ru_variant(CLI::App& app,
@@ -1695,9 +2023,12 @@ void srsran::configure_cli11_with_gnb_appconfig_schema(CLI::App& app, gnb_appcon
// variable, but as it is requested later, the variable needs to be static.
// RU section.
static ru_ofh_appconfig ofh_cfg;
- parse_ru_ofh_config(app, ofh_cfg);
+ CLI::App* ru_ofh_subcmd = app.add_subcommand("ru_ofh", "Open Fronthaul Radio Unit configuration")->configurable();
+ configure_cli11_ru_ofh_args(*ru_ofh_subcmd, ofh_cfg);
+
static ru_sdr_appconfig sdr_cfg;
- parse_ru_sdr_config(app, sdr_cfg);
+ CLI::App* ru_sdr_subcmd = app.add_subcommand("ru_sdr", "SDR Radio Unit configuration")->configurable();
+ configure_cli11_ru_sdr_args(*ru_sdr_subcmd, sdr_cfg);
// Common cell parameters.
CLI::App* common_cell_subcmd = app.add_subcommand("cell_cfg", "Default cell configuration")->configurable();
@@ -1774,18 +2105,18 @@ void srsran::configure_cli11_with_gnb_appconfig_schema(CLI::App& app, gnb_appcon
// Buffer pool section.
CLI::App* buffer_pool_subcmd = app.add_subcommand("buffer_pool", "Buffer pool configuration")->configurable();
- parse_buffer_pool_config(*buffer_pool_subcmd, gnb_cfg.buffer_pool_config);
+ configure_cli11_buffer_pool_args(*buffer_pool_subcmd, gnb_cfg.buffer_pool_config);
// Test mode section.
CLI::App* test_mode_subcmd = app.add_subcommand("test_mode", "Test mode configuration")->configurable();
configure_cli11_test_mode_args(*test_mode_subcmd, gnb_cfg.test_mode_cfg);
// Expert section.
- CLI::App* expert_subcmd = app.add_subcommand("expert", "Expert configuration")->configurable();
- parse_expert_config(*expert_subcmd, gnb_cfg.expert_config);
+ CLI::App* expert_subcmd = app.add_subcommand("expert_execution", "Expert execution configuration")->configurable();
+ configure_cli11_expert_execution_args(*expert_subcmd, gnb_cfg.expert_execution_cfg);
CLI::App* hal_subcmd = app.add_subcommand("hal", "HAL configuration")->configurable();
- parse_hal_config(*hal_subcmd, gnb_cfg.hal_config);
+ configure_cli11_hal_args(*hal_subcmd, gnb_cfg.hal_config);
app.callback([&]() {
manage_ru_variant(app, gnb_cfg, sdr_cfg, ofh_cfg);
diff --git a/apps/gnb/gnb_appconfig_translators.cpp b/apps/gnb/gnb_appconfig_translators.cpp
index 4474f11104..06c27f6514 100644
--- a/apps/gnb/gnb_appconfig_translators.cpp
+++ b/apps/gnb/gnb_appconfig_translators.cpp
@@ -125,7 +125,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.inactivity_timer = std::chrono::seconds{config.cu_cp_cfg.inactivity_timer};
+ out_cfg.ngap_config.ue_context_setup_timeout_s = std::chrono::seconds{config.cu_cp_cfg.ue_context_setup_timeout_s};
+ 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 =
config.cu_cp_cfg.mobility_config.trigger_handover_from_measurements;
@@ -255,6 +257,14 @@ srs_cu_cp::cu_cp_configuration srsran::generate_cu_cp_config(const gnb_appconfig
return out_cfg;
}
+srs_cu_up::cu_up_configuration srsran::generate_cu_up_config(const gnb_appconfig& config)
+{
+ srs_cu_up::cu_up_configuration out_cfg;
+ out_cfg.statistics_report_period = std::chrono::seconds{config.metrics_cfg.cu_up_statistics_report_period};
+
+ return out_cfg;
+}
+
static pcch_config generate_pcch_config(const base_cell_appconfig& cell)
{
pcch_config cfg{};
@@ -308,11 +318,6 @@ static unsigned get_nof_rbs(const base_cell_appconfig& cell_cfg)
cell_cfg.channel_bw_mhz, cell_cfg.common_scs, band_helper::get_freq_range(*cell_cfg.band));
}
-static unsigned get_nof_dl_ports(const base_cell_appconfig& cell_cfg)
-{
- return cell_cfg.pdsch_cfg.nof_ports.has_value() ? *cell_cfg.pdsch_cfg.nof_ports : cell_cfg.nof_antennas_dl;
-}
-
static tdd_ul_dl_config_common generate_tdd_pattern(subcarrier_spacing scs, const tdd_ul_dl_appconfig& config)
{
tdd_ul_dl_config_common out;
@@ -342,7 +347,7 @@ static void fill_csi_resources(serving_cell_config& out_cell, const base_cell_ap
csi_helper::csi_builder_params csi_params{};
csi_params.pci = cell_cfg.pci;
csi_params.nof_rbs = get_nof_rbs(cell_cfg);
- csi_params.nof_ports = get_nof_dl_ports(cell_cfg);
+ csi_params.nof_ports = cell_cfg.nof_antennas_dl;
csi_params.csi_rs_period = static_cast(csi_cfg.csi_rs_period_msec *
get_nof_slots_per_subframe(cell_cfg.common_scs));
if (cell_cfg.tdd_ul_dl_cfg.has_value()) {
@@ -407,7 +412,7 @@ std::vector srsran::generate_du_cell_config(const gnb_appconfig&
param.band = *base_cell.band;
// Enable CSI-RS if the PDSCH mcs is dynamic (min_ue_mcs != max_ue_mcs).
param.csi_rs_enabled = cell.cell.pdsch_cfg.min_ue_mcs != cell.cell.pdsch_cfg.max_ue_mcs;
- param.nof_dl_ports = get_nof_dl_ports(base_cell);
+ param.nof_dl_ports = base_cell.nof_antennas_dl;
param.search_space0_index = base_cell.pdcch_cfg.common.ss0_index;
param.min_k1 = base_cell.pucch_cfg.min_k1;
param.min_k2 = base_cell.pusch_cfg.min_k2;
@@ -466,6 +471,43 @@ std::vector srsran::generate_du_cell_config(const gnb_appconfig&
out_cell.ssb_cfg.ssb_block_power = base_cell.ssb_cfg.ssb_block_power;
out_cell.ssb_cfg.pss_to_sss_epre = base_cell.ssb_cfg.pss_to_sss_epre;
+ // SI message config.
+ if (not base_cell.sib_cfg.si_sched_info.empty()) {
+ out_cell.si_config.emplace();
+ out_cell.si_config->si_window_len_slots = base_cell.sib_cfg.si_window_len_slots;
+ out_cell.si_config->si_sched_info.resize(base_cell.sib_cfg.si_sched_info.size());
+ std::vector sibs_included;
+ for (unsigned i = 0; i != base_cell.sib_cfg.si_sched_info.size(); ++i) {
+ auto& out_si = out_cell.si_config->si_sched_info[i];
+ out_si.si_period_radio_frames = base_cell.sib_cfg.si_sched_info[i].si_period_rf;
+ out_si.sib_mapping_info.resize(base_cell.sib_cfg.si_sched_info[i].sib_mapping_info.size());
+ for (unsigned j = 0; j != base_cell.sib_cfg.si_sched_info[i].sib_mapping_info.size(); ++j) {
+ sibs_included.push_back(base_cell.sib_cfg.si_sched_info[i].sib_mapping_info[j]);
+ out_si.sib_mapping_info[j] = static_cast(sibs_included.back());
+ }
+ }
+ for (const uint8_t sib_id : sibs_included) {
+ sib_info item;
+ switch (sib_id) {
+ case 19: {
+ item = base_cell.sib_cfg.sib19;
+ } break;
+ default:
+ report_error("SIB{} not supported\n", sib_id);
+ }
+ out_cell.si_config->sibs.push_back(item);
+ }
+ }
+
+ // UE timers and constants config.
+ out_cell.ue_timers_and_constants.t300 = std::chrono::milliseconds(base_cell.sib_cfg.ue_timers_and_constants.t300);
+ out_cell.ue_timers_and_constants.t301 = std::chrono::milliseconds(base_cell.sib_cfg.ue_timers_and_constants.t301);
+ out_cell.ue_timers_and_constants.t310 = std::chrono::milliseconds(base_cell.sib_cfg.ue_timers_and_constants.t310);
+ out_cell.ue_timers_and_constants.n310 = base_cell.sib_cfg.ue_timers_and_constants.n310;
+ out_cell.ue_timers_and_constants.t311 = std::chrono::milliseconds(base_cell.sib_cfg.ue_timers_and_constants.t311);
+ out_cell.ue_timers_and_constants.n311 = base_cell.sib_cfg.ue_timers_and_constants.n311;
+ out_cell.ue_timers_and_constants.t319 = std::chrono::milliseconds(base_cell.sib_cfg.ue_timers_and_constants.t319);
+
// Carrier config.
out_cell.dl_carrier.nof_ant = base_cell.nof_antennas_dl;
out_cell.ul_carrier.nof_ant = base_cell.nof_antennas_ul;
@@ -483,8 +525,10 @@ std::vector srsran::generate_du_cell_config(const gnb_appconfig&
}
// PRACH config.
- rach_config_common& rach_cfg = *out_cell.ul_cfg_common.init_ul_bwp.rach_cfg_common;
- rach_cfg.rach_cfg_generic.prach_config_index = base_cell.prach_cfg.prach_config_index.value();
+ rach_config_common& rach_cfg = *out_cell.ul_cfg_common.init_ul_bwp.rach_cfg_common;
+ rach_cfg.rach_cfg_generic.prach_config_index = base_cell.prach_cfg.prach_config_index.value();
+ rach_cfg.rach_cfg_generic.preamble_trans_max = base_cell.prach_cfg.preamble_trans_max;
+ rach_cfg.rach_cfg_generic.power_ramping_step_db = base_cell.prach_cfg.power_ramping_step_db;
const bool is_long_prach =
is_long_preamble(prach_configuration_get(band_helper::get_freq_range(param.band.value()),
band_helper::get_duplex_mode(param.band.value()),
@@ -843,6 +887,7 @@ std::map srsran::generate_du_qos_config(const gnb_appc
out_rlc.am.tx.max_retx_thresh = qos.rlc.am.tx.max_retx_thresh;
out_rlc.am.tx.poll_pdu = qos.rlc.am.tx.poll_pdu;
out_rlc.am.tx.poll_byte = qos.rlc.am.tx.poll_byte;
+ out_rlc.am.tx.max_window = qos.rlc.am.tx.max_window;
//< RX SN
if (!from_number(out_rlc.am.rx.sn_field_length, qos.rlc.am.rx.sn_field_length)) {
report_error("Invalid RLC AM RX SN: 5QI={}, SN={}\n", qos.five_qi, qos.rlc.am.rx.sn_field_length);
@@ -861,11 +906,12 @@ std::map srsran::generate_du_qos_config(const gnb_appc
}
/// Fills the given low PHY configuration from the given gnb configuration.
-static void generate_low_phy_config(lower_phy_configuration& out_cfg,
- const cell_appconfig& config,
- const ru_sdr_appconfig& ru_cfg,
- const ru_sdr_cell_appconfig& ru_cell_cfg,
- unsigned max_processing_delay_slot)
+static void generate_low_phy_config(lower_phy_configuration& out_cfg,
+ const cell_appconfig& config,
+ const ru_sdr_appconfig& ru_cfg,
+ const ru_sdr_cell_appconfig& ru_cell_cfg,
+ const lower_phy_threads_appconfig& low_phy_threads_cfg,
+ unsigned max_processing_delay_slot)
{
const base_cell_appconfig& cell_cfg = config.cell;
out_cfg.scs = cell_cfg.common_scs;
@@ -888,7 +934,7 @@ static void generate_low_phy_config(lower_phy_configuration& out_cfg,
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 (ru_cfg.expert_cfg.lphy_executor_profile == lower_phy_thread_profile::single) {
+ } 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.
@@ -1081,6 +1127,7 @@ static void generate_ru_generic_config(ru_generic_configuration& out_cfg, const
config.cells_cfg[i],
ru_cfg,
ru_cfg.cells[i],
+ config.expert_execution_cfg.threads.lower_threads,
config.expert_phy_cfg.max_processing_delay_slots);
}
}
@@ -1150,11 +1197,11 @@ generate_ru_ofh_config(ru_ofh_configuration& out_cfg, const gnb_appconfig& confi
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.ul_compression_params = {ofh::to_compression_type(cell_cfg.cell.compression_method_ul),
- cell_cfg.cell.compresion_bitwidth_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.compresion_bitwidth_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.compresion_bitwidth_prach};
+ cell_cfg.cell.compression_bitwidth_prach};
sector_cfg.iq_scaling = cell_cfg.cell.iq_scaling;
sector_cfg.tci = cell_cfg.vlan_tag;
@@ -1219,8 +1266,8 @@ std::vector srsran::generate_du_low_config(const gnb_appconfig
ldpc::MAX_MESSAGE_SIZE;
// Assume the minimum number of codeblocks per softbuffer.
const unsigned min_cb_softbuffer = 2;
- // Assume that the maximum number of codeblocks is equal to the number of HARQ processes times the maximum number of
- // codeblocks per slot.
+ // Assume that the maximum number of codeblocks is equal to the number of HARQ processes times the maximum number
+ // of codeblocks per slot.
const unsigned max_nof_codeblocks =
std::max(max_harq_process * max_nof_pusch_cb_slot, min_cb_softbuffer * max_softbuffers);
@@ -1234,6 +1281,9 @@ std::vector srsran::generate_du_low_config(const gnb_appconfig
const prach_configuration prach_cfg =
prach_configuration_get(frequency_range::FR1, duplex, cell.prach_cfg.prach_config_index.value());
+ // Maximum number of HARQ processes for a PUSCH HARQ process.
+ static constexpr unsigned max_nof_pusch_harq = 16;
+
cfg.log_level = srslog::str_to_basic_level(config.log_cfg.phy_level);
cfg.enable_logging_broadcast = config.log_cfg.broadcast_enabled;
cfg.rx_symbol_printer_filename = config.log_cfg.phy_rx_symbols_filename;
@@ -1247,7 +1297,10 @@ std::vector srsran::generate_du_low_config(const gnb_appconfig
cfg.nof_dl_processors = dl_pipeline_depth;
cfg.nof_slots_ul_rg = ul_pipeline_depth;
cfg.nof_ul_processors = ul_pipeline_depth;
- cfg.max_ul_thread_concurrency = config.expert_phy_cfg.nof_ul_threads + 1;
+ cfg.max_ul_thread_concurrency = config.expert_execution_cfg.threads.upper_threads.nof_ul_threads + 1;
+ cfg.max_pusch_concurrency = MAX_UE_PDUS_PER_SLOT * max_nof_pusch_harq;
+ cfg.nof_pusch_decoder_threads = config.expert_execution_cfg.threads.upper_threads.nof_pusch_decoder_threads +
+ config.expert_execution_cfg.threads.upper_threads.nof_ul_threads;
cfg.nof_prach_buffer = prach_pipeline_depth * nof_slots_per_subframe;
cfg.max_nof_td_prach_occasions = prach_cfg.nof_occasions_within_slot;
cfg.max_nof_fd_prach_occasions = 1;
@@ -1294,14 +1347,20 @@ scheduler_expert_config srsran::generate_scheduler_expert_config(const gnb_appco
const pdsch_appconfig& pdsch = config.common_cell_cfg.pdsch_cfg;
out_cfg.ue.dl_mcs = {pdsch.min_ue_mcs, pdsch.max_ue_mcs};
out_cfg.ue.pdsch_rv_sequence.assign(pdsch.rv_sequence.begin(), pdsch.rv_sequence.end());
+ out_cfg.ue.dl_harq_la_cqi_drop_threshold = pdsch.harq_la_cqi_drop_threshold;
+ out_cfg.ue.dl_harq_la_ri_drop_threshold = pdsch.harq_la_ri_drop_threshold;
+ out_cfg.ue.max_pdschs_per_slot = pdsch.max_pdschs_per_slot;
+ out_cfg.ue.max_pdcch_alloc_attempts_per_slot = pdsch.max_pdcch_alloc_attempts_per_slot;
+ out_cfg.ue.pdsch_nof_rbs = {pdsch.min_rb_size, pdsch.max_rb_size};
+ out_cfg.ue.olla_dl_target_bler = pdsch.olla_target_bler;
+ out_cfg.ue.olla_cqi_inc = pdsch.olla_cqi_inc;
+ out_cfg.ue.olla_max_cqi_offset = pdsch.olla_max_cqi_offset;
+
const pusch_appconfig& pusch = config.common_cell_cfg.pusch_cfg;
out_cfg.ue.ul_mcs = {pusch.min_ue_mcs, pusch.max_ue_mcs};
out_cfg.ue.pusch_rv_sequence.assign(pusch.rv_sequence.begin(), pusch.rv_sequence.end());
- out_cfg.ue.pdsch_nof_rbs = {pdsch.min_rb_size, pdsch.max_rb_size};
out_cfg.ue.initial_ul_dc_offset = pusch.dc_offset;
- out_cfg.ue.olla_dl_target_bler = pdsch.olla_target_bler;
- out_cfg.ue.olla_cqi_inc = pdsch.olla_cqi_inc;
- out_cfg.ue.olla_max_cqi_offset = pdsch.olla_max_cqi_offset;
+ out_cfg.ue.max_puschs_per_slot = pusch.max_puschs_per_slot;
out_cfg.ue.olla_ul_target_bler = pusch.olla_target_bler;
out_cfg.ue.olla_ul_snr_inc = pusch.olla_snr_inc;
out_cfg.ue.olla_max_ul_snr_offset = pusch.olla_max_snr_offset;
diff --git a/apps/gnb/gnb_appconfig_translators.h b/apps/gnb/gnb_appconfig_translators.h
index d94975a98c..94edf6f81d 100644
--- a/apps/gnb/gnb_appconfig_translators.h
+++ b/apps/gnb/gnb_appconfig_translators.h
@@ -23,6 +23,7 @@
#pragma once
#include "srsran/cu_cp/cu_cp_configuration.h"
+#include "srsran/cu_up/cu_up_configuration.h"
#include "srsran/du/du_cell_config.h"
#include "srsran/du/du_qos_config.h"
#include "srsran/e2/e2ap_configuration.h"
@@ -52,6 +53,9 @@ srsran::sctp_network_gateway_config generate_ngap_nw_config(const gnb_appconfig&
/// Converts and returns the given gnb application configuration to a CU-CP configuration.
srs_cu_cp::cu_cp_configuration generate_cu_cp_config(const gnb_appconfig& config);
+/// Converts and returns the given gnb application configuration to a CU-UP configuration.
+srs_cu_up::cu_up_configuration generate_cu_up_config(const gnb_appconfig& config);
+
/// Converts and returns the given gnb application configuration to a DU cell configuration.
std::vector generate_du_cell_config(const gnb_appconfig& config);
diff --git a/apps/gnb/gnb_appconfig_validators.cpp b/apps/gnb/gnb_appconfig_validators.cpp
index 2812136d18..af31cb0fd7 100644
--- a/apps/gnb/gnb_appconfig_validators.cpp
+++ b/apps/gnb/gnb_appconfig_validators.cpp
@@ -111,6 +111,22 @@ static bool validate_ru_ofh_appconfig(const gnb_appconfig& config)
return false;
}
+ if (cell_cfg.nof_antennas_ul > ofh_cell.ru_ul_port_id.size()) {
+ fmt::print("RU number of uplink ports={} must be equal or greater than the number of reception antennas={}\n",
+ ofh_cell.ru_ul_port_id.size(),
+ cell_cfg.nof_antennas_ul);
+
+ return false;
+ }
+
+ if (cell_cfg.nof_antennas_ul > ofh_cell.ru_prach_port_id.size()) {
+ fmt::print("RU number of PRACH ports={} must be equal or greater than the number of reception antennas={}\n",
+ ofh_cell.ru_prach_port_id.size(),
+ cell_cfg.nof_antennas_ul);
+
+ return false;
+ }
+
if (!validate_ru_duplicated_ports(ofh_cell.ru_dl_port_id)) {
fmt::print("Detected duplicated downlink port identifiers\n");
@@ -296,6 +312,38 @@ static bool validate_dl_arfcn_and_band(const base_cell_appconfig& config)
return true;
}
+static bool validate_cell_sib_config(const base_cell_appconfig& cell_cfg)
+{
+ const sib_appconfig& sib_cfg = cell_cfg.sib_cfg;
+
+ for (const auto& si_msg : sib_cfg.si_sched_info) {
+ const unsigned si_period_slots =
+ si_msg.si_period_rf * get_nof_slots_per_subframe(cell_cfg.common_scs) * NOF_SUBFRAMES_PER_FRAME;
+ if (sib_cfg.si_window_len_slots > si_period_slots) {
+ fmt::print("The SI window length in slots {} is larger than the SI message period {}.\n",
+ sib_cfg.si_window_len_slots,
+ si_period_slots);
+ return false;
+ }
+ }
+
+ // Check if there are repeated SIBs in the SI messages.
+ std::vector sibs_included;
+ for (const auto& si_msg : sib_cfg.si_sched_info) {
+ for (const uint8_t sib_it : si_msg.sib_mapping_info) {
+ sibs_included.push_back(sib_it);
+ }
+ }
+ std::sort(sibs_included.begin(), sibs_included.end());
+ const auto duplicate_it = std::adjacent_find(sibs_included.begin(), sibs_included.end());
+ if (duplicate_it != sibs_included.end()) {
+ fmt::print("The SIB{} cannot be included more than once in the broadcast SI messages", *duplicate_it);
+ return false;
+ }
+
+ return true;
+}
+
/// Validates the given cell application configuration. Returns true on success, otherwise false.
static bool validate_base_cell_appconfig(const base_cell_appconfig& config)
{
@@ -326,6 +374,15 @@ static bool validate_base_cell_appconfig(const base_cell_appconfig& config)
return false;
}
+ const auto ssb_scs =
+ band_helper::get_most_suitable_ssb_scs(band_helper::get_band_from_dl_arfcn(config.dl_arfcn), config.common_scs);
+ if (ssb_scs != config.common_scs) {
+ fmt::print("Common SCS {}kHz is not equal to SSB SCS {}kHz. Different SCS for common and SSB is not supported.\n",
+ scs_to_khz(config.common_scs),
+ scs_to_khz(ssb_scs));
+ return false;
+ }
+
if (!validate_pdsch_cell_app_config(config.pdsch_cfg)) {
return false;
}
@@ -348,10 +405,7 @@ static bool validate_base_cell_appconfig(const base_cell_appconfig& config)
return false;
}
- if (config.pdsch_cfg.nof_ports.has_value() and config.nof_antennas_dl < *config.pdsch_cfg.nof_ports) {
- fmt::print("Number of PDSCH ports {} cannot be higher than the number of DL antennas {}\n",
- *config.pdsch_cfg.nof_ports,
- config.nof_antennas_dl);
+ if (!validate_cell_sib_config(config)) {
return false;
}
@@ -504,23 +558,8 @@ static bool validate_log_appconfig(const log_appconfig& config)
/// Validates expert physical layer configuration parameters.
static bool validate_expert_phy_appconfig(const expert_upper_phy_appconfig& config)
{
- static const interval nof_ul_dl_threads_range(1, std::thread::hardware_concurrency());
- static const interval nof_pdsch_threads_range(2, std::thread::hardware_concurrency());
-
bool valid = true;
- if (!nof_ul_dl_threads_range.contains(config.nof_ul_threads)) {
- fmt::print(
- "Number of PHY UL threads (i.e., {}) must be in range {}.\n", config.nof_ul_threads, nof_ul_dl_threads_range);
- valid = false;
- }
-
- if ((config.pdsch_processor_type != "auto") && (config.pdsch_processor_type != "concurrent") &&
- config.pdsch_processor_type != "generic" && (config.pdsch_processor_type != "lite")) {
- fmt::print("Invalid PDSCH processor type. Valid types are: auto, generic, concurrent and lite.\n");
- valid = false;
- }
-
if ((config.pusch_sinr_calc_method != "channel_estimator") &&
(config.pusch_sinr_calc_method != "post_equalization") && (config.pusch_sinr_calc_method != "evm")) {
fmt::print(
@@ -528,35 +567,6 @@ static bool validate_expert_phy_appconfig(const expert_upper_phy_appconfig& conf
valid = false;
}
- if ((config.pdsch_processor_type == "concurrent") && !nof_pdsch_threads_range.contains(config.nof_pdsch_threads)) {
- fmt::print("For concurrent PDSCH processor. Number of PHY PDSCH threads (i.e., {}) must be in range {}.\n",
- config.nof_pdsch_threads,
- nof_pdsch_threads_range);
- valid = false;
- } else if ((config.pdsch_processor_type == "auto") && !nof_ul_dl_threads_range.contains(config.nof_pdsch_threads)) {
- fmt::print("For auto PDSCH processor. Number of PHY PDSCH threads (i.e., {}) must be in range {}.\n",
- config.nof_pdsch_threads,
- nof_ul_dl_threads_range);
- valid = false;
- } else if ((config.pdsch_processor_type != "auto") && (config.pdsch_processor_type != "concurrent") &&
- (config.nof_pdsch_threads > 1)) {
- fmt::print("Number of PHY PDSCH threads (i.e., {}) is ignored.\n", config.nof_pdsch_threads);
- }
-
- if (!nof_ul_dl_threads_range.contains(config.nof_dl_threads)) {
- fmt::print(
- "Number of PHY DL threads (i.e., {}) must be in range {}.\n", config.nof_dl_threads, nof_ul_dl_threads_range);
- valid = false;
- }
-
- if (config.nof_dl_threads > config.max_processing_delay_slots) {
- fmt::print("Number of PHY DL threads (i.e., {}) cannot be larger than the maximum processing delay in slots "
- "(i.e., {}).\n",
- config.nof_dl_threads,
- config.max_processing_delay_slots);
- valid = false;
- }
-
if (config.pusch_decoder_max_iterations == 0) {
fmt::print("Maximum PUSCH LDPC decoder iterations cannot be zero.\n");
valid = false;
@@ -640,13 +650,10 @@ static bool validate_test_mode_appconfig(const gnb_appconfig& config)
fmt::print("For test mode, RI shall not be set if UE is configured to use DCI format 1_0\n");
return false;
}
- unsigned nof_ports = config.common_cell_cfg.pdsch_cfg.nof_ports.has_value()
- ? *config.common_cell_cfg.pdsch_cfg.nof_ports
- : config.common_cell_cfg.nof_antennas_dl;
- if (config.test_mode_cfg.test_ue.ri > nof_ports) {
+ if (config.test_mode_cfg.test_ue.ri > config.common_cell_cfg.nof_antennas_dl) {
fmt::print("For test mode, RI cannot be higher than the number of DL antenna ports ({} > {})\n",
config.test_mode_cfg.test_ue.ri,
- config.common_cell_cfg.pdsch_cfg.nof_ports);
+ config.common_cell_cfg.nof_antennas_dl);
return false;
}
@@ -716,6 +723,68 @@ static bool validate_hal_config(const optional& config)
return true;
}
+static bool validate_upper_phy_threads_appconfig(const upper_phy_threads_appconfig& config,
+ unsigned max_processing_delay_slots)
+{
+ static const interval nof_ul_dl_threads_range(1, std::thread::hardware_concurrency());
+ static const interval nof_pdsch_threads_range(2, std::thread::hardware_concurrency());
+
+ bool valid = true;
+
+ if (!nof_ul_dl_threads_range.contains(config.nof_ul_threads)) {
+ fmt::print(
+ "Number of PHY UL threads (i.e., {}) must be in range {}.\n", config.nof_ul_threads, nof_ul_dl_threads_range);
+ valid = false;
+ }
+
+ if ((config.pdsch_processor_type != "auto") && (config.pdsch_processor_type != "concurrent") &&
+ config.pdsch_processor_type != "generic" && (config.pdsch_processor_type != "lite")) {
+ fmt::print("Invalid PDSCH processor type. Valid types are: auto, generic, concurrent and lite.\n");
+ valid = false;
+ }
+
+ if ((config.pdsch_processor_type == "concurrent") && !nof_pdsch_threads_range.contains(config.nof_pdsch_threads)) {
+ fmt::print("For concurrent PDSCH processor. Number of PHY PDSCH threads (i.e., {}) must be in range {}.\n",
+ config.nof_pdsch_threads,
+ nof_pdsch_threads_range);
+ valid = false;
+ } else if ((config.pdsch_processor_type == "auto") && !nof_ul_dl_threads_range.contains(config.nof_pdsch_threads)) {
+ fmt::print("For auto PDSCH processor. Number of PHY PDSCH threads (i.e., {}) must be in range {}.\n",
+ config.nof_pdsch_threads,
+ nof_ul_dl_threads_range);
+ valid = false;
+ } else if ((config.pdsch_processor_type != "auto") && (config.pdsch_processor_type != "concurrent") &&
+ (config.nof_pdsch_threads > 1)) {
+ fmt::print("Number of PHY PDSCH threads (i.e., {}) is ignored.\n", config.nof_pdsch_threads);
+ }
+
+ if (!nof_ul_dl_threads_range.contains(config.nof_dl_threads)) {
+ fmt::print(
+ "Number of PHY DL threads (i.e., {}) must be in range {}.\n", config.nof_dl_threads, nof_ul_dl_threads_range);
+ valid = false;
+ }
+
+ if (config.nof_dl_threads > max_processing_delay_slots) {
+ fmt::print("Number of PHY DL threads (i.e., {}) cannot be larger than the maximum processing delay in slots "
+ "(i.e., {}).\n",
+ config.nof_dl_threads,
+ max_processing_delay_slots);
+ valid = false;
+ }
+
+ return valid;
+}
+
+static bool validate_expert_execution_appconfig(const gnb_appconfig& config)
+{
+ if (!validate_upper_phy_threads_appconfig(config.expert_execution_cfg.threads.upper_threads,
+ config.expert_phy_cfg.max_processing_delay_slots)) {
+ return false;
+ }
+
+ return true;
+}
+
bool srsran::validate_appconfig(const gnb_appconfig& config)
{
if (!validate_log_appconfig(config.log_cfg)) {
@@ -800,5 +869,9 @@ bool srsran::validate_appconfig(const gnb_appconfig& config)
return false;
}
+ if (!validate_expert_execution_appconfig(config)) {
+ return false;
+ }
+
return true;
}
diff --git a/apps/gnb/gnb_du_factory.cpp b/apps/gnb/gnb_du_factory.cpp
index 3696806ef8..b0ce63f107 100644
--- a/apps/gnb/gnb_du_factory.cpp
+++ b/apps/gnb/gnb_du_factory.cpp
@@ -35,6 +35,7 @@ static du_low_configuration create_du_low_config(const gnb_appconfig&
span dl_executors,
task_executor* pucch_executor,
task_executor* pusch_executor,
+ task_executor* pusch_decoder_executor,
task_executor* prach_executor,
task_executor* pdsch_codeblock_executor,
upper_phy_rx_symbol_request_notifier* rx_symbol_request_notifier)
@@ -46,20 +47,25 @@ static du_low_configuration create_du_low_config(const gnb_appconfig&
du_lo_cfg.dl_proc_cfg.ldpc_encoder_type = "auto";
du_lo_cfg.dl_proc_cfg.crc_calculator_type = "auto";
- if ((params.expert_phy_cfg.pdsch_processor_type == "lite") ||
- ((params.expert_phy_cfg.pdsch_processor_type == "auto") && (params.expert_phy_cfg.nof_pdsch_threads == 1))) {
+ const upper_phy_threads_appconfig& upper_phy_threads_cfg = params.expert_execution_cfg.threads.upper_threads;
+
+ if ((upper_phy_threads_cfg.pdsch_processor_type == "lite") ||
+ ((upper_phy_threads_cfg.pdsch_processor_type == "auto") && (upper_phy_threads_cfg.nof_pdsch_threads == 1))) {
du_lo_cfg.dl_proc_cfg.pdsch_processor.emplace();
- } else if ((params.expert_phy_cfg.pdsch_processor_type == "concurrent") ||
- ((params.expert_phy_cfg.pdsch_processor_type == "auto") &&
- (params.expert_phy_cfg.nof_pdsch_threads > 1))) {
+ } else if ((upper_phy_threads_cfg.pdsch_processor_type == "concurrent") ||
+ ((upper_phy_threads_cfg.pdsch_processor_type == "auto") &&
+ (upper_phy_threads_cfg.nof_pdsch_threads > 1))) {
pdsch_processor_concurrent_configuration pdsch_proc_config;
- pdsch_proc_config.nof_pdsch_codeblock_threads = params.expert_phy_cfg.nof_pdsch_threads;
+ pdsch_proc_config.nof_pdsch_codeblock_threads = params.expert_execution_cfg.threads.upper_threads.nof_dl_threads +
+ params.expert_execution_cfg.threads.upper_threads.nof_pdsch_threads;
+ pdsch_proc_config.max_nof_simultaneous_pdsch =
+ (MAX_UE_PDUS_PER_SLOT + 1) * params.expert_phy_cfg.max_processing_delay_slots;
pdsch_proc_config.pdsch_codeblock_task_executor = pdsch_codeblock_executor;
du_lo_cfg.dl_proc_cfg.pdsch_processor.emplace(pdsch_proc_config);
- } else if (params.expert_phy_cfg.pdsch_processor_type == "generic") {
+ } else if (upper_phy_threads_cfg.pdsch_processor_type == "generic") {
du_lo_cfg.dl_proc_cfg.pdsch_processor.emplace();
} else {
- srsran_assert(false, "Invalid PDSCH processor type {}.", params.expert_phy_cfg.pdsch_processor_type);
+ srsran_assert(false, "Invalid PDSCH processor type {}.", upper_phy_threads_cfg.pdsch_processor_type);
}
du_lo_cfg.upper_phy = generate_du_low_config(params);
@@ -70,6 +76,7 @@ static du_low_configuration create_du_low_config(const gnb_appconfig&
cfg.dl_executors = dl_executors;
cfg.pucch_executor = pucch_executor;
cfg.pusch_executor = pusch_executor;
+ cfg.pusch_decoder_executor = pusch_decoder_executor;
cfg.prach_executor = prach_executor;
cfg.rx_symbol_request_notifier = rx_symbol_request_notifier;
cfg.crc_calculator_type = "auto";
@@ -109,7 +116,12 @@ std::vector> srsran::make_gnb_dus(const gnb_appconfig&
}
// Connect Console Aggregator to DU Scheduler UE metrics.
- source_->add_subscriber(console_helper.get_metrics_notifier());
+ source_->add_subscriber(console_helper.get_stdout_metrics_notifier());
+
+ // Connect JSON metrics reporter to DU Scheduler UE metrics.
+ if (gnb_cfg.metrics_cfg.enable_json_metrics) {
+ source_->add_subscriber(console_helper.get_json_metrics_notifier());
+ }
// Connect E2 agent to DU Scheduler UE metrics.
if (gnb_cfg.e2_cfg.enable_du_e2) {
@@ -137,6 +149,7 @@ std::vector> srsran::make_gnb_dus(const gnb_appconfig&
du_low_dl_exec,
workers.upper_pucch_exec[i],
workers.upper_pusch_exec[i],
+ workers.upper_pusch_decoder_exec[i],
workers.upper_prach_exec[i],
workers.upper_pdsch_exec[i],
&rx_symbol_request_notifier);
diff --git a/apps/gnb/gnb_os_sched_affinity_manager.h b/apps/gnb/gnb_os_sched_affinity_manager.h
new file mode 100644
index 0000000000..0e66c271d4
--- /dev/null
+++ b/apps/gnb/gnb_os_sched_affinity_manager.h
@@ -0,0 +1,166 @@
+/*
+ *
+ * Copyright 2021-2023 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/unique_thread.h"
+
+namespace srsran {
+
+/// Types of CPU affinity masks in the gNB.
+enum class gnb_sched_affinity_mask_types { l1_dl, l1_ul, l2_cell, ru, low_priority, last };
+
+/// Thread pinning policy to a CPU affinity mask in the gNB.
+enum class gnb_sched_affinity_mask_policy {
+ /// Each thread is pinned to a single CPU in a mask in RR fashion.
+ round_robin = 0,
+ /// All threads are sharing the same affinity mask.
+ mask = 1,
+ last
+};
+
+/// Converts the given sting into an affinity mask policy or returns last if it could not convert it.
+inline gnb_sched_affinity_mask_policy to_affinity_mask_policy(const std::string& value)
+{
+ if (value == "round-robin") {
+ return gnb_sched_affinity_mask_policy::round_robin;
+ }
+ if (value == "mask") {
+ return gnb_sched_affinity_mask_policy::mask;
+ }
+
+ return gnb_sched_affinity_mask_policy::last;
+}
+
+/// Converts and returns the given affinity mask type to an integer.
+inline unsigned to_unsigned(gnb_sched_affinity_mask_types value)
+{
+ return static_cast(value);
+}
+
+/// Converts and returns the given value to an affinity mask type.
+inline gnb_sched_affinity_mask_types to_affinity_mask_type(unsigned value)
+{
+ return static_cast(value);
+}
+
+/// CPU affinity configuration in the gNB app.
+struct gnb_os_sched_affinity_config {
+ /// Affinity mask.
+ os_sched_affinity_bitmask mask;
+ /// Thread pinning policy.
+ gnb_sched_affinity_mask_policy pinning_policy = gnb_sched_affinity_mask_policy::round_robin;
+};
+
+/// \brief Scheduler affinity mask manager.
+///
+/// Manages the CPU affinity mask for the gNB application. It calculates and returns a mask using on a round-robin
+/// algorithm on a configured mask.
+class gnb_os_sched_affinity_manager
+{
+ /// Interface of the class managing affinity mask assignments depending on the policy.
+ class affinity_entry
+ {
+ public:
+ explicit affinity_entry(const os_sched_affinity_bitmask& mask_) : mask(mask_) {}
+ virtual ~affinity_entry() = default;
+
+ virtual os_sched_affinity_bitmask calculate_mask() = 0;
+
+ protected:
+ os_sched_affinity_bitmask mask;
+ };
+
+ /// Helper class that manages the mask and implements the round-robin algorithm.
+ class affinity_entry_rr : public affinity_entry
+ {
+ public:
+ explicit affinity_entry_rr(const os_sched_affinity_bitmask& mask_) : affinity_entry(mask_) {}
+
+ /// Calculates and returns a bitmask using a round-robin algorithm over the configured mask. If no CPU is enabled in
+ /// the configured mask, it returns the default mask.
+ os_sched_affinity_bitmask calculate_mask() override
+ {
+ // No CPU is selected.
+ if (mask.none()) {
+ return {};
+ }
+
+ int pos = -1;
+
+ while (pos < 0) {
+ pos = mask.find_lowest(last_pos_used, mask.size());
+
+ last_pos_used = (pos < 0) ? 0 : pos + 1;
+ }
+
+ return os_sched_affinity_bitmask(pos);
+ }
+
+ private:
+ unsigned last_pos_used = 0;
+ };
+
+ /// Helper class that manages the mask and return user-defined mask.
+ class affinity_entry_mask : public affinity_entry
+ {
+ public:
+ explicit affinity_entry_mask(const os_sched_affinity_bitmask& mask_) : affinity_entry(mask_) {}
+
+ os_sched_affinity_bitmask calculate_mask() override { return mask; }
+ };
+
+public:
+ gnb_os_sched_affinity_manager(const std::vector& entries)
+ {
+ srsran_assert(entries.size() == to_unsigned(gnb_sched_affinity_mask_types::last),
+ "Invalid number of CPU affinity masks received. Got '{}' while expected '{}' ",
+ entries.size(),
+ to_unsigned(gnb_sched_affinity_mask_types::last));
+
+ for (const auto& entry : entries) {
+ switch (entry.pinning_policy) {
+ case gnb_sched_affinity_mask_policy::round_robin:
+ cpu_masks.push_back(std::make_unique(entry.mask));
+ break;
+ case gnb_sched_affinity_mask_policy::mask:
+ cpu_masks.push_back(std::make_unique(entry.mask));
+ break;
+ default:
+ srsran_assert(0, "Invalid affinity mask policy={}", entry.pinning_policy);
+ }
+ }
+ }
+
+ /// Calculate and returns the affinity mask for the given type.
+ os_sched_affinity_bitmask calcute_affinity_mask(gnb_sched_affinity_mask_types type)
+ {
+ srsran_assert(type != gnb_sched_affinity_mask_types::last, "Invalid type '{}'", to_unsigned(type));
+
+ return cpu_masks[to_unsigned(type)]->calculate_mask();
+ }
+
+private:
+ std::vector> cpu_masks;
+};
+
+} // namespace srsran
diff --git a/apps/gnb/gnb_worker_manager.cpp b/apps/gnb/gnb_worker_manager.cpp
index e9c41cc972..d2cd4950b5 100644
--- a/apps/gnb/gnb_worker_manager.cpp
+++ b/apps/gnb/gnb_worker_manager.cpp
@@ -31,40 +31,42 @@ using namespace srsran;
static const uint32_t task_worker_queue_size = 2048;
-worker_manager::worker_manager(const gnb_appconfig& appcfg)
+static gnb_os_sched_affinity_config get_mask_from_config(const cpu_affinities_appconfig& config,
+ gnb_sched_affinity_mask_types mask_type)
{
- bool is_blocking_mode_active = false;
- if (variant_holds_alternative(appcfg.ru_cfg)) {
- const ru_sdr_appconfig& sdr_cfg = variant_get(appcfg.ru_cfg);
- is_blocking_mode_active = sdr_cfg.device_driver == "zmq";
+ switch (mask_type) {
+ case gnb_sched_affinity_mask_types::l1_dl:
+ return config.l1_dl_cpu_cfg;
+ case gnb_sched_affinity_mask_types::l1_ul:
+ return config.l1_ul_cpu_cfg;
+ case gnb_sched_affinity_mask_types::l2_cell:
+ return config.l2_cell_cpu_cfg;
+ case gnb_sched_affinity_mask_types::ru:
+ return config.ru_cpu_cfg;
+ case gnb_sched_affinity_mask_types::low_priority:
+ return config.low_priority_cpu_cfg;
+ default:
+ srsran_assert(0, "Invalid affinity mask type '{}'", to_unsigned(mask_type));
}
+ return {};
+}
- if (appcfg.expert_config.enable_tuned_affinity_profile) {
- use_tuned_profile = true;
- affinity_manager = std::make_unique(appcfg.expert_config.nof_threads_per_cpu,
- appcfg.expert_config.nof_cores_for_non_prio_workers);
- } else {
- affinity_manager = std::make_unique();
- }
+static std::vector build_affinity_manager_dependencies(const gnb_appconfig& config)
+{
+ std::vector out;
- // If an OFH RU is configured, create its executors first.
- if (variant_holds_alternative(appcfg.ru_cfg)) {
- create_ofh_executors(appcfg.cells_cfg, variant_get(appcfg.ru_cfg).is_downlink_parallelized);
- }
+ const cpu_affinities_appconfig& affinities_cfg = config.expert_execution_cfg.affinities;
- // Select the PDSCH concurrent thread only if the PDSCH processor type is set to concurrent or auto.
- unsigned nof_pdsch_threads = 1;
- if ((appcfg.expert_phy_cfg.pdsch_processor_type == "concurrent") ||
- (appcfg.expert_phy_cfg.pdsch_processor_type == "auto")) {
- nof_pdsch_threads = appcfg.expert_phy_cfg.nof_pdsch_threads;
+ for (unsigned i = 0, e = to_unsigned(srsran::gnb_sched_affinity_mask_types::last); i != e; ++i) {
+ out.push_back(get_mask_from_config(affinities_cfg, to_affinity_mask_type(i)));
}
- create_du_cu_executors(is_blocking_mode_active,
- appcfg.expert_phy_cfg.nof_ul_threads,
- appcfg.expert_phy_cfg.nof_dl_threads,
- nof_pdsch_threads,
- appcfg.cells_cfg,
- appcfg.expert_phy_cfg.max_processing_delay_slots);
+ return out;
+}
+
+worker_manager::worker_manager(const gnb_appconfig& appcfg) : affinity_mng(build_affinity_manager_dependencies(appcfg))
+{
+ create_du_cu_executors(appcfg);
create_ru_executors(appcfg);
}
@@ -83,10 +85,14 @@ void worker_manager::create_worker_pool(const std::string&
{
using namespace execution_config_helper;
+ concurrent_queue_policy queue_policy = concurrent_queue_policy::locking_mpmc;
+
const worker_pool pool{name,
nof_workers,
- {concurrent_queue_policy::locking_mpmc, queue_size},
+ {queue_policy, queue_size},
execs,
+ queue_policy == concurrent_queue_policy::locking_mpmc ? optional{}
+ : std::chrono::microseconds{10},
prio,
std::vector{cpu_masks.begin(), cpu_masks.end()}};
if (not exec_mng.add_execution_context(create_execution_context(pool))) {
@@ -97,41 +103,50 @@ void worker_manager::create_worker_pool(const std::string&
void worker_manager::create_prio_worker(const std::string& name,
unsigned queue_size,
const std::vector& execs,
+ const os_sched_affinity_bitmask& mask,
os_thread_realtime_priority prio)
{
using namespace execution_config_helper;
- const single_worker worker_desc{name,
- {concurrent_queue_policy::locking_mpsc, queue_size},
- execs,
- nullopt,
- prio,
- calculate_affinity_mask(name, prio)};
+ const single_worker worker_desc{
+ name, {concurrent_queue_policy::locking_mpsc, queue_size}, execs, nullopt, prio, mask};
if (not exec_mng.add_execution_context(create_execution_context(worker_desc))) {
report_fatal_error("Failed to instantiate {} execution context", worker_desc.name);
}
}
-void worker_manager::create_du_cu_executors(bool is_blocking_mode_active,
- unsigned nof_ul_workers,
- unsigned nof_dl_workers,
- unsigned nof_pdsch_workers,
- span cells_cfg,
- unsigned pipeline_depth)
+void worker_manager::create_du_cu_executors(const gnb_appconfig& appcfg)
{
using namespace execution_config_helper;
+ bool is_blocking_mode_active = false;
+ if (variant_holds_alternative(appcfg.ru_cfg)) {
+ const ru_sdr_appconfig& sdr_cfg = variant_get(appcfg.ru_cfg);
+ is_blocking_mode_active = sdr_cfg.device_driver == "zmq";
+ }
+
+ span cells_cfg = appcfg.cells_cfg;
+
// Worker for handling UE PDU traffic.
- const single_worker gnb_ue_worker{"gnb_ue",
- {concurrent_queue_policy::locking_mpsc, task_worker_queue_size},
- {{"cu_up_exec"}, {"gtpu_pdu_exec", false}, {"du_ue_exec"}, {"cu_up_e2_exec"}},
- nullopt};
+ const priority_multiqueue_worker gnb_ue_worker{
+ "gnb_ue",
+ // Three queues, one for UE UP maintenance tasks, one for UL PDUs and one for DL PDUs.
+ {{concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size},
+ {concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size},
+ // The IO-broker is currently single threaded, so we can use a SPSC.
+ {concurrent_queue_policy::lockfree_spsc, task_worker_queue_size}},
+ std::chrono::microseconds{200},
+ {{"ue_up_ctrl_exec", task_priority::max},
+ {"ue_ul_exec", task_priority::max - 1, false},
+ {"ue_dl_exec", task_priority::max - 2, false}},
+ os_thread_realtime_priority::max() - 30,
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::low_priority)};
if (not exec_mng.add_execution_context(create_execution_context(gnb_ue_worker))) {
report_fatal_error("Failed to instantiate gNB UE execution context");
}
- cu_up_exec = exec_mng.executors().at("cu_up_exec");
- gtpu_pdu_exec = exec_mng.executors().at("gtpu_pdu_exec");
- cu_up_e2_exec = exec_mng.executors().at("cu_up_e2_exec");
+ cu_up_exec = exec_mng.executors().at("ue_up_ctrl_exec");
+ gtpu_pdu_exec = exec_mng.executors().at("ue_dl_exec");
+ cu_up_e2_exec = exec_mng.executors().at("ue_up_ctrl_exec");
// Worker for handling DU, CU and UE control procedures.
const priority_multiqueue_worker gnb_ctrl_worker{
@@ -146,7 +161,7 @@ void worker_manager::create_du_cu_executors(bool is_blocki
{"du_timer_exec", task_priority::max},
{"du_e2_exec", task_priority::min}},
os_thread_realtime_priority::max() - 20,
- calculate_affinity_mask("gnb_ctrl", os_thread_realtime_priority::max() - 20)};
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::l2_cell)};
if (not exec_mng.add_execution_context(create_execution_context(gnb_ctrl_worker))) {
report_fatal_error("Failed to instantiate gNB control execution context");
}
@@ -159,13 +174,14 @@ void worker_manager::create_du_cu_executors(bool is_blocki
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, 8}, {concurrent_queue_policy::locking_mpsc, task_worker_queue_size}},
+ {{concurrent_queue_policy::lockfree_spsc, 8}, {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.
+ // Create Cell and slot indication executors. In case of ZMQ, we make the slot indication executor
+ // synchronous.
{{"cell_exec#" + cell_id_str, task_priority::min},
{"slot_exec#" + cell_id_str, task_priority::max, true, is_blocking_mode_active}},
os_thread_realtime_priority::max() - 2,
- calculate_affinity_mask("du_cell#" + cell_id_str, os_thread_realtime_priority::max() - 2)};
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::l2_cell)};
if (not exec_mng.add_execution_context(create_execution_context(du_cell_worker))) {
report_fatal_error("Failed to instantiate {} execution context", du_cell_worker.name);
@@ -183,7 +199,9 @@ void worker_manager::create_du_cu_executors(bool is_blocki
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)});
- auto ue_exec_mapper = std::make_unique(exec_list{exec_map.at("du_ue_exec")});
+ auto ue_exec_mapper = std::make_unique(exec_list{exec_map.at("ue_up_ctrl_exec")},
+ exec_list{exec_map.at("ue_ul_exec")},
+ exec_list{exec_map.at("ue_dl_exec")});
du_item.du_high_exec_mapper = std::make_unique(std::move(cell_exec_mapper),
std::move(ue_exec_mapper),
*exec_map.at("du_ctrl_exec"),
@@ -191,14 +209,28 @@ void worker_manager::create_du_cu_executors(bool is_blocki
*exec_map.at("du_e2_exec"));
}
- create_du_low_executors(
- is_blocking_mode_active, nof_ul_workers, nof_dl_workers, nof_pdsch_workers, cells_cfg, pipeline_depth);
+ // Select the PDSCH concurrent thread only if the PDSCH processor type is set to concurrent or auto.
+ unsigned nof_pdsch_workers = 1;
+ const upper_phy_threads_appconfig& upper_phy_threads_cfg = appcfg.expert_execution_cfg.threads.upper_threads;
+ if ((upper_phy_threads_cfg.pdsch_processor_type == "concurrent") ||
+ (upper_phy_threads_cfg.pdsch_processor_type == "auto")) {
+ nof_pdsch_workers = upper_phy_threads_cfg.nof_pdsch_threads;
+ }
+
+ create_du_low_executors(is_blocking_mode_active,
+ upper_phy_threads_cfg.nof_ul_threads,
+ upper_phy_threads_cfg.nof_dl_threads,
+ nof_pdsch_workers,
+ upper_phy_threads_cfg.nof_pusch_decoder_threads,
+ cells_cfg,
+ appcfg.expert_phy_cfg.max_processing_delay_slots);
}
void worker_manager::create_du_low_executors(bool is_blocking_mode_active,
unsigned nof_ul_workers,
unsigned nof_dl_workers,
unsigned nof_pdsch_workers,
+ unsigned nof_pusch_decoder_workers,
span cells_cfg,
unsigned pipeline_depth)
{
@@ -209,7 +241,11 @@ void worker_manager::create_du_low_executors(bool is_block
if (is_blocking_mode_active) {
// Create a single worker, shared by the whole PHY.
- create_prio_worker("phy_worker", task_worker_queue_size, {{"phy_exec"}}, os_thread_realtime_priority::max());
+ create_prio_worker("phy_worker",
+ task_worker_queue_size,
+ {{"phy_exec"}},
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::l1_dl),
+ os_thread_realtime_priority::max());
for (unsigned cell_id = 0, cell_end = cells_cfg.size(); cell_id != cell_end; ++cell_id) {
upper_pusch_exec.push_back(exec_mng.executors().at("phy_exec"));
@@ -223,12 +259,10 @@ void worker_manager::create_du_low_executors(bool is_block
for (unsigned cell_id = 0, cell_end = cells_cfg.size(); cell_id != cell_end; ++cell_id) {
const std::string cell_id_str = std::to_string(cell_id);
const std::string name_ul = "up_phy_ul#" + cell_id_str;
- const auto prio = os_thread_realtime_priority::max() - 20;
+ const auto prio = os_thread_realtime_priority::max() - 15;
std::vector cpu_masks;
- if (use_tuned_profile) {
- for (unsigned w = 0; w != nof_ul_workers; ++w) {
- cpu_masks.push_back(affinity_manager->reserve_cpu(name_ul, prio));
- }
+ for (unsigned w = 0; w != nof_ul_workers; ++w) {
+ cpu_masks.push_back(affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::l1_ul));
}
// Instantiate PHY UL workers.
@@ -244,7 +278,11 @@ void worker_manager::create_du_low_executors(bool is_block
// Instantiate dedicated PRACH worker.
const std::string name_prach = "phy_prach#" + cell_id_str;
const std::string prach_exec = "prach_exec#" + cell_id_str;
- create_prio_worker(name_prach, task_worker_queue_size, {{prach_exec}}, os_thread_realtime_priority::max() - 2);
+ create_prio_worker(name_prach,
+ task_worker_queue_size,
+ {{prach_exec}},
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::l1_ul),
+ os_thread_realtime_priority::max() - 2);
upper_prach_exec.push_back(exec_mng.executors().at("prach_exec#" + cell_id_str));
// Instantiate dedicated PHY DL workers.
@@ -253,12 +291,39 @@ void worker_manager::create_du_low_executors(bool is_block
const std::string suffix = std::to_string(cell_id) + "#" + std::to_string(i_dl_worker);
const std::string worker_name = "up_phy_dl#" + suffix;
const std::string exec_name = "du_low_dl_exec#" + suffix;
- create_prio_worker(worker_name, task_worker_queue_size, {{exec_name}}, os_thread_realtime_priority::max() - 10);
+ create_prio_worker(worker_name,
+ task_worker_queue_size,
+ {{exec_name}},
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::l1_dl),
+ os_thread_realtime_priority::max() - 10);
du_low_dl_executors[cell_id].emplace_back(exec_mng.executors().at("du_low_dl_exec#" + suffix));
}
}
}
+ // Instantiate dedicated PUSCH decoder workers for each cell.
+ for (unsigned cell_id = 0, cell_end = cells_cfg.size(); cell_id != cell_end; ++cell_id) {
+ if (nof_pusch_decoder_workers > 0) {
+ const std::string cell_id_str = std::to_string(cell_id);
+ const std::string name_pusch_decoder = "pusch#" + cell_id_str;
+ const auto prio = os_thread_realtime_priority::max() - 30;
+ std::vector cpu_masks;
+ for (unsigned w = 0; w != nof_pusch_decoder_workers; ++w) {
+ cpu_masks.push_back(affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::low_priority));
+ }
+
+ create_worker_pool(name_pusch_decoder,
+ nof_pusch_decoder_workers,
+ task_worker_queue_size,
+ {{name_pusch_decoder}},
+ prio,
+ cpu_masks);
+ upper_pusch_decoder_exec.push_back(exec_mng.executors().at(name_pusch_decoder));
+ } else {
+ upper_pusch_decoder_exec.push_back(nullptr);
+ }
+ }
+
for (unsigned cell_id = 0, cell_end = cells_cfg.size(); cell_id != cell_end; ++cell_id) {
if (nof_pdsch_workers > 1) {
const std::string name_pdsch = "pdsch#" + std::to_string(cell_id);
@@ -270,10 +335,8 @@ void worker_manager::create_du_low_executors(bool is_block
const auto prio = os_thread_realtime_priority::max() - 10;
std::vector cpu_masks;
- if (use_tuned_profile) {
- for (unsigned w = 0; w != nof_pdsch_workers; ++w) {
- cpu_masks.push_back(affinity_manager->reserve_cpu(name_pdsch, prio));
- }
+ for (unsigned w = 0; w != nof_pdsch_workers; ++w) {
+ cpu_masks.push_back(affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::l1_dl));
}
create_worker_pool(name_pdsch,
@@ -287,14 +350,6 @@ void worker_manager::create_du_low_executors(bool is_block
}
}
-/// Returns an affinity bitmask using the given affinity mask manager.
-static os_sched_affinity_bitmask
-get_affinity_mask(affinity_mask_manager& manager, const std::string& name, unsigned priority_from_max)
-{
- const os_thread_realtime_priority priority = os_thread_realtime_priority::max() - priority_from_max;
- return manager.reserve_cpu(name, priority);
-}
-
void worker_manager::create_ofh_executors(span cells, bool is_downlink_parallelized)
{
using namespace execution_config_helper;
@@ -322,8 +377,8 @@ void worker_manager::create_ofh_executors(span cells, bool
{{exec_name}},
std::chrono::microseconds{0},
os_thread_realtime_priority::max() - 0,
- get_affinity_mask(*affinity_manager, name, 0)};
- if (not exec_mng.add_execution_context(create_execution_context(ru_worker))) {
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::ru)};
+ if (!exec_mng.add_execution_context(create_execution_context(ru_worker))) {
report_fatal_error("Failed to instantiate {} execution context", ru_worker.name);
}
ru_timing_exec = exec_mng.executors().at(exec_name);
@@ -342,7 +397,7 @@ void worker_manager::create_ofh_executors(span cells, bool
{{exec_name}},
nullopt,
os_thread_realtime_priority::max() - 5,
- get_affinity_mask(*affinity_manager, name, 5)};
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::ru)};
if (not exec_mng.add_execution_context(create_execution_context(ru_worker))) {
report_fatal_error("Failed to instantiate {} execution context", ru_worker.name);
}
@@ -359,7 +414,7 @@ void worker_manager::create_ofh_executors(span cells, bool
{{exec_name}},
std::chrono::microseconds{5},
os_thread_realtime_priority::max() - 1,
- get_affinity_mask(*affinity_manager, name, 1)};
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::ru)};
if (not exec_mng.add_execution_context(create_execution_context(ru_worker))) {
report_fatal_error("Failed to instantiate {} execution context", ru_worker.name);
}
@@ -376,7 +431,7 @@ void worker_manager::create_ofh_executors(span cells, bool
{{exec_name}},
std::chrono::microseconds{1},
os_thread_realtime_priority::max() - 1,
- get_affinity_mask(*affinity_manager, name, 1)};
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::ru)};
if (not exec_mng.add_execution_context(create_execution_context(ru_worker))) {
report_fatal_error("Failed to instantiate {} execution context", ru_worker.name);
}
@@ -390,11 +445,17 @@ void worker_manager::create_lower_phy_executors(lower_phy_thread_profile lower_p
using namespace execution_config_helper;
// Radio Unit worker and executor.
- create_prio_worker("radio", task_worker_queue_size, {{"radio_exec"}});
+ create_prio_worker("radio",
+ task_worker_queue_size,
+ {{"radio_exec"}},
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::ru));
radio_exec = exec_mng.executors().at("radio_exec");
// Radio Unit statistics worker and executor.
- create_prio_worker("ru_stats_worker", 1, {{"ru_printer_exec"}});
+ create_prio_worker("ru_stats_worker",
+ 1,
+ {{"ru_printer_exec"}},
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::low_priority));
ru_printer_exec = exec_mng.executors().at("ru_printer_exec");
for (unsigned cell_id = 0; cell_id != nof_cells; ++cell_id) {
@@ -417,7 +478,11 @@ void worker_manager::create_lower_phy_executors(lower_phy_thread_profile lower_p
const std::string name = "lower_phy#" + std::to_string(cell_id);
const std::string exec_name = "lower_phy_exec#" + std::to_string(cell_id);
- create_prio_worker(name, 128, {{exec_name}}, os_thread_realtime_priority::max());
+ create_prio_worker(name,
+ 128,
+ {{exec_name}},
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::ru),
+ os_thread_realtime_priority::max());
task_executor* phy_exec = exec_mng.executors().at(exec_name);
lower_phy_tx_exec.push_back(phy_exec);
@@ -435,8 +500,16 @@ void worker_manager::create_lower_phy_executors(lower_phy_thread_profile lower_p
const std::string name_ul = "lower_phy_ul#" + std::to_string(cell_id);
const std::string exec_ul = "lower_phy_ul_exec#" + std::to_string(cell_id);
- create_prio_worker(name_dl, 128, {{exec_dl}}, os_thread_realtime_priority::max());
- create_prio_worker(name_ul, 2, {{exec_ul}}, os_thread_realtime_priority::max() - 1);
+ create_prio_worker(name_dl,
+ 128,
+ {{exec_dl}},
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::ru),
+ os_thread_realtime_priority::max());
+ create_prio_worker(name_ul,
+ 2,
+ {{exec_ul}},
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::ru),
+ os_thread_realtime_priority::max() - 1);
lower_phy_tx_exec.push_back(exec_mng.executors().at(exec_dl));
lower_phy_rx_exec.push_back(exec_mng.executors().at(exec_ul));
@@ -457,10 +530,26 @@ void worker_manager::create_lower_phy_executors(lower_phy_thread_profile lower_p
const std::string name_rx = "lower_phy_rx#" + std::to_string(cell_id);
const std::string exec_rx = "lower_phy_rx_exec#" + std::to_string(cell_id);
- create_prio_worker(name_tx, 128, {{exec_tx}}, os_thread_realtime_priority::max());
- create_prio_worker(name_rx, 1, {{exec_rx}}, os_thread_realtime_priority::max() - 2);
- create_prio_worker(name_dl, 128, {{exec_dl}}, os_thread_realtime_priority::max() - 1);
- create_prio_worker(name_ul, 128, {{exec_ul}}, os_thread_realtime_priority::max() - 3);
+ create_prio_worker(name_tx,
+ 128,
+ {{exec_tx}},
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::ru),
+ os_thread_realtime_priority::max());
+ create_prio_worker(name_rx,
+ 1,
+ {{exec_rx}},
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::ru),
+ os_thread_realtime_priority::max() - 2);
+ create_prio_worker(name_dl,
+ 128,
+ {{exec_dl}},
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::ru),
+ os_thread_realtime_priority::max() - 1);
+ create_prio_worker(name_ul,
+ 128,
+ {{exec_ul}},
+ affinity_mng.calcute_affinity_mask(gnb_sched_affinity_mask_types::ru),
+ os_thread_realtime_priority::max() - 3);
lower_phy_tx_exec.push_back(exec_mng.executors().at(exec_tx));
lower_phy_rx_exec.push_back(exec_mng.executors().at(exec_rx));
@@ -477,13 +566,15 @@ void worker_manager::create_lower_phy_executors(lower_phy_thread_profile lower_p
void worker_manager::create_ru_executors(const gnb_appconfig& appcfg)
{
if (variant_holds_alternative(appcfg.ru_cfg)) {
+ create_ofh_executors(appcfg.cells_cfg, appcfg.expert_execution_cfg.threads.ofh_threads.is_downlink_parallelized);
+
return;
}
const ru_sdr_appconfig& sdr_cfg = variant_get(appcfg.ru_cfg);
std::string driver = sdr_cfg.device_driver;
- create_lower_phy_executors((driver != "zmq") ? sdr_cfg.expert_cfg.lphy_executor_profile
+ create_lower_phy_executors((driver != "zmq") ? appcfg.expert_execution_cfg.threads.lower_threads.execution_profile
: lower_phy_thread_profile::blocking,
appcfg.cells_cfg.size());
}
@@ -504,12 +595,3 @@ void worker_manager::get_du_low_dl_executors(std::vector& execut
executors[i_exec] = du_low_exec[i_exec];
}
}
-
-os_sched_affinity_bitmask worker_manager::calculate_affinity_mask(const std::string& worker_name,
- os_thread_realtime_priority prio)
-{
- if (use_tuned_profile) {
- return affinity_manager->reserve_cpu(worker_name, prio);
- }
- return os_sched_affinity_bitmask{};
-}
diff --git a/apps/gnb/gnb_worker_manager.h b/apps/gnb/gnb_worker_manager.h
index 6be77e5f06..3e36d13009 100644
--- a/apps/gnb/gnb_worker_manager.h
+++ b/apps/gnb/gnb_worker_manager.h
@@ -23,6 +23,7 @@
#pragma once
#include "gnb_appconfig.h"
+#include "gnb_os_sched_affinity_manager.h"
#include "srsran/adt/expected.h"
#include "srsran/du_high/du_high_executor_mapper.h"
#include "srsran/support/executors/task_execution_manager.h"
@@ -31,72 +32,6 @@
namespace srsran {
-/// \brief Affinity mask manager.
-///
-/// It manages the CPUs that have been used to set an affinity mask accounting for a task priority.
-class affinity_mask_manager
-{
-public:
- /// Creates the tuned affinity mask manager with the given number of threads per core, reserves given amount of CPU
- /// cores for non priority tasks.
- explicit affinity_mask_manager(unsigned nof_threads_per_core_, unsigned nof_cores_for_non_prio_threads_) :
- nof_threads_per_core(nof_threads_per_core_),
- nof_cores_for_non_prio_threads(nof_cores_for_non_prio_threads_),
- cpu_bitset(compute_host_nof_hardware_threads())
- {
- srsran_assert(
- nof_cores_for_non_prio_threads < compute_host_nof_hardware_threads(),
- "Number of CPU cores reserved for non-priority tasks cannot exceed number of CPU cores in the machine");
-
- for (unsigned bit = 0; bit != nof_cores_for_non_prio_threads; ++bit) {
- non_prio_thread_mask.set(bit);
- }
- cpu_bitset.fill(0, nof_cores_for_non_prio_threads, true);
- }
-
- /// Default constructor.
- affinity_mask_manager() :
- nof_threads_per_core(2U),
- nof_cores_for_non_prio_threads(compute_host_nof_hardware_threads() / 2),
- cpu_bitset(compute_host_nof_hardware_threads())
- {
- cpu_bitset.fill(0, nof_cores_for_non_prio_threads, true);
- }
-
- /// \brief Returns an affinity mask with assigned CPU indexes.
- ///
- /// \param name Name of the task trying to reserve a CPU core.
- /// \param prio Priority of a task trying to reserve a CPU core.
- /// \return CPU affinity mask.
- os_sched_affinity_bitmask reserve_cpu(const std::string& name,
- os_thread_realtime_priority prio = os_thread_realtime_priority::no_realtime())
- {
- if (prio == os_thread_realtime_priority::no_realtime()) {
- return non_prio_thread_mask;
- }
- int start_pos = std::min(size_t(cpu_bitset.find_highest()) + nof_threads_per_core, cpu_bitset.size() - 1);
- int pos = cpu_bitset.find_lowest(start_pos, cpu_bitset.size(), false);
-
- if (pos == -1) {
- fmt::print("Could not set the affinity for the {} worker\n", name);
- return {};
- }
-
- cpu_bitset.set(pos);
- return os_sched_affinity_bitmask(pos);
- }
-
-private:
- /// Number of threads per physical CPU.
- const unsigned nof_threads_per_core;
- /// Number of CPU cores assigned for non-priority tasks.
- const unsigned nof_cores_for_non_prio_threads;
- /// Bitmask used to keep track of reserved CPUs.
- bounded_bitset<1024> cpu_bitset;
- /// Affinity mask assigned for non-priority tasks.
- os_sched_affinity_bitmask non_prio_thread_mask;
-};
-
/// Manages the workers of the app.
struct worker_manager {
explicit worker_manager(const gnb_appconfig& appcfg);
@@ -123,6 +58,7 @@ struct worker_manager {
std::vector lower_phy_ul_exec;
std::vector lower_prach_exec;
std::vector upper_pusch_exec;
+ std::vector upper_pusch_decoder_exec;
std::vector upper_pucch_exec;
std::vector upper_prach_exec;
std::vector upper_pdsch_exec;
@@ -145,20 +81,20 @@ struct worker_manager {
struct du_high_executor_storage {
std::unique_ptr du_high_exec_mapper;
};
-
- std::unique_ptr affinity_manager;
- bool use_tuned_profile = false;
-
std::vector du_high_executors;
std::vector> du_low_dl_executors;
/// Manager of execution contexts and respective executors instantiated by the gNB application.
task_execution_manager exec_mng;
+ /// CPU affinity bitmask manager.
+ gnb_os_sched_affinity_manager affinity_mng;
+
/// Helper method to create workers with non zero priority.
void create_prio_worker(const std::string& name,
unsigned queue_size,
const std::vector& execs,
+ const os_sched_affinity_bitmask& mask,
os_thread_realtime_priority prio = os_thread_realtime_priority::no_realtime());
/// Helper method to create worker pool.
@@ -170,17 +106,14 @@ struct worker_manager {
span cpu_masks = {});
/// Helper method that creates the Control and Distributed Units executors.
- void create_du_cu_executors(bool is_blocking_mode_active,
- unsigned nof_ul_workers,
- unsigned nof_dl_workers,
- unsigned nof_pdsch_workers,
- span cells_cfg,
- unsigned pipeline_depth);
+ void create_du_cu_executors(const gnb_appconfig& appcfg);
+ /// Helper method that creates the low Distributed Unit executors.
void create_du_low_executors(bool is_blocking_mode_active,
unsigned nof_ul_workers,
unsigned nof_dl_workers,
unsigned nof_pdsch_workers,
+ unsigned nof_pusch_decoder_workers,
span cells_cfg,
unsigned pipeline_depth);
@@ -192,9 +125,6 @@ struct worker_manager {
/// Helper method that creates the Open Fronthaul executors.
void create_ofh_executors(span cells, bool is_downlink_parallelized);
-
- /// Assign a CPU affinity bitmask to a given worker, based on its priority.
- os_sched_affinity_bitmask calculate_affinity_mask(const std::string& worker_name, os_thread_realtime_priority prio);
};
} // namespace srsran
diff --git a/apps/gnb/helpers/gnb_console_helper.cpp b/apps/gnb/helpers/gnb_console_helper.cpp
index 93113a3098..386bea5edb 100644
--- a/apps/gnb/helpers/gnb_console_helper.cpp
+++ b/apps/gnb/helpers/gnb_console_helper.cpp
@@ -33,8 +33,8 @@
using namespace srsran;
-gnb_console_helper::gnb_console_helper(io_broker& io_broker_) :
- logger(srslog::fetch_basic_logger("GNB")), io_broker_handle(io_broker_)
+gnb_console_helper::gnb_console_helper(io_broker& io_broker_, srslog::log_channel& log_chan_) :
+ logger(srslog::fetch_basic_logger("GNB")), io_broker_handle(io_broker_), metrics_json(log_chan_)
{
// set STDIN file descripter into non-blocking mode
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
@@ -143,4 +143,4 @@ void gnb_console_helper::on_app_running()
void gnb_console_helper::on_app_stopping()
{
fmt::print("Stopping ..\n");
-}
\ No newline at end of file
+}
diff --git a/apps/gnb/helpers/gnb_console_helper.h b/apps/gnb/helpers/gnb_console_helper.h
index b4e17f5e02..a8868a8c12 100644
--- a/apps/gnb/helpers/gnb_console_helper.h
+++ b/apps/gnb/helpers/gnb_console_helper.h
@@ -22,6 +22,7 @@
#pragma once
+#include "metrics_plotter_json.h"
#include "metrics_plotter_stdout.h"
#include "srsran/du/du_cell_config.h"
#include "srsran/scheduler/scheduler_metrics.h"
@@ -47,10 +48,11 @@ class app_state_notifier
class gnb_console_helper : public app_state_notifier
{
public:
- gnb_console_helper(io_broker& io_broker_);
+ gnb_console_helper(io_broker& io_broker_, srslog::log_channel& log_chan_);
~gnb_console_helper();
- scheduler_ue_metrics_notifier& get_metrics_notifier() { return metrics_plotter; };
+ scheduler_ue_metrics_notifier& get_stdout_metrics_notifier() { return metrics_plotter; };
+ scheduler_ue_metrics_notifier& get_json_metrics_notifier() { return metrics_json; };
void on_app_starting() override;
void on_app_running() override;
@@ -66,6 +68,7 @@ class gnb_console_helper : public app_state_notifier
srslog::basic_logger& logger;
io_broker& io_broker_handle;
metrics_plotter_stdout metrics_plotter;
+ metrics_plotter_json metrics_json;
std::vector cells;
};
diff --git a/apps/gnb/helpers/metrics_plotter_json.cpp b/apps/gnb/helpers/metrics_plotter_json.cpp
new file mode 100644
index 0000000000..1e5c508b92
--- /dev/null
+++ b/apps/gnb/helpers/metrics_plotter_json.cpp
@@ -0,0 +1,112 @@
+/*
+ *
+ * Copyright 2021-2023 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 "metrics_plotter_json.h"
+#include "srsran/support/math_utils.h"
+
+using namespace srsran;
+
+namespace {
+
+/// UE container metrics.
+DECLARE_METRIC("pci", metric_pci, pci_t, "");
+DECLARE_METRIC("rnti", metric_rnti, uint16_t, "");
+DECLARE_METRIC("cqi", metric_cqi, uint8_t, "");
+DECLARE_METRIC("ri", metric_ri, uint8_t, "");
+DECLARE_METRIC("dl_mcs", metric_dl_mcs, uint8_t, "");
+DECLARE_METRIC("dl_brate", metric_dl_brate, double, "");
+DECLARE_METRIC("dl_nof_ok", metric_dl_nof_ok, unsigned, "");
+DECLARE_METRIC("dl_nof_nok", metric_dl_nof_nok, unsigned, "");
+DECLARE_METRIC("dl_bs", metric_dl_bs, unsigned, "");
+DECLARE_METRIC("pusch_snr_db", metric_pusch_snr_db, float, "");
+DECLARE_METRIC("ul_mcs", metric_ul_mcs, uint8_t, "");
+DECLARE_METRIC("ul_brate", metric_ul_brate, double, "");
+DECLARE_METRIC("ul_nof_ok", metric_ul_nof_ok, unsigned, "");
+DECLARE_METRIC("ul_nof_nok", metric_ul_nof_nok, unsigned, "");
+DECLARE_METRIC("bsr", metric_bsr, unsigned, "");
+DECLARE_METRIC_SET("ue_container",
+ mset_ue_container,
+ metric_pci,
+ metric_rnti,
+ metric_cqi,
+ metric_ri,
+ metric_dl_mcs,
+ metric_dl_brate,
+ metric_dl_nof_ok,
+ metric_dl_nof_nok,
+ metric_dl_bs,
+ metric_pusch_snr_db,
+ metric_ul_mcs,
+ metric_ul_brate,
+ metric_ul_nof_ok,
+ metric_ul_nof_nok,
+ metric_bsr);
+
+/// Metrics root object.
+DECLARE_METRIC("timestamp", metric_timestamp_tag, double, "");
+DECLARE_METRIC_LIST("ue_list", mlist_ues, std::vector);
+
+/// Metrics context.
+using metric_context_t = srslog::build_context_type;
+
+} // namespace
+
+/// Returns the current time in seconds with ms precision since UNIX epoch.
+static double get_time_stamp()
+{
+ auto tp = std::chrono::system_clock::now().time_since_epoch();
+ return std::chrono::duration_cast(tp).count() * 1e-3;
+}
+
+void metrics_plotter_json::report_metrics(span ue_metrics)
+{
+ metric_context_t ctx("JSON Metrics");
+
+ for (const auto& ue : ue_metrics) {
+ ctx.get().emplace_back();
+ auto& output = ctx.get().back();
+
+ output.write(ue.pci);
+ output.write(ue.rnti);
+ if (ue.cqi) {
+ output.write(ue.cqi);
+ }
+ output.write(ue.ri);
+ output.write(ue.dl_mcs.to_uint());
+ output.write(ue.dl_brate_kbps * 1e3);
+ output.write(ue.dl_nof_ok);
+ output.write(ue.dl_nof_nok);
+ output.write(ue.dl_bs);
+ if (!std::isnan(ue.pusch_snr_db) && !iszero(ue.pusch_snr_db)) {
+ output.write(clamp(ue.pusch_snr_db, -99.9f, 99.9f));
+ }
+ output.write(ue.ul_mcs.to_uint());
+ output.write(ue.ul_brate_kbps * 1e3);
+ output.write(ue.ul_nof_ok);
+ output.write(ue.ul_nof_nok);
+ output.write(ue.bsr);
+ }
+
+ // Log the context.
+ ctx.write(get_time_stamp());
+ log_chan(ctx);
+}
diff --git a/apps/gnb/helpers/metrics_plotter_json.h b/apps/gnb/helpers/metrics_plotter_json.h
new file mode 100644
index 0000000000..d1fc697b64
--- /dev/null
+++ b/apps/gnb/helpers/metrics_plotter_json.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright 2021-2023 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/scheduler/scheduler_metrics.h"
+
+namespace srsran {
+
+/// Class used to receive metrics reports from scheduler and format them into a JSON file.
+class metrics_plotter_json : public scheduler_ue_metrics_notifier
+{
+public:
+ explicit metrics_plotter_json(srslog::log_channel& log_chan_) : log_chan(log_chan_) {}
+
+ /// Notifier called from the scheduler.
+ void report_metrics(span ue_metrics) override;
+
+private:
+ srslog::log_channel& log_chan;
+};
+
+} // namespace srsran
diff --git a/apps/gnb/helpers/metrics_plotter_stdout.cpp b/apps/gnb/helpers/metrics_plotter_stdout.cpp
index d0200001c8..4dd816004b 100644
--- a/apps/gnb/helpers/metrics_plotter_stdout.cpp
+++ b/apps/gnb/helpers/metrics_plotter_stdout.cpp
@@ -22,18 +22,18 @@
#include "metrics_plotter_stdout.h"
#include "srsran/support/math_utils.h"
-#include
+#include
#include
-#include
+#include
using namespace srsran;
static std::string scaled_fmt_integer(uint64_t num)
{
- constexpr static std::array suffixes = {"", "k", "M", "G", "T", "P", "E", "Z"};
- const static std::array max_nums = []() {
+ static constexpr std::array suffixes = {"", "k", "M", "G", "T", "P", "E", "Z"};
+ static const std::array max_nums = []() {
std::array nums{0};
- for (unsigned i = 0; i < nums.size(); ++i) {
+ for (unsigned i = 0, e = nums.size(); i != e; ++i) {
nums[i] = (uint64_t)std::pow(10, i * 3);
}
return nums;
@@ -43,28 +43,93 @@ static std::string scaled_fmt_integer(uint64_t num)
return fmt::format("{:>6}", num);
}
- for (unsigned i = 1; i != max_nums.size() - 1; ++i) {
+ for (unsigned i = 1, e = max_nums.size() - 1; i != e; ++i) {
if (num < max_nums[i + 1]) {
return fmt::format("{:>5.3g}{}", num / static_cast(max_nums[i]), suffixes[i]);
}
}
- return std::string("Invalid number");
+
+ return "Invalid number";
}
-void metrics_plotter_stdout::print_header()
+static void print_header()
{
fmt::print("\n");
fmt::print(" -----------------DL-----------------------|------------------UL--------------------\n");
fmt::print(" pci rnti cqi ri mcs brate ok nok (%) dl_bs | pusch mcs brate ok nok (%) bsr\n");
}
+static std::string float_to_string(float f, int digits, int field_width)
+{
+ std::ostringstream os;
+ int precision;
+
+ if (std::isnan(f) || std::abs(f) < 0.0001f) {
+ f = 0.f;
+ precision = digits - 1;
+ } else {
+ precision = digits - (int)(std::log10(std::abs(f + 0.0001f)) - 2 * DBL_EPSILON);
+ }
+
+ precision = std::max(precision, 0);
+
+ os << std::setw(field_width) << std::fixed << std::setprecision(precision) << f;
+ return os.str();
+}
+
+static std::string float_to_eng_string(float f, int digits)
+{
+ static char const* const prefixes[2][9] = {
+ {
+ "",
+ "m",
+ "u",
+ "n",
+ "p",
+ "f",
+ "a",
+ "z",
+ "y",
+ },
+ {
+ "",
+ "k",
+ "M",
+ "G",
+ "T",
+ "P",
+ "E",
+ "Z",
+ "Y",
+ },
+ };
+
+ const int degree = (f == 0.f) ? 0 : std::lrint(std::floor(std::log10(std::abs(f)) / 3));
+
+ std::string factor;
+
+ if (std::abs(degree) < 9) {
+ factor = prefixes[(degree < 0) ? 0 : 1][std::abs(degree)];
+ } else {
+ return "failed";
+ }
+
+ const double scaled = f * std::pow(1000.0, -degree);
+ if (degree != 0) {
+ return float_to_string(scaled, digits, 5) + factor;
+ }
+ return " " + float_to_string(scaled, digits, 5 - factor.length()) + factor;
+}
+
void metrics_plotter_stdout::report_metrics(span ue_metrics)
{
- if (not print_metrics) {
+ if (!print_metrics) {
return;
}
- if (++nof_lines > 10 && !ue_metrics.empty()) {
+ if (ue_metrics.size() > 10) {
+ print_header();
+ } else if (++nof_lines > 10 && !ue_metrics.empty()) {
nof_lines = 0;
print_header();
}
@@ -72,7 +137,7 @@ void metrics_plotter_stdout::report_metrics(span ue_
for (const auto& ue : ue_metrics) {
fmt::print("{:>4}", ue.pci);
fmt::print("{:>5x}", ue.rnti);
- if (not iszero(ue.cqi)) {
+ if (!iszero(ue.cqi)) {
fmt::print(" {:>3}", int(ue.cqi));
} else {
fmt::print(" {:>3.3}", "n/a");
@@ -80,11 +145,7 @@ void metrics_plotter_stdout::report_metrics(span ue_
fmt::print(" {:>2}", int(ue.ri));
- if (not std::isnan(ue.dl_mcs.to_uint())) {
- fmt::print(" {:>2}", int(ue.dl_mcs.to_uint()));
- } else {
- fmt::print(" {:>2}", 0);
- }
+ fmt::print(" {:>2}", int(ue.dl_mcs.to_uint()));
if (ue.dl_brate_kbps > 0) {
fmt::print(" {:>6.6}", float_to_eng_string(ue.dl_brate_kbps * 1e3, 1));
} else {
@@ -102,17 +163,13 @@ void metrics_plotter_stdout::report_metrics(span ue_
fmt::print(" |");
- if (not std::isnan(ue.pusch_snr_db) and not iszero(ue.pusch_snr_db)) {
+ if (!std::isnan(ue.pusch_snr_db) && !iszero(ue.pusch_snr_db)) {
fmt::print(" {:>5.1f}", clamp(ue.pusch_snr_db, -99.9f, 99.9f));
} else {
fmt::print(" {:>5.5}", "n/a");
}
- if (not std::isnan(ue.ul_mcs.to_uint())) {
- fmt::print(" {:>2}", ue.ul_mcs.to_uint());
- } else {
- fmt::print(" {:>2}", 0);
- }
+ fmt::print(" {:>2}", ue.ul_mcs.to_uint());
if (ue.ul_brate_kbps > 0) {
fmt::print(" {:>6.6}", float_to_eng_string(ue.ul_brate_kbps * 1e3, 1));
} else {
@@ -137,68 +194,3 @@ void metrics_plotter_stdout::toggle_print()
{
print_metrics = !print_metrics;
}
-
-static char const* const prefixes[2][9] = {
- {
- "",
- "m",
- "u",
- "n",
- "p",
- "f",
- "a",
- "z",
- "y",
- },
- {
- "",
- "k",
- "M",
- "G",
- "T",
- "P",
- "E",
- "Z",
- "Y",
- },
-};
-
-std::string metrics_plotter_stdout::float_to_string(float f, int digits, int field_width)
-{
- std::ostringstream os;
- int precision;
- if (std::isnan(f) or std::fabs(f) < 0.0001) {
- f = 0.0;
- precision = digits - 1;
- } else {
- precision = digits - (int)(log10f(fabs(f + 0.0001)) - 2 * DBL_EPSILON);
- }
- if (precision == -1) {
- precision = 0;
- }
- os << std::setw(field_width) << std::fixed << std::setprecision(precision) << f;
- return os.str();
-}
-
-std::string metrics_plotter_stdout::float_to_eng_string(float f, int digits)
-{
- const int degree = (f == 0.0) ? 0 : lrint(floor(log10f(fabs(f)) / 3));
-
- std::string factor;
-
- if (abs(degree) < 9) {
- if (degree < 0)
- factor = prefixes[0][abs(degree)];
- else
- factor = prefixes[1][abs(degree)];
- } else {
- return "failed";
- }
-
- const double scaled = f * pow(1000.0, -degree);
- if (degree != 0) {
- return float_to_string(scaled, digits, 5) + factor;
- } else {
- return " " + float_to_string(scaled, digits, 5 - factor.length()) + factor;
- }
-}
\ No newline at end of file
diff --git a/apps/gnb/helpers/metrics_plotter_stdout.h b/apps/gnb/helpers/metrics_plotter_stdout.h
index b466bc5a72..e58dc2ff6f 100644
--- a/apps/gnb/helpers/metrics_plotter_stdout.h
+++ b/apps/gnb/helpers/metrics_plotter_stdout.h
@@ -26,23 +26,20 @@
namespace srsran {
-/// \brief Class used to receive metrics reports from scheduler and pretty-print them to the console.
+/// Class used to receive metrics reports from scheduler and pretty-print them to the console.
class metrics_plotter_stdout : public scheduler_ue_metrics_notifier
{
public:
metrics_plotter_stdout() = default;
- /// \brief Notifier called from the scheduler.
+ /// Notifier called from the scheduler.
void report_metrics(span ue_metrics) override;
- /// \brief This can be called from another execution context to turn on/off the actual plotting.
+ /// This can be called from another execution context to turn on/off the actual plotting.
void toggle_print();
private:
- std::string float_to_string(float f, int digits, int field_width);
- std::string float_to_eng_string(float f, int digits);
- void print_header();
- int nof_lines = 10;
+ unsigned nof_lines = 10;
std::atomic print_metrics = {false};
};
diff --git a/configs/gnb_ru_rpqn4800e_tdd_n78_20mhz.yml b/configs/gnb_ru_rpqn4800e_tdd_n78_20mhz.yml
index a882af8569..638ca89b42 100644
--- a/configs/gnb_ru_rpqn4800e_tdd_n78_20mhz.yml
+++ b/configs/gnb_ru_rpqn4800e_tdd_n78_20mhz.yml
@@ -15,8 +15,8 @@ ru_ofh:
t1a_min_cp_ul: 250 # Minimum T1a on Control-Plane for Uplink in microseconds.
t1a_max_up: 250 # Maximum T1a on User-Plane in microseconds.
t1a_min_up: 80 # Minimum T1a on User-Plane in microseconds.
- ta4_max: 150 # Maximum Ta4 on User-Plane in microseconds.
- ta4_min: 25 # Minimum Ta4 on User-Plane in microseconds.
+ ta4_max: 200 # Maximum Ta4 on User-Plane in microseconds.
+ ta4_min: 0 # Minimum Ta4 on User-Plane in microseconds.
is_prach_cp_enabled: true # Configures if Control-Plane messages should be used to receive PRACH messages.
ignore_ecpri_payload_size: true # Configures if eCPRI payload size field should be ignored by the eCPRI packet decoder.
compr_method_ul: bfp # Uplink compression method.
diff --git a/docker/README.md b/docker/README.md
index da94b86b42..48dc77105a 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -1,4 +1,4 @@
-# SRSRAN Project Docker
+# srsRAN Project Docker
To build and launch the gnb, please run:
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 15bdf1df3f..7152f1f938 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -27,7 +27,7 @@ services:
context: open5gs
target: open5gs
args:
- OS_VERSION: "20.04"
+ OS_VERSION: "22.04"
OPEN5GS_VERSION: "v2.6.1"
environment:
MONGODB_IP: ${MONGODB_IP:-127.0.0.1}
diff --git a/docker/open5gs/Dockerfile b/docker/open5gs/Dockerfile
index b0059b2431..ba2c1e93f4 100644
--- a/docker/open5gs/Dockerfile
+++ b/docker/open5gs/Dockerfile
@@ -4,11 +4,11 @@ FROM ubuntu:$OS_VERSION as base
ENV PYTHONBUFFERED=1
ENV DEBIAN_FRONTEND=noninteractive
-RUN apt-get update \
+RUN DEBIAN_FRONTEND=noninteractive apt-get update \
&& apt install -y software-properties-common \
&& rm -rf /var/lib/apt/lists/*
-RUN apt-get update \
+RUN DEBIAN_FRONTEND=noninteractive apt-get update \
&& apt-get install -y \
python3-pip \
python3-setuptools \
@@ -32,7 +32,6 @@ RUN apt-get update \
libnghttp2-dev \
libtins-dev \
meson \
- mongodb \
curl \
gettext \
gdb \
@@ -43,10 +42,19 @@ RUN apt-get update \
iperf \
iperf3 \
libtalloc-dev \
- cmake
+ cmake \
+ && rm -rf /var/lib/apt/lists/*
+
+ARG MONGO_MAJOR_VERSION=6
+RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends wget gnupg \
+ && wget -qO - https://www.mongodb.org/static/pgp/server-${MONGO_MAJOR_VERSION}.0.asc | apt-key add \
+ && . /etc/os-release \
+ && echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu $UBUNTU_CODENAME/mongodb-org/${MONGO_MAJOR_VERSION}.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-${MONGO_MAJOR_VERSION}.0.list \
+ && DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends mongodb-org \
+ && apt-get autoremove && apt-get clean
# To set a fixed version of open5gs use
-ARG OPEN5GS_VERSION
+ARG OPEN5GS_VERSION=v2.6.1
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 7c972684fc..90a5fd4ee6 100644
--- a/docker/open5gs/README.md
+++ b/docker/open5gs/README.md
@@ -1,16 +1,23 @@
-This is a all-in-one Docker container for Open5GS. At build, the container will use the latest tag of the open5gs repository (). To run a specific version of open5gs, line 48 in .Dockerfile (`# RUN echo "v2.4.3" > ./open5gsversion)` must be uncommented.
+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
+). 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)
+# RUN git ls-remote --tags https://github.com/open5gs/open5gs | sort -t '/' -k 3 -V | awk -F/ '{ print $3 }' | awk '!/\^\{\}/' | tail -n 1 > ./open5gsversion
+```
+must be uncommented.
+
# Container Parameters
In [open5gs.env](open5gs.env) the following parameters can be set:
-- `MONGODB_IP` (default: 127.0.0.1): This is the IP of the mongodb to use. 127.0.0.1 is the mongodb that runs inside this container.
-- `OPEN5GS_IP`: This must be set to the IP of the container (here: 10.53.1.2).
-- `UE_IP_BASE`: Defines the IP base used for connected UEs (here: 10.45.0).
-- `DEBUG` (default: false): This can be set to true to run Open5GS in debug mode.
-- `SUBSCRIBER_DB` (default: "001010123456780,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,8000,9,10.45.1.2") contains either:
+- MONGODB_IP (default: 127.0.0.1): This is the IP of the mongodb to use. 127.0.0.1 is the mongodb that runs inside this container.
+- SUBSCRIBER_DB (default: "001010123456780,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,8000,10.45.1.2"): This adds subscriber data for a single or multiple users to the Open5GS mongodb. It contains either:
- Comma separated string with information to define a subscriber
- - A path to a csv file that contains entries to add to open5gs mongodb. Each entry will represent a subscriber.
+ - `subscriber_db.csv`. This is a csv file that contains entries to add to open5gs mongodb. Each entry will represent a subscriber. It must be stored in `srsgnb/docker/open5gs/`
+- OPEN5GS_IP: This must be set to the IP of the container (here: 10.53.1.2).
+- UE_IP_BASE: Defines the IP base used for connected UEs (here: 10.45.0).
+- DEBUG (default: false): This can be set to true to run Open5GS in debug mode.
```
# Kept in the following format: "Name,IMSI,Key,OP_Type,OP/OPc,AMF,QCI,IP_alloc"
@@ -47,6 +54,8 @@ 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`
+
Then run the docker container with:
`docker run --net open5gsnet --ip 10.53.1.2 --env-file open5gs.env --privileged --publish 3000:3000 open5gs-docker ./build/tests/app/5gc -c open5gs-5gc.yml`
diff --git a/docker/open5gs/subscriber_db.csv.example b/docker/open5gs/subscriber_db.csv.example
index 4dcc2198c9..e9a82814c6 100644
--- a/docker/open5gs/subscriber_db.csv.example
+++ b/docker/open5gs/subscriber_db.csv.example
@@ -9,9 +9,7 @@
# OP/OPc: Operator Code/Cyphered Operator Code, stored in hexadecimal
# AMF: Authentication management field, stored in hexadecimal
# QCI: QoS Class Identifier for the UE's default bearer.
-# IP_alloc: IP allocation stratagy for the SPGW.
-# With 'dynamic' the SPGW will automatically allocate IPs
-# With a valid IPv4 (e.g. '10.45.0.2') the UE will have a statically assigned IP.
+# IP_alloc: Statically assigned IP for the UE.
#
# Note: Lines starting by '#' are ignored and will be overwritten
# List of UEs with IMSI, and key increasing by one for each new UE. Useful for testing with AmariUE simulator and ue_count option
diff --git a/docs/index.html b/docs/index.html
index 97a6453e2a..aad6a772f7 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -87,51 +87,14 @@ srsRAN Project documentation
Documentation
- Doxygen
+ Doxygen
Cppcheck Analysis
- Clang Tidy Analysis
-
-
Code coverage
-
-
- e2e test:
-
-