Fix map.clear() usage for array type maps
authorBrenden Blanco <bblanco@plumgrid.com>
Mon, 24 Aug 2015 05:59:38 +0000 (22:59 -0700)
committerBrenden Blanco <bblanco@plumgrid.com>
Mon, 24 Aug 2015 06:26:58 +0000 (23:26 -0700)
Calling delete on an array type map entry does not have an effect.
Instead, the entry needs to be zeroed out, since the array slot always
exists. To avoid unnecessary calls to update(), only call update() when
the map type is array-like. The type was not exposed to python up until
now, so add it.

Signed-off-by: Brenden Blanco <bblanco@plumgrid.com>
src/cc/bpf_common.cc
src/cc/bpf_common.h
src/cc/bpf_module.cc
src/cc/bpf_module.h
src/cc/frontends/b/codegen_llvm.cc
src/cc/frontends/clang/b_frontend_action.cc
src/cc/table_desc.h
src/python/bpf/__init__.py
tools/pidpersec

index 6c779a1..029da49 100644 (file)
@@ -122,6 +122,18 @@ int bpf_table_fd_id(void *program, size_t id) {
   return mod->table_fd(id);
 }
 
+int bpf_table_type(void *program, const char *table_name) {
+  auto mod = static_cast<ebpf::BPFModule *>(program);
+  if (!mod) return -1;
+  return mod->table_type(table_name);
+}
+
+int bpf_table_type_id(void *program, size_t id) {
+  auto mod = static_cast<ebpf::BPFModule *>(program);
+  if (!mod) return -1;
+  return mod->table_type(id);
+}
+
 const char * bpf_table_name(void *program, size_t id) {
   auto mod = static_cast<ebpf::BPFModule *>(program);
   if (!mod) return nullptr;
index 48adf0b..e066c58 100644 (file)
@@ -40,6 +40,8 @@ size_t bpf_num_tables(void *program);
 size_t bpf_table_id(void *program, const char *table_name);
 int bpf_table_fd(void *program, const char *table_name);
 int bpf_table_fd_id(void *program, size_t id);
+int bpf_table_type(void *program, const char *table_name);
+int bpf_table_type_id(void *program, size_t id);
 const char * bpf_table_name(void *program, size_t id);
 const char * bpf_table_key_desc(void *program, const char *table_name);
 const char * bpf_table_key_desc_id(void *program, size_t id);
index 3b2b6e3..6738e3a 100644 (file)
@@ -488,6 +488,15 @@ int BPFModule::table_fd(size_t id) const {
   return (*tables_)[id].fd;
 }
 
+int BPFModule::table_type(const string &name) const {
+  return table_type(table_id(name));
+}
+
+int BPFModule::table_type(size_t id) const {
+  if (id >= tables_->size()) return -1;
+  return (*tables_)[id].type;
+}
+
 const char * BPFModule::table_name(size_t id) const {
   if (id >= tables_->size()) return nullptr;
   return (*tables_)[id].name.c_str();
index 320ebf3..0ff1abf 100644 (file)
@@ -68,6 +68,8 @@ class BPFModule {
   int table_fd(size_t id) const;
   int table_fd(const std::string &name) const;
   const char * table_name(size_t id) const;
+  int table_type(const std::string &name) const;
+  int table_type(size_t id) const;
   const char * table_key_desc(size_t id) const;
   const char * table_key_desc(const std::string &name) const;
   size_t table_key_size(size_t id) const;
index f470e81..b0c6554 100644 (file)
@@ -1232,9 +1232,15 @@ StatusTuple CodegenLLVM::visit(Node* root, vector<TableDesc> &tables) {
   //TRY2(print_parser());
 
   for (auto table : tables_) {
+    bpf_map_type map_type = BPF_MAP_TYPE_UNSPEC;
+    if (table.first->type_id()->name_ == "FIXED_MATCH")
+      map_type = BPF_MAP_TYPE_HASH;
+    else if (table.first->type_id()->name_ == "INDEXED")
+      map_type = BPF_MAP_TYPE_ARRAY;
     tables.push_back({
       table.first->id_->name_,
       table_fds_[table.first],
+      map_type,
       table.first->key_type_->bit_width_ >> 3,
       table.first->leaf_type_->bit_width_ >> 3,
       table.first->size_,
index b4350dc..803efa4 100644 (file)
@@ -408,6 +408,7 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
         return false;
       }
     }
+    table.type = map_type;
     table.fd = bpf_create_map(map_type, table.key_size, table.leaf_size, table.max_entries);
     if (table.fd < 0) {
       C.getDiagnostics().Report(Decl->getLocStart(), diag::err_expected)
index 13cc685..1d18145 100644 (file)
@@ -26,6 +26,7 @@ namespace ebpf {
 struct TableDesc {
   std::string name;
   int fd;
+  int type;
   size_t key_size;  // sizes are in bytes
   size_t leaf_size;
   size_t max_entries;
index 9a58313..f05e38d 100644 (file)
@@ -44,6 +44,8 @@ lib.bpf_table_id.restype = ct.c_ulonglong
 lib.bpf_table_id.argtypes = [ct.c_void_p, ct.c_char_p]
 lib.bpf_table_fd.restype = ct.c_int
 lib.bpf_table_fd.argtypes = [ct.c_void_p, ct.c_char_p]
+lib.bpf_table_type_id.restype = ct.c_int
+lib.bpf_table_type_id.argtypes = [ct.c_void_p, ct.c_ulonglong]
 lib.bpf_table_key_desc.restype = ct.c_char_p
 lib.bpf_table_key_desc.argtypes = [ct.c_void_p, ct.c_char_p]
 lib.bpf_table_leaf_desc.restype = ct.c_char_p
@@ -103,6 +105,10 @@ class BPF(object):
     SCHED_CLS = 3
     SCHED_ACT = 4
 
+    HASH = 1
+    ARRAY = 2
+    PROG_ARRAY = 3
+
     class Function(object):
         def __init__(self, bpf, name, fd):
             self.bpf = bpf
@@ -116,6 +122,7 @@ class BPF(object):
             self.map_fd = map_fd
             self.Key = keytype
             self.Leaf = leaftype
+            self.ttype = lib.bpf_table_type_id(self.bpf.module, self.map_id)
 
         def key_sprintf(self, key):
             key_p = ct.pointer(key)
@@ -180,9 +187,33 @@ class BPF(object):
 
         def __delitem__(self, key):
             key_p = ct.pointer(key)
-            res = lib.bpf_delete_elem(self.map_fd, ct.cast(key_p, ct.c_void_p))
-            if res < 0:
-                raise KeyError
+            ttype = lib.bpf_table_type_id(self.bpf.module, self.map_id)
+            # Deleting from array type maps does not have an effect, so
+            # zero out the entry instead.
+            if ttype in (BPF.ARRAY, BPF.PROG_ARRAY):
+                leaf = self.Leaf()
+                leaf_p = ct.pointer(leaf)
+                res = lib.bpf_update_elem(self.map_fd,
+                        ct.cast(key_p, ct.c_void_p),
+                        ct.cast(leaf_p, ct.c_void_p), 0)
+                if res < 0:
+                    raise Exception("Could not clear item")
+            else:
+                res = lib.bpf_delete_elem(self.map_fd,
+                        ct.cast(key_p, ct.c_void_p))
+                if res < 0:
+                    raise KeyError
+
+        def clear(self):
+            if self.ttype in (BPF.ARRAY, BPF.PROG_ARRAY):
+                # Special case clear, since this class is currently behaving
+                # like a dict but popitem on an array causes an infinite loop.
+                # TODO: derive Table from array.array instead
+                for k in self.keys():
+                    self.__delitem__(k)
+            else:
+                super(BPF.Table, self).clear()
+
 
         def __iter__(self):
             return BPF.Table.Iter(self, self.Key)
index 6976ba6..2d3c731 100755 (executable)
@@ -1,4 +1,5 @@
 #!/usr/bin/python
+# vim: ts=8 noet sw=8
 #
 # pidpersec    Count new processes (via fork).
 #              For Linux, uses BCC, eBPF. See .c file.
@@ -32,8 +33,8 @@ while (1):
        try:
                sleep(1)
        except KeyboardInterrupt:
-               pass; exit()
+               exit()
 
        print("%s: PIDs/sec: %d" % (strftime("%H:%M:%S"),
-           (b["stats"][S_COUNT].value - last)))
-       last = b["stats"][S_COUNT].value
+           (b["stats"][S_COUNT].value)))
+       b["stats"].clear()