From de11d07d0a25ac5b94745b2bea61e1b0098517dc Mon Sep 17 00:00:00 2001
From: Huapeng Zhou <hzhou@fb.com>
Date: Tue, 6 Dec 2016 18:10:38 -0800
Subject: [PATCH] add support for bpf map flags

---
 src/cc/bpf_common.cc                        | 12 ++++++++++
 src/cc/bpf_common.h                         |  2 ++
 src/cc/bpf_module.cc                        |  9 +++++++
 src/cc/bpf_module.h                         |  2 ++
 src/cc/export/helpers.h                     |  8 +++++--
 src/cc/frontends/b/codegen_llvm.cc          |  3 ++-
 src/cc/frontends/clang/b_frontend_action.cc | 18 +++++++-------
 src/cc/frontends/clang/loader.h             |  5 ----
 src/cc/libbpf.c                             |  7 +++---
 src/cc/libbpf.h                             |  2 +-
 src/cc/table_desc.h                         |  1 +
 src/lua/bcc/libbcc.lua                      |  4 +++-
 src/python/bcc/libbcc.py                    |  2 ++
 src/python/bcc/table.py                     |  1 +
 tests/python/test_flags.py                  | 26 +++++++++++++++++++++
 15 files changed, 81 insertions(+), 21 deletions(-)
 create mode 100644 tests/python/test_flags.py

diff --git a/src/cc/bpf_common.cc b/src/cc/bpf_common.cc
index 15f56020..4a711978 100644
--- a/src/cc/bpf_common.cc
+++ b/src/cc/bpf_common.cc
@@ -146,6 +146,18 @@ size_t bpf_table_max_entries_id(void *program, size_t id) {
   return mod->table_max_entries(id);
 }
 
+int bpf_table_flags(void *program, const char *table_name) {
+  auto mod = static_cast<ebpf::BPFModule *>(program);
+  if (!mod) return -1;
+  return mod->table_flags(table_name);
+}
+
+int bpf_table_flags_id(void *program, size_t id) {
+  auto mod = static_cast<ebpf::BPFModule *>(program);
+  if (!mod) return -1;
+  return mod->table_flags(id);
+}
+
 const char * bpf_table_name(void *program, size_t id) {
   auto mod = static_cast<ebpf::BPFModule *>(program);
   if (!mod) return nullptr;
diff --git a/src/cc/bpf_common.h b/src/cc/bpf_common.h
index 2b3ea542..0abdbd48 100644
--- a/src/cc/bpf_common.h
+++ b/src/cc/bpf_common.h
@@ -44,6 +44,8 @@ int bpf_table_type(void *program, const char *table_name);
 int bpf_table_type_id(void *program, size_t id);
 size_t bpf_table_max_entries(void *program, const char *table_name);
 size_t bpf_table_max_entries_id(void *program, size_t id);
+int bpf_table_flags(void *program, const char *table_name);
+int bpf_table_flags_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);
diff --git a/src/cc/bpf_module.cc b/src/cc/bpf_module.cc
index d9700b3f..be0a5242 100644
--- a/src/cc/bpf_module.cc
+++ b/src/cc/bpf_module.cc
@@ -542,6 +542,15 @@ size_t BPFModule::table_max_entries(size_t id) const {
   return (*tables_)[id].max_entries;
 }
 
+int BPFModule::table_flags(const string &name) const {
+  return table_flags(table_id(name));
+}
+
+int BPFModule::table_flags(size_t id) const {
+  if (id >= tables_->size()) return -1;
+  return (*tables_)[id].flags;
+}
+
 const char * BPFModule::table_name(size_t id) const {
   if (id >= tables_->size()) return nullptr;
   return (*tables_)[id].name.c_str();
diff --git a/src/cc/bpf_module.h b/src/cc/bpf_module.h
index 86fbdcf4..aca8633a 100644
--- a/src/cc/bpf_module.h
+++ b/src/cc/bpf_module.h
@@ -72,6 +72,8 @@ class BPFModule {
   int table_type(size_t id) const;
   size_t table_max_entries(const std::string &name) const;
   size_t table_max_entries(size_t id) const;
+  int table_flags(const std::string &name) const;
+  int table_flags(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;
diff --git a/src/cc/export/helpers.h b/src/cc/export/helpers.h
index 6530dd38..c1408513 100644
--- a/src/cc/export/helpers.h
+++ b/src/cc/export/helpers.h
@@ -38,7 +38,7 @@ R"********(
 #define SEC(NAME) __attribute__((section(NAME), used))
 
 // Changes to the macro require changes in BFrontendAction classes
-#define BPF_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries) \
+#define BPF_F_TABLE(_table_type, _key_type, _leaf_type, _name, _max_entries, _flags) \
 struct _name##_table_t { \
   _key_type key; \
   _leaf_type leaf; \
@@ -50,9 +50,13 @@ struct _name##_table_t { \
   void (*increment) (_key_type); \
   int (*get_stackid) (void *, u64); \
   _leaf_type data[_max_entries]; \
+  int flags; \
 }; \
 __attribute__((section("maps/" _table_type))) \
-struct _name##_table_t _name
+struct _name##_table_t _name = { .flags = (_flags) }
+
+#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 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) \
diff --git a/src/cc/frontends/b/codegen_llvm.cc b/src/cc/frontends/b/codegen_llvm.cc
index 3b4f36b6..410bfd6b 100644
--- a/src/cc/frontends/b/codegen_llvm.cc
+++ b/src/cc/frontends/b/codegen_llvm.cc
@@ -1100,7 +1100,7 @@ StatusTuple CodegenLLVM::visit_table_decl_stmt_node(TableDeclStmtNode *n) {
     decl_gvar->setSection("maps");
     tables_[n] = decl_gvar;
 
-    int map_fd = bpf_create_map(map_type, key->bit_width_ / 8, leaf->bit_width_ / 8, n->size_);
+    int map_fd = bpf_create_map(map_type, key->bit_width_ / 8, leaf->bit_width_ / 8, n->size_, 0);
     if (map_fd >= 0)
       table_fds_[n] = map_fd;
   } else {
@@ -1244,6 +1244,7 @@ StatusTuple CodegenLLVM::visit(Node* root, vector<TableDesc> &tables) {
       table.first->key_type_->bit_width_ >> 3,
       table.first->leaf_type_->bit_width_ >> 3,
       table.first->size_,
+      0,
       "", "",
     });
   }
diff --git a/src/cc/frontends/clang/b_frontend_action.cc b/src/cc/frontends/clang/b_frontend_action.cc
index 57d8454c..9370386f 100644
--- a/src/cc/frontends/clang/b_frontend_action.cc
+++ b/src/cc/frontends/clang/b_frontend_action.cc
@@ -599,13 +599,6 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
     }
     const RecordDecl *RD = R->getDecl()->getDefinition();
 
-    int major = 0, minor = 0;
-    struct utsname un;
-    if (uname(&un) == 0) {
-      // release format: <major>.<minor>.<revision>[-<othertag>]
-      sscanf(un.release, "%d.%d.", &major, &minor);
-    }
-
     TableDesc table = {};
     table.name = Decl->getName();
 
@@ -630,9 +623,18 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
         visitor.TraverseType(F->getType());
       } else if (F->getName() == "data") {
         table.max_entries = sz / table.leaf_size;
+      } else if (F->getName() == "flags") {
+        unsigned idx = F->getFieldIndex();
+        if (auto I = dyn_cast_or_null<InitListExpr>(Decl->getInit())) {
+          llvm::APSInt res;
+          if (I->getInit(idx)->EvaluateAsInt(res, C)) {
+            table.flags = res.getExtValue();
+          }
+        }
       }
       ++i;
     }
+
     bool is_extern = false;
     bpf_map_type map_type = BPF_MAP_TYPE_UNSPEC;
     if (A->getName() == "maps/hash") {
@@ -695,7 +697,7 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
       }
 
       table.type = map_type;
-      table.fd = bpf_create_map(map_type, table.key_size, table.leaf_size, table.max_entries);
+      table.fd = bpf_create_map(map_type, table.key_size, table.leaf_size, table.max_entries, table.flags);
     }
     if (table.fd < 0) {
       error(Decl->getLocStart(), "could not open bpf map: %0\nis %1 map type enabled in your kernel?") <<
diff --git a/src/cc/frontends/clang/loader.h b/src/cc/frontends/clang/loader.h
index 67b99698..5e7c490a 100644
--- a/src/cc/frontends/clang/loader.h
+++ b/src/cc/frontends/clang/loader.h
@@ -30,11 +30,6 @@ namespace ebpf {
 
 struct TableDesc;
 
-namespace cc {
-class Parser;
-class CodegenLLVM;
-}
-
 class ClangLoader {
  public:
   explicit ClangLoader(llvm::LLVMContext *ctx, unsigned flags);
diff --git a/src/cc/libbpf.c b/src/cc/libbpf.c
index 6df35d2e..24967b94 100644
--- a/src/cc/libbpf.c
+++ b/src/cc/libbpf.c
@@ -68,7 +68,7 @@ static __u64 ptr_to_u64(void *ptr)
   return (__u64) (unsigned long) ptr;
 }
 
-int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, int max_entries)
+int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, int max_entries, int map_flags)
 {
   union bpf_attr attr;
   memset(&attr, 0, sizeof(attr));
@@ -76,6 +76,7 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, int
   attr.key_size = key_size;
   attr.value_size = value_size;
   attr.max_entries = max_entries;
+  attr.map_flags = map_flags;
 
   int ret = syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
   if (ret < 0 && errno == EPERM) {
@@ -171,7 +172,7 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
   }
 
   ret = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
-   
+
   if (ret < 0 && errno == EPERM) {
     // When EPERM is returned, two reasons are possible:
     //  1. user has no permissions for bpf()
@@ -221,7 +222,7 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
 
     fprintf(stderr, "bpf: %s\n%s\n", strerror(errno), bpf_log_buffer);
 
-    free(bpf_log_buffer); 
+    free(bpf_log_buffer);
   }
   return ret;
 }
diff --git a/src/cc/libbpf.h b/src/cc/libbpf.h
index f55fa33c..cc4e0f37 100644
--- a/src/cc/libbpf.h
+++ b/src/cc/libbpf.h
@@ -25,7 +25,7 @@ extern "C" {
 #endif
 
 int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
-		   int max_entries);
+		   int max_entries, int map_flags);
 int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags);
 int bpf_lookup_elem(int fd, void *key, void *value);
 int bpf_delete_elem(int fd, void *key);
diff --git a/src/cc/table_desc.h b/src/cc/table_desc.h
index 2161c094..a5196e26 100644
--- a/src/cc/table_desc.h
+++ b/src/cc/table_desc.h
@@ -32,6 +32,7 @@ struct TableDesc {
   size_t key_size;  // sizes are in bytes
   size_t leaf_size;
   size_t max_entries;
+  int flags;
   std::string key_desc;
   std::string leaf_desc;
   llvm::Function *key_sscanf;
diff --git a/src/lua/bcc/libbcc.lua b/src/lua/bcc/libbcc.lua
index db356a86..26284450 100644
--- a/src/lua/bcc/libbcc.lua
+++ b/src/lua/bcc/libbcc.lua
@@ -24,7 +24,7 @@ enum bpf_prog_type {
   BPF_PROG_TYPE_SCHED_ACT,
 };
 
-int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, int max_entries);
+int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, int max_entries, int map_flags);
 int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags);
 int bpf_lookup_elem(int fd, void *key, void *value);
 int bpf_delete_elem(int fd, void *key);
@@ -72,6 +72,8 @@ int bpf_table_type(void *program, const char *table_name);
 int bpf_table_type_id(void *program, size_t id);
 size_t bpf_table_max_entries(void *program, const char *table_name);
 size_t bpf_table_max_entries_id(void *program, size_t id);
+int bpf_table_flags(void *program, const char *table_name);
+int bpf_table_flags_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);
diff --git a/src/python/bcc/libbcc.py b/src/python/bcc/libbcc.py
index 04563c1f..f03a1b73 100644
--- a/src/python/bcc/libbcc.py
+++ b/src/python/bcc/libbcc.py
@@ -47,6 +47,8 @@ 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_max_entries_id.restype = ct.c_ulonglong
 lib.bpf_table_max_entries_id.argtypes = [ct.c_void_p, ct.c_ulonglong]
+lib.bpf_table_flags_id.restype = ct.c_int
+lib.bpf_table_flags_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
diff --git a/src/python/bcc/table.py b/src/python/bcc/table.py
index 49bf2a2b..516cfa12 100644
--- a/src/python/bcc/table.py
+++ b/src/python/bcc/table.py
@@ -118,6 +118,7 @@ class TableBase(MutableMapping):
         self.Key = keytype
         self.Leaf = leaftype
         self.ttype = lib.bpf_table_type_id(self.bpf.module, self.map_id)
+        self.flags = lib.bpf_table_flags_id(self.bpf.module, self.map_id)
         self._cbs = {}
 
     def key_sprintf(self, key):
diff --git a/tests/python/test_flags.py b/tests/python/test_flags.py
new file mode 100644
index 00000000..a5d2b42d
--- /dev/null
+++ b/tests/python/test_flags.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+# Copyright (c) PLUMgrid, Inc.
+# Licensed under the Apache License, Version 2.0 (the "License")
+
+import unittest
+from bcc import BPF
+
+class TestLru(unittest.TestCase):
+    def test_lru_map_flags(self):
+        test_prog1 = """
+        BPF_F_TABLE("lru_hash", int, u64, lru, 1024, BPF_F_NO_COMMON_LRU);
+        """
+        b = BPF(text=test_prog1)
+        t = b["lru"]
+        self.assertEqual(t.flags, 2);
+
+    def test_hash_map_flags(self):
+        test_prog1 = """
+        BPF_F_TABLE("hash", int, u64, hash, 1024, BPF_F_NO_PREALLOC);
+        """
+        b = BPF(text=test_prog1)
+        t = b["hash"]
+        self.assertEqual(t.flags, 1);
+
+if __name__ == "__main__":
+    unittest.main()
-- 
2.30.9