Skip to content

Commit d434083

Browse files
committed
Support --load-modules option for simple_switch_grpc
Since this is useful for many targets (simple_switch, simple_switch_grpc and possibly psa_switch) and has been around for a while, I decided to move support for --load-modules to the core bmv2 library, by adding a new reference target-specific option parser (`TargetParserBasicWithDynModules`) that targets can use directly. The reason why this functionality did not find its way into the main bmv2 option parser is because it requires the target to be linked with -rdynamic, so I believe it is better for the target to choose whether this functionality is required or not. Not moving it to the main option parser also guarantees backward-compatibility for simple_switch users. Fixes #719
1 parent 0557502 commit d434083

File tree

6 files changed

+111
-74
lines changed

6 files changed

+111
-74
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,18 @@ switch is running:
168168
The script will display events of significance (table hits / misses, parser
169169
transitions, ...) for each packet.
170170

171+
## Loading shared objects dynamically
172+
173+
Some targets (simple_switch and simple_switch_grpc) let the user load shared
174+
libraries dynamically at runtime. This is done by using the target-specific
175+
command-line option `--load-modules`, which takes as a parameter a
176+
comma-separated list of shared objects. This functionality is currently only
177+
available on systems where `dlopen` is available. Make sure that the shared
178+
objects are visible by the dynamic loader (e.g. by setting `LD_LIBRARY_PATH`
179+
appropriately on Linux). You can control whether this feature is available by
180+
using `--enable-modules` / `--disable-modules` when configuring bmv2. By
181+
default, this feature is enabled when `dlopen` is available.
182+
171183
## Integrating with Mininet
172184

173185
We will provide more information in a separate document. However you can test

include/bm/bm_sim/target_parser.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,30 @@ class TargetParserBasic : public TargetParserIface {
124124
std::unique_ptr<TargetParserBasicStore> var_store;
125125
};
126126

127+
//! Same as TargetParserBasic but supports the '--load-modules' command-line
128+
//! option by default. This option is used to load shared objects dynamically at
129+
//! runtime. Often, these objects / modules contain new primtive action or
130+
//! extern definitions. If you plan on using '--load-modules' in your target,
131+
//! and you plan on providing primitive / extern definitions dynamically, you
132+
//! will need to link your executable with -rdynamic.
133+
class TargetParserBasicWithDynModules : public TargetParserBasic {
134+
public:
135+
TargetParserBasicWithDynModules();
136+
~TargetParserBasicWithDynModules();
137+
138+
//! See bm::TargetParserIface::parse. Make sure all possible options have been
139+
//! registered using add_string_option(), add_int_option(), add_uint_option()
140+
//! or add_flag_option().
141+
int parse(const std::vector<std::string> &more_options,
142+
std::ostream *errstream) override;
143+
144+
private:
145+
//! Name of the option.
146+
static constexpr char load_modules_option[] = "load-modules";
147+
148+
int load_modules(std::ostream *errstream);
149+
};
150+
127151
} // namespace bm
128152

129153
#endif // BM_BM_SIM_TARGET_PARSER_H_

src/bm_sim/target_parser.cpp

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,23 @@
1818
*
1919
*/
2020

21+
#include <bm/config.h>
22+
23+
#ifdef BM_HAVE_DLOPEN
24+
#include <dlfcn.h>
25+
#endif // BM_HAVE_DLOPEN
26+
27+
#include <bm/bm_sim/_assert.h>
2128
#include <bm/bm_sim/target_parser.h>
2229

2330
#include <boost/program_options.hpp>
2431

25-
#include <string>
26-
#include <vector>
2732
#include <memory>
28-
#include <unordered_set>
2933
#include <ostream>
34+
#include <sstream>
35+
#include <string>
36+
#include <unordered_set>
37+
#include <vector>
3038

3139
namespace po = boost::program_options;
3240

@@ -173,4 +181,54 @@ TargetParserBasic::get_flag_option(const std::string &name, bool *v) const {
173181
return var_store->get_option<bool>(name, v);
174182
}
175183

184+
TargetParserBasicWithDynModules::TargetParserBasicWithDynModules() {
185+
#ifdef BM_ENABLE_MODULES
186+
add_string_option(load_modules_option,
187+
"Load the given .so files (comma-separated) as modules.");
188+
#endif // BM_ENABLE_MODULES
189+
}
190+
191+
TargetParserBasicWithDynModules::~TargetParserBasicWithDynModules() = default;
192+
193+
int
194+
TargetParserBasicWithDynModules::parse(
195+
const std::vector<std::string> &more_options,
196+
std::ostream *errstream) {
197+
int rc = 0;
198+
if ((rc = TargetParserBasic::parse(more_options, errstream)) != 0) return rc;
199+
#ifdef BM_ENABLE_MODULES
200+
if ((rc = load_modules(errstream)) != 0) return rc;
201+
#endif // BM_ENABLE_MODULES
202+
return 0;
203+
}
204+
205+
int
206+
TargetParserBasicWithDynModules::load_modules(std::ostream *errstream) {
207+
_BM_UNUSED(errstream);
208+
#ifdef BM_ENABLE_MODULES
209+
std::string modules;
210+
ReturnCode retval = get_string_option(load_modules_option, &modules);
211+
if (retval == ReturnCode::OPTION_NOT_PROVIDED)
212+
return 0;
213+
if (retval != ReturnCode::SUCCESS)
214+
return 1; // Unexpected error
215+
std::istringstream iss(modules);
216+
std::string module;
217+
while (std::getline(iss, module, ',')) {
218+
#ifdef BM_HAVE_DLOPEN
219+
if (!dlopen(module.c_str(), RTLD_NOW | RTLD_GLOBAL)) {
220+
*errstream << "WARNING: Skipping module: " << module << ": "
221+
<< dlerror() << std::endl;
222+
}
223+
#else // BM_HAVE_DLOPEN
224+
#error modules enabled, but no loading method available
225+
#endif // BM_HAVE_DLOPEN
226+
}
227+
#endif // BM_ENABLE_MODULES
228+
return 0;
229+
}
230+
231+
/* static */
232+
constexpr char TargetParserBasicWithDynModules::load_modules_option[];
233+
176234
} // namespace bm

targets/simple_switch/main.cpp

Lines changed: 11 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -22,81 +22,14 @@
2222

2323
#include <bm/config.h>
2424

25-
#ifdef BM_HAVE_DLOPEN
26-
# include <dlfcn.h>
27-
#endif // BM_HAVE_DLOPEN
2825
#include <bm/SimpleSwitch.h>
2926
#include <bm/bm_runtime/bm_runtime.h>
3027
#include <bm/bm_sim/target_parser.h>
3128

32-
#include <sstream>
33-
#include <string>
34-
#include <vector>
35-
3629
#include "simple_switch.h"
3730

3831
namespace {
3932
SimpleSwitch *simple_switch;
40-
41-
std::string load_modules_option = "load-modules";
42-
43-
class SimpleSwitchParser : public bm::TargetParserBasic {
44-
public:
45-
SimpleSwitchParser() {
46-
add_flag_option("enable-swap",
47-
"enable JSON swapping at runtime");
48-
#ifdef BM_ENABLE_MODULES
49-
add_string_option(load_modules_option,
50-
"load the given .so files as modules");
51-
#endif // BM_ENABLE_MODULES
52-
}
53-
54-
int parse(const std::vector<std::string> &more_options,
55-
std::ostream *errstream) override {
56-
int result = ::bm::TargetParserBasic::parse(more_options, errstream);
57-
#ifdef BM_ENABLE_MODULES
58-
load_modules(errstream);
59-
#endif // BM_ENABLE_MODULES
60-
set_enable_swap();
61-
return result;
62-
}
63-
64-
protected:
65-
#ifdef BM_ENABLE_MODULES
66-
int load_modules(std::ostream *errstream) {
67-
std::string modules;
68-
ReturnCode retval = get_string_option(load_modules_option, &modules);
69-
if (retval == ReturnCode::OPTION_NOT_PROVIDED) {
70-
return 0;
71-
}
72-
if (retval != ReturnCode::SUCCESS) {
73-
return -1; // Unexpected error
74-
}
75-
std::istringstream iss(modules);
76-
std::string module;
77-
while (std::getline(iss, module, ',')) {
78-
# ifdef BM_HAVE_DLOPEN
79-
if (!dlopen(module.c_str(), RTLD_NOW | RTLD_GLOBAL)) {
80-
*errstream << "WARNING: Skipping module: " << module << ": "
81-
<< dlerror() << std::endl;
82-
}
83-
# else // BM_HAVE_DLOPEN
84-
#error modules enabled, but no loading method available
85-
# endif // BM_HAVE_DLOPEN
86-
}
87-
return 0;
88-
}
89-
#endif // BM_ENABLE_MODULES
90-
91-
void set_enable_swap() {
92-
bool enable_swap = false;
93-
if (get_flag_option("enable-swap", &enable_swap) != ReturnCode::SUCCESS)
94-
std::exit(1);
95-
if (enable_swap) simple_switch->enable_config_swap();
96-
}
97-
};
98-
99-
SimpleSwitchParser *simple_switch_parser;
10033
} // namespace
10134

10235
namespace sswitch_runtime {
@@ -106,11 +39,20 @@ shared_ptr<SimpleSwitchIf> get_handler(SimpleSwitch *sw);
10639
int
10740
main(int argc, char* argv[]) {
10841
simple_switch = new SimpleSwitch();
109-
simple_switch_parser = new SimpleSwitchParser();
42+
bm::TargetParserBasicWithDynModules simple_switch_parser;
43+
simple_switch_parser.add_flag_option("enable-swap",
44+
"enable JSON swapping at runtime");
11045
int status = simple_switch->init_from_command_line_options(
111-
argc, argv, simple_switch_parser);
46+
argc, argv, &simple_switch_parser);
11247
if (status != 0) std::exit(status);
11348

49+
bool enable_swap_flag = false;
50+
if (simple_switch_parser.get_flag_option("enable-swap", &enable_swap_flag)
51+
!= bm::TargetParserBasic::ReturnCode::SUCCESS) {
52+
std::exit(1);
53+
}
54+
if (enable_swap_flag) simple_switch->enable_config_swap();
55+
11456
int thrift_port = simple_switch->get_runtime_port();
11557
bm_runtime::start_server(simple_switch, thrift_port);
11658
using ::sswitch_runtime::SimpleSwitchIf;

targets/simple_switch_grpc/Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ libsimple_switch_grpc.la
2727
# We follow this tutorial to link with grpc++_reflection:
2828
# https://github.com/grpc/grpc/blob/master/doc/server_reflection_tutorial.md
2929
simple_switch_grpc_LDFLAGS = \
30-
-Wl,--no-as-needed,-lgrpc++_reflection,--as-needed
30+
-Wl,--no-as-needed,-lgrpc++_reflection,--as-needed \
31+
-rdynamic
3132

3233
lib_LTLIBRARIES = libbm_grpc_dataplane.la
3334
noinst_LTLIBRARIES = libsimple_switch_grpc.la

targets/simple_switch_grpc/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
int
2929
main(int argc, char* argv[]) {
30-
bm::TargetParserBasic simple_switch_parser;
30+
bm::TargetParserBasicWithDynModules simple_switch_parser;
3131
simple_switch_parser.add_flag_option(
3232
"disable-swap",
3333
"disable JSON swapping at runtime");

0 commit comments

Comments
 (0)