Skip to content

Commit 38538f8

Browse files
viviBarath Raghavan
authored andcommitted
Add backpressure queue (#504)
* Add backpressure queue * Address Barath's comments * Add RemoveEdge * Address Barath's comments, fix water level bug * Tweaks remove `overload_` check, use static cast, change comment style
1 parent 39d1229 commit 38538f8

14 files changed

Lines changed: 439 additions & 18 deletions

File tree

core/module.cc

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,85 @@
1616
const Commands Module::cmds;
1717

1818
std::map<std::string, Module *> ModuleBuilder::all_modules_;
19+
std::unordered_map<std::string, Node> ModuleBuilder::module_graph_;
20+
std::unordered_set<std::string> ModuleBuilder::tasks_;
21+
22+
bool ModuleBuilder::AddEdge(const std::string &from, const std::string &to) {
23+
auto from_it = module_graph_.find(from);
24+
if (from_it == module_graph_.end() || module_graph_.count(to) == 0) {
25+
return false;
26+
}
27+
from_it->second.AddChild(to);
28+
return UpdateTaskGraph();
29+
}
30+
31+
bool ModuleBuilder::RemoveEdge(const std::string &from, const std::string &to) {
32+
auto from_node = module_graph_.find(from);
33+
if (from_node == module_graph_.end() || module_graph_.count(to) == 0) {
34+
return false;
35+
}
36+
37+
from_node->second.RemoveChild(to);
38+
39+
// We need to regenerate the task graph.
40+
for (auto const &task : tasks_) {
41+
auto it = all_modules_.find(task);
42+
if (it != all_modules_.end()) {
43+
it->second->parent_tasks_.clear();
44+
}
45+
}
46+
return UpdateTaskGraph();
47+
}
48+
49+
bool ModuleBuilder::UpdateTaskGraph() {
50+
for (auto const &task : tasks_) {
51+
std::unordered_set<std::string> visited;
52+
if (!FindNextTask(task, task, &visited)) {
53+
return false;
54+
}
55+
}
56+
return true;
57+
}
58+
59+
bool ModuleBuilder::FindNextTask(const std::string &node_name,
60+
const std::string &parent_name,
61+
std::unordered_set<std::string> *visited) {
62+
visited->insert(node_name);
63+
// While traversing the module graph, if `node` is in the task graph and is
64+
// not `parent`, then it must be the child of `parent`.
65+
if (node_name != parent_name && tasks_.find(node_name) != tasks_.end()) {
66+
auto parent_it = all_modules_.find(parent_name);
67+
auto node_it = all_modules_.find(node_name);
68+
if (parent_it == all_modules_.end() || node_it == all_modules_.end()) {
69+
return false;
70+
}
71+
72+
Module *node = node_it->second;
73+
Module *parent = parent_it->second;
74+
for (Module *it : node->parent_tasks_) {
75+
if (it == parent) {
76+
return true;
77+
}
78+
}
79+
node->parent_tasks_.push_back(parent);
80+
return true;
81+
}
82+
83+
auto node_it = module_graph_.find(node_name);
84+
if (node_it == module_graph_.end()) {
85+
return false;
86+
}
87+
88+
for (auto &child_name : node_it->second.children()) {
89+
if (visited->count(child_name) > 0) {
90+
continue;
91+
}
92+
if (!FindNextTask(child_name, parent_name, visited)) {
93+
return false;
94+
}
95+
}
96+
return true;
97+
}
1998

2099
Module *ModuleBuilder::CreateModule(const std::string &name,
21100
bess::metadata::Pipeline *pipeline) const {
@@ -27,7 +106,21 @@ Module *ModuleBuilder::CreateModule(const std::string &name,
27106
}
28107

29108
bool ModuleBuilder::AddModule(Module *m) {
30-
return all_modules_.insert({m->name(), m}).second;
109+
if (m->is_task_) {
110+
if (!tasks_.insert(m->name()).second) {
111+
return false;
112+
}
113+
}
114+
115+
bool module_added = all_modules_.insert({m->name(), m}).second;
116+
if (!module_added) {
117+
return false;
118+
}
119+
120+
return module_graph_
121+
.emplace(std::piecewise_construct, std::forward_as_tuple(m->name()),
122+
std::forward_as_tuple(m))
123+
.second;
31124
}
32125

33126
int ModuleBuilder::DestroyModule(Module *m, bool erase) {
@@ -57,6 +150,11 @@ int ModuleBuilder::DestroyModule(Module *m, bool erase) {
57150
all_modules_.erase(m->name());
58151
}
59152

153+
module_graph_.erase(m->name());
154+
if (m->is_task_) {
155+
tasks_.erase(m->name());
156+
}
157+
60158
delete m;
61159
return 0;
62160
}
@@ -402,7 +500,8 @@ int Module::ConnectModules(gate_idx_t ogate_idx, Module *m_next,
402500
ogate->AddHook(new Track());
403501
igate->PushOgate(ogate);
404502

405-
return 0;
503+
// Update graph
504+
return !ModuleBuilder::AddEdge(name_, m_next->name_);
406505
}
407506

408507
int Module::DisconnectModules(gate_idx_t ogate_idx) {
@@ -425,6 +524,11 @@ int Module::DisconnectModules(gate_idx_t ogate_idx) {
425524

426525
igate = ogate->igate();
427526

527+
// Remove edge in module graph.
528+
if (!ModuleBuilder::RemoveEdge(name_, igate->module()->name_)) {
529+
return 1;
530+
}
531+
428532
/* Does the igate become inactive as well? */
429533
igate->RemoveOgate(ogate);
430534
if (igate->ogates_upstream().empty()) {
@@ -462,6 +566,12 @@ int Module::DisconnectModulesUpstream(gate_idx_t igate_idx) {
462566
Module *m_prev = ogate->module();
463567
m_prev->ogates_[ogate->gate_idx()] = nullptr;
464568
ogate->ClearHooks();
569+
570+
// Remove edge in module graph
571+
if (!ModuleBuilder::RemoveEdge(ogate->module()->name_, name_)) {
572+
return 1;
573+
}
574+
465575
delete ogate;
466576
}
467577

core/module.h

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#ifndef BESS_MODULE_H_
22
#define BESS_MODULE_H_
33

4+
#include <atomic>
45
#include <map>
56
#include <string>
7+
#include <unordered_map>
68
#include <unordered_set>
79
#include <utility>
810
#include <vector>
@@ -20,6 +22,33 @@ using bess::gate_idx_t;
2022
#define MAX_TASKS_PER_MODULE 32
2123
#define UNCONSTRAINED_SOCKET ((0x1ull << MAX_NUMA_NODE) - 1)
2224

25+
// Represents a node in `module_graph_`.
26+
class Node {
27+
public:
28+
// Creates a new Node that represents `module_`.
29+
Node(Module *module) : module_(module), children_() {}
30+
31+
// Add a child to the node.
32+
bool AddChild(const std::string &child) {
33+
return children_.insert(child).second;
34+
}
35+
36+
// Remove a child from the node.
37+
void RemoveChild(const std::string &child) { children_.erase(child); }
38+
39+
const Module *module() const { return module_; }
40+
const std::unordered_set<std::string> &children() const { return children_; }
41+
42+
private:
43+
// Module that this Node represents.
44+
Module *module_;
45+
46+
// Children of `module_` in the pipeline.
47+
std::unordered_set<std::string> children_;
48+
49+
DISALLOW_COPY_AND_ASSIGN(Node);
50+
};
51+
2352
struct task_result {
2453
bool block;
2554
uint32_t packets;
@@ -137,7 +166,29 @@ class ModuleBuilder {
137166

138167
CommandResponse RunInit(Module *m, const google::protobuf::Any &arg) const;
139168

169+
// Connects two modules (`to` and `from`) together in `module_graph_`.
170+
static bool AddEdge(const std::string &from, const std::string &to);
171+
172+
// Disconnects two modules (`to` and `from`) together in `module_graph_`.
173+
static bool RemoveEdge(const std::string &from, const std::string &to);
174+
140175
private:
176+
// Updates the parents of modules with tasks by traversing `module_graph_` and
177+
// ignoring all modules that are not tasks.
178+
static bool UpdateTaskGraph();
179+
180+
// Finds the next module that implements a task, and updates it's parents
181+
// accordingly.
182+
static bool FindNextTask(const std::string &node_name,
183+
const std::string &parent_name,
184+
std::unordered_set<std::string> *visited);
185+
186+
// A graph of all the modules in the current pipeline.
187+
static std::unordered_map<std::string, Node> module_graph_;
188+
189+
// All modules that are tasks in the current pipeline.
190+
static std::unordered_set<std::string> tasks_;
191+
141192
const std::function<Module *()> module_generator_;
142193

143194
static std::map<std::string, Module *> all_modules_;
@@ -178,6 +229,10 @@ class Module {
178229
ogates_(),
179230
active_workers_(Worker::kMaxWorkers, false),
180231
visited_tasks_(),
232+
is_task_(false),
233+
parent_tasks_(),
234+
children_overload_(0),
235+
overload_(false),
181236
node_constraints_(UNCONSTRAINED_SOCKET),
182237
min_allowed_workers_(1),
183238
max_allowed_workers_(1),
@@ -318,6 +373,38 @@ class Module {
318373

319374
virtual CheckConstraintResult CheckModuleConstraints() const;
320375

376+
// For testing.
377+
int children_overload() const { return children_overload_; };
378+
const std::vector<Module *> &parent_tasks() const { return parent_tasks_; };
379+
380+
// Signals to parent task(s) that module is overloaded.
381+
// TODO: SignalOverload and SignalUnderload are only safe if the module is not
382+
// thread safe (e.g. multiple workers should not be able to simultaneously
383+
// call these methods)
384+
void SignalOverload() {
385+
if (overload_) {
386+
return;
387+
}
388+
for (auto const &p : parent_tasks_) {
389+
++(p->children_overload_);
390+
}
391+
392+
overload_ = true;
393+
}
394+
395+
// Signals to parent task(s) that module is underloaded.
396+
void SignalUnderload() {
397+
if (!overload_) {
398+
return;
399+
}
400+
401+
for (auto const &p : parent_tasks_) {
402+
--(p->children_overload_);
403+
}
404+
405+
overload_ = false;
406+
}
407+
321408
private:
322409
void DestroyAllTasks();
323410
void DeregisterAllAttributes();
@@ -349,6 +436,18 @@ class Module {
349436
std::vector<const ModuleTask *> visited_tasks_;
350437

351438
protected:
439+
// Whether the module overrides RunTask or not.
440+
bool is_task_;
441+
442+
// Parent tasks of this module in the current pipeline.
443+
std::vector<Module *> parent_tasks_;
444+
445+
// # of child tasks of this module that are overloaded.
446+
std::atomic<int> children_overload_;
447+
448+
// Whether the module itself is overloaded.
449+
bool overload_;
450+
352451
// TODO[apanda]: Move to some constraint structure?
353452
// Placement constraints for this module. We use this to update the task based
354453
// on all upstream tasks.

0 commit comments

Comments
 (0)