Skip to content

Commit

Permalink
add BPFQueueStackTable and tests
Browse files Browse the repository at this point in the history
This commit aims to introduce a new abstraction for these new map types: BPFQueueStackTableBase.
As all the allowed operation on these map types are different from the
"traditional" ones, I thought to introduce a new abstraction, following the
already used programming style (template classes and utility func).
Moreover, I had to update libbpf.h/c to insert the new bpf_map_lookup_and_delete_elem(),
used when calling "pop()"

Signed-off-by: Simone Magnani <[email protected]>
  • Loading branch information
smagnani96 authored and yonghong-song committed Jun 29, 2020
1 parent fbde62b commit 30a420d
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/cc/api/BPF.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ class BPF {
return BPFPercpuCgStorageTable<ValueType>({});
}

template <class ValueType>
BPFQueueStackTable<ValueType> get_queuestack_table(const std::string& name) {
TableStorage::iterator it;
if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
return BPFQueueStackTable<ValueType>(it->second);
return BPFQueueStackTable<ValueType>({});
}

void* get_bsymcache(void) {
if (bsymcache_ == NULL) {
bsymcache_ = bcc_buildsymcache_new();
Expand Down
67 changes: 67 additions & 0 deletions src/cc/api/BPFTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,44 @@

namespace ebpf {

template<class ValueType>
class BPFQueueStackTableBase {
public:
size_t capacity() const { return desc.max_entries; }

StatusTuple string_to_leaf(const std::string& value_str, ValueType* value) {
return desc.leaf_sscanf(value_str.c_str(), value);
}

StatusTuple leaf_to_string(const ValueType* value, std::string& value_str) {
char buf[8 * desc.leaf_size];
StatusTuple rc = desc.leaf_snprintf(buf, sizeof(buf), value);
if (!rc.code())
value_str.assign(buf);
return rc;
}

int get_fd() { return desc.fd; }

protected:
explicit BPFQueueStackTableBase(const TableDesc& desc) : desc(desc) {}

bool pop(void *value) {
return bpf_lookup_and_delete(desc.fd, nullptr, value) >= 0;
}
// Flags are extremely useful, since they completely changes extraction behaviour
// (eg. if flag BPF_EXIST, then if the queue/stack is full remove the oldest one)
bool push(void *value, unsigned long long int flags) {
return bpf_update_elem(desc.fd, nullptr, value, flags) >= 0;
}

bool peek(void *value) {
return bpf_lookup_elem(desc.fd, nullptr, value) >= 0;
}

const TableDesc& desc;
};

template <class KeyType, class ValueType>
class BPFTableBase {
public:
Expand Down Expand Up @@ -124,6 +162,35 @@ void* get_value_addr(std::vector<ValueType>& t) {
return t.data();
}

template<class ValueType>
class BPFQueueStackTable : public BPFQueueStackTableBase<void> {
public:
explicit BPFQueueStackTable(const TableDesc& desc) : BPFQueueStackTableBase(desc) {
if (desc.type != BPF_MAP_TYPE_QUEUE &&
desc.type != BPF_MAP_TYPE_STACK)
throw std::invalid_argument("Table '" + desc.name +
"' is not a queue/stack table");
}

virtual StatusTuple pop_value(ValueType& value) {
if (!this->pop(get_value_addr(value)))
return StatusTuple(-1, "Error getting value: %s", std::strerror(errno));
return StatusTuple::OK();
}

virtual StatusTuple push_value(const ValueType& value, unsigned long long int flags = 0) {
if (!this->push(get_value_addr(const_cast<ValueType&>(value)), flags))
return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
return StatusTuple::OK();
}

virtual StatusTuple get_head(const ValueType& value) {
if (!this->peek(get_value_addr(const_cast<ValueType&>(value))))
return StatusTuple(-1, "Error peeking value: %s", std::strerror(errno));
return StatusTuple::OK();
}
};

template <class ValueType>
class BPFArrayTable : public BPFTableBase<int, ValueType> {
public:
Expand Down
5 changes: 5 additions & 0 deletions src/cc/libbpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,11 @@ int bpf_delete_elem(int fd, void *key)
return bpf_map_delete_elem(fd, key);
}

int bpf_lookup_and_delete(int fd, void *key, void *value)
{
return bpf_map_lookup_and_delete_elem(fd, key, value);
}

int bpf_get_first_key(int fd, void *key, size_t key_size)
{
int i, res;
Expand Down
1 change: 1 addition & 0 deletions src/cc/libbpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ int bpf_lookup_elem(int fd, void *key, void *value);
int bpf_delete_elem(int fd, void *key);
int bpf_get_first_key(int fd, void *key, size_t key_size);
int bpf_get_next_key(int fd, void *key, void *next_key);
int bpf_lookup_and_delete(int fd, void *key, void *value);

/*
* Load a BPF program, and return the FD of the loaded program.
Expand Down
1 change: 1 addition & 0 deletions tests/cc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ set(TEST_LIBBCC_SOURCES
test_perf_event.cc
test_pinned_table.cc
test_prog_table.cc
test_queuestack_table.cc
test_shared_table.cc
test_sk_storage.cc
test_sock_table.cc
Expand Down
101 changes: 101 additions & 0 deletions tests/cc/test_queuestack_table.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2020 Politecnico di Torino
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "BPF.h"
#include "catch.hpp"
#include <iostream>
#include <linux/version.h>

//Queue/Stack types are available only from 5.0.0
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
TEST_CASE("queue table", "[queue_table]") {
const std::string BPF_PROGRAM = R"(
BPF_QUEUE(myqueue, int, 30);
)";

ebpf::BPF bpf;
ebpf::StatusTuple res(0);
res = bpf.init(BPF_PROGRAM);
REQUIRE(res.code() == 0);

ebpf::BPFQueueStackTable<int> t = bpf.get_queuestack_table<int>("myqueue");

SECTION("standard methods") {
int i, val;
std::string value;

// insert elements
for (i=0; i<30; i++) {
res = t.push_value(i);
REQUIRE(res.code() == 0);
}

// checking head (peek)
res = t.get_head(val);
REQUIRE(res.code() == 0);
REQUIRE(val == 0);

// retrieve elements
for (i=0; i<30; i++) {
res = t.pop_value(val);
REQUIRE(res.code() == 0);
REQUIRE(val == i);
}
// get non existing element
res = t.pop_value(val);
REQUIRE(res.code() != 0);
}
}

TEST_CASE("stack table", "[stack_table]") {
const std::string BPF_PROGRAM = R"(
BPF_STACK(mystack, int, 30);
)";

ebpf::BPF bpf;
ebpf::StatusTuple res(0);
res = bpf.init(BPF_PROGRAM);
REQUIRE(res.code() == 0);

ebpf::BPFQueueStackTable<int> t = bpf.get_queuestack_table<int>("mystack");

SECTION("standard methods") {
int i, val;
std::string value;

// insert elements
for (i=0; i<30; i++) {
res = t.push_value(i);
REQUIRE(res.code() == 0);
}

// checking head (peek)
res = t.get_head(val);
REQUIRE(res.code() == 0);
REQUIRE(val == 29);

// retrieve elements
for (i=0; i<30; i++) {
res = t.pop_value(val);
REQUIRE(res.code() == 0);
REQUIRE( val == (30 - 1 - i));
}
// get non existing element
res = t.pop_value(val);
REQUIRE(res.code() != 0);
}
}
#endif

0 comments on commit 30a420d

Please sign in to comment.