Skip to content

Commit

Permalink
map.insert bcc helper to expose the BPF_NOEXIST flag (iovisor#1085)
Browse files Browse the repository at this point in the history
Inserts element in map only if it does not already exist. Throws a
warning during rewriter step if used on a BPF array.
  • Loading branch information
pchaigno authored and goldshtn committed Apr 1, 2017
1 parent 9e718f5 commit 6ceb329
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 14 deletions.
19 changes: 14 additions & 5 deletions docs/reference_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ Syntax: ```BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries)```

Creates a map named ```_name```. Most of the time this will be used via higher-level macros, like BPF_HASH, BPF_HIST, etc.

Methods (covered later): map.lookup(), map.lookup_or_init(), map.delete(), map.update(), map.increment().
Methods (covered later): map.lookup(), map.lookup_or_init(), map.delete(), map.update(), map.insert(), map.increment().

Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=BPF_TABLE+path%3Aexamples&type=Code),
Expand All @@ -397,7 +397,7 @@ BPF_HASH(start, struct request *);
This creates a hash named ```start``` where the key is a ```struct request *```, and the value defaults to u64. This hash is used by the disksnoop.py example for saving timestamps for each I/O request, where the key is the pointer to struct request, and the value is the timestamp.
Methods (covered later): map.lookup(), map.lookup_or_init(), map.delete(), map.update(), map.increment().
Methods (covered later): map.lookup(), map.lookup_or_init(), map.delete(), map.update(), map.insert(), map.increment().
Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=BPF_HASH+path%3Aexamples&type=Code),
Expand Down Expand Up @@ -530,7 +530,16 @@ 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)

### 11. map.increment()
### 11. map.insert()

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

Associate the value in the second argument to the key, only if there was no previous value.

Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=insert+path%3Aexamples&type=Code)

### 12. map.increment()

Syntax: ```map.increment(key)```

Expand All @@ -540,7 +549,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)

### 12. map.get_stackid()
### 13. map.get_stackid()

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

Expand All @@ -550,7 +559,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)

### 13. map.perf_read()
### 14. map.perf_read()

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

Expand Down
2 changes: 1 addition & 1 deletion examples/networking/neighbor_sharing/tc_neighbor_sharing.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ int classify_neighbor(struct __sk_buff *skb) {
u32 sip = ip->src;
struct ipkey key = {.client_ip=sip};
int val = 1;
learned_ips.update(&key, &val);
learned_ips.insert(&key, &val);
goto EOP;
}
EOP:
Expand Down
1 change: 1 addition & 0 deletions src/cc/export/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ struct _name##_table_t { \
_leaf_type * (*lookup) (_key_type *); \
_leaf_type * (*lookup_or_init) (_key_type *, _leaf_type *); \
int (*update) (_key_type *, _leaf_type *); \
int (*insert) (_key_type *, _leaf_type *); \
int (*delete) (_key_type *); \
void (*call) (void *, int index); \
void (*increment) (_key_type); \
Expand Down
12 changes: 8 additions & 4 deletions src/cc/frontends/clang/b_frontend_action.cc
Original file line number Diff line number Diff line change
Expand Up @@ -369,20 +369,18 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) {
}
string fd = to_string(table_it->fd);
string prefix, suffix;
string map_update_policy = "BPF_ANY";
string txt;
auto rewrite_start = Call->getLocStart();
auto rewrite_end = Call->getLocEnd();
if (memb_name == "lookup_or_init") {
map_update_policy = "BPF_NOEXIST";
string name = Ref->getDecl()->getName();
string arg0 = rewriter_.getRewrittenText(expansionRange(Call->getArg(0)->getSourceRange()));
string arg1 = rewriter_.getRewrittenText(expansionRange(Call->getArg(1)->getSourceRange()));
string lookup = "bpf_map_lookup_elem_(bpf_pseudo_fd(1, " + fd + ")";
string update = "bpf_map_update_elem_(bpf_pseudo_fd(1, " + fd + ")";
txt = "({typeof(" + name + ".leaf) *leaf = " + lookup + ", " + arg0 + "); ";
txt += "if (!leaf) {";
txt += " " + update + ", " + arg0 + ", " + arg1 + ", " + map_update_policy + ");";
txt += " " + update + ", " + arg0 + ", " + arg1 + ", BPF_NOEXIST);";
txt += " leaf = " + lookup + ", " + arg0 + ");";
txt += " if (!leaf) return 0;";
txt += "}";
Expand Down Expand Up @@ -433,7 +431,13 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) {
suffix = ")";
} else if (memb_name == "update") {
prefix = "bpf_map_update_elem";
suffix = ", " + map_update_policy + ")";
suffix = ", BPF_ANY)";
} else if (memb_name == "insert") {
if (table_it->type == BPF_MAP_TYPE_ARRAY) {
warning(Call->getLocStart(), "all element of an array already exist; insert() will have no effect");
}
prefix = "bpf_map_update_elem";
suffix = ", BPF_NOEXIST)";
} else if (memb_name == "delete") {
prefix = "bpf_map_delete_elem";
suffix = ")";
Expand Down
27 changes: 23 additions & 4 deletions tests/python/test_clang.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Licensed under the Apache License, Version 2.0 (the "License")

from bcc import BPF
import ctypes
import ctypes as ct
from unittest import main, TestCase
import os
import sys
Expand Down Expand Up @@ -113,7 +113,7 @@ def test_sscanf_array(self):
t = b.get_table("stats")
s1 = t.key_sprintf(t.Key(2))
self.assertEqual(s1, b"0x2")
s2 = t.leaf_sprintf(t.Leaf((ctypes.c_uint * 3)(1,2,3), 4))
s2 = t.leaf_sprintf(t.Leaf((ct.c_uint * 3)(1,2,3), 4))
self.assertEqual(s2, b"{ [ 0x1 0x2 0x3 ] 0x4 }")
l = t.leaf_scanf(s2)
self.assertEqual(l.a[0], 1)
Expand Down Expand Up @@ -330,8 +330,7 @@ def test_complex_leaf_types(self):
BPF_ARRAY(t3, union emptyu, 1);
"""
b = BPF(text=text)
import ctypes
self.assertEqual(ctypes.sizeof(b["t3"].Leaf), 8)
self.assertEqual(ct.sizeof(b["t3"].Leaf), 8)

def test_cflags(self):
text = """
Expand Down Expand Up @@ -485,5 +484,25 @@ def test_printk_2s(self):
self.assertIn(expectedWarn, output)
r.close()

def test_map_insert(self):
text = """
BPF_HASH(dummy);
void do_trace(struct pt_regs *ctx) {
u64 key = 0, val = 2;
dummy.insert(&key, &val);
key = 1;
dummy.update(&key, &val);
}
"""
b = BPF(text=text)
c_val = ct.c_ulong(1)
b["dummy"][ct.c_ulong(0)] = c_val
b["dummy"][ct.c_ulong(1)] = c_val
b.attach_kprobe(event="sys_sync", fn_name="do_trace")
libc = ct.CDLL("libc.so.6")
libc.sync()
self.assertEqual(1, b["dummy"][ct.c_ulong(0)].value)
self.assertEqual(2, b["dummy"][ct.c_ulong(1)].value)

if __name__ == "__main__":
main()

0 comments on commit 6ceb329

Please sign in to comment.