Эта статья представляет собой дальнейшее развитие нашей предыдущей статьи, только здесь всё будет на 20 % круче. Мы полностью вынесем функциональность базовой станции на отдельный ПК, возьмём более мощное железо для ядра сети, а также развернём другой open source-стек для радиочасти. В общем, сделаем шаг вперёд и попробуем перейти от демонстратора идеи к тестовому стенду 5G. В этой статье вас ждут:

  1. Установка ядра с низкой задержкой.

  2. Установка DataPlane Development Kit (DPDK).

  3. Установка драйвера UHD из исходных кодов.

  4. Подключение USRP X310 к CU/DU с применением DPDK.

  5. Развёртывание OpenAirInterface и подключение gNodeB к ядру сети.

Текущая конфигурация сети будет значительно более продвинутой по сравнению с предыдущей, собранной из доступных компонентов с маркетплейсов. Но всё так же основной целью будет работа с COTS UE, или стандартными пользовательскими устройствами с полок магазинов. Блок-схема в этот раз будет выглядеть следующим образом:

Набор документов и инструкций, которые мы будем использовать:

В этом руководстве мы не будем останавливаться на настройке ядра сети, в качестве него снова выступит хорошо знакомый нам Open5GS. Но, как видно по блок-схеме, функциональность базовой станции вынесем за пределы хоста на отдельный высокопроизводительный ПК, что потребует небольшой донастройки ядра.

Установка ядра с низкой задержкой

Компания Ettus Research рекомендует отказаться от использования любых видов виртуализации на хосте, отвечающем за взаимодействие с USRP, а также использовать low latency kernel или Ubuntu Desktop. По всей видимости, для взаимодействия USRP с UHD наиболее критичным параметром является частота прерывания ЦП, равная 1000 ГЦ как для Ubuntu Desktop, так и для ядра с низкой задержкой. Так как GUI нам без надобности, мы просто установим Low Latency Kernel.

sudo apt install linux-lowlatency
sudo update-grub

После этого перезагружаемся и проверяем, какая версия ядра у нас загрузилась: uname -a.

DPDK

Следующим пунктом мы рекомендуем установку именно DataPlane Development Kit (DPDK), потому что в дальнейшем это чуточку упростит установку драйвера UHD. DPDK — это решение с открытым кодом для повышения производительности сетевого стека в приложениях с высокой сетевой нагрузкой. Оно позволяет драйверу UHD взаимодействовать с устройством USRP в обход ядра Linux, что, по замыслу создателей, увеличивает пропускную способность сетевого стека, уменьшает задержки и потери пакетов. В нашем случае тяжёлый поток представляют собой сырые данные, идущие с SDR в приложения обработки радиосигнала. Кроме этого, у нас есть адаптер для 10-гигабитного соединения базовой станции с ядром сети, но его мы будем использовать в стандартной конфигурации, без DPDK с обычными драйверами Linux. В настоящее время в узле UPF из Open5GS, насколько нам известно, нет поддержки DPDK, поэтому если вам необходимо повышать производительность сетевого стека в ядре сети 5G, то следует рассмотреть другие решения UPF.

Начинаем установку DPDK: sudo apt install dpdk dpdk-dev.

Далее необходимо включить возможность проброса PCI-устройств и hugepages. Их рекомендуется включать при загрузке системы, поэтому включение желательно прописать в grub. На нашем стенде используется процессор AMD, поэтому команды в grub будут выглядеть так:

GRUB_CMDLINE_LINUX_DEFAULT="iommu=pt amd_iommu=on hugepages=2048".

После этого необходимо создать конфигурацию привязки ядер процессора к будущим адаптерам DPDK и задать настройки сетевого стека для драйвера UHD. Делается это посредством создания нового или редактирования существующего файла uhd.conf. Мы создали его в /root/.config/, также можно поместить его в /etc/uhd/. В него необходимо добавить следующую секцию:

[use_dpdk=1]
 dpdk_mtu=9000
 dpdk_driver=/usr/lib/x86_64-linux-gnu/dpdk/pmds-24.0/
 dpdk_corelist=2,3,4
 dpdk_num_mbufs=4096
 dpdk_num_desc=4096
 dpdk_mbuf_cache_size=315
 [dpdk_mac=38:05:25:x1:y1:z1]
 dpdk_lcore=3
 dpdk_ipv4=192.168.30.1/24
 [dpdk_mac=38:05:25:x2:y2:z2]
 dpdk_lcore=4
 dpdk_ipv4=192.168.40.1/24

Для приложений DPDK важно включить опцию RT_RUNTIME_SHARE. DPDK-приложения часто работают в режиме бесконечного опроса (polling), потребляя все ресурсы ядра. По умолчанию Linux ограничивает задачи реального времени (RT) до 0,95 секунды из каждой секунды, чтобы оставить время системным процессам. Без этой опции ядро принудительно приостановит поток DPDK на 0,05 сек, что приведёт к мгновенной потере пакетов и резким скачкам задержки. Кроме того, опция позволяет ядру, которое обрабатывает основной поток пакетов и исчерпало свой RT-лимит, «занять» неиспользуемое время у других ядер. Это даёт высоконагруженному потоку DPDK работать дольше без прерываний со стороны планировщика. 

Несмотря на пользу для DPDK, эта опция несёт риски для стабильности всей ОС. Если поток DPDK «заберёт» слишком много времени, он может лишить ресурсов критические системные процессы (например, kworkers), что приведёт к зависанию системы. Именно поэтому во многих дистрибутивах она по умолчанию выключена. Но в нашем случае с опцией, выключённой по умолчанию, система работает крайне нестабильно, поэтому мы её включим: 

echo RT_RUNTIME_SHARE > /sys/kernel/debug/sched/features

UHD

В этот раз мы будем устанавливать драйвер UHD из исходных кодов. Этот способ считается предпочтительным, нежели установка из бинарников, как в прошлый раз.

Начинаем с установки зависимостей:

sudo apt install autoconf automake build-essential ccache cmake cpufrequtils doxygen ethtool g++ git inetutils-tools libboost-all-dev libncurses5 libncurses5-dev libusb-1.0-0 libusb-1.0-0-dev libusb-dev python3-dev python3-mako python3-numpy python3-requests python3-scipy python3-setuptools python3-ruamel.yaml

Клонируем к себе исходный код с Github:

mkdir $HOME/git
cd $HOME/git
git clone http://github.com/EttusResearch/uhd.git

При написании статьи статьи мы столкнулись с несовместимостью последней версии UHD из ветки master с нашим USRP X310. При запуске приложений драйвер выдавал ошибку несовместимости образа FPGA с этой версией и предлагал либо понизить версию драйвера, либо обновить образ FPGA. Поскольку обновление образа с помощью встроенной в UHD утилиты результата не дал, мы перешли в ветку v4.9.0.0, с ней всё работает нормально.

cd uhd/host
mkdir build
cd build
git checkout v4.9.0.0

После перехода в нужную ветку начинаем компилирование исходных кодов:

cmake ../
make -j $(nproc)
sudo make install
sudo ldconfig

При компилировании кодов UHD увидит, что в системе установлен DPDK, и сразу сам скомпилируется в варианте с поддержкой DPDK.

Имеет смысл также обновить коллекцию образов для USRP: sudo uhd_images_downloader. Эта утилита скачает набор образов, соответствующих вашей версии драйвера UHD, для всех устройств USRP.

Подключение USRP X310 с использованием DPDK

На нашем ПК установлен адаптер Intel X710 DA2 с двумя портами SFP+. В них мы и подключим USRP. По умолчанию в USRP X310 интерфейсы сконфигурированы как 1GE + 10GE. Для того, чтобы запускать на USRP каналы с широкой полосой, может понадобиться использовать устройство на полной скорости master clock rate, и в этом случае одного канала в 10 гигабит уже не хватает. Чтобы соединить USRP с хостом двумя интерфейсами по 10 гигабит, для начала необходимо прошить в USRP X310 образ XG, в котором как раз можно использовать два порта по 10GE.

uhd_image_loader --args="type=x300,addr=192.168.10.2,fpga=XG"

После прошивки образа UHD порекомендует нам перезагрузить устройство, чтобы новый образ начал работать. Теперь у нас есть два порта по 10GE, и каждый из них находится в собственной подсети. Адреса по умолчанию 192.168.30.2/24 и 192.168.40.2/24.

Для проверки, что образ установился нормально, можно задать на сетевых адаптерах адреса из этих подсетей и попинговать адреса USRP. Если пинги проходят нормально, то можно запустить команду проверки USRP-устройства:

uhd_usrp_probe --args="type=x300,addr=192.168.30.2"

После того, как мы убедились, что устройство работает в стандартной конфигурации, пришло время подключать его через DPDK. Для этого убеждаемся, что в файле uhd.conf прописаны правильные параметры, в первую очередь MAC-адреса и IP-адреса. После этого загружаем драйвер прямого доступа к PCI-устройствам: modprobe vfio-pci.

После проверки всех конфигурационных файлов отключаем USRP от хоста просто ради того, чтобы перевести сетевые интерфейсы в down, и подключаем их через драйвер прямого доступа.

sudo dpdk-devbind.py --bind=vfio-pci 05:00.0
sudo dpdk-devbind.py --bind=vfio-pci 05:00.1

Проверяем, что сетевые карты появились в списке DPDK-совместимых адаптеров:

После того, как мы увидели наши адаптеры в списке работающих, через драйвер vfio можно включать USRP обратно. Так как Linux перестал видеть наши сетевые карты, теперь драйверу UHD необходимо дать указание искать устройство USRP через DPDK. Для этого в команду проверки USRP добавляем аргументы:

uhd_usrp_probe --args="use_dpdk=1,type=x300,addr=192.168.30.2"

Мы должны увидеть, что драйвер UHD использует DPDK для доступа к USRP.

Изменения в ядре сети

Так как мы вынесли базовую станцию с хоста, на котором работает ядро сети, теперь надо настроить IP-связность между базовой станцией и ядром сети. Для этого нам понадобится перенести адрес AMF с loopback в обычную подсеть, с обычным «серым» IP-адресом. В нашем случае мы сразу сконфигурируем IP-адреса для узлов AMF и MME. Это позволит в дальнейшем подключать к нашей сети как LTE, так и NR-радиочасть. Для этого в amf.yaml изменяем раздел ngap:

ngap:
    server:
      - address: 192.168.1.101

А в mme.yaml изменяем раздел s1ap:

s1ap:
  server:
     - address: 192.168.1.102

Для дальнейшей работы также удобно установить на этом же хосте пару измерителей скоростей пакетных соединений iperf3 и OpenSpeedTest.

OpenAirInterface (OAI)

Это стек авторства Eurecom. Его исходный код открыт, как и у стека srsRAN_Project, при этом в целом он считается более гибким, но и более сложным в настройке. Кстати, стек srsRAN_Project переехал на Gitlab и теперь называется OCUDU. Стек OAI более закрытый, нежели OCUDU, ибо изменять код могут только зарегистрированные на Gitlab Eurecom, а там довольно жёсткая фильтрация желающих. Стек создан для сотрудников Eurecom, студентов и сотрудников технических университетов, и зарегистрироваться с обычным @gmail.com там не получится.

У стека OAI от OCUDU отличается набор фич, да и в целом было интересно взглянуть на другое решение.

OAI предполагает установку из исходного кода, после чего вы можете скомпилировать для себя различные устройства OAI, базовую станцию LTE, 5G или 4G/5G-модем. Мы ограничимся gNodeB.

Получаем исходный код OAI:

git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
cd ~/openairinterface5g
git checkout develop

Устанавливаем зависимости OAI:

 cd ~/openairinterface5g/cmake_targets
 ./build_oai -I

После чего говорим, что мы хотим получить приложение для gNodeB с радиочастью на основе USRP: ./build_oai -w USRP --ninja --gNB -C.

После выполнения этой команды у нас появится необходимый нам nr-softmodem. Это приложение, также как и OCUDU gnb, запускается с конфигурационным файлом, в котором и указываются основные параметры базовой станции. Примеры таких файлов находятся в /openairinterface5g/targets/PROJECTS. Файлы, которые там находятся, требуют редактирования, и, в отличие от OCUDU, в них сильно меньше терпимости к ошибкам пользователя. Если OCUDU может за вас рассчитать какие-то параметры и подставить в конфигурацию расчётные значения, если вы их не указали, то OAI требует явно указывать все необходимые данные, причём так, чтобы всё было в рамках стандартов 3GPP. Поэтому пока мы возьмём готовый конфигурационный файл для базовой станции 5G в конфигурации SISO 100 Мгц, бэнд 77, дуплекс TDD, тип сети StandAlone gnb.sa.band77.fr1.273PRB.usrpx300.conf. Поменяем в нём только необходимый нам минимум: IP-адреса gNodeB и AMF, а также укажем, что наша X310 работает через DPDK.

Указываем адрес AMF:

////////// AMF parameters:

  amf_ip_address = ({ ipv4 = "192.168.1.101"; });

Адрес, с которого gNodeB будет подключаться к AMF:

 NETWORK_INTERFACES :

  {
    GNB_IPV4_ADDRESS_FOR_NG_AMF              = "192.168.1.240/24";
    GNB_IPV4_ADDRESS_FOR_NGU                 = "192.168.1.240/24";
    GNB_PORT_FOR_S1U                         = 2152; # Spec 2152
  }

В разделе RU к опции sdr_addrs добавляем use_dpdk=1:

sdr_addrs = "use_dpdk=1,addr=192.168.30.2,second_addr=192.168.40.2, clock_source=internal,time_source=internal"

Не забываем проверить, что настройки источника синхронизации соответствуют вашей реальной конфигурации.

Запуск!

Ну вот, кажется, теперь наша тестовая лаборатория готова к проведению тестов. Запускаем gNodeB:

cd ~/openairinterface5g/cmake_targets/ran_build/build

sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.fr1.273PRB.usrpx300.conf --usrp-tx-thread-config 1 -E --continuous-tx

На AMF мы должны увидеть сообщение о том, что БС успешно присоединилась к ядру сети по интерфейсу N2.

После этого включаем телефон в режим NR Only через сервисное меню и смотрим на лог AMF, который показывает, что телефон успешно зарегистрировался в сети.

В строке статуса соединения должен появиться значок 5G, а в сервисном меню вы должны увидеть статус Connected, а тип сети NR_SA.

Первичные измерения скорости соединения дают следующий результат:

В теории, мы, наверное, должны были бы получить скорость около 400+ мегабит, но, видимо, в нашей системе остались ещё какие-то не до конца исследованные узкие места. Исследовать их мы будем, наверное, уже в следующей статье. 

Оставайтесь на связи!