feature: support create new map and pin it to bpffs as file(BPF_TABLE_PINNED) (#3382)
authorzcy <zcy.chenyue.zhou@gmail.com>
Fri, 30 Apr 2021 04:36:53 +0000 (12:36 +0800)
committerGitHub <noreply@github.com>
Fri, 30 Apr 2021 04:36:53 +0000 (21:36 -0700)
Support create a new map and pin it if the pinned file is not available.

Co-authored-by: chenyue.zhou <chenyue.zhou@upai.com>
docs/reference_guide.md
src/cc/bpf_module.cc
src/cc/frontends/clang/b_frontend_action.cc
src/cc/frontends/clang/b_frontend_action.h
src/cc/libbpf.c
src/cc/libbpf.h
src/cc/table_storage.h
tests/cc/test_pinned_table.cc

index ceffe2b87d1f5b06965627f8e8e6c8ce84365d0a..a922479cea6a4e8d5ad2d52e4834e7a18fb1e665 100644 (file)
@@ -871,8 +871,9 @@ Examples in situ:
 
 #### Pinned Maps
 
-Maps that were pinned to the BPF filesystem can be accessed through an extended syntax: ```BPF_TABLE_PINNED(_table_type, _key_type, _leaf_type, _name, _max_entries, "/sys/fs/bpf/xyz")```
-The type information is not enforced and the actual map type depends on the map that got pinned to the location.
+Syntax: ```BPF_TABLE_PINNED(_table_type, _key_type, _leaf_type, _name, _max_entries, "/sys/fs/bpf/xyz")```
+
+Create a new map if it doesn't exist and pin it to the bpffs as a FILE, otherwise use the map that was pinned to the bpffs. The type information is not enforced and the actual map type depends on the map that got pinned to the location.
 
 For example:
 
index b0539d664ddb57903561b2db1a691bef9f21adae..007d6ad3ea1f3135ef90413660a1b846ca98d5a0 100644 (file)
@@ -287,8 +287,9 @@ int BPFModule::create_maps(std::map<std::string, std::pair<int, int>> &map_tids,
 
   for (auto map : fake_fd_map_) {
     int fd, fake_fd, map_type, key_size, value_size, max_entries, map_flags;
+    int pinned_id;
     const char *map_name;
-    unsigned int pinned_id;
+    const char *pinned;
     std::string inner_map_name;
     int inner_map_fd = 0;
 
@@ -317,26 +318,26 @@ int BPFModule::create_maps(std::map<std::string, std::pair<int, int>> &map_tids,
         inner_map_fd = inner_map_fds[inner_map_name];
     }
 
-    if (pinned_id) {
-        fd = bpf_map_get_fd_by_id(pinned_id);
-    } else {
-        struct bpf_create_map_attr attr = {};
-        attr.map_type = (enum bpf_map_type)map_type;
-        attr.name = map_name;
-        attr.key_size = key_size;
-        attr.value_size = value_size;
-        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();
-          attr.btf_key_type_id = map_tids[map_name].first;
-          attr.btf_value_type_id = map_tids[map_name].second;
-        }
+    if (pinned_id <= 0) {
+      struct bpf_create_map_attr attr = {};
+      attr.map_type = (enum bpf_map_type)map_type;
+      attr.name = map_name;
+      attr.key_size = key_size;
+      attr.value_size = value_size;
+      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();
+        attr.btf_key_type_id = map_tids[map_name].first;
+        attr.btf_value_type_id = map_tids[map_name].second;
+      }
 
-        fd = bcc_create_map_xattr(&attr, allow_rlimit_);
+      fd = bcc_create_map_xattr(&attr, allow_rlimit_);
+    } else {
+      fd = bpf_map_get_fd_by_id(pinned_id);
     }
 
     if (fd < 0) {
@@ -345,6 +346,15 @@ int BPFModule::create_maps(std::map<std::string, std::pair<int, int>> &map_tids,
       return -1;
     }
 
+    if (pinned_id == -1) {
+      pinned = get<8>(map.second).c_str();
+      if (bpf_obj_pin(fd, pinned)) {
+        fprintf(stderr, "failed to pin map: %s, error: %s\n",
+                pinned, strerror(errno));
+        return -1;
+      }
+    }
+
     if (for_inner_map)
       inner_map_fds[map_name] = fd;
 
index 9b3c24893e0b68d5caf375518109ba7abeea356f..1c67322650d706a21346468f8187eeb57a3996e9 100644 (file)
@@ -1392,24 +1392,36 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
       ++i;
     }
 
-    std::string section_attr = string(A->getName());
+    std::string section_attr = string(A->getName()), pinned;
     size_t pinned_path_pos = section_attr.find(":");
-    unsigned int pinned_id = 0; // 0 is not a valid map ID, they start with 1
+    // 0 is not a valid map ID, -1 is to create and pin it to file
+    int pinned_id = 0;
 
     if (pinned_path_pos != std::string::npos) {
-      std::string pinned = section_attr.substr(pinned_path_pos + 1);
+      pinned = section_attr.substr(pinned_path_pos + 1);
       section_attr = section_attr.substr(0, pinned_path_pos);
       int fd = bpf_obj_get(pinned.c_str());
-      struct bpf_map_info info = {};
-      unsigned int info_len = sizeof(info);
+      if (fd < 0) {
+        if (bcc_make_parent_dir(pinned.c_str()) ||
+            bcc_check_bpffs_path(pinned.c_str())) {
+          return false;
+        }
 
-      if (bpf_obj_get_info_by_fd(fd, &info, &info_len)) {
-        error(GET_BEGINLOC(Decl), "map not found: %0") << pinned;
-        return false;
+        pinned_id = -1;
+      } else {
+        struct bpf_map_info info = {};
+        unsigned int info_len = sizeof(info);
+
+        if (bpf_obj_get_info_by_fd(fd, &info, &info_len)) {
+          error(GET_BEGINLOC(Decl), "get map info failed: %0")
+                << strerror(errno);
+          return false;
+        }
+
+        pinned_id = info.id;
       }
 
       close(fd);
-      pinned_id = info.id;
     }
 
     // Additional map specific information
@@ -1535,7 +1547,7 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
       fe_.add_map_def(table.fake_fd, std::make_tuple((int)map_type, std::string(table.name),
                       (int)table.key_size, (int)table.leaf_size,
                       (int)table.max_entries, table.flags, pinned_id,
-                      inner_map_name));
+                      inner_map_name, pinned));
     }
 
     if (!table.is_extern)
index bdbbc365abf39ac391f951153dd0744548cf2c14..530d322a6e561c67fb90a10cd1859d1ea5ab495a 100644 (file)
@@ -177,7 +177,8 @@ class BFrontendAction : public clang::ASTFrontendAction {
   // negative fake_fd to be different from real fd in bpf_pseudo_fd.
   int get_next_fake_fd() { return next_fake_fd_--; }
   void add_map_def(int fd,
-    std::tuple<int, std::string, int, int, int, int, unsigned int, std::string> map_def) {
+    std::tuple<int, std::string, int, int, int, int, int, std::string,
+               std::string> map_def) {
     fake_fd_map_[fd] = move(map_def);
   }
 
index 7a838f12212032776814a4c6d832f7e8b5c83d8d..f7d1a14823566a091ac66c2560aac5ab014c716f 100644 (file)
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <libgen.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/vfs.h>
 #include <unistd.h>
 #include <linux/if_alg.h>
 
 
 #define PERF_UPROBE_REF_CTR_OFFSET_SHIFT 32
 
+#ifndef BPF_FS_MAGIC
+#define BPF_FS_MAGIC           0xcafe4a11
+#endif
+
 struct bpf_helper {
   char *name;
   char *required_version;
@@ -1526,3 +1532,49 @@ int bcc_iter_create(int link_fd)
 {
     return bpf_iter_create(link_fd);
 }
+
+int bcc_make_parent_dir(const char *path) {
+  int   err = 0;
+  char *dname, *dir;
+
+  dname = strdup(path);
+  if (dname == NULL)
+    return -ENOMEM;
+
+  dir = dirname(dname);
+  if (mkdir(dir, 0700) && errno != EEXIST)
+    err = -errno;
+
+  free(dname);
+  if (err)
+    fprintf(stderr, "failed to mkdir %s: %s\n", path, strerror(-err));
+
+  return err;
+}
+
+int bcc_check_bpffs_path(const char *path) {
+  struct statfs st_fs;
+  char  *dname, *dir;
+  int    err = 0;
+
+  if (path == NULL)
+    return -EINVAL;
+
+  dname = strdup(path);
+  if (dname == NULL)
+    return -ENOMEM;
+
+  dir = dirname(dname);
+  if (statfs(dir, &st_fs)) {
+    err = -errno;
+    fprintf(stderr, "failed to statfs %s: %s\n", path, strerror(-err));
+  }
+
+  free(dname);
+  if (!err && st_fs.f_type != BPF_FS_MAGIC) {
+    err = -EINVAL;
+    fprintf(stderr, "specified path %s is not on BPF FS\n", path);
+  }
+
+  return err;
+}
index 12cfd4a19611b1fe7a699ada798e122da4476821..657ed7c9728bb0a8f8e1b3677ecaa346606dfa79 100644 (file)
@@ -147,6 +147,8 @@ int bpf_obj_get_info_by_fd(int prog_fd, void *info, uint32_t *info_len);
 int bcc_iter_attach(int prog_fd, union bpf_iter_link_info *link_info,
                     uint32_t link_info_len);
 int bcc_iter_create(int link_fd);
+int bcc_make_parent_dir(const char *path);
+int bcc_check_bpffs_path(const char *path);
 
 #define LOG_BUF_SIZE 65536
 
index b83fbdd719364a2d848565db42f33bd952eee48c..11c0645027efb4bd1f48ec99c81b24670ab27e64 100644 (file)
@@ -27,7 +27,7 @@
 
 namespace ebpf {
 
-typedef std::map<int, std::tuple<int, std::string, int, int, int, int, unsigned int, std::string>>
+typedef std::map<int, std::tuple<int, std::string, int, int, int, int, int, std::string, std::string>>
         fake_fd_map_def;
 
 class TableStorageImpl;
index bd2ac5c6f26103549af8623a990602b64104bf2a..10df90d5e7bd00ad1acd45afd7f8eb0128b0c3b0 100644 (file)
@@ -67,7 +67,7 @@ TEST_CASE("test pinned table", "[pinned_table]") {
     REQUIRE(t[key] == value);
   }
 
-  // test failure
+  // test create if not exist
   {
     const std::string BPF_PROGRAM = R"(
       BPF_TABLE_PINNED("hash", u64, u64, ids, 1024, "/sys/fs/bpf/test_pinned_table");
@@ -76,7 +76,8 @@ TEST_CASE("test pinned table", "[pinned_table]") {
     ebpf::BPF bpf;
     ebpf::StatusTuple res(0);
     res = bpf.init(BPF_PROGRAM);
-    REQUIRE(res.code() != 0);
+    REQUIRE(res.code() == 0);
+    unlink("/sys/fs/bpf/test_pinned_table");
   }
 
   if (mounted) {