From 43b44d7ee5abec9ff3b4904f9d20cd50a786eea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=9C=D0=B0=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=BA=D0=B5=D0=B2=D0=B8=D1=87?= Date: Wed, 3 Sep 2025 20:12:47 +0700 Subject: [PATCH] =?UTF-8?q?qosd:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=B0=D1=82=D1=80=D0=B8=D0=B1=D1=83=D1=82?= =?UTF-8?q?=D1=8B=20=D1=83=D0=B7=D0=BB=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Мне показалось, будет удобно использовать вложенную структуру в качестве аргумента конструктора. --- include/nodes/BFIFONode.h | 17 ++++++++++++++++- include/nodes/HTBNode.h | 34 ++++++++++++++++++++++++++++++++-- include/nodes/PFIFONode.h | 17 ++++++++++++++++- include/nodes/REDNode.h | 30 +++++++++++++++++++++++++++++- include/nodes/SFQNode.h | 24 +++++++++++++++++++++++- include/nodes/WREDNode.h | 21 ++++++++++++++++++++- qdiscs.md | 12 ++++++++---- 7 files changed, 144 insertions(+), 11 deletions(-) diff --git a/include/nodes/BFIFONode.h b/include/nodes/BFIFONode.h index 56a4ab6..e826a34 100644 --- a/include/nodes/BFIFONode.h +++ b/include/nodes/BFIFONode.h @@ -1,5 +1,6 @@ #pragma once +#include #include "nodes/LeafNode.h" #include "mixins/FabricMixin.h" #include "Logger.h" @@ -8,14 +9,28 @@ class BFIFONode : public LeafNode, public FabricMixin { public: + struct Config { + /// Размер очереди в байтах. + std::uint64_t limit = 0; + }; + ~BFIFONode() { Logger::get("ConDes").dbg(std::string("--- BFIFO destructor called for: ") + name_); } + + const Config& config() const { return config_; } private: friend class FabricMixin; - BFIFONode(std::string name) : LeafNode(std::move(name), "BFIFO") { + BFIFONode(std::string&& name) : LeafNode(std::move(name), "BFIFO") { Logger::get("ConDes").dbg(std::string("--- BFIFO constructor called for: ") + name_); } + + BFIFONode(std::string&& name, Config&& config) : LeafNode(std::move(name), "BFIFO"), + config_(std::move(config)) { + Logger::get("ConDes").dbg(std::string("--- BFIFO constructor called for: ") + name_); + } + + Config config_{}; }; diff --git a/include/nodes/HTBNode.h b/include/nodes/HTBNode.h index 1a923f4..da16671 100644 --- a/include/nodes/HTBNode.h +++ b/include/nodes/HTBNode.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include "nodes/ComplexNode.h" #include "mixins/FabricMixin.h" #include "Logger.h" @@ -8,16 +10,44 @@ class HTBNode : public ComplexNode, public FabricMixin { public: + struct Config { + std::uint64_t cir = 0; ///< Разрешённая полоса, CIR, бит/с. + std::uint64_t cburst = 0; ///< Токены для CIR, байты. + std::uint64_t pir = 0; ///< Допустимая полоса, PIR, бит/с. + std::uint64_t pburst = 0; ///< Токены для PIR, байты. + std::uint32_t prio = 0; ///< Приоритет класса. + std::uint32_t quantum = 0; ///< Квант DRR, байты. + + /// \brief Поправка размера пакета, байты. + /// + /// \note Вообще, этот параметр исторически взят с Linux/TC HTB. + /// И в таком контексте: речь **не** про STAB, а именно про + /// параметр класса HTB. Он, в отличие от STAB, не влияет на сам + /// размер пакета, а используется только для поправки шейпера + /// конкретно этого класса. Впрочем, в BC2 есть такая же местная + /// поправка для шейперов, см. FS 31.5.2.3 Packet Length Offset. + std::int32_t overhead = 0; + }; + ~HTBNode() { Logger::get("ConDes").dbg(std::string("--- HTB destructor called for: ") + name_); } + + const Config& config() const { return config_; } private: friend class FabricMixin; - HTBNode(std::string name) - : ComplexNode(std::move(name)) { + HTBNode(std::string&& name) : ComplexNode(std::move(name)) { kind_ = "HTB"; Logger::get("ConDes").dbg(std::string("--- HTB constructor called for: ") + name_); } + + HTBNode(std::string&& name, Config&& config) : ComplexNode(std::move(name)), + config_(std::move(config)) { + kind_ = "HTB"; + Logger::get("ConDes").dbg(std::string("--- HTB constructor called for: ") + name_); + } + + Config config_{}; }; diff --git a/include/nodes/PFIFONode.h b/include/nodes/PFIFONode.h index 4da4a5d..ba330cb 100644 --- a/include/nodes/PFIFONode.h +++ b/include/nodes/PFIFONode.h @@ -1,5 +1,6 @@ #pragma once +#include #include "nodes/LeafNode.h" #include "mixins/FabricMixin.h" #include "Logger.h" @@ -8,14 +9,28 @@ class PFIFONode : public LeafNode, public FabricMixin { public: + struct Config { + /// \brief Кол-во пакетов. + std::uint32_t limit; + }; + ~PFIFONode() { Logger::get("ConDes").dbg(std::string("--- PFIFO destructor called for: ") + name_); } + + const Config& config() const { return config_; } private: friend class FabricMixin; - PFIFONode(std::string name) : LeafNode(std::move(name), "PFIFO") { + PFIFONode(std::string&& name) : LeafNode(std::move(name), "PFIFO") { Logger::get("ConDes").dbg(std::string("--- PFIFO constructor called for: ") + name_); } + + PFIFONode(std::string&& name, Config&& config) : LeafNode(std::move(name), "PFIFO"), + config_(std::move(config)) { + Logger::get("ConDes").dbg(std::string("--- PFIFO constructor called for: ") + name_); + } + + Config config_{}; }; diff --git a/include/nodes/REDNode.h b/include/nodes/REDNode.h index bb2fd97..4ad14f1 100644 --- a/include/nodes/REDNode.h +++ b/include/nodes/REDNode.h @@ -1,21 +1,49 @@ #pragma once +#include #include "nodes/LeafNode.h" #include "mixins/FabricMixin.h" #include "Logger.h" /// \brief Узел дисциплины RED. Лист; не допускает дочерних элементов. +/// +/// \note Может быть подключен к: +/// + WRED для настройки VQ; +/// + SFQ для настройки per-flow RED. class REDNode : public LeafNode, public FabricMixin { public: + struct Config { + std::uint64_t limit = 0; ///< Размер буфера, байты (обязателен). + std::uint64_t min = 0; ///< Нижняя граница для разметки, байты. + std::uint64_t max = 0; ///< Верхняя граница для разметки, байты. + std::uint32_t avpkt = 0; ///< Средний размер пакета, байты (обязателен). + std::uint64_t burst = 0; ///< Всплеск, байты. + double probability = 0.0; ///< Максимальная вероятность, 0..1. + std::uint64_t bandwidth = 0; ///< Скорость интерфейса, бит/с. + bool ecn = false; ///< Включить ECN-разметку вместо дропа. + bool harddrop = false; ///< Жёсткий дроп при превышении max. + bool nodrop = false; ///< Не дропать non-ECN-пакеты. + }; + ~REDNode() { Logger::get("ConDes").dbg(std::string("--- RED destructor called for: ") + name_); } + + const Config& config() const { return config_; } private: friend class FabricMixin; - REDNode(std::string name) : LeafNode(std::move(name), "RED") { + REDNode(std::string&& name) + : LeafNode(std::move(name), "RED") { Logger::get("ConDes").dbg(std::string("--- RED constructor called for: ") + name_); } + + REDNode(std::string&& name, Config&& config) + : LeafNode(std::move(name), "RED"), config_(std::move(config)) { + Logger::get("ConDes").dbg(std::string("--- RED constructor called for: ") + name_); + } + + Config config_{}; }; diff --git a/include/nodes/SFQNode.h b/include/nodes/SFQNode.h index 492ee56..9bffdb9 100644 --- a/include/nodes/SFQNode.h +++ b/include/nodes/SFQNode.h @@ -1,5 +1,6 @@ #pragma once +#include #include "nodes/BaseNode.h" #include "mixins/LazyLinkMixin.h" #include "mixins/FabricMixin.h" @@ -12,15 +13,36 @@ class SFQNode : public BaseNode, virtual public LazyLinkMixin>, public FabricMixin { public: + /// \note Параметры RED per-flow задаются через дочерний REDNode. + struct Config { + std::uint32_t limit = 0; ///< Суммарный лимит по всем потокам, пакеты + std::uint32_t depth = 0; ///< Лимит на один поток, пакеты + std::uint32_t divisor = 1024; ///< Размер хэш-таблицы (степень двойки) + std::uint32_t perturb = 0; ///< Период пертурбации, секунды + std::uint32_t flows = 0; ///< Число потоков + std::uint32_t quantum = 0; ///< Порция RR, байты + bool headdrop = false; ///< Отбрасывать из головы очереди + }; + ~SFQNode() { Logger::get("ConDes").dbg(std::string("--- SFQ destructor called for: ") + name_); } + + const Config& config() const { return config_; } private: friend class FabricMixin; - SFQNode(std::string name) : BaseNode(std::move(name)) { + SFQNode(std::string&& name) : BaseNode(std::move(name)) { kind_ = "SFQ"; Logger::get("ConDes").dbg(std::string("--- SFQ constructor called for: ") + name_); } + + SFQNode(std::string&& name, Config&& config) + : BaseNode(std::move(name)), config_(std::move(config)) { + kind_ = "SFQ"; + Logger::get("ConDes").dbg(std::string("--- SFQ constructor called for: ") + name_); + } + + Config config_{}; }; diff --git a/include/nodes/WREDNode.h b/include/nodes/WREDNode.h index 46bdc3b..08b251f 100644 --- a/include/nodes/WREDNode.h +++ b/include/nodes/WREDNode.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include "nodes/BaseNode.h" #include "mixins/LazyLinkMixin.h" #include "mixins/FabricMixin.h" @@ -11,15 +13,32 @@ class WREDNode : public BaseNode, virtual public LazyLinkMixin>, public FabricMixin { public: + struct Config { + bool grio = false; ///< Наследование буферов (GRIO) + std::uint64_t limit = 0; ///< Глобальный лимит буфера, байты (необязателен) + bool ecn = false; ///< Включить ECN на дисциплине + bool harddrop = false; ///< Жёсткий дроп при превышении max + }; + ~WREDNode() { Logger::get("ConDes").dbg(std::string("--- WRED destructor called for: ") + name_); } + + const Config& config() const { return config_; } private: friend class FabricMixin; - WREDNode(std::string name) : BaseNode(std::move(name)) { + WREDNode(std::string&& name) : BaseNode(std::move(name)) { kind_ = "WRED"; Logger::get("ConDes").dbg(std::string("--- WRED constructor called for: ") + name_); } + + WREDNode(std::string&& name, Config&& config) + : BaseNode(std::move(name)), config_(std::move(config)) { + kind_ = "WRED"; + Logger::get("ConDes").dbg(std::string("--- WRED constructor called for: ") + name_); + } + + Config config_{}; }; diff --git a/qdiscs.md b/qdiscs.md index 9482fc3..a8e4a19 100644 --- a/qdiscs.md +++ b/qdiscs.md @@ -13,10 +13,10 @@ Из дисциплины: + `default` — Минорная часть ID класса по умолчанию. Dunno, надо ли нам такое вообще. Я об этом не думал. Сейчас у нас всё на фильтры `u32` завязано. -+ `r2q` — Кванты DRR (computed as rate in Bps/r2q). КМК, нам это не надо, мы этим никогда не пользовались. Единственное: мы раньше не пользовались DRR, а сейчас я хочу на этом построить Basic QoS. ++ `r2q` — Кванты DRR (computed as rate in Bps/r2q). КМК, нам это не надо, мы этим никогда не пользовались. Единственное: мы раньше не пользовались DRR, а сейчас я хочу на этом построить Basic QoS. — **UPD:** Это можно в глобальной конфигурации задать. + `debug` — Какой-то "string of 16 numbers each 0-3", в душе не знаю, что это. -+ `direct_qlen` — Лимит прямой очереди в штуках пакетов. Мы этим вряд ли воспользуемся, т.к. у нас нет такого сценария, что какой-то пакет нужной пропускать мимо QoS. Ну, я таких сценариев не знаю. ++ `direct_qlen` — Лимит прямой очереди в штуках пакетов. Мы этим вряд ли воспользуемся, т.к. у нас нет такого сценария, что какой-то пакет нужной пропускать мимо QoS. Ну, я таких сценариев не знаю. — **UPD:** В целом, можно вписать в мою архитектуру, если явно отобразить такой узел в дереве: будет класс, который матчит весь трафик (как в Complex QoS `class-default`), и в качестве дочернего узла у него некий DirectNode. + `offload` — Включение аппаратного QoS. У нас сейчас точно нигде поддержки нет. Вроде бы в новом SDK появилась какая-то поддержка на каких-то платформах Marvell. Для абстрактного дерева эта опция не подходит. Из классов: @@ -77,8 +77,10 @@ Random Early Detection. Тут вводится понятие "виртуальной очереди" (VQ). И к самому Linux TC оно отношения не имеет. Оно даже к очередям отношение имеет опосредованное: никакой очереди фактически не выделяется, поэтому она и *виртуальная*. И настройка этих очередей довольно виктимная: нужно послать `CHANGE` на уже созданную дисциплину и в качестве параметра передать номер очереди, настройки которой мы хотим изменить. -+ `vqs` — Кол-во виртуальных очередей. (обязательно) -+ `default` — Виртуальная очередь по умолчанию. (обязательно) +В этом проекте присутствует как узел WRED. Узел WRED несёт в себе только глобальные настройки дисциплины. Для конфигурирования VQ к узлу WRED прикрепляются узлы RED. + ++ `vqs` — Кол-во виртуальных очередей. (обязательно) — **UPD:** Вообще, нам это не нужно: мы и так можем посчитать, сколько VQ мы определили, по самим дочерним узлам. ++ `default` — Виртуальная очередь по умолчанию. (обязательно) — **UPD:** Это тоже не нужно, т.к. мы фильтрами на самой очереди скажем, что она дефолтная. + `grio` — Вкл. наследование буферов. Условно, приоритетный трафик затрагивает, в том числе, счётчики менее приоритетного трафика. + `limit` — Размер буфера, байты. Здесь, в отличие от RED и VQ, является необязательным. Ограничивает очередь *мимо* VQ, а также задаёт верхнюю планку для каждой VQ (больше этого `limit` нельзя задавать `limit` для VQ). + `ecn` — Возможность включить маркировку пакетов на всю дисциплину, либо включение только для отдельной VQ. Вроде бы, и там, и тут включить нельзя. Кроме того, на 4.14, например, эти флаги на VQ вообще не используются. @@ -110,3 +112,5 @@ Random Early Detection. + `headdrop` — Флаг для отбрасывания трафика из головы очереди, а не с хвоста. Гипотетически, так отклик TCP лучше. + `redflowlimit` — Ограничение в байтах на очередь **потока**. Заодно включает сам RED на каждом потоке SFQ. Да, но мы этим тоже не пользуемся. + Параметры RED третий раз перечислять не очень-то хочется. Но тут поддерживаются все параметры из [[#RED]], кроме `adaptive`. + +Параметры RED на SFQ конфигурируются аналогично WRED и VQ: к SFQ подключается RED, с которого и будет считываться конфигурация per-flow RED.