Skip to content

Commit

Permalink
Add map-in-map support
Browse files Browse the repository at this point in the history
Add BPF_MAP_TYPE_HASH_OF_MAPS and BPF_MAP_TYPE_HASH_OF_MAPS
supports in bcc. Two new constructs below are introduced
to bpf program:
  BPF_HASH_OF_MAPS(map_name, "inner_map_name", max_entries)
  BPF_ARRAY_OF_MAPS(map_name, "inner_map_name", max_entries)
In the above, "inner_map_name" is for metadata purpose and there
must be a map defined in bpf program with map name "inner_map_name".

Both python and C++ APIs are added.

For python, a new Table API get_fd() is introduced to get
the fd of a map so that the fd can be used by a map-in-map
do update. The get_fd() is already exposed as API function
in C++. For C++, without get_fd(), we will need to
templatize basic functions like update_value etc, which
I feed too heavy weight. Because of C++ using get_fd()
mechanism, so I exposed similar API on python side
for parity reason.

For map-in-map, the inner map lookup/update/delete won't have
explicit map names. Considering map-in-map is not
used very frequently, I feel looking primitive
bpf_map_{lookup,update,delete}_elem() probably okay,
so I did not create any new bcc specific
constructs for this purpose.

Added both C++ and python test cases to show how to
use the above two new map type in bcc.

Signed-off-by: Yonghong Song <[email protected]>
  • Loading branch information
yonghong-song committed Nov 26, 2019
1 parent 0272a3d commit 149c1c8
Show file tree
Hide file tree
Showing 15 changed files with 574 additions and 56 deletions.
46 changes: 36 additions & 10 deletions docs/reference_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,33 @@ Methods (covered later): map.redirect_map().
Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=BPF_CPUMAP+path%3Aexamples&type=Code),
### 12. map.lookup()
### 12. BPF_ARRAY_OF_MAPS
Syntax: ```BPF_ARRAY_OF_MAPS(name, inner_map_name, size)```

This creates an array map with a map-in-map type (BPF_MAP_TYPE_HASH_OF_MAPS) map named ```name``` with ```size``` entries. The inner map meta data is provided by map ```inner_map_name``` and can be most of array or hash maps except ```BPF_MAP_TYPE_PROG_ARRAY```, ```BPF_MAP_TYPE_CGROUP_STORAGE``` and ```BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE```.

For example:
```C
BPF_TABLE("hash", int, int, ex1, 1024);
BPF_TABLE("hash", int, int, ex2, 1024);
BPF_ARRAY_OF_MAPS(maps_array, "ex1", 10);
```
### 13. BPF_HASH_OF_MAPS
Syntax: ```BPF_HASH_OF_MAPS(name, inner_map_name, size)```

This creates a hash map with a map-in-map type (BPF_MAP_TYPE_HASH_OF_MAPS) map named ```name``` with ```size``` entries. The inner map meta data is provided by map ```inner_map_name``` and can be most of array or hash maps except ```BPF_MAP_TYPE_PROG_ARRAY```, ```BPF_MAP_TYPE_CGROUP_STORAGE``` and ```BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE```.

For example:
```C
BPF_ARRAY(ex1, int, 1024);
BPF_ARRAY(ex2, int, 1024);
BPF_HASH_OF_MAPS(maps_hash, "ex1", 10);
```
### 14. map.lookup()
Syntax: ```*val map.lookup(&key)```

Expand All @@ -769,7 +795,7 @@ Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=lookup+path%3Aexamples&type=Code),
[search /tools](https://github.com/iovisor/bcc/search?q=lookup+path%3Atools&type=Code)

### 13. map.lookup_or_try_init()
### 15. map.lookup_or_try_init()

Syntax: ```*val map.lookup_or_try_init(&key, &zero)```

Expand All @@ -782,7 +808,7 @@ Examples in situ:
Note: The old map.lookup_or_init() may cause return from the function, so lookup_or_try_init() is recommended as it
does not have this side effect.

### 14. map.delete()
### 16. map.delete()

Syntax: ```map.delete(&key)```

Expand All @@ -792,7 +818,7 @@ Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=delete+path%3Aexamples&type=Code),
[search /tools](https://github.com/iovisor/bcc/search?q=delete+path%3Atools&type=Code)

### 15. map.update()
### 17. map.update()

Syntax: ```map.update(&key, &val)```

Expand All @@ -802,7 +828,7 @@ Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=update+path%3Aexamples&type=Code),
[search /tools](https://github.com/iovisor/bcc/search?q=update+path%3Atools&type=Code)

### 16. map.insert()
### 18. map.insert()

Syntax: ```map.insert(&key, &val)```

Expand All @@ -812,7 +838,7 @@ Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=insert+path%3Aexamples&type=Code),
[search /tools](https://github.com/iovisor/bcc/search?q=insert+path%3Atools&type=Code)

### 17. map.increment()
### 19. map.increment()

Syntax: ```map.increment(key[, increment_amount])```

Expand All @@ -822,7 +848,7 @@ Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=increment+path%3Aexamples&type=Code),
[search /tools](https://github.com/iovisor/bcc/search?q=increment+path%3Atools&type=Code)

### 18. map.get_stackid()
### 20. map.get_stackid()

Syntax: ```int map.get_stackid(void *ctx, u64 flags)```

Expand All @@ -832,7 +858,7 @@ Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=get_stackid+path%3Aexamples&type=Code),
[search /tools](https://github.com/iovisor/bcc/search?q=get_stackid+path%3Atools&type=Code)

### 19. map.perf_read()
### 21. map.perf_read()

Syntax: ```u64 map.perf_read(u32 cpu)```

Expand All @@ -841,7 +867,7 @@ This returns the hardware performance counter as configured in [5. BPF_PERF_ARRA
Examples in situ:
[search /tests](https://github.com/iovisor/bcc/search?q=perf_read+path%3Atests&type=Code)

### 20. map.call()
### 22. map.call()

Syntax: ```void map.call(void *ctx, int index)```

Expand Down Expand Up @@ -880,7 +906,7 @@ Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?l=C&q=call+path%3Aexamples&type=Code),
[search /tests](https://github.com/iovisor/bcc/search?l=C&q=call+path%3Atests&type=Code)

### 21. map.redirect_map()
### 23. map.redirect_map()

Syntax: ```int map.redirect_map(int index, int flags)```

Expand Down
7 changes: 7 additions & 0 deletions src/cc/api/BPF.cc
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,13 @@ BPFStackBuildIdTable BPF::get_stackbuildid_table(const std::string &name, bool u
return BPFStackBuildIdTable({}, use_debug_file, check_debug_file_crc, get_bsymcache());
}

BPFMapInMapTable BPF::get_map_in_map_table(const std::string& name) {
TableStorage::iterator it;
if (bpf_module_->table_storage().Find(Path({bpf_module_->id(), name}), it))
return BPFMapInMapTable(it->second);
return BPFMapInMapTable({});
}

bool BPF::add_module(std::string module)
{
return bcc_buildsymcache_add_module(get_bsymcache(), module.c_str()) != 0 ?
Expand Down
2 changes: 2 additions & 0 deletions src/cc/api/BPF.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ class BPF {
bool use_debug_file = true,
bool check_debug_file_crc = true);

BPFMapInMapTable get_map_in_map_table(const std::string& name);

bool add_module(std::string module);

StatusTuple open_perf_event(const std::string& name, uint32_t type,
Expand Down
21 changes: 21 additions & 0 deletions src/cc/api/BPFTable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -653,4 +653,25 @@ StatusTuple BPFDevmapTable::remove_value(const int& index) {
return StatusTuple(0);
}

BPFMapInMapTable::BPFMapInMapTable(const TableDesc& desc)
: BPFTableBase<int, int>(desc) {
if(desc.type != BPF_MAP_TYPE_ARRAY_OF_MAPS &&
desc.type != BPF_MAP_TYPE_HASH_OF_MAPS)
throw std::invalid_argument("Table '" + desc.name +
"' is not a map-in-map table");
}

StatusTuple BPFMapInMapTable::update_value(const int& index,
const int& inner_map_fd) {
if (!this->update(const_cast<int*>(&index), const_cast<int*>(&inner_map_fd)))
return StatusTuple(-1, "Error updating value: %s", std::strerror(errno));
return StatusTuple(0);
}

StatusTuple BPFMapInMapTable::remove_value(const int& index) {
if (!this->remove(const_cast<int*>(&index)))
return StatusTuple(-1, "Error removing value: %s", std::strerror(errno));
return StatusTuple(0);
}

} // namespace ebpf
7 changes: 7 additions & 0 deletions src/cc/api/BPFTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,14 @@ class BPFDevmapTable : public BPFTableBase<int, int> {
StatusTuple update_value(const int& index, const int& value);
StatusTuple get_value(const int& index, int& value);
StatusTuple remove_value(const int& index);
};

class BPFMapInMapTable : public BPFTableBase<int, int> {
public:
BPFMapInMapTable(const TableDesc& desc);

StatusTuple update_value(const int& index, const int& inner_map_fd);
StatusTuple remove_value(const int& index);
};

} // namespace ebpf
131 changes: 88 additions & 43 deletions src/cc/bpf_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <sys/stat.h>
#include <unistd.h>
#include <vector>
#include <set>
#include <linux/bpf.h>
#include <net/if.h>

Expand Down Expand Up @@ -271,56 +272,25 @@ void BPFModule::load_btf(sec_map_def &sections) {
btf_ = btf;
}

int BPFModule::load_maps(sec_map_def &sections) {
// find .maps.<table_name> sections and retrieve all map key/value type id's
std::map<std::string, std::pair<int, int>> map_tids;
if (btf_) {
for (auto section : sections) {
auto sec_name = section.first;
if (strncmp(".maps.", sec_name.c_str(), 6) == 0) {
std::string map_name = sec_name.substr(6);
unsigned key_tid = 0, value_tid = 0;
unsigned expected_ksize = 0, expected_vsize = 0;

// skip extern maps, which won't be in fake_fd_map_ as they do not
// require explicit bpf_create_map.
bool is_extern = false;
for (auto &t : tables_) {
if (t->name == map_name) {
is_extern = t->is_extern;
break;
}
}
if (is_extern)
continue;

for (auto map : fake_fd_map_) {
std::string name;

name = get<1>(map.second);
if (map_name == name) {
expected_ksize = get<2>(map.second);
expected_vsize = get<3>(map.second);
break;
}
}

int ret = btf_->get_map_tids(map_name, expected_ksize,
expected_vsize, &key_tid, &value_tid);
if (ret)
continue;

map_tids[map_name] = std::make_pair(key_tid, value_tid);
}
int BPFModule::create_maps(std::map<std::string, std::pair<int, int>> &map_tids,
std::map<int, int> &map_fds,
std::map<std::string, int> &inner_map_fds,
bool for_inner_map) {
std::set<std::string> inner_maps;
if (for_inner_map) {
for (auto map : fake_fd_map_) {
std::string inner_map_name = get<7>(map.second);
if (inner_map_name.size())
inner_maps.insert(inner_map_name);
}
}

// create maps
std::map<int, int> map_fds;
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;
std::string inner_map_name;
int inner_map_fd = 0;

fake_fd = map.first;
map_type = get<0>(map.second);
Expand All @@ -330,6 +300,22 @@ int BPFModule::load_maps(sec_map_def &sections) {
max_entries = get<4>(map.second);
map_flags = get<5>(map.second);
pinned_id = get<6>(map.second);
inner_map_name = get<7>(map.second);

if (for_inner_map) {
if (inner_maps.find(map_name) == inner_maps.end())
continue;
if (inner_map_name.size()) {
fprintf(stderr, "inner map %s has inner map %s\n",
map_name, inner_map_name.c_str());
return -1;
}
} else {
if (inner_map_fds.find(map_name) != inner_map_fds.end())
continue;
if (inner_map_name.size())
inner_map_fd = inner_map_fds[inner_map_name];
}

if (pinned_id) {
fd = bpf_map_get_fd_by_id(pinned_id);
Expand All @@ -342,6 +328,7 @@ int BPFModule::load_maps(sec_map_def &sections) {
attr.max_entries = max_entries;
attr.map_flags = map_flags;
attr.map_ifindex = ifindex_;
attr.inner_map_fd = inner_map_fd;

if (map_tids.find(map_name) != map_tids.end()) {
attr.btf_fd = btf_->get_fd();
Expand All @@ -358,9 +345,67 @@ int BPFModule::load_maps(sec_map_def &sections) {
return -1;
}

if (for_inner_map)
inner_map_fds[map_name] = fd;

map_fds[fake_fd] = fd;
}

return 0;
}

int BPFModule::load_maps(sec_map_def &sections) {
// find .maps.<table_name> sections and retrieve all map key/value type id's
std::map<std::string, std::pair<int, int>> map_tids;
if (btf_) {
for (auto section : sections) {
auto sec_name = section.first;
if (strncmp(".maps.", sec_name.c_str(), 6) == 0) {
std::string map_name = sec_name.substr(6);
unsigned key_tid = 0, value_tid = 0;
unsigned expected_ksize = 0, expected_vsize = 0;

// skip extern maps, which won't be in fake_fd_map_ as they do not
// require explicit bpf_create_map.
bool is_extern = false;
for (auto &t : tables_) {
if (t->name == map_name) {
is_extern = t->is_extern;
break;
}
}
if (is_extern)
continue;

for (auto map : fake_fd_map_) {
std::string name;

name = get<1>(map.second);
if (map_name == name) {
expected_ksize = get<2>(map.second);
expected_vsize = get<3>(map.second);
break;
}
}

int ret = btf_->get_map_tids(map_name, expected_ksize,
expected_vsize, &key_tid, &value_tid);
if (ret)
continue;

map_tids[map_name] = std::make_pair(key_tid, value_tid);
}
}
}

// create maps
std::map<std::string, int> inner_map_fds;
std::map<int, int> map_fds;
if (create_maps(map_tids, map_fds, inner_map_fds, true) < 0)
return -1;
if (create_maps(map_tids, map_fds, inner_map_fds, false) < 0)
return -1;

// update map table fd's
for (auto it = ts_->begin(), up = ts_->end(); it != up; ++it) {
TableDesc &table = it->second;
Expand Down
4 changes: 4 additions & 0 deletions src/cc/bpf_module.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ class BPFModule {
const void *val);
void load_btf(sec_map_def &sections);
int load_maps(sec_map_def &sections);
int create_maps(std::map<std::string, std::pair<int, int>> &map_tids,
std::map<int, int> &map_fds,
std::map<std::string, int> &inner_map_fds,
bool for_inner_map);

public:
BPFModule(unsigned flags, TableStorage *ts = nullptr, bool rw_engine_enabled = true,
Expand Down
6 changes: 6 additions & 0 deletions src/cc/export/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,12 @@ struct _name##_table_t _name = { .max_entries = (_max_entries) }
#define BPF_CPUMAP(_name, _max_entries) \
BPF_XDP_REDIRECT_MAP("cpumap", u32, _name, _max_entries)

#define BPF_ARRAY_OF_MAPS(_name, _inner_map_name, _max_entries) \
BPF_TABLE("array_of_maps$" _inner_map_name, int, int, _name, _max_entries)

#define BPF_HASH_OF_MAPS(_name, _inner_map_name, _max_entries) \
BPF_TABLE("hash_of_maps$" _inner_map_name, int, int, _name, _max_entries)

// packet parsing state machine helpers
#define cursor_advance(_cursor, _len) \
({ void *_tmp = _cursor; _cursor += _len; _tmp; })
Expand Down
Loading

0 comments on commit 149c1c8

Please sign in to comment.