Load balancing в OpenWrt
Вступ
Цей посібник пояснює, як вручну реалізувати балансування навантаження (Load balancing) в OpenWrt, закріплюючи переривання (IRQ) за певними Ethernet-портами та призначаючи один або кілька ядер CPU для обробки мережевих черг.
SMP IRQ Affinity та встановлення бітової маски
З джерела: https://www.kernel.org/doc/html/latest/core-api/irq/irq-affinity.html
`/proc/irq/IRQ#/smp_affinity` і `/proc/irq/IRQ#/smp_affinity_list` визначають, які CPU дозволені для певного джерела переривань (IRQ). Це бітова маска (`smp_affinity`) або список CPU (`smp_affinity_list`). Заборонено відключати всі CPU одразу. Якщо контролер IRQ не підтримує afініті (affinity), значення не змінюється і залишається за замовчуванням — усі CPU.
`/proc/irq/default_smp_affinity` визначає маску спорідненості (affinity) за замовчуванням, яка застосовується до всіх неактивних IRQ. Після активації IRQ його бітова маска спорідненості буде встановлена в значення за замовчуванням. Її потім можна змінити, як описано вище. Маска за замовчуванням — `0xffffffff`.
Щоб призначити IRQ конкретному CPU або групі CPU, потрібно використовувати бітову маску. Для цього вмикаємо потрібні CPU у двійковому представленні та перетворюємо це число у шістнадцяткове. Це дозволяє обмежити обробку певних IRQ певними ядрами CPU — для досягнення балансування або в гетерогенних SoC, наприклад, при розподілі між продуктивними та енергоефективними ядрами.
Бітові маски для CPU:
Бінарне | Hex | CPU-ядра |
---|---|---|
00000001 | 1 | 0 |
00000010 | 2 | 1 |
00000011 | 3 | 0,1 |
00000100 | 4 | 2 |
00000101 | 5 | 0,2 |
00000110 | 6 | 1,2 |
00000111 | 7 | 0,1,2 |
00001000 | 8 | 3 |
00001001 | 9 | 0,3 |
00001010 | A | 1,3 |
00001011 | B | 0,1,3 |
00001100 | C | 2,3 |
00001101 | D | 0,2,3 |
00001110 | E | 1,2,3 |
00001111 | F | 0,1,2,3 |
… | … | … |
00110000 | 30 | 4,5 |
Стандартні налаштування в OpenWrt
У OpenWrt за замовчуванням використовується підтримка багатоядерної обробки (SMP).
Наступні скрипти відповідають за ці налаштування:
cat /etc/hotplug.d/net/20-smp-packet-steering cat /etc/hotplug.d/net/40-net-smp-affinity
Більш автоматизоване рішення — використання irqbalance:
irqbalance — це демон для Linux, що розподіляє апаратні переривання між логічними ядрами процесора. Його мета — покращення загальної продуктивності, що дозволяє рівномірніше розподіляти навантаження та оптимізувати енергоспоживання.
Однак `irqbalance` не завжди забезпечує передбачуваний розподіл навантаження. Тому краще використовувати ручне налаштування для точнішого контролю над балансуванням.
Переривання
Насамперед потрібно знайти та ідентифікувати активні переривання.
Щоб переглянути поточні налаштування або зміни:
cat /proc/interrupts
Нижче наведено приклад з NanoPi R4S, який має 4 ядра A53 (CPU 0–3) і 2 ядра A72 (CPU 4 і 5):
root@OpenWrt:~# cat /proc/interrupts CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 23: 27142318 12185540 5391618 2352924 137831569 145154023 GICv3 30 Level arch_timer 25: 67873664 61308794 11619382 2662637 16876546 43550490 GICv3 113 Level rk_timer 26: 0 0 0 0 0 0 GICv3-23 0 Level arm-pmu 27: 0 0 0 0 0 0 GICv3-23 1 Level arm-pmu 28: 0 0 0 0 0 0 GICv3 37 Level ff6d0000.dma-controller 29: 0 0 0 0 0 0 GICv3 38 Level ff6d0000.dma-controller 30: 0 0 0 0 0 0 GICv3 39 Level ff6e0000.dma-controller 31: 0 0 0 0 0 0 GICv3 40 Level ff6e0000.dma-controller 32: 1 0 0 0 0 0 GICv3 81 Level pcie-sys 34: 0 0 0 0 0 0 GICv3 83 Level pcie-client 35: 0 0 0 0 165575364 0 GICv3 44 Level eth0 36: 20438175 0 0 0 0 0 GICv3 97 Level dw-mci 37: 0 0 0 0 0 0 GICv3 58 Level ehci_hcd:usb1 38: 0 0 0 0 0 0 GICv3 60 Level ohci_hcd:usb3 39: 0 0 0 0 0 0 GICv3 62 Level ehci_hcd:usb2 40: 0 0 0 0 0 0 GICv3 64 Level ohci_hcd:usb4 42: 0 0 0 0 0 0 GICv3 91 Level ff110000.i2c 43: 6 0 0 0 0 0 GICv3 67 Level ff120000.i2c 44: 0 0 0 0 0 0 GICv3 68 Level ff160000.i2c 45: 6 0 0 0 0 0 GICv3 132 Level ttyS2 46: 0 0 0 0 0 0 GICv3 129 Level rockchip_thermal 47: 6393498 0 0 0 0 0 GICv3 89 Level ff3c0000.i2c 50: 0 0 0 0 0 0 GICv3 147 Level ff650800.iommu 52: 0 0 0 0 0 0 GICv3 149 Level ff660480.iommu 56: 0 0 0 0 0 0 GICv3 151 Level ff8f3f00.iommu 57: 0 0 0 0 0 0 GICv3 150 Level ff903f00.iommu 58: 0 0 0 0 0 0 GICv3 75 Level ff914000.iommu 59: 0 0 0 0 0 0 GICv3 76 Level ff924000.iommu 69: 0 0 0 0 0 0 GICv3 59 Level rockchip_usb2phy 70: 0 0 0 0 0 0 GICv3 137 Level xhci-hcd:usb5 71: 0 0 0 0 0 0 GICv3 142 Level xhci-hcd:usb7 72: 0 0 0 0 0 0 rockchip_gpio_irq 21 Level rk808 78: 0 0 0 0 0 0 rk808 5 Edge RTC alarm 82: 0 0 0 0 0 0 rockchip_gpio_irq 7 Edge fe320000.mmc cd 84: 0 0 0 0 0 0 ITS-MSI 0 Edge PCIe PME, aerdrv 85: 10 0 0 0 0 0 rockchip_gpio_irq 10 Level stmmac-0:01 86: 0 0 0 0 0 0 rockchip_gpio_irq 22 Edge gpio-keys 87: 0 0 0 0 0 1156859750 ITS-MSI 524288 Edge eth1 IPI0: 7085496 10371429 7027071 6124604 310818 114897 Rescheduling interrupts IPI1: 2817025 2457651 882759 515246 2752519 543745 Function call interrupts IPI2: 0 0 0 0 0 0 CPU stop interrupts IPI3: 0 0 0 0 0 0 CPU stop (for crash dump) interrupts IPI4: 5558568 4633615 2762056 1122565 763629 3435183 Timer broadcast interrupts IPI5: 413711 300799 161541 117511 109020 76881 IRQ work interrupts IPI6: 0 0 0 0 0 0 CPU wake-up interrupts Err: 0
Щоб знайти IRQ для ваших Ethernet-інтерфейсів, використовуйте:
grep eth /proc/interrupts
Приклад:
root@OpenWrt:~# grep eth /proc/interrupts 35: 0 0 0 0 165661665 0 GICv3 44 Level eth0 87: 0 0 0 0 0 1157284700 ITS-MSI 524288 Edge eth1
Отже, тут `eth0` — це IRQ 35, а `eth1` — IRQ 87.
Одне переривання (IRQ) можна прив’язати лише до одного ядра CPU.
Прив’язка переривань до ядер CPU
Призначити переривання від `eth0` ядру CPU 0:
echo 1 > /proc/irq/35/smp_affinity
Призначити переривання від `eth1` ядру CPU 1:
echo 2 > /proc/irq/87/smp_affinity
Автоматичне визначення IRQ для інтерфейсу:
# IRQ для eth0: echo f > /proc/irq/$(grep eth0 /proc/interrupts | awk -F ':' '{print $1}' | xargs)/smp_affinity # IRQ для eth1: echo f > /proc/irq/$(grep eth1 /proc/interrupts | awk -F ':' '{print $1}' | xargs)/smp_affinity
Команда `grep` знаходить рядок із `/proc/interrupts`, `awk` витягує перший стовпчик (номер IRQ), а `xargs` прибирає зайві пробіли.
У ядрах Linux версії 5.15 і новіших потрібно використовувати:
echo -n #HEX# > /proc/irq/#НОМЕР_IRQ#/smp_affinity
Якщо ви перезапустите Smart Queue Management (SQM) або зміните його налаштування, це скине прив’язку переривань до CPU. Вам потрібно повторно застосувати налаштування вручну.
Мережеві черги
Джерело: Receive Packet Steering (RPS)
Receive Packet Steering (RPS) подібний до Receive Side Scaling (RSS) тим, що дозволяє скеровувати пакети на певні ядра CPU для обробки. Але RPS реалізований програмно, що дозволяє уникнути перенавантаження апаратної черги одного мережевого інтерфейсу, яка могла б стати “вузьким місцем” при великому трафіку.
Мережеві черги можуть бути розподілені між усіма ядрами CPU або ж прив’язані до одного ядра.
Призначити чергу eth0 ядру 3:
echo 4 > /sys/class/net/eth0/queues/rx-0/rps_cpus
Призначити чергу eth1 ядру 4:
echo 8 > /sys/class/net/eth1/queues/rx-0/rps_cpus
Призначити черги eth0 та eth1 всім 6 ядрам:
echo 3f > /sys/class/net/eth0/queues/rx-0/rps_cpus echo 3f > /sys/class/net/eth1/queues/rx-0/rps_cpus
Перманентне збереження налаштувань
Ви можете або відредагувати існуючий скрипт:
cat /etc/hotplug.d/net/40-net-smp-affinity
Або створити власний скрипт із новими значеннями, і помістити його в каталог:
/etc/hotplug.d/net/
Приклад:
# /etc/hotplug.d/net/50-mysettings-for-net-smp-affinity # Призначення переривань # eth0 → ядро 0 echo 1 > /proc/irq/35/smp_affinity # eth1 → ядро 2 echo 2 > /proc/irq/87/smp_affinity # RPS: використовувати всі ядра (0–5) echo 3f > /sys/class/net/eth0/queues/rx-0/rps_cpus echo 3f > /sys/class/net/eth1/queues/rx-0/rps_cpus
Тепер перезавантажте пристрій і перевірте, чи збереглися налаштування.
Примітки
Подяка за обговорення та внесок:
- mercygroundabyss
- moeller0
- walmartshopper
- xShARkx
Джерела/тематики обговорень: