qosd: добавлены базовые дисциплины Complex QoS ESR
А именно: + [B|P]FIFO; + HTB; + RED; + WRED (a.k.a. `gred` в Linux/TC и нашем CLI); + SFQ. Два замечания: 1. Вместо GRED и RED теперь WRED и RED: RED — как был, так и есть, а WRED — специальный узел, который несёт глобальные настройки GRED, но параметры RED на VQ задаются через подключение дочерних узлов RED. 2. Параметры RED для SFQ также были оптимизированы: в узле SFQ их не будет, но зато к SFQ можно подключить один дочерний узел. Если подключить туда RED, то настройки RED будут применяться для per-flow RED дисциплины SFQ.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "ifaces/ILinkMixin.h"
|
||||
|
||||
/// \brief Интерфейс для классов-связей между элементами.
|
||||
/// \tparam TElem Тип элемента, между которыми устанавливается связь.
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
#include <stdexcept>
|
||||
|
||||
/// \brief Связь для листового узла, не допускающая дочерних элементов.
|
||||
class LeafLink : public BaseLink {
|
||||
template <class TElem>
|
||||
class LeafLink : public BaseLink<TElem> {
|
||||
public:
|
||||
using BaseLink::BaseLink;
|
||||
using BaseLink<TElem>::BaseLink;
|
||||
void addChild(const NodePtr&) override {
|
||||
throw std::logic_error("LeafLink cannot have children");
|
||||
}
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
#pragma once
|
||||
#include "links/BaseLink.h"
|
||||
|
||||
/// \brief Связь "один-ко-многим" между элементами одного типа.
|
||||
/// Каждый дочерний элемент должен быть того же типа, что и родитель.
|
||||
/// \brief Связь "один-ко-многим" между элементами любого типа.
|
||||
/// \tparam TElem Тип элемента.
|
||||
template <class TElem>
|
||||
class OneToManyLink : public BaseLink<TElem> {
|
||||
public:
|
||||
OneToManyLink(std::shared_ptr<TElem> e) : BaseLink<TElem>(e) {}
|
||||
void addChild(const std::shared_ptr<TElem>& child) override {
|
||||
/* Each child must be exactly the same type as parent */
|
||||
if (typeid(*child) != typeid(*this->owner_node_.lock()))
|
||||
throw std::logic_error("Foundling child");
|
||||
BaseLink<TElem>::addChild(child);
|
||||
}
|
||||
};
|
||||
using OneToManyLink = BaseLink<TElem>;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <stdexcept>
|
||||
|
||||
/// \brief Обёртка для связей, чтобы сделать их типизированными.
|
||||
/// Типизированная связь позволяет подключать только узлы заданного типа.
|
||||
/// \tparam TElem Тип элемента.
|
||||
/// \tparam TBase Базовый Link. После проверки типа, управление передаётся этому классу.
|
||||
/// \tparam TExpected Опциональный ожидаемый конкретный тип детей.
|
||||
/// Если не задан, сравниваем с родителем.
|
||||
template <class TElem, class TBase, class TExpected = void>
|
||||
class TypedLink : public TBase {
|
||||
public:
|
||||
using ElemPtr = std::shared_ptr<TElem>;
|
||||
|
||||
TypedLink(ElemPtr e) : TBase(e) {}
|
||||
|
||||
void addChild(const ElemPtr& child) override {
|
||||
/* Validate type according to policy */
|
||||
if constexpr (std::is_void_v<TExpected>) {
|
||||
/* Default behavior: child must be exactly the same type as parent */
|
||||
if (typeid(*child) != typeid(*this->owner_node_.lock()))
|
||||
throw std::logic_error("Foundling child");
|
||||
} else {
|
||||
/* Explicit expected child type */
|
||||
if (typeid(*child) != typeid(TExpected))
|
||||
throw std::logic_error("Unexpected child type");
|
||||
}
|
||||
|
||||
TBase::addChild(child);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "links/TypedLink.h"
|
||||
#include "links/OneToManyLink.h"
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
|
||||
/// \brief Связь "один-ко-многим" между элементами одного типа.
|
||||
/// Каждый дочерний элемент должен быть того же типа, что и родитель.
|
||||
/// \tparam TElem Тип элемента.
|
||||
/// \tparam TExpected Опциональный ожидаемый конкретный тип детей.
|
||||
template <class TElem, class TExpected = void>
|
||||
using TypedOneToManyLink = TypedLink<TElem, OneToManyLink<TElem>, TExpected>;
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "links/BaseLink.h"
|
||||
#include <stdexcept>
|
||||
|
||||
/// \brief Связь "один-к-одному" между элементами заданного типа.
|
||||
/// Позволяет добавить только одного дочернего элемента.
|
||||
/// \tparam TElem Тип элемента.
|
||||
/// \tparam TExpected Опциональный ожидаемый конкретный тип детей.
|
||||
/// Если не задан, сравниваем с родителем.
|
||||
template <class TElem, class TExpected = void>
|
||||
using TypedOneToOneLink = TypedLink<TElem, OneToOneLink<TElem>, TExpected>;
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <iostream>
|
||||
#include "mixins/LazyLinkMixin.h"
|
||||
#include <memory>
|
||||
#include "links/OneToManyLink.h"
|
||||
#include "links/TypedOneToManyLink.h"
|
||||
#include "links/OneToOneLink.h"
|
||||
#include "Logger.h"
|
||||
|
||||
@@ -34,7 +34,7 @@ protected:
|
||||
|
||||
if (typeid(*child) == typeid(*this)) {
|
||||
Logger::get("Mixin").dbg("--- Mutate to OneToMany");
|
||||
newLink = std::make_shared<OneToManyLink<TElem>>(*this);
|
||||
newLink = std::make_shared<TypedOneToManyLink<TElem>>(*this);
|
||||
} else {
|
||||
Logger::get("Mixin").dbg("--- Mutate to OneToOne");
|
||||
newLink = std::make_shared<OneToOneLink<TElem>>(*this);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "nodes/LeafNode.h"
|
||||
#include "mixins/FabricMixin.h"
|
||||
#include "Logger.h"
|
||||
|
||||
/// \brief Узел дисциплины BFIFO. Лист.
|
||||
class BFIFONode : public LeafNode,
|
||||
public FabricMixin<BFIFONode> {
|
||||
public:
|
||||
~BFIFONode() {
|
||||
Logger::get("ConDes").dbg(std::string("--- BFIFO destructor called for: ") + name_);
|
||||
}
|
||||
private:
|
||||
friend class FabricMixin<BFIFONode>;
|
||||
BFIFONode(std::string name) : LeafNode(std::move(name), "BFIFO") {
|
||||
Logger::get("ConDes").dbg(std::string("--- BFIFO constructor called for: ") + name_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -7,9 +7,13 @@
|
||||
/// Содержит имя и тип узла, реализует интерфейс INode.
|
||||
class BaseNode : public virtual INode {
|
||||
public:
|
||||
BaseNode(std::string name) : name_(std::move(name)) {
|
||||
/// \brief Конструктор узла.
|
||||
/// \param name Неуникальное имя узла.
|
||||
BaseNode(std::string&& name, std::string kind) :
|
||||
name_(std::move(name)), kind_(std::move(kind)) {
|
||||
Logger::get("ConDes").dbg(std::string("--- Base constructor called for: ") + name_);
|
||||
}
|
||||
/// \brief Неуникальное имя узла.
|
||||
const std::string& name() const override { return name_; }
|
||||
const std::string& kind() const override { return kind_; }
|
||||
~BaseNode() {
|
||||
|
||||
@@ -9,15 +9,14 @@
|
||||
/// Может содержать несколько дочерних узлов такого же типа или один узел
|
||||
/// отличного типа.
|
||||
class ComplexNode : public BaseNode,
|
||||
virtual public HierarchicalLinkMixin<INode>,
|
||||
public FabricMixin<ComplexNode> {
|
||||
virtual public HierarchicalLinkMixin<INode> {
|
||||
public:
|
||||
~ComplexNode() {
|
||||
Logger::get("ConDes").dbg(std::string("--- Complex destructor called for: ") + name_);
|
||||
}
|
||||
private:
|
||||
friend class FabricMixin<ComplexNode>;
|
||||
ComplexNode(std::string name) : BaseNode(std::move(name)) {
|
||||
protected:
|
||||
ComplexNode(std::string&& name, std::string kind) :
|
||||
BaseNode(std::move(name), std::move(kind)) {
|
||||
Logger::get("ConDes").dbg(std::string("--- Complex constructor called for: ") + name_);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "nodes/ComplexNode.h"
|
||||
#include "mixins/FabricMixin.h"
|
||||
#include "Logger.h"
|
||||
|
||||
/// \brief Узел дисциплины HTB. Составной, допускает нескольких детей.
|
||||
class HTBNode : public ComplexNode,
|
||||
public FabricMixin<HTBNode> {
|
||||
public:
|
||||
~HTBNode() {
|
||||
Logger::get("ConDes").dbg(std::string("--- HTB destructor called for: ") + name_);
|
||||
}
|
||||
private:
|
||||
friend class FabricMixin<HTBNode>;
|
||||
HTBNode(std::string&& name) : ComplexNode(std::move(name), "HTB") {
|
||||
Logger::get("ConDes").dbg(std::string("--- HTB constructor called for: ") + name_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "nodes/BaseNode.h"
|
||||
#include "mixins/LazyLinkMixin.h"
|
||||
#include "mixins/FabricMixin.h"
|
||||
#include "links/LeafLink.h"
|
||||
#include "Logger.h"
|
||||
|
||||
/// \brief Базовый класс для краевых (листовых) узлов дисциплин.
|
||||
/// Не допускает дочерних элементов.
|
||||
class LeafNode : public BaseNode,
|
||||
virtual public LazyLinkMixin<LeafLink<INode>> {
|
||||
public:
|
||||
~LeafNode() {
|
||||
Logger::get("ConDes").dbg(
|
||||
std::string("--- Leaf destructor called for: ") + name_
|
||||
);
|
||||
}
|
||||
protected:
|
||||
LeafNode(std::string&& name, std::string kind) :
|
||||
BaseNode(std::move(name), std::move(kind)) {
|
||||
Logger::get("ConDes").dbg(
|
||||
std::string("--- Leaf constructor called for: ") +
|
||||
name_ + ", kind=" + kind_
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "nodes/LeafNode.h"
|
||||
#include "mixins/FabricMixin.h"
|
||||
#include "Logger.h"
|
||||
|
||||
/// \brief Узел дисциплины PFIFO. Лист.
|
||||
class PFIFONode : public LeafNode,
|
||||
public FabricMixin<PFIFONode> {
|
||||
public:
|
||||
~PFIFONode() {
|
||||
Logger::get("ConDes").dbg(std::string("--- PFIFO destructor called for: ") + name_);
|
||||
}
|
||||
private:
|
||||
friend class FabricMixin<PFIFONode>;
|
||||
PFIFONode(std::string name) : LeafNode(std::move(name), "PFIFO") {
|
||||
Logger::get("ConDes").dbg(std::string("--- PFIFO constructor called for: ") + name_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "nodes/LeafNode.h"
|
||||
#include "mixins/FabricMixin.h"
|
||||
#include "Logger.h"
|
||||
|
||||
/// \brief Узел дисциплины RED. Лист; не допускает дочерних элементов.
|
||||
class REDNode : public LeafNode,
|
||||
public FabricMixin<REDNode> {
|
||||
public:
|
||||
~REDNode() {
|
||||
Logger::get("ConDes").dbg(std::string("--- RED destructor called for: ") + name_);
|
||||
}
|
||||
private:
|
||||
friend class FabricMixin<REDNode>;
|
||||
REDNode(std::string name) : LeafNode(std::move(name), "RED") {
|
||||
Logger::get("ConDes").dbg(std::string("--- RED constructor called for: ") + name_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "nodes/BaseNode.h"
|
||||
#include "mixins/LazyLinkMixin.h"
|
||||
#include "mixins/FabricMixin.h"
|
||||
#include "links/TypedOneToOneLink.h"
|
||||
#include "nodes/REDNode.h"
|
||||
#include "Logger.h"
|
||||
|
||||
/// \brief Узел дисциплины SFQ. Допускает одного ребёнка типа REDNode для настройки per-flow RED.
|
||||
class SFQNode : public BaseNode,
|
||||
virtual public LazyLinkMixin<TypedOneToOneLink<INode, REDNode>>,
|
||||
public FabricMixin<SFQNode> {
|
||||
public:
|
||||
~SFQNode() {
|
||||
Logger::get("ConDes").dbg(std::string("--- SFQ destructor called for: ") + name_);
|
||||
}
|
||||
private:
|
||||
friend class FabricMixin<SFQNode>;
|
||||
SFQNode(std::string&& name) : BaseNode(std::move(name), "SFQ") {
|
||||
Logger::get("ConDes").dbg(std::string("--- SFQ constructor called for: ") + name_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -9,15 +9,13 @@
|
||||
/// \brief Класс простого (листового) узла дерева.
|
||||
/// Может содержать только одного дочернего ComplexNode.
|
||||
class SimpleNode : public BaseNode,
|
||||
virtual public LazyLinkMixin<OneToOneLink<INode>>,
|
||||
public FabricMixin<SimpleNode> {
|
||||
virtual public LazyLinkMixin<OneToOneLink<INode>>{
|
||||
public:
|
||||
~SimpleNode() {
|
||||
Logger::get("ConDes").dbg(std::string("--- Simple destructor called for: ") + name_);
|
||||
}
|
||||
private:
|
||||
friend class FabricMixin<SimpleNode>;
|
||||
SimpleNode(std::string name) : BaseNode(std::move(name)) {
|
||||
protected:
|
||||
SimpleNode(std::string&& name) : BaseNode(std::move(name)) {
|
||||
Logger::get("ConDes").dbg(std::string("--- Simple constructor called for: ") + name_);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "nodes/BaseNode.h"
|
||||
#include "mixins/LazyLinkMixin.h"
|
||||
#include "mixins/FabricMixin.h"
|
||||
#include "links/TypedOneToManyLink.h"
|
||||
#include "nodes/REDNode.h"
|
||||
#include "Logger.h"
|
||||
|
||||
class WREDNode : public BaseNode,
|
||||
virtual public LazyLinkMixin<TypedOneToManyLink<INode, REDNode>>,
|
||||
public FabricMixin<WREDNode> {
|
||||
public:
|
||||
~WREDNode() {
|
||||
Logger::get("ConDes").dbg(std::string("--- WRED destructor called for: ") + name_);
|
||||
}
|
||||
private:
|
||||
friend class FabricMixin<WREDNode>;
|
||||
WREDNode(std::string&& name) : BaseNode(std::move(name), "WRED") {
|
||||
Logger::get("ConDes").dbg(std::string("--- WRED constructor called for: ") + name_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# Классы внутреннего представления
|
||||
|
||||
1. [[#HTB]];
|
||||
2. [[#FIFO]] (хотя бы одну);
|
||||
3. [[#RED]];
|
||||
4. [[#GRED]];
|
||||
5. [[#SFQ]].
|
||||
|
||||
## HTB
|
||||
|
||||
Иерархичный TBF. Имеет классы, к которым могут подключаться другие дисциплины. Т.е. к ней могут быть подключены несколько других узлов.
|
||||
|
||||
Из дисциплины:
|
||||
|
||||
+ `default` — Минорная часть ID класса по умолчанию. Dunno, надо ли нам такое вообще. Я об этом не думал. Сейчас у нас всё на фильтры `u32` завязано.
|
||||
+ `r2q` — Кванты DRR (computed as rate in Bps/r2q). КМК, нам это не надо, мы этим никогда не пользовались. Единственное: мы раньше не пользовались DRR, а сейчас я хочу на этом построить Basic QoS.
|
||||
+ `debug` — Какой-то "string of 16 numbers each 0-3", в душе не знаю, что это.
|
||||
|
||||
+ `direct_qlen` — Лимит прямой очереди в штуках пакетов. Мы этим вряд ли воспользуемся, т.к. у нас нет такого сценария, что какой-то пакет нужной пропускать мимо QoS. Ну, я таких сценариев не знаю.
|
||||
+ `offload` — Включение аппаратного QoS. У нас сейчас точно нигде поддержки нет. Вроде бы в новом SDK появилась какая-то поддержка на каких-то платформах Marvell. Для абстрактного дерева эта опция не подходит.
|
||||
|
||||
Из классов:
|
||||
|
||||
+ `rate` — Разрешённая полоса (можно заимствовать выше этой полосы). (обязательный)
|
||||
+ `burst` — Кол-во токенов (байтов) TBF для этого класса.
|
||||
+ `mpu` — "minimum packet size used in rate computations" — Мне кажется, что этот параметр вообще давно не работает: RTAB'ы померли, `iproute2` заполняет их только для обратной совместимости (актуальные ядра сразу делают на них `qdisc_put_rtab()`).
|
||||
+ `overhead` — Поправка размера пакета для вычислений. Не путать со STAB'ом: здесь это атрибут класса HTB, и он не влияет размер пакета за пределами класса. По сути, это поправка к введённым `rate` и `ceil`.
|
||||
+ `linklay` — Протокол передачи. Кроме Ethenet по умолчанию знаю только "e.g. atm". Бесполезно.
|
||||
+ `ceil` — Допустимая полоса (заимствовать нельзя).
|
||||
+ `cburst` — Аналог `burst` для `ceil`.
|
||||
+ `mtu` — В дисциплине TBF это что-то вроде микро-burst, используемы для `peakrate`. Но здесь это такая же бесполезная вещь, как и `mpu`. По тем же причинам.
|
||||
+ `prio` — Приоритет. Определённо надо.
|
||||
+ `quantum` — Позволяет настроить *жадность* опустошения очередей в процессе DRR. По дефолту `r2q`. В целом, наверно, надо. Сейчас мы это не настраиваем, но, как я писал выше, для Basic QoS может пригодиться.
|
||||
|
||||
У этой дисциплины может быть лишь одна дочерняя дисциплина.
|
||||
|
||||
## FIFO
|
||||
|
||||
FIFO как настоящая очередь может быть только краевым узлом, то есть не имеет дочерних дисциплин и классов.
|
||||
|
||||
### PFIFO
|
||||
|
||||
Очередь с ограничением в штуках пакетов. Дисциплина по умолчанию для классов HTB. Параметры дисциплины:
|
||||
|
||||
+ `limit` — Размер очереди, кол-во пакетов (по умолчанию[^1] — `qlen` с интерфейса).
|
||||
|
||||
### BFIFO
|
||||
|
||||
Очередь с ограничением в байтах. Дисциплина по умолчанию для TBF. Параметры дисциплины:
|
||||
|
||||
+ `limit` — Размер очереди, байты (по умолчанию[^1] — `limit` с TBF).
|
||||
|
||||
[^1]: Вообще, для FIFO по умолчанию параметр `limit` равен `qlen` или `qlen * MTU`. Т.е., HTB вообще никакой параметр туда не передаёт, а TBF передаёт свой же `limit`, который проецирует на дочернюю дисциплину, если это FIFO (даже если руками новую FIFO создать).
|
||||
|
||||
## RED
|
||||
|
||||
Random Early Detection.
|
||||
|
||||
Может быть только краевым узлом.
|
||||
|
||||
+ `limit` — Размер буфера, байты. (обязательный, используем)
|
||||
+ `min`, `max` — Границы для разметки пакетов: до минимума вообще не метим пакеты; после максимума вероятность метки максимальная. Заполненность очереди, байты. (используем)
|
||||
+ `avpkt` — Средний размер пакета. Байты. Используется для каких-то хитрых вычислений. (обязательный, используем)
|
||||
+ `burst` — Всплеск. Смысл такой же, как и везде, но как именно он работает — мне неясно. Факт в том, что чем выше `burst`, тем дольше до RED доходит, что нужно маркировать трафик. Байты. (используем)
|
||||
+ `adaptive` — Какая-то умная шняга для автоматической подгонки параметров под текущее поведение трафика. Мы это не используем, кстати. Гля [[adaptiveRed.pdf]].
|
||||
+ `probability` — Максимальная вероятность *помаркать* пакет. Число с плавающей точкой, континуум 0-1. (используем)
|
||||
+ `bandwidth` — Не про шейпинг, но про скорость интерфейса, используется для расчёта средней длины очереди. Биты в секунду.
|
||||
+ `ecn` — Включить именно маркировку пакетов, вместо отбрасывания, для сдерживания полосы потока. Пакет всё же будет отброшен, если длина очереди для этого потока превысит `limit`. Рекомендуемо, но мы этим не пользуемся.
|
||||
+ `harddrop` — С этим параметром пакет будет отброшен, если длина очереди превысит `max`.
|
||||
+ `nodrop` — С этим параметром дисциплина не будет отбрасывать пакеты, которые не может маркировать ("not ECN-capable"). По умолчанию такие пакеты дропаются.
|
||||
|
||||
Параметры, связанные с QEVENTS (`man 8 tc`), рассматривать точно не будем.
|
||||
|
||||
## GRED
|
||||
|
||||
А это, типа, "Generalized RED". Что бы это не значило. Мы это как WRED используем. Литературы по нему мало.
|
||||
|
||||
Тут вводится понятие "виртуальной очереди" (VQ). И к самому Linux TC оно отношения не имеет. Оно даже к очередям отношение имеет опосредованное: никакой очереди фактически не выделяется, поэтому она и *виртуальная*. И настройка этих очередей довольно виктимная: нужно послать `CHANGE` на уже созданную дисциплину и в качестве параметра передать номер очереди, настройки которой мы хотим изменить.
|
||||
|
||||
+ `vqs` — Кол-во виртуальных очередей. (обязательно)
|
||||
+ `default` — Виртуальная очередь по умолчанию. (обязательно)
|
||||
+ `grio` — Вкл. наследование буферов. Условно, приоритетный трафик затрагивает, в том числе, счётчики менее приоритетного трафика.
|
||||
+ `limit` — Размер буфера, байты. Здесь, в отличие от RED и VQ, является необязательным. Ограничивает очередь *мимо* VQ, а также задаёт верхнюю планку для каждой VQ (больше этого `limit` нельзя задавать `limit` для VQ).
|
||||
+ `ecn` — Возможность включить маркировку пакетов на всю дисциплину, либо включение только для отдельной VQ. Вроде бы, и там, и тут включить нельзя. Кроме того, на 4.14, например, эти флаги на VQ вообще не используются.
|
||||
+ `harddrop` — С этим параметром пакет будет отброшен, если длина очереди превысит `max`. Аналогично `ecn`.
|
||||
|
||||
Ну и далее для VQ аналогично RED:
|
||||
|
||||
+ `limit` — Размер буфера, байты. (обязательный, используем)
|
||||
+ `min`, `max` — Границы для разметки пакетов: до минимума вообще не метим пакеты; после максимума вероятность метки максимальная. Заполненность очереди, байты. (используем)
|
||||
+ `avpkt` — Средний размер пакета. Байты. Используется для каких-то хитрых вычислений. (обязательный, используем)
|
||||
+ `burst` — Всплеск. Смысл такой же, как и везде, но как именно он работает — мне неясно. Факт в том, что чем выше `burst`, тем дольше до RED доходит, что нужно маркировать трафик. Байты. (используем)
|
||||
+ `probability` — Максимальная вероятность *помаркать* пакет. Число с плавающей точкой, континуум 0-1. (используем)
|
||||
+ `bandwidth` — Не про шейпинг, но про скорость интерфейса, используется для расчёта средней длины очереди. Биты в секунду.
|
||||
|
||||
## SFQ
|
||||
|
||||
Также может быть только краевым узлом.
|
||||
|
||||
Дисциплина работает по принципу "отобрать и поделить". Т.е. поток трафика на исходящем интерфейсе распределяется по множеству динамически создаваемых очередей на основании хэша пакета. Эти динамические очереди обслуживаются условным RR. За счёт такого подхода, гипотетически, никакая сессия не может монополизировать линк. Грубо говоря, социалистическая революция в рамках дисциплины на интерфейсе.
|
||||
|
||||
Несмотря на то, что у этой дисциплины при создании нет обязательных параметров, этих параметров всё же очень много:
|
||||
|
||||
+ `limit` — Кол-во пакетов, задерживаемое дисциплиной. Это суммарное ограничение по всем потокам.
|
||||
+ `depth` — Кол-во пакетов, задерживаемое одним потоком.
|
||||
+ `divisor` — Размер хэш-таблицы. Чем больше потоков дозволено, тем медленнее будет происходить поиск по ним, но увеличение размера таблицы сокращает время поиска. Должно быть степенью двойки, по умолчанию 1024.
|
||||
+ `perturb` — Кол-во секунд для периода пертурбации, т.е. как часто изменять хэш-функцию (за счёт соли).
|
||||
+ `flows` — Кол-во потоков. (единственное, что мы настраиваем в CLI; хотя `brasd` SFQ тоже пользуется и настраивает больше параметров!)
|
||||
+ `quantum` — Порция трафика для одного раунда RR-процесса. Байты.
|
||||
+ `headdrop` — Флаг для отбрасывания трафика из головы очереди, а не с хвоста. Гипотетически, так отклик TCP лучше.
|
||||
+ `redflowlimit` — Ограничение в байтах на очередь **потока**. Заодно включает сам RED на каждом потоке SFQ. Да, но мы этим тоже не пользуемся.
|
||||
+ Параметры RED третий раз перечислять не очень-то хочется. Но тут поддерживаются все параметры из [[#RED]], кроме `adaptive`.
|
||||
+132
-108
@@ -2,8 +2,12 @@
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
#include "nodes/SimpleNode.h"
|
||||
#include "nodes/ComplexNode.h"
|
||||
#include "nodes/HTBNode.h"
|
||||
#include "nodes/PFIFONode.h"
|
||||
#include "nodes/BFIFONode.h"
|
||||
#include "nodes/REDNode.h"
|
||||
#include "nodes/WREDNode.h"
|
||||
#include "nodes/SFQNode.h"
|
||||
#include "iterators/Traversal.h"
|
||||
|
||||
|
||||
@@ -27,136 +31,156 @@ void printTreeLadder(INode& node) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
Logger::setMinSeverity("MAIN", Logger::Severity::Debug);
|
||||
Logger::setMinSeverity("MAIN", Logger::Severity::Warning);
|
||||
Logger::suppressCategory("Node");
|
||||
Logger::suppressCategory("Link");
|
||||
Logger::suppressCategory("Mixin");
|
||||
|
||||
Logger::suppressCategory("ConDes");
|
||||
Logger::setMinSeverity("ConDes", Logger::Severity::Info);
|
||||
Logger::setMinSeverity("ConDes", Logger::Severity::Warning);
|
||||
|
||||
auto& logger = Logger::get("MAIN");
|
||||
logger.info("Entering main scope...");
|
||||
|
||||
int negative_fails_count = 0;
|
||||
|
||||
{
|
||||
auto root = ComplexNode::create("ComplexRoot");
|
||||
auto child1 = ComplexNode::create("ComplexChild1");
|
||||
// Строим дерево HTB с несколькими уровнями и разными краевыми дисциплинами
|
||||
auto root = HTBNode::create("HTB_root");
|
||||
|
||||
root->linkChild(child1);
|
||||
auto htbA = HTBNode::create("HTB_A");
|
||||
auto htbB = HTBNode::create("HTB_B");
|
||||
root->linkChild(htbA);
|
||||
root->linkChild(htbB);
|
||||
|
||||
std::cout << "\nInit tree:\n";
|
||||
// Ветка A: PFIFO, RED, BFIFO на краях
|
||||
auto htbA1 = HTBNode::create("HTB_A1");
|
||||
auto htbA2 = HTBNode::create("HTB_A2");
|
||||
auto htbA3 = HTBNode::create("HTB_A3");
|
||||
htbA->linkChild(htbA1);
|
||||
htbA->linkChild(htbA2);
|
||||
htbA->linkChild(htbA3);
|
||||
|
||||
auto pfifoLeaf = PFIFONode::create("PFIFO_leaf");
|
||||
htbA1->linkChild(pfifoLeaf);
|
||||
htbA2->linkChild(REDNode::create("RED_leaf"));
|
||||
htbA3->linkChild(BFIFONode::create("BFIFO_leaf"));
|
||||
|
||||
// Ветка B: WRED (с RED детьми) и SFQ (с одним RED)
|
||||
auto htbB1 = HTBNode::create("HTB_B1");
|
||||
auto htbB2 = HTBNode::create("HTB_B2");
|
||||
htbB->linkChild(htbB1);
|
||||
htbB->linkChild(htbB2);
|
||||
|
||||
// WRED как лист с несколькими RED
|
||||
auto wred = WREDNode::create("WRED_leaf");
|
||||
wred->linkChild(REDNode::create("RED_vq1"));
|
||||
wred->linkChild(REDNode::create("RED_vq2"));
|
||||
htbB1->linkChild(wred);
|
||||
|
||||
// SFQ с одним RED
|
||||
auto sfq = SFQNode::create("SFQ_leaf");
|
||||
htbB2->linkChild(sfq);
|
||||
|
||||
// Негативный сценарий: SFQ должен дозволять подключать к себе
|
||||
// только узлы RED.
|
||||
logger.info("[Negative] Попытка добавить не-RED в SFQ (должно упасть)");
|
||||
try {
|
||||
sfq->linkChild(HTBNode::create("nonRED_on_SFQ"));
|
||||
logger.err("Удалось подключить HTB к SFQ!");
|
||||
negative_fails_count++;
|
||||
} catch (const std::logic_error& e) {
|
||||
logger.info(std::string("[Ожидаемое исключение] ") + e.what());
|
||||
|
||||
// И всё-таки подключим SFQ
|
||||
sfq->linkChild(REDNode::create("RED_on_SFQ"));
|
||||
}
|
||||
|
||||
// Другие негативные сценарии
|
||||
logger.info("[Negative] Попытка подключить не-RED к SFQ (должно упасть)");
|
||||
try {
|
||||
pfifoLeaf->linkChild(HTBNode::create("ShouldFailOnSFQ"));
|
||||
logger.err("Удалось подключить ребёнка к SFQ!");
|
||||
negative_fails_count++;
|
||||
} catch (const std::logic_error& e) {
|
||||
logger.info(std::string("[Ожидаемое исключение] ") + e.what());
|
||||
}
|
||||
|
||||
logger.info("[Negative] Попытка подключить что-то к PFIFO (должно упасть)");
|
||||
try {
|
||||
pfifoLeaf->linkChild(REDNode::create("ShouldFailOnPFIFO"));
|
||||
logger.err("Удалось подключить ребёнка к PFIFO!");
|
||||
negative_fails_count++;
|
||||
} catch (const std::logic_error& e) {
|
||||
logger.info(std::string("[Ожидаемое исключение] ") + e.what());
|
||||
}
|
||||
|
||||
logger.info("[Negative] Попытка подключить второй не-HTB узел к HTB_A1 (должно упасть)");
|
||||
try {
|
||||
htbA1->linkChild(BFIFONode::create("SecondLeafOnHTB_A1"));
|
||||
logger.err("Удалось подключить второй лист к HTB_A1!");
|
||||
negative_fails_count++;
|
||||
} catch (const std::logic_error& e) {
|
||||
logger.info(std::string("[Ожидаемое исключение] ") + e.what());
|
||||
}
|
||||
|
||||
logger.info("[Negative] Попытка подключить не-HTB узел к HTB_A (должно упасть)");
|
||||
try {
|
||||
htbA->linkChild(REDNode::create("AnotherLeafOnHTB_A"));
|
||||
logger.err("Удалось подключить второй лист к HTB_A1!");
|
||||
negative_fails_count++;
|
||||
} catch (const std::logic_error& e) {
|
||||
logger.info(std::string("[Ожидаемое исключение] ") + e.what());
|
||||
}
|
||||
|
||||
logger.info("[Negative] Попытка добавить не-RED в WRED (должно упасть)");
|
||||
try {
|
||||
wred->linkChild(PFIFONode::create("NotRED_for_WRED"));
|
||||
logger.err("Удалось подключить не-RED к WRED!");
|
||||
negative_fails_count++;
|
||||
} catch (const std::logic_error& e) {
|
||||
logger.info(std::string("[Ожидаемое исключение] ") + e.what());
|
||||
}
|
||||
|
||||
logger.info("[Negative] Попытка добавить второго ребёнка в SFQ (должно упасть)");
|
||||
try {
|
||||
sfq->linkChild(REDNode::create("SecondRED_on_SFQ"));
|
||||
logger.err("Удалось подключить второй ребёнок к SFQ!");
|
||||
negative_fails_count++;
|
||||
} catch (const std::logic_error& e) {
|
||||
logger.info(std::string("[Ожидаемое исключение] ") + e.what());
|
||||
}
|
||||
|
||||
logger.info("[Negative] Попытка RED->RED (должно упасть, RED — лист)");
|
||||
try {
|
||||
auto redLeaf = REDNode::create("RED_alone");
|
||||
redLeaf->linkChild(REDNode::create("NestedRED"));
|
||||
logger.err("Удалось подключить RED к RED!");
|
||||
negative_fails_count++;
|
||||
} catch (const std::logic_error& e) {
|
||||
logger.info(std::string("[Ожидаемое исключение] ") + e.what());
|
||||
}
|
||||
|
||||
// Покрасуемся получившимся деревом:
|
||||
std::cout << "\nHTB tree:\n";
|
||||
printTreeLadder(*root);
|
||||
std::cout << "\n";
|
||||
|
||||
{
|
||||
auto child2 = ComplexNode::create("ComplexChild2");
|
||||
root->linkChild(child2);
|
||||
|
||||
auto subchild2 = SimpleNode::create("SimpleSubChild2");
|
||||
child2->linkChild(subchild2);
|
||||
|
||||
auto child3 = ComplexNode::create("ComplexChild3");
|
||||
root->linkChild(child3);
|
||||
|
||||
child3->linkChild(SimpleNode::create("SimpleSubChild3"));
|
||||
|
||||
{
|
||||
// Негативный сценарий 1: попытка добавить второй SimpleNode к ComplexNode
|
||||
try {
|
||||
child2->linkChild(SimpleNode::create("ShouldFail"));
|
||||
logger.err("[ERROR] Не должно было получиться добавить второй SimpleNode к ComplexNode!");
|
||||
} catch (const std::logic_error& e) {
|
||||
logger.warn(std::string("[Ожидаемое исключение] ") + e.what());
|
||||
}
|
||||
|
||||
// Негативный сценарий 2: попытка добавить ComplexNode к SimpleNode
|
||||
// Это допустимо: SimpleNode может иметь ComplexNode в качестве единственного ребёнка
|
||||
subchild2->linkChild(ComplexNode::create("GoodComplex"));
|
||||
logger.info("[OK] ComplexNode успешно добавлен к SimpleNode как единственный ребёнок.");
|
||||
|
||||
// Негативный сценарий: попытка добавить второго ребёнка к SimpleNode
|
||||
try {
|
||||
subchild2->linkChild(SimpleNode::create("ShouldFail2"));
|
||||
logger.err("[ERROR] Не должно было получиться добавить второго ребёнка к SimpleNode!");
|
||||
} catch (const std::logic_error& e) {
|
||||
logger.warn(std::string("[Ожидаемое исключение] ") + e.what());
|
||||
}
|
||||
|
||||
// Негативный сценарий: попытка добавить SimpleNode в ComplexNode, который уже содержит несколько ComplexNode-дочерних
|
||||
try {
|
||||
root->linkChild(SimpleNode::create("BadSimple"));
|
||||
logger.err("[ERROR] Не должно было получиться добавить SimpleNode в ComplexNode с несколькими ComplexNode-дочерними!");
|
||||
} catch (const std::logic_error& e) {
|
||||
logger.warn(std::string("[Ожидаемое исключение] ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\nAnother tree:\n";
|
||||
printTreeLadder(*root);
|
||||
std::cout << "\n";
|
||||
|
||||
std::cout << "\nList:\n";
|
||||
// Обход в глубину:
|
||||
std::cout << "\nDFS list:\n";
|
||||
printTreeList(*root);
|
||||
std::cout << "\n";
|
||||
|
||||
std::cout << "\nBFS:\n";
|
||||
// Обход в ширину:
|
||||
std::cout << "\nBFS list:\n";
|
||||
printTreeBFS(*root);
|
||||
std::cout << "\n";
|
||||
|
||||
logger.info("Unlinking ComplexChild2...\n");
|
||||
child2->unlinkParent();
|
||||
|
||||
std::cout << "\nTree after unlink:\n";
|
||||
printTreeLadder(*root);
|
||||
std::cout << "\n";
|
||||
|
||||
logger.info("Put refs of ComplexChild2, ComplexChild3 and its children");
|
||||
}
|
||||
|
||||
std::cout << "\nTree after scope out:\n";
|
||||
printTreeLadder(*root);
|
||||
std::cout << "\n";
|
||||
|
||||
logger.info("Unlinking ComplexChild1 and ComplexChild3...\n");
|
||||
child1->unlinkParent();
|
||||
for (auto child : traversal::BFS(root)) {
|
||||
if (child->name() == "ComplexChild3") {
|
||||
/* Avoid of disconnecting a node of the tree
|
||||
* we're traversing. Method holds reference
|
||||
* to it's object itself. But iterator ignores
|
||||
* smart pointers, feeling free to dereference
|
||||
* the destroyed object.
|
||||
*/
|
||||
#if 1
|
||||
/* Keep in mind to keep your reference until
|
||||
* the cycle ends, if you want more than just
|
||||
* remove subtree.
|
||||
*/
|
||||
NodePtr node = *child;
|
||||
node->unlinkParent();
|
||||
std::cout << "Unlinked: "
|
||||
<< node->name() << std::endl;
|
||||
break;
|
||||
#else
|
||||
/* Otherwise, just break immediately you
|
||||
* unlinked a node. The current pointer is not
|
||||
* a valid pointer anymore.
|
||||
*/
|
||||
child->unlinkParent();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
root->linkChild(SimpleNode::create("SimpleChild4"));
|
||||
|
||||
std::cout << "\nTree flush and link SimpleChild4:\n";
|
||||
printTreeLadder(*root);
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
logger.info("Exited main scope. All smart pointers destroyed.");
|
||||
logger.info("(It don't? Check ConDes logger)");
|
||||
if (negative_fails_count)
|
||||
logger.err(std::string("Negative fails: ") + std::to_string(negative_fails_count));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user