Skip to content

Commit

Permalink
Merge pull request polycube-network#343 from s41m0n/dynmon/refactor_name
Browse files Browse the repository at this point in the history
changed CodeRewriter enumeration names
  • Loading branch information
frisso committed Oct 30, 2020
2 parents 680d771 + 4ba5f40 commit 7af9192
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 43 deletions.
18 changes: 9 additions & 9 deletions Documentation/services/pcn-dynmon/dynmon.rst
Expand Up @@ -229,14 +229,14 @@ The code compilation is performed every time new code is injected, both for Ingr

There are two different type of compilation:

- ENHANCED
- PROGRAM_INDEX_SWAP

- BASE
- PROGRAM_RELOAD

Enhanced Compilation
PROGRAM_INDEX_SWAP rewrite
^^^^^^^^^^^^^^^^^^^^

The Enhanced compilation type is the best you can get from this rewriter by now. It is extremely sophisticated and not easy at all to undestand, since we have tried to take into account as many scenarios as possible. This said, let's analyze it.
The PROGRAM_INDEX_SWAP rewrite type is the best you can get from this rewriter by now. It is extremely sophisticated and not easy at all to understand, since we have tried to take into account as many scenarios as possible. This said, let's analyze it.

During the first phase of this compilation, all the maps declared with the ``"swap-on-read"`` feature enabled are parsed, checking if their declaration in the code matches one of the following rules:

Expand All @@ -251,23 +251,23 @@ If a user created a map of such type, then he probably wants to use another prev

If the map did not match one of these rules, then it is left unchanged in the cloned code, meaning that there will be another program-local map with limited scope that will be read alternatively.

The second phase consists is checking all those maps which are not declared as swappable. The rewriter retrieve all those declarations and checks for them to see if it is able to modify it. In fact, during this phase, whenever it encounters a declaration which it is unable to modify, it stops and uses the BASE compilation as fallback, to let everything run as required, even though in an sub-optimal optimized way.
The second phase consists is checking all those maps which are not declared as swappable. The rewriter retrieve all those declarations and checks for them to see if it is able to modify it. In fact, during this phase, whenever it encounters a declaration which it is unable to modify, it stops and uses the PROGRAM_RELOAD compilation as fallback, to let everything run as required, even though in an sub-optimal optimized way.

Since those map must not swap, the rewriter tries to declare a map which is shared among the original and cloned program, in order to make the map visible from both of them. For all those maps, these rules are applied:

- if the map is declared as _PINNED or "extern", then it will be left unchanged in the cloned program, since the user is using an extern map which should exists a priori
- if the map is NOT declared using the standard (BPF_TABLE and BPF_QUEUESTACK) helpers, then the compilation stops and the BASE one is used, since the rewriter is not able by now to change such declarations into specific one (eg. from BPF_ARRAY(...) to BPF_TABLE("array"...), too many possibilities and variadic parameters)
- if the map is NOT declared using the standard (BPF_TABLE and BPF_QUEUESTACK) helpers, then the compilation stops and the PROGRAM_RELOAD one is used, since the rewriter is not able by now to change such declarations into specific one (eg. from BPF_ARRAY(...) to BPF_TABLE("array"...), too many possibilities and variadic parameters)
- if the map is declared as _SHARED or _PUBLIC, then the declaration is changed in the cloned code into "extern", meaning that the map is already declared in the original code
- otherwise, the declaration in the original code is changed into BPF_TABLE_SHARED/BPF_QUEUESTACK_SHARED and in the cloned code the map will be declared as "extern". Moreover, the map name will be changed into ``MAP_NAME_INGRESS`` or ``MAP_NAME_EGRESS`` to avoid such much to collide with others declared in a different program type.

Once finished, both the original and cloned code are ready to be inserted in the probe. Since it is an enhanced compilation which allows users to save time every time they want to read their metrics, we have used a very efficient technique to alternate the code execution. These two programs are compiled also from LLVM one time, and then they are inserted in the probe but not as primarly code. In fact, this compilation delivers also a master PIVOTING code which will be injected as code to be executed every time there is an incoming/outgoing packet.

The PIVOTING code simply calls the original/cloned program main function according to the current program index. This program index is stored in an internal BPF_TABLE and it is changed every time a user performs a read. When the index refers to the original code, the PIVOTING function will call the original code main function, and vice versa.

Thanks to this technique, every time a user requires metrics there's only almost 4ms overhead due to changing the index from ControlPlane, which compared to the 400ms using the BASE compilation, is an extremely advantage we are proud of having developed.
Thanks to this technique, every time a user requires metrics there's only almost 4ms overhead due to changing the index from ControlPlane, which compared to the 400ms using the PROGRAM_RELOAD compilation, is an extremely advantage we are proud of having developed.


Basic Compilation
PROGRAM_RELOAD Compilation
^^^^^^^^^^^^^^^^^

This compilation type is quite simple to understand. It is used as a fallback compilation, since it achieves the map swap function, but in a more time expensive way. In fact, when this option is used, it is generated a new code starting from the original injected one, and then the following steps are followed:
Expand All @@ -280,6 +280,6 @@ Since we have to guarantee map read atomicity, we declare a new parallel map wit

Both the new and old map declaration need to be places in the codes, otherwise they would not know about the other maps other than the ones they have declared.

The codes are, as said, alternatively injected in the probe, but it is worth noticing that although the Enhanced compilation, this one requires LLVM to compile the current code every time it is swapped.
The codes are, as said, alternatively injected in the probe, but it is worth noticing that although the PROGRAM_INDEX_SWAP compilation, this one requires LLVM to compile the current code every time it is swapped.

Some tests have been run and their results led to 400ms on average of overhead each time the user requires metrics, due to the LLVM compilation time and the time to inject the code in the probe. Obviously, it is not the better solution, but at least it provides the user all the functionality he asked for, even though the enhanced compilation went wrong.
24 changes: 12 additions & 12 deletions src/services/pcn-dynmon/src/Dynmon.cpp
Expand Up @@ -62,12 +62,12 @@ void Dynmon::setEgressPathConfig(const PathConfigJsonObject &config) {
reload(config.getCode(), 0, ProgramType::EGRESS);
break;
}
case CodeRewriter::CompileType::BASE: {
case CodeRewriter::CompileType::PROGRAM_RELOAD: {
/*Retrieve current code to load, if original or swap*/
reload(egressSwapState.getCodeToLoad(), 0, ProgramType::EGRESS);
break;
}
case CodeRewriter::CompileType::ENHANCED: {
case CodeRewriter::CompileType::PROGRAM_INDEX_SWAP: {
/*Load PIVOTING code at index 0, tuned original at index 1 and tuned swap at index 2*/
logger()->debug("[Dynmon] Swap enabled for EGRESS program");
reload(egressSwapState.getMasterCode(), 0, ProgramType::EGRESS);
Expand Down Expand Up @@ -114,12 +114,12 @@ void Dynmon::setIngressPathConfig(const PathConfigJsonObject &config) {
reload(config.getCode(), 0, ProgramType::INGRESS);
break;
}
case CodeRewriter::CompileType::BASE: {
case CodeRewriter::CompileType::PROGRAM_RELOAD: {
/*Retrieve current code to load, if original or swap*/
reload(ingressSwapState.getCodeToLoad(), 0, ProgramType::INGRESS);
break;
}
case CodeRewriter::CompileType::ENHANCED: {
case CodeRewriter::CompileType::PROGRAM_INDEX_SWAP: {
/*Load PIVOTING code at index 0, tuned original at index 1 and tuned swap at index 2*/
logger()->debug("[Dynmon] Swap enabled for INGRESS program");
reload(ingressSwapState.getMasterCode(), 0, ProgramType::INGRESS);
Expand Down Expand Up @@ -161,8 +161,8 @@ void Dynmon::resetEgressPathConfig() {
m_dpConfig->replaceEgressPathConfig(conf);
reload(DEFAULT_PATH_CODE, 0, ProgramType::EGRESS);

/*Check if there were other programs loaded (enhanced compilation)*/
if(egressSwapState.getCompileType() == CodeRewriter::CompileType::ENHANCED) {
/*Check if there were other programs loaded (PROGRAM_INDEX_SWAP compilation)*/
if(egressSwapState.getCompileType() == CodeRewriter::CompileType::PROGRAM_INDEX_SWAP) {
del_program(2, ProgramType::EGRESS);
del_program(1, ProgramType::EGRESS);
}
Expand All @@ -181,8 +181,8 @@ void Dynmon::resetIngressPathConfig() {
m_dpConfig->replaceIngressPathConfig(conf);
reload(DEFAULT_PATH_CODE, 0, ProgramType::INGRESS);

/*Check if there were other programs loaded (enhanced compilation)*/
if(ingressSwapState.getCompileType() == CodeRewriter::CompileType::ENHANCED) {
/*Check if there were other programs loaded (PROGRAM_INDEX_SWAP compilation)*/
if(ingressSwapState.getCompileType() == CodeRewriter::CompileType::PROGRAM_INDEX_SWAP) {
del_program(2, ProgramType::INGRESS);
del_program(1, ProgramType::INGRESS);
}
Expand Down Expand Up @@ -500,14 +500,14 @@ void Dynmon::triggerReadEgress() {
switch(egressSwapState.getCompileType()) {
case CodeRewriter::CompileType::NONE:
break;
case CodeRewriter::CompileType::BASE: {
case CodeRewriter::CompileType::PROGRAM_RELOAD: {
/* Triggering read egress and retrieve index and code to load*/
auto index = egressSwapState.triggerRead();
logger()->debug("[Dynmon] Triggered read EGRESS! Swapping code with {} ", index == 1? "original" : "swap");
reload(egressSwapState.getCodeToLoad(), 0, ProgramType::EGRESS);
break;
}
case CodeRewriter::CompileType::ENHANCED: {
case CodeRewriter::CompileType::PROGRAM_INDEX_SWAP: {
/* Triggering read egress and changing the PIVOTING map index to call the right program*/
auto index = egressSwapState.triggerRead();
logger()->debug("[Dynmon] Triggered read EGRESS! Changing map index to {}", index);
Expand All @@ -525,14 +525,14 @@ void Dynmon::triggerReadIngress() {
switch(ingressSwapState.getCompileType()) {
case CodeRewriter::CompileType::NONE:
break;
case CodeRewriter::CompileType::BASE: {
case CodeRewriter::CompileType::PROGRAM_RELOAD: {
/* Triggering read ingress and retrieve index and code to load*/
auto index = ingressSwapState.triggerRead();
logger()->debug("[Dynmon] Triggered read INGRESS! Swapping code with {} ", index == 1? "original" : "swap");
reload(ingressSwapState.getCodeToLoad(), 0, ProgramType::INGRESS);
break;
}
case CodeRewriter::CompileType::ENHANCED: {
case CodeRewriter::CompileType::PROGRAM_INDEX_SWAP: {
/* Triggering read ingress and changing the PIVOTING map index to call the right program*/
auto index = ingressSwapState.triggerRead();
logger()->debug("[Dynmon] Triggered read INGRESS! Changing map index to {} ", index);
Expand Down
18 changes: 9 additions & 9 deletions src/services/pcn-dynmon/src/swap/CodeRewriter.cpp
Expand Up @@ -6,11 +6,11 @@
#include <unordered_map>

/*The suffix to be added to the original NON_SWAPPABLE map name when swapped
* in the EGRESS program and CompileType=ENHANCED*/
* in the EGRESS program and CompileType=PROGRAM_INDEX_SWAP*/
const char ENHANCED_SWAP_MAP_NAME_FORMAT_EGRESS[] = "_egress";

/*The suffix to be added to the original NON_SWAPPABLE map name when swapped
* in the INGRESS program and CompileType=ENHANCED*/
* in the INGRESS program and CompileType=PROGRAM_INDEX_SWAP*/
const char ENHANCED_SWAP_MAP_NAME_FORMAT_INGRESS[] = "_ingress";

/*The suffix to be added to the original SWAPPABLE map name when swapped and CompileType=BASE*/
Expand All @@ -31,7 +31,7 @@ const char dynmon_swap_master[] = R"(
)";

/**
* Internal method called during enhanced compilation to adjust NON-SWAPPABLE maps
* Internal method called during PROGRAM_INDEX_SWAP compilation to adjust NON-SWAPPABLE maps
*
* Checks for all non-swappable maps and declare them SHARED/extern. This way they
* can be shared among the two programs (original/swapped).
Expand Down Expand Up @@ -222,13 +222,13 @@ bool try_enhance_compilation(std::string original_code, ProgramType type,
dynmon_swap_master, std::regex("PROGRAMTYPE"),
type == ProgramType::INGRESS ? "ingress" : "egress");

config = SwapStateConfig(original_code, swapped_code, CompileType::ENHANCED,
config = SwapStateConfig(original_code, swapped_code, CompileType::PROGRAM_INDEX_SWAP,
master_code, modified_names, modified_global_names);
return true;
}

/**
* Internal functions to perform the base compilation.
* Internal functions to perform the PROGRAM_RELOAD compilation.
*
* @param original_code the original code to compile
* @param maps_to_swap the list of map which need to be swapped
Expand Down Expand Up @@ -267,7 +267,7 @@ bool do_base_compile(std::string original_code,
return false;
}
}
config = SwapStateConfig(original_code, swapped_code, CompileType::BASE, "", modified_names);
config = SwapStateConfig(original_code, swapped_code, CompileType::PROGRAM_RELOAD, "", modified_names);
return true;
}

Expand All @@ -286,14 +286,14 @@ void CodeRewriter::compile(std::string &original_code, ProgramType type,
is_enabled = true;
}
}
/*If is enabled, try enhanced compilation or basic compilation if it has failed.*/
/*If is enabled, try PROGRAM_INDEX_SWAP compilation or PROGRAM_RELOAD compilation if it has failed.*/
if(!is_enabled) {
config = {};
logger->info("[Dynmon_CodeRewriter] No map marked as swappable, no rewrites performed");
} else if(try_enhance_compilation(original_code, type, maps_to_swap, config)) {
logger->info("[Dynmon_CodeRewriter] Successfully rewritten with ENHANCED tuning");
logger->info("[Dynmon_CodeRewriter] Successfully rewritten using PROGRAM_INDEX_SWAP technique.");
} else if(do_base_compile(original_code, maps_to_swap, config)) {
logger->info("[Dynmon_CodeRewriter] Successfully rewritten with BASE tuning");
logger->info("[Dynmon_CodeRewriter] Successfully rewritten using PROGRAM_RELOAD technique.");
} else {
config = {};
logger->info("[Dynmon_CodeRewriter] Error while trying to rewrite the code, no rewrites performed");
Expand Down
22 changes: 11 additions & 11 deletions src/services/pcn-dynmon/src/swap/CodeRewriter.h
Expand Up @@ -14,30 +14,30 @@ enum class ProgramType;
namespace CodeRewriter {
/*
* Enum class to define the Compilation Type:
* - None => there was no need to optimize/compile the injected code
* - BASE => the codes will be alternatively swapped, replacing the current active one
* (every GET requires almost 400ms seconds to reload the code in the probe)
* - ENHANCED => the codes are both injected as two different programs and they will be
* managed by a Master code which will pivot them, calling the right
* alternatively.
* (every GET requires almos 5ms to change the pivoting index)
* - None => there was no need to optimize/compile the injected code
* - PROGRAM_RELOAD => the codes will be alternatively swapped, replacing the current active one
* (every GET requires almost 400ms seconds to reload the code in the probe)
* - PROGRAM_INDEX_SWAP => the codes are both injected as two different programs and they will be
* managed by a Master code which will pivot them, calling the right
* alternatively.
* (every GET requires almost 4ms to change the pivoting index)
*/
enum class CompileType { NONE, BASE, ENHANCED};
enum class CompileType { NONE, PROGRAM_RELOAD, PROGRAM_INDEX_SWAP};

/*The Master/Pivoting program map name where to store the current active program when CompileType=ENHANCED*/
/*The Master/Pivoting program map name where to store the current active program when CompileType=PROGRAM_INDEX_SWAP*/
const char SWAP_MASTER_INDEX_MAP[] = "INTERNAL_PROGRAM_INDEX";

/**
* Method to compile/optimize user injected code according to the parameter he has inserted.
* Firstly, it is checked if there's the need of performing optimization.
* If some map has been declared as swappable, then will be performed the following
* optimization in cascade if some of them fails:
* - Enhanced: a master pivoting code will control which program to call when new
* - PROGRAM_INDEX_SWAP: a master pivoting code will control which program to call when new
* packets arrive; new parallel maps will be declared and for those which
* have not been declared swappable, a SHARED map will be used among the
* two programs.
* Read time: 5ms (index change) + time to read map
* - Base: two codes will be generated and they will alternatively be loaded into
* - PROGRAM_RELOAD: two codes will be generated and they will alternatively be loaded into
* the probe. Every swappable map has a modified name in the second program,
* while the non-swappable will be shared thanks to program map stealing.
* Read time: 400ms (program reload) + time to read map
Expand Down
4 changes: 2 additions & 2 deletions src/services/pcn-dynmon/src/swap/SwapStateConfig.cpp
Expand Up @@ -19,7 +19,7 @@ int SwapStateConfig::getProgramIndexAndMapNameToRead(std::string &mapName) {
switch (compileType) {
case CompileType::NONE:
break;
case CompileType::ENHANCED: {
case CompileType::PROGRAM_INDEX_SWAP: {
/*Check if map has tuned name or, in case the "original" program is loaded, it has
* a globally tuned name among INGRESS-EGRESS */
auto tuned = enhanced_names.find(mapName);
Expand All @@ -29,7 +29,7 @@ int SwapStateConfig::getProgramIndexAndMapNameToRead(std::string &mapName) {
index = (current_program_index % 2) + 1;
break;
}
case CompileType::BASE: {
case CompileType::PROGRAM_RELOAD: {
/* Check if the map has a tuned name */
auto tuned = enhanced_names.find(mapName);
if(current_program_index == 1 && tuned != enhanced_names.end())
Expand Down

0 comments on commit 7af9192

Please sign in to comment.