diff --git a/docs/reference_guide.md b/docs/reference_guide.md index 5e849e06fe2f..0a9ce1ceda0e 100644 --- a/docs/reference_guide.md +++ b/docs/reference_guide.md @@ -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), @@ -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), @@ -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)``` @@ -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)``` @@ -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)``` diff --git a/examples/networking/neighbor_sharing/tc_neighbor_sharing.c b/examples/networking/neighbor_sharing/tc_neighbor_sharing.c index d0eadd694e9e..6594862787fe 100644 --- a/examples/networking/neighbor_sharing/tc_neighbor_sharing.c +++ b/examples/networking/neighbor_sharing/tc_neighbor_sharing.c @@ -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: diff --git a/src/cc/export/helpers.h b/src/cc/export/helpers.h index 451224a17db1..009ce165e3b7 100644 --- a/src/cc/export/helpers.h +++ b/src/cc/export/helpers.h @@ -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); \ diff --git a/src/cc/frontends/clang/b_frontend_action.cc b/src/cc/frontends/clang/b_frontend_action.cc index eb2c9e50b65b..f12d2b7e81c1 100644 --- a/src/cc/frontends/clang/b_frontend_action.cc +++ b/src/cc/frontends/clang/b_frontend_action.cc @@ -369,12 +369,10 @@ 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())); @@ -382,7 +380,7 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) { 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 += "}"; @@ -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 = ")"; diff --git a/tests/python/test_clang.py b/tests/python/test_clang.py index 96bec114cd9c..0e8a975ecfe7 100755 --- a/tests/python/test_clang.py +++ b/tests/python/test_clang.py @@ -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 @@ -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) @@ -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 = """ @@ -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()