Skip to content

Commit 675f7c9

Browse files
yunhongxufacebook-github-bot
authored andcommitted
Benchmark test for NetlinkFibHandler
Summary: This is for the benchmark test of NetlinkFibHandler. Given varied number of routes (10, 100, 600), this benchmark computes the time performance of updating routes through Netlink. Reviewed By: saifhhasan Differential Revision: D15879635 fbshipit-source-id: 57e549273a6e3bf4c880129138f2ad833b829001
1 parent 0018ee8 commit 675f7c9

File tree

1 file changed

+232
-0
lines changed

1 file changed

+232
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
#include <map>
8+
#include <memory>
9+
#include <string>
10+
#include <thread>
11+
12+
#include <benchmark/benchmark.h>
13+
#include <fbzmq/async/ZmqEventLoop.h>
14+
#include <fbzmq/zmq/Zmq.h>
15+
#include <folly/Exception.h>
16+
#include <folly/Format.h>
17+
#include <folly/MacAddress.h>
18+
#include <folly/Random.h>
19+
#include <folly/gen/Base.h>
20+
#include <folly/init/Init.h>
21+
#include <folly/test/TestUtils.h>
22+
#include <openr/fib/tests/PrefixGenerator.h>
23+
#include <openr/nl/NetlinkSocket.h>
24+
#include <openr/platform/NetlinkFibHandler.h>
25+
26+
extern "C" {
27+
#include <net/if.h>
28+
#include <netlink/route/link/veth.h>
29+
#include <netlink/route/route.h>
30+
#include <sys/ioctl.h>
31+
}
32+
33+
using namespace openr::fbnl;
34+
35+
namespace {
36+
// Virtual interfaces
37+
const std::string kVethNameX("vethTestX");
38+
const std::string kVethNameY("vethTestY");
39+
// Prefix length of a subnet
40+
static const uint8_t kBitMaskLen = 128;
41+
// Number of nexthops
42+
const uint8_t kNumOfNexthops = 128;
43+
44+
} // namespace
45+
46+
namespace openr {
47+
48+
const int16_t kFibId{static_cast<int16_t>(thrift::FibClient::OPENR)};
49+
50+
// This class creates virtual interface (veths)
51+
// which the Benchmark test can use to add routes (via interface)
52+
class NetlinkFibWrapper {
53+
public:
54+
struct RouteCallbackContext {
55+
struct nl_cache* routeCache{nullptr};
56+
std::vector<Route> results;
57+
};
58+
59+
struct AddressCallbackContext {
60+
struct nl_cache* linkeCache{nullptr};
61+
std::vector<IfAddress> results;
62+
};
63+
64+
NetlinkFibWrapper() {
65+
// Allocate socket
66+
socket_ = nl_socket_alloc();
67+
nl_connect(socket_, NETLINK_ROUTE);
68+
rtnl_link_alloc_cache(socket_, AF_UNSPEC, &linkCache_);
69+
rtnl_addr_alloc_cache(socket_, &addrCache_);
70+
rtnl_route_alloc_cache(socket_, AF_UNSPEC, 0, &routeCache_);
71+
72+
// Virtual interface and virtual link
73+
link_ = rtnl_link_veth_alloc();
74+
auto peerLink = rtnl_link_veth_get_peer(link_);
75+
rtnl_link_set_name(link_, kVethNameX.c_str());
76+
rtnl_link_set_name(peerLink, kVethNameY.c_str());
77+
nl_object_put(OBJ_CAST(peerLink));
78+
79+
rtnl_link_add(socket_, link_, NLM_F_CREATE);
80+
81+
nl_cache_refill(socket_, linkCache_);
82+
addAddress(kVethNameX, "169.254.0.101");
83+
addAddress(kVethNameY, "169.254.0.102");
84+
85+
// set interface status to up
86+
bringUpIntf(kVethNameX);
87+
bringUpIntf(kVethNameY);
88+
89+
// Create NetlinkProtocolSocket
90+
std::unique_ptr<openr::Netlink::NetlinkProtocolSocket> nlProtocolSocket;
91+
nlProtocolSocket =
92+
std::make_unique<openr::Netlink::NetlinkProtocolSocket>(&evl2);
93+
nlProtocolSocketThread = std::thread([&]() {
94+
nlProtocolSocket->init();
95+
evl2.run();
96+
evl2.waitUntilStopped();
97+
});
98+
evl2.waitUntilRunning();
99+
100+
// Create netlink route socket
101+
nlSocket = std::make_shared<NetlinkSocket>(
102+
&evl, nullptr, true, std::move(nlProtocolSocket));
103+
104+
// Run the zmq event loop in its own thread
105+
// We will either timeout if expected events are not received
106+
// or stop after we receive expected events
107+
eventThread = std::thread([&]() {
108+
evl.run();
109+
evl.waitUntilStopped();
110+
});
111+
evl.waitUntilRunning();
112+
113+
// Start FibService thread
114+
fibHandler = std::make_shared<NetlinkFibHandler>(&evl, nlSocket);
115+
}
116+
117+
~NetlinkFibWrapper() {
118+
if (evl.isRunning()) {
119+
evl.stop();
120+
eventThread.join();
121+
}
122+
if (evl2.isRunning()) {
123+
evl2.stop();
124+
nlProtocolSocketThread.join();
125+
}
126+
127+
nlSocket.reset();
128+
129+
rtnl_link_delete(socket_, link_);
130+
nl_cache_free(linkCache_);
131+
nl_cache_free(addrCache_);
132+
nl_cache_free(routeCache_);
133+
nl_socket_free(socket_);
134+
rtnl_link_veth_release(link_);
135+
}
136+
137+
fbzmq::Context context;
138+
std::shared_ptr<NetlinkSocket> nlSocket;
139+
fbzmq::ZmqEventLoop evl;
140+
fbzmq::ZmqEventLoop evl2;
141+
std::thread eventThread;
142+
std::thread nlProtocolSocketThread;
143+
std::shared_ptr<NetlinkFibHandler> fibHandler;
144+
PrefixGenerator prefixGenerator;
145+
146+
struct rtnl_link* link_{nullptr};
147+
struct nl_sock* socket_{nullptr};
148+
struct nl_cache* linkCache_{nullptr};
149+
struct nl_cache* addrCache_{nullptr};
150+
struct nl_cache* routeCache_{nullptr};
151+
152+
private:
153+
void
154+
addAddress(const std::string& ifName, const std::string& address) {
155+
int ifIndex = rtnl_link_name2i(linkCache_, ifName.c_str());
156+
157+
auto addrMask = std::make_pair(folly::IPAddress(address), 16);
158+
struct nl_addr* nlAddr = nl_addr_build(
159+
addrMask.first.family(),
160+
(void*)addrMask.first.bytes(),
161+
addrMask.first.byteCount());
162+
nl_addr_set_prefixlen(nlAddr, addrMask.second);
163+
164+
struct rtnl_addr* addr = rtnl_addr_alloc();
165+
rtnl_addr_set_local(addr, nlAddr);
166+
rtnl_addr_set_ifindex(addr, ifIndex);
167+
rtnl_addr_add(socket_, addr, 0);
168+
nl_addr_put(nlAddr);
169+
rtnl_addr_put(addr);
170+
}
171+
172+
static void
173+
bringUpIntf(const std::string& ifName) {
174+
// Prepare socket
175+
auto sockFd = socket(PF_INET, SOCK_DGRAM, 0);
176+
177+
// Prepare request
178+
struct ifreq ifr;
179+
memset(&ifr, 0, sizeof(ifr));
180+
folly::strlcpy(ifr.ifr_name, ifName.c_str(), IFNAMSIZ);
181+
182+
// Get existing flags
183+
ioctl(sockFd, SIOCGIFFLAGS, static_cast<void*>(&ifr));
184+
185+
// Mutate flags and set them back
186+
ifr.ifr_flags |= IFF_UP;
187+
ioctl(sockFd, SIOCSIFFLAGS, static_cast<void*>(&ifr));
188+
}
189+
};
190+
191+
static void
192+
getProcessTimeBM(benchmark::State& state) {
193+
/* Benchmark test to measure the time performance */
194+
auto netlinkFibWrapper = std::make_unique<NetlinkFibWrapper>();
195+
const uint32_t numOfPrefixes = state.range(0);
196+
197+
// Randomly generate IPV6 prefixes
198+
auto prefixes = netlinkFibWrapper->prefixGenerator.ipv6PrefixGenerator(
199+
numOfPrefixes, kBitMaskLen);
200+
201+
for (auto _ : state) {
202+
auto routes = std::make_unique<std::vector<thrift::UnicastRoute>>();
203+
routes->reserve(prefixes.size());
204+
205+
// Update routes by randomly regenerating nextHops for kDeltaSize prefixes.
206+
for (auto index = 0; index < numOfPrefixes; index++) {
207+
routes->emplace_back(createUnicastRoute(
208+
prefixes[index],
209+
netlinkFibWrapper->prefixGenerator.getRandomNextHopsUnicast(
210+
kNumOfNexthops, kVethNameY)));
211+
}
212+
213+
// Add new routes through netlink
214+
netlinkFibWrapper->fibHandler
215+
->future_addUnicastRoutes(kFibId, std::move(routes))
216+
.wait();
217+
}
218+
219+
state.SetItemsProcessed(state.iterations());
220+
}
221+
BENCHMARK(getProcessTimeBM)->RangeMultiplier(10)->Range(10, 10000);
222+
} // namespace openr
223+
224+
int
225+
main(int argc, char** argv) {
226+
/* Use main instead of macro BENCHMARK_MAIN() since we need folly::init*/
227+
folly::init(&argc, &argv);
228+
::benchmark::Initialize(&argc, argv);
229+
if (::benchmark::ReportUnrecognizedArguments(argc, argv))
230+
return 1;
231+
::benchmark::RunSpecifiedBenchmarks();
232+
}

0 commit comments

Comments
 (0)