Skip to content

Commit

Permalink
Support pinned maps through BPF_TABLE_PINNED macro
Browse files Browse the repository at this point in the history
Define a map to be loaded from a pinned path through
the new macro BPF_TABLE_PINNED(..., "/sys/fs/bpf/xyz");
instead of creating a new map.

Internally the path is appended to the section attribute
with a ':' delimiter.
  • Loading branch information
pothos authored and yonghong-song committed Jun 25, 2019
1 parent b1cc550 commit 2627bae
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 36 deletions.
11 changes: 11 additions & 0 deletions docs/reference_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,17 @@ Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=BPF_TABLE+path%3Aexamples&type=Code),
[search /tools](https://github.com/iovisor/bcc/search?q=BPF_TABLE+path%3Atools&type=Code)

#### Pinned Maps

Maps that were pinned to the BPF filesystem can be accessed through an extended syntax: ```BPF_TABLE_PINNED(_table_type, _key_type, _leaf_type, _name, _max_entries, "/sys/fs/bpf/xyz")```
The type information is not enforced and the actual map type depends on the map that got pinned to the location.

For example:

```C
BPF_TABLE_PINNED("hash", u64, u64, ids, 1024, "/sys/fs/bpf/ids");
```
### 2. BPF_HASH
Syntax: ```BPF_HASH(name [, key_type [, leaf_type [, size]]])```
Expand Down
4 changes: 4 additions & 0 deletions src/cc/api/BPFTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ class BPFTableBase {
return rc;
}

int get_fd() {
return desc.fd;
}

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

Expand Down
33 changes: 20 additions & 13 deletions src/cc/bpf_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ int BPFModule::load_maps(sec_map_def &sections) {
for (auto map : fake_fd_map_) {
int fd, fake_fd, map_type, key_size, value_size, max_entries, map_flags;
const char *map_name;
unsigned int pinned_id;

fake_fd = map.first;
map_type = get<0>(map.second);
Expand All @@ -325,22 +326,28 @@ int BPFModule::load_maps(sec_map_def &sections) {
value_size = get<3>(map.second);
max_entries = get<4>(map.second);
map_flags = get<5>(map.second);
pinned_id = get<6>(map.second);

if (pinned_id) {
fd = bpf_map_get_fd_by_id(pinned_id);
} else {
struct bpf_create_map_attr attr = {};
attr.map_type = (enum bpf_map_type)map_type;
attr.name = map_name;
attr.key_size = key_size;
attr.value_size = value_size;
attr.max_entries = max_entries;
attr.map_flags = map_flags;

if (map_tids.find(map_name) != map_tids.end()) {
attr.btf_fd = btf_->get_fd();
attr.btf_key_type_id = map_tids[map_name].first;
attr.btf_value_type_id = map_tids[map_name].second;
}

struct bpf_create_map_attr attr = {};
attr.map_type = (enum bpf_map_type)map_type;
attr.name = map_name;
attr.key_size = key_size;
attr.value_size = value_size;
attr.max_entries = max_entries;
attr.map_flags = map_flags;

if (map_tids.find(map_name) != map_tids.end()) {
attr.btf_fd = btf_->get_fd();
attr.btf_key_type_id = map_tids[map_name].first;
attr.btf_value_type_id = map_tids[map_name].second;
fd = bcc_create_map_xattr(&attr, allow_rlimit_);
}

fd = bcc_create_map_xattr(&attr, allow_rlimit_);
if (fd < 0) {
fprintf(stderr, "could not open bpf map: %s, error: %s\n",
map_name, strerror(errno));
Expand Down
3 changes: 3 additions & 0 deletions src/cc/export/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ BPF_ANNOTATE_KV_PAIR(_name, _key_type, _leaf_type)
#define BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries) \
BPF_F_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries, 0)

#define BPF_TABLE_PINNED(_table_type, _key_type, _leaf_type, _name, _max_entries, _pinned) \
BPF_TABLE(_table_type ":" _pinned, _key_type, _leaf_type, _name, _max_entries)

// define a table same as above but allow it to be referenced by other modules
#define BPF_TABLE_PUBLIC(_table_type, _key_type, _leaf_type, _name, _max_entries) \
BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries); \
Expand Down
61 changes: 41 additions & 20 deletions src/cc/frontends/clang/b_frontend_action.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "loader.h"
#include "table_storage.h"
#include "arch_helper.h"
#include "libbpf/src/bpf.h"

#include "libbpf.h"

Expand Down Expand Up @@ -1168,46 +1169,66 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
++i;
}

std::string section_attr = A->getName();
size_t pinned_path_pos = section_attr.find(":");
unsigned int pinned_id = 0; // 0 is not a valid map ID, they start with 1

if (pinned_path_pos != std::string::npos) {
std::string pinned = section_attr.substr(pinned_path_pos + 1);
section_attr = section_attr.substr(0, pinned_path_pos);
int fd = bpf_obj_get(pinned.c_str());
struct bpf_map_info info = {};
unsigned int info_len = sizeof(info);

if (bpf_obj_get_info_by_fd(fd, &info, &info_len)) {
error(GET_BEGINLOC(Decl), "map not found: %0") << pinned;
return false;
}

close(fd);
pinned_id = info.id;
}

bpf_map_type map_type = BPF_MAP_TYPE_UNSPEC;
if (A->getName() == "maps/hash") {
if (section_attr == "maps/hash") {
map_type = BPF_MAP_TYPE_HASH;
} else if (A->getName() == "maps/array") {
} else if (section_attr == "maps/array") {
map_type = BPF_MAP_TYPE_ARRAY;
} else if (A->getName() == "maps/percpu_hash") {
} else if (section_attr == "maps/percpu_hash") {
map_type = BPF_MAP_TYPE_PERCPU_HASH;
} else if (A->getName() == "maps/percpu_array") {
} else if (section_attr == "maps/percpu_array") {
map_type = BPF_MAP_TYPE_PERCPU_ARRAY;
} else if (A->getName() == "maps/lru_hash") {
} else if (section_attr == "maps/lru_hash") {
map_type = BPF_MAP_TYPE_LRU_HASH;
} else if (A->getName() == "maps/lru_percpu_hash") {
} else if (section_attr == "maps/lru_percpu_hash") {
map_type = BPF_MAP_TYPE_LRU_PERCPU_HASH;
} else if (A->getName() == "maps/lpm_trie") {
} else if (section_attr == "maps/lpm_trie") {
map_type = BPF_MAP_TYPE_LPM_TRIE;
} else if (A->getName() == "maps/histogram") {
} else if (section_attr == "maps/histogram") {
map_type = BPF_MAP_TYPE_HASH;
if (key_type->isSpecificBuiltinType(BuiltinType::Int))
map_type = BPF_MAP_TYPE_ARRAY;
if (!leaf_type->isSpecificBuiltinType(BuiltinType::ULongLong))
error(GET_BEGINLOC(Decl), "histogram leaf type must be u64, got %0") << leaf_type;
} else if (A->getName() == "maps/prog") {
} else if (section_attr == "maps/prog") {
map_type = BPF_MAP_TYPE_PROG_ARRAY;
} else if (A->getName() == "maps/perf_output") {
} else if (section_attr == "maps/perf_output") {
map_type = BPF_MAP_TYPE_PERF_EVENT_ARRAY;
int numcpu = get_possible_cpus().size();
if (numcpu <= 0)
numcpu = 1;
table.max_entries = numcpu;
} else if (A->getName() == "maps/perf_array") {
} else if (section_attr == "maps/perf_array") {
map_type = BPF_MAP_TYPE_PERF_EVENT_ARRAY;
} else if (A->getName() == "maps/cgroup_array") {
} else if (section_attr == "maps/cgroup_array") {
map_type = BPF_MAP_TYPE_CGROUP_ARRAY;
} else if (A->getName() == "maps/stacktrace") {
} else if (section_attr == "maps/stacktrace") {
map_type = BPF_MAP_TYPE_STACK_TRACE;
} else if (A->getName() == "maps/devmap") {
} else if (section_attr == "maps/devmap") {
map_type = BPF_MAP_TYPE_DEVMAP;
} else if (A->getName() == "maps/cpumap") {
} else if (section_attr == "maps/cpumap") {
map_type = BPF_MAP_TYPE_CPUMAP;
} else if (A->getName() == "maps/extern") {
} else if (section_attr == "maps/extern") {
if (!fe_.table_storage().Find(maps_ns_path, table_it)) {
if (!fe_.table_storage().Find(global_path, table_it)) {
error(GET_BEGINLOC(Decl), "reference to undefined table");
Expand All @@ -1216,7 +1237,7 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
}
table = table_it->second.dup();
table.is_extern = true;
} else if (A->getName() == "maps/export") {
} else if (section_attr == "maps/export") {
if (table.name.substr(0, 2) == "__")
table.name = table.name.substr(2);
Path local_path({fe_.id(), table.name});
Expand All @@ -1227,7 +1248,7 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
}
fe_.table_storage().Insert(global_path, table_it->second.dup());
return true;
} else if(A->getName() == "maps/shared") {
} else if(section_attr == "maps/shared") {
if (table.name.substr(0, 2) == "__")
table.name = table.name.substr(2);
Path local_path({fe_.id(), table.name});
Expand All @@ -1242,15 +1263,15 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {

if (!table.is_extern) {
if (map_type == BPF_MAP_TYPE_UNSPEC) {
error(GET_BEGINLOC(Decl), "unsupported map type: %0") << A->getName();
error(GET_BEGINLOC(Decl), "unsupported map type: %0") << section_attr;
return false;
}

table.type = map_type;
table.fake_fd = fe_.get_next_fake_fd();
fe_.add_map_def(table.fake_fd, std::make_tuple((int)map_type, std::string(table.name),
(int)table.key_size, (int)table.leaf_size,
(int)table.max_entries, table.flags));
(int)table.max_entries, table.flags, pinned_id));
}

if (!table.is_extern)
Expand Down
4 changes: 2 additions & 2 deletions src/cc/frontends/clang/b_frontend_action.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ class BFrontendAction : public clang::ASTFrontendAction {
void DoMiscWorkAround();
// negative fake_fd to be different from real fd in bpf_pseudo_fd.
int get_next_fake_fd() { return next_fake_fd_--; }
void add_map_def(int fd, std::tuple<int, std::string, int, int, int, int> map_def) {
fake_fd_map_[fd] = map_def;
void add_map_def(int fd, std::tuple<int, std::string, int, int, int, int, unsigned int> map_def) {
fake_fd_map_[fd] = move(map_def);
}

private:
Expand Down
2 changes: 1 addition & 1 deletion src/cc/table_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

namespace ebpf {

typedef std::map<int, std::tuple<int, std::string, int, int, int, int>> fake_fd_map_def;
typedef std::map<int, std::tuple<int, std::string, int, int, int, int, unsigned int>> fake_fd_map_def;

class TableStorageImpl;
class TableStorageIteratorImpl;
Expand Down
1 change: 1 addition & 0 deletions tests/cc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ add_executable(test_libbcc
test_bpf_table.cc
test_hash_table.cc
test_perf_event.cc
test_pinned_table.cc
test_prog_table.cc
test_shared_table.cc
test_usdt_args.cc
Expand Down
86 changes: 86 additions & 0 deletions tests/cc/test_pinned_table.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2019 Kinvolk GmbH
*
* 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
*
* http: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 <linux/version.h>
#include <unistd.h>
#include <string>
#include <sys/mount.h>

#include "BPF.h"
#include "catch.hpp"

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
TEST_CASE("test pinned table", "[pinned_table]") {
bool mounted = false;
if (system("mount | grep /sys/fs/bpf")) {
REQUIRE(system("mkdir -p /sys/fs/bpf") == 0);
REQUIRE(system("mount -o nosuid,nodev,noexec,mode=700 -t bpf bpf /sys/fs/bpf") == 0);
mounted = true;
}
// prepare test by pinning table to bpffs
{
const std::string BPF_PROGRAM = R"(
BPF_TABLE("hash", u64, u64, ids, 1024);
)";

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

REQUIRE(bpf_obj_pin(bpf.get_hash_table<int, int>("ids").get_fd(), "/sys/fs/bpf/test_pinned_table") == 0);
}

// test table access
{
const std::string BPF_PROGRAM = R"(
BPF_TABLE_PINNED("hash", u64, u64, ids, 1024, "/sys/fs/bpf/test_pinned_table");
)";

ebpf::BPF bpf;
ebpf::StatusTuple res(0);
res = bpf.init(BPF_PROGRAM);
unlink("/sys/fs/bpf/test_pinned_table"); // can delete table here already
REQUIRE(res.code() == 0);

auto t = bpf.get_hash_table<int, int>("ids");
int key, value;

// write element
key = 0x08;
value = 0x43;
res = t.update_value(key, value);
REQUIRE(res.code() == 0);
REQUIRE(t[key] == value);
}

// test failure
{
const std::string BPF_PROGRAM = R"(
BPF_TABLE_PINNED("hash", u64, u64, ids, 1024, "/sys/fs/bpf/test_pinned_table");
)";

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

if (mounted) {
REQUIRE(umount("/sys/fs/bpf") == 0);
}
}
#endif

0 comments on commit 2627bae

Please sign in to comment.