Snap for 8426163 from 8c794ee2e5cdaeab0ee5f935865f0e8610b58e76 to mainline-tzdata2-release
Change-Id: Id6d1c5965819ec3154a66509f46233013d06eb4c
diff --git a/Android.bp b/Android.bp
index c8c1681..a1d6d9a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,20 +1,3 @@
-package {
- default_applicable_licenses: ["system_bpf_license"],
-}
-
-// Added automatically by a large-scale-change
-// http://go/android-license-faq
-license {
- name: "system_bpf_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- ],
- license_text: [
- "NOTICE",
- ],
-}
-
cc_defaults {
name: "bpf_defaults",
cflags: [
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/bpfloader/Android.bp b/bpfloader/Android.bp
index 7e7139f..0b4bbd4 100644
--- a/bpfloader/Android.bp
+++ b/bpfloader/Android.bp
@@ -17,14 +17,6 @@
//
// bpfLoad binary
//
-package {
- // http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // the below license kinds from "system_bpf_license":
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["system_bpf_license"],
-}
-
cc_binary {
name: "bpfloader",
@@ -36,7 +28,6 @@
],
sanitize: {
integer_overflow: true,
- memtag_heap: true,
},
clang: true,
shared_libs: [
diff --git a/bpfloader/BpfLoader.cpp b/bpfloader/BpfLoader.cpp
index 7a68894..e67c469 100644
--- a/bpfloader/BpfLoader.cpp
+++ b/bpfloader/BpfLoader.cpp
@@ -51,37 +51,22 @@
using android::base::EndsWith;
using std::string;
-struct {
- const char* const dir;
- const char* const prefix;
-} locations[] = {
- // Tethering mainline module
- {
- .dir = "/apex/com.android.tethering/etc/bpf/",
- .prefix = "tethering/",
- },
- // Core operating system
- {
- .dir = "/system/etc/bpf/",
- .prefix = "",
- },
-};
+#define BPF_PROG_PATH "/system/etc/bpf/"
-int loadAllElfObjects(const char* const progDir, const char* const prefix) {
+int loadAllElfObjects(void) {
int retVal = 0;
DIR* dir;
struct dirent* ent;
- if ((dir = opendir(progDir)) != NULL) {
+ if ((dir = opendir(BPF_PROG_PATH)) != NULL) {
while ((ent = readdir(dir)) != NULL) {
string s = ent->d_name;
if (!EndsWith(s, ".o")) continue;
- string progPath(progDir);
- progPath += s;
+ string progPath = BPF_PROG_PATH + s;
bool critical;
- int ret = android::bpf::loadProg(progPath.c_str(), &critical, prefix);
+ int ret = android::bpf::loadProg(progPath.c_str(), &critical);
if (ret) {
if (critical) retVal = ret;
ALOGE("Failed to load object: %s, ret: %s", progPath.c_str(), std::strerror(-ret));
@@ -94,36 +79,17 @@
return retVal;
}
-void createSysFsBpfSubDir(const char* const prefix) {
- if (*prefix) {
- mode_t prevUmask = umask(0);
-
- string s = "/sys/fs/bpf/";
- s += prefix;
-
- errno = 0;
- int ret = mkdir(s.c_str(), S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
- if (ret && errno != EEXIST) {
- ALOGW("Failed to create directory: %s, ret: %s", s.c_str(), std::strerror(errno));
- }
-
- umask(prevUmask);
- }
-}
-
int main() {
+ if (!android::bpf::isBpfSupported()) return 0;
+
// Load all ELF objects, create programs and maps, and pin them
- for (const auto location : locations) {
- createSysFsBpfSubDir(location.prefix);
- if (loadAllElfObjects(location.dir, location.prefix) != 0) {
- ALOGE("=== CRITICAL FAILURE LOADING BPF PROGRAMS FROM %s ===", location.dir);
- ALOGE("If this triggers reliably, you're probably missing kernel options or patches.");
- ALOGE("If this triggers randomly, you might be hitting some memory allocation "
- "problems or startup script race.");
- ALOGE("--- DO NOT EXPECT SYSTEM TO BOOT SUCCESSFULLY ---");
- sleep(20);
- return 2;
- }
+ if (loadAllElfObjects() != 0) {
+ ALOGE("=== CRITICAL FAILURE LOADING BPF PROGRAMS ===");
+ ALOGE("If this triggers reliably, you're probably missing kernel options or patches.");
+ ALOGE("If this triggers randomly, you might be hitting some memory allocation problems or "
+ "startup script race.");
+ ALOGE("--- DO NOT EXPECT SYSTEM TO BOOT SUCCESSFULLY ---");
+ return 2;
}
if (android::base::SetProperty("bpf.progs_loaded", "1") == false) {
diff --git a/bpfloader/bpfloader.rc b/bpfloader/bpfloader.rc
index 3c56c43..04d9b81 100644
--- a/bpfloader/bpfloader.rc
+++ b/bpfloader/bpfloader.rc
@@ -7,6 +7,9 @@
# - /sys/fs/bpf is already mounted,
# - apex (incl. rollback) is initialized (so that in the future we can load bpf
# programs shipped as part of apex mainline modules)
+# - system properties have been set, this is because isBpfSupported() calls
+# getUncachedBpfSupportLevel() which depends on
+# ro.kernel.ebpf.supported, ro.product.first_api_level & ro.build.version.sdk
# - logd is ready for us to log stuff
#
# At the same time we want to be as early as possible to reduce races and thus
@@ -20,10 +23,10 @@
write /proc/sys/net/core/bpf_jit_enable 1
# Enable JIT kallsyms export for privileged users only
write /proc/sys/net/core/bpf_jit_kallsyms 1
- exec_start bpfloader
+ start bpfloader
service bpfloader /system/bin/bpfloader
- capabilities CHOWN SYS_ADMIN NET_ADMIN
+ capabilities CHOWN SYS_ADMIN
#
# Set RLIMIT_MEMLOCK to 1GiB for bpfloader
#
diff --git a/libbpf_android/Android.bp b/libbpf_android/Android.bp
index 06528c8..cd2342e 100644
--- a/libbpf_android/Android.bp
+++ b/libbpf_android/Android.bp
@@ -14,35 +14,22 @@
// limitations under the License.
//
-package {
- // http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // the below license kinds from "system_bpf_license":
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["system_bpf_license"],
-}
-
cc_library_headers {
name: "libbpf_android_headers",
vendor_available: false,
host_supported: false,
- native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
linux_bionic: {
enabled: true,
},
},
- sdk_version: "30",
- header_libs: ["bpf_syscall_wrappers"],
- export_header_lib_headers: ["bpf_syscall_wrappers"],
}
cc_library {
name: "libbpf_android",
vendor_available: false,
host_supported: false,
- native_bridge_supported: true,
target: {
android: {
srcs: [
diff --git a/libbpf_android/BpfLoadTest.cpp b/libbpf_android/BpfLoadTest.cpp
index 09cd36c..b384c11 100644
--- a/libbpf_android/BpfLoadTest.cpp
+++ b/libbpf_android/BpfLoadTest.cpp
@@ -38,6 +38,8 @@
int mProgFd;
void SetUp() {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
unlink(tp_prog_path);
unlink(tp_map_path);
@@ -53,6 +55,8 @@
}
void TearDown() {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
close(mProgFd);
unlink(tp_prog_path);
unlink(tp_map_path);
@@ -84,6 +88,8 @@
};
TEST_F(BpfLoadTest, bpfCheckMap) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
checkMapNonZero();
}
diff --git a/libbpf_android/BpfMapTest.cpp b/libbpf_android/BpfMapTest.cpp
index d0737b0..91b9bf3 100644
--- a/libbpf_android/BpfMapTest.cpp
+++ b/libbpf_android/BpfMapTest.cpp
@@ -54,6 +54,8 @@
BpfMapTest() {}
void SetUp() {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
EXPECT_EQ(0, setrlimitForTest());
if (!access(PINNED_MAP_PATH, R_OK)) {
EXPECT_EQ(0, remove(PINNED_MAP_PATH));
@@ -61,6 +63,8 @@
}
void TearDown() {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
if (!access(PINNED_MAP_PATH, R_OK)) {
EXPECT_EQ(0, remove(PINNED_MAP_PATH));
}
@@ -103,6 +107,8 @@
};
TEST_F(BpfMapTest, constructor) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
BpfMap<uint32_t, uint32_t> testMap1;
checkMapInvalid(testMap1);
@@ -111,6 +117,8 @@
}
TEST_F(BpfMapTest, basicHelpers) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
uint32_t key = TEST_KEY1;
uint32_t value_write = TEST_VALUE1;
@@ -125,6 +133,8 @@
}
TEST_F(BpfMapTest, reset) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
uint32_t key = TEST_KEY1;
uint32_t value_write = TEST_VALUE1;
@@ -137,6 +147,8 @@
}
TEST_F(BpfMapTest, moveConstructor) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
BpfMap<uint32_t, uint32_t> testMap1(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
BpfMap<uint32_t, uint32_t> testMap2;
testMap2 = std::move(testMap1);
@@ -147,6 +159,8 @@
}
TEST_F(BpfMapTest, SetUpMap) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
EXPECT_NE(0, access(PINNED_MAP_PATH, R_OK));
BpfMap<uint32_t, uint32_t> testMap1(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
ASSERT_EQ(0, bpfFdPin(testMap1.getMap(), PINNED_MAP_PATH));
@@ -163,6 +177,8 @@
}
TEST_F(BpfMapTest, iterate) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
populateMap(TEST_MAP_SIZE, testMap);
int totalCount = 0;
@@ -181,6 +197,8 @@
}
TEST_F(BpfMapTest, iterateWithValue) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
populateMap(TEST_MAP_SIZE, testMap);
int totalCount = 0;
@@ -201,6 +219,8 @@
}
TEST_F(BpfMapTest, mapIsEmpty) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
expectMapEmpty(testMap);
uint32_t key = TEST_KEY1;
@@ -231,6 +251,8 @@
}
TEST_F(BpfMapTest, mapClear) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
populateMap(TEST_MAP_SIZE, testMap);
Result<bool> isEmpty = testMap.isEmpty();
diff --git a/libbpf_android/BpfUtils.cpp b/libbpf_android/BpfUtils.cpp
index 8689192..3b6c764 100644
--- a/libbpf_android/BpfUtils.cpp
+++ b/libbpf_android/BpfUtils.cpp
@@ -34,6 +34,7 @@
#include <sstream>
#include <string>
+#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include <log/log.h>
#include <processgroup/processgroup.h>
@@ -94,6 +95,8 @@
return res;
}
+#define KVER(a, b, c) ((a)*65536 + (b)*256 + (c))
+
unsigned kernelVersion() {
struct utsname buf;
int ret = uname(&buf);
@@ -110,5 +113,52 @@
return KVER(kver_major, kver_minor, kver_sub);
}
+std::string BpfLevelToString(BpfLevel bpfLevel) {
+ switch (bpfLevel) {
+ case BpfLevel::NONE:
+ return "None [pre-4.9 or pre-P]";
+ case BpfLevel::BASIC_4_9:
+ return "Basic [4.9 P+]";
+ case BpfLevel::EXTENDED_4_14:
+ return "Extended [4.14]";
+ case BpfLevel::EXTENDED_4_19:
+ return "Extended [4.19]";
+ case BpfLevel::EXTENDED_5_4:
+ return "Extended [5.4+]";
+ // No default statement. We want to see errors of the form:
+ // "enumeration value 'BPF_LEVEL_xxx' not handled in switch [-Werror,-Wswitch]".
+ }
+}
+
+static BpfLevel getUncachedBpfSupportLevel() {
+ unsigned kver = kernelVersion();
+
+ if (kver >= KVER(5, 4, 0)) return BpfLevel::EXTENDED_5_4;
+ if (kver >= KVER(4, 19, 0)) return BpfLevel::EXTENDED_4_19;
+ if (kver >= KVER(4, 14, 0)) return BpfLevel::EXTENDED_4_14;
+
+ // Override for devices launched with O but now on a 4.9-P+ kernel.
+ bool ebpf_supported = base::GetBoolProperty("ro.kernel.ebpf.supported", false);
+ if (ebpf_supported) return BpfLevel::BASIC_4_9;
+
+ uint64_t api_level = base::GetUintProperty<uint64_t>("ro.product.first_api_level", 0);
+ if (api_level == 0) {
+ ALOGE("Cannot determine initial API level of the device");
+ api_level = base::GetUintProperty<uint64_t>("ro.build.version.sdk", 0);
+ }
+
+ // Check if the device is shipped originally with android P.
+ if (api_level < MINIMUM_API_REQUIRED) return BpfLevel::NONE;
+
+ if (kver >= KVER(4, 9, 0)) return BpfLevel::BASIC_4_9;
+
+ return BpfLevel::NONE;
+}
+
+BpfLevel getBpfSupportLevel() {
+ static BpfLevel cache = getUncachedBpfSupportLevel();
+ return cache;
+}
+
} // namespace bpf
} // namespace android
diff --git a/libbpf_android/Loader.cpp b/libbpf_android/Loader.cpp
index 259068a..da6a109 100644
--- a/libbpf_android/Loader.cpp
+++ b/libbpf_android/Loader.cpp
@@ -28,12 +28,8 @@
#include <sys/utsname.h>
#include <unistd.h>
-// This is BpfLoader v0.2
-#define BPFLOADER_VERSION_MAJOR 0u
-#define BPFLOADER_VERSION_MINOR 2u
-#define BPFLOADER_VERSION ((BPFLOADER_VERSION_MAJOR << 16) | BPFLOADER_VERSION_MINOR)
-
#include "../progs/include/bpf_map_def.h"
+#include "LoaderUtils.h"
#include "bpf/BpfUtils.h"
#include "include/libbpf_android.h"
@@ -44,6 +40,7 @@
#include <string>
#include <vector>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -63,17 +60,6 @@
namespace android {
namespace bpf {
-static string pathToFilename(const string& path, bool noext = false) {
- vector<string> spath = android::base::Split(path, "/");
- string ret = spath.back();
-
- if (noext) {
- size_t lastindex = ret.find_last_of('.');
- return ret.substr(0, lastindex);
- }
- return ret;
-}
-
typedef struct {
const char* name;
enum bpf_prog_type type;
@@ -87,16 +73,15 @@
* is the name of the program, and tracepoint is the type.
*/
sectionType sectionNameTypes[] = {
- {"kprobe", BPF_PROG_TYPE_KPROBE},
- {"tracepoint", BPF_PROG_TYPE_TRACEPOINT},
- {"skfilter", BPF_PROG_TYPE_SOCKET_FILTER},
- {"cgroupskb", BPF_PROG_TYPE_CGROUP_SKB},
- {"schedcls", BPF_PROG_TYPE_SCHED_CLS},
- {"cgroupsock", BPF_PROG_TYPE_CGROUP_SOCK},
- {"xdp", BPF_PROG_TYPE_XDP},
+ {"kprobe", BPF_PROG_TYPE_KPROBE},
+ {"tracepoint", BPF_PROG_TYPE_TRACEPOINT},
+ {"skfilter", BPF_PROG_TYPE_SOCKET_FILTER},
+ {"cgroupskb", BPF_PROG_TYPE_CGROUP_SKB},
+ {"schedcls", BPF_PROG_TYPE_SCHED_CLS},
+ {"cgroupsock", BPF_PROG_TYPE_CGROUP_SOCK},
- /* End of table */
- {"END", BPF_PROG_TYPE_UNSPEC},
+ /* End of table */
+ {"END", BPF_PROG_TYPE_UNSPEC},
};
typedef struct {
@@ -140,8 +125,11 @@
/* Read a section by its index - for ex to get sec hdr strtab blob */
static int readSectionByIdx(ifstream& elfFile, int id, vector<char>& sec) {
vector<Elf64_Shdr> shTable;
- int ret = readSectionHeadersAll(elfFile, shTable);
+ int entries, ret = 0;
+
+ ret = readSectionHeadersAll(elfFile, shTable);
if (ret) return ret;
+ entries = shTable.size();
elfFile.seekg(shTable[id].sh_offset);
if (elfFile.fail()) return -1;
@@ -155,7 +143,9 @@
/* Read whole section header string table */
static int readSectionHeaderStrtab(ifstream& elfFile, vector<char>& strtab) {
Elf64_Ehdr eh;
- int ret = readElfHeader(elfFile, &eh);
+ int ret = 0;
+
+ ret = readElfHeader(elfFile, &eh);
if (ret) return ret;
ret = readSectionByIdx(elfFile, eh.e_shstrndx, strtab);
@@ -210,29 +200,6 @@
return -2;
}
-unsigned int readSectionUint(const char* name, ifstream& elfFile, unsigned int defVal) {
- vector<char> theBytes;
- int ret = readSectionByName(name, elfFile, theBytes);
- if (ret) {
- ALOGD("Couldn't find section %s (defaulting to %u [0x%x]).\n", name, defVal, defVal);
- return defVal;
- } else if (theBytes.size() < sizeof(unsigned int)) {
- ALOGE("Section %s too short (defaulting to %u [0x%x]).\n", name, defVal, defVal);
- return defVal;
- } else {
- // decode first 4 bytes as LE32 uint, there will likely be more bytes due to alignment.
- unsigned int value = static_cast<unsigned char>(theBytes[3]);
- value <<= 8;
- value += static_cast<unsigned char>(theBytes[2]);
- value <<= 8;
- value += static_cast<unsigned char>(theBytes[1]);
- value <<= 8;
- value += static_cast<unsigned char>(theBytes[0]);
- ALOGI("Section %s value is %u [0x%x]\n", name, value, value);
- return value;
- }
-}
-
static int readSectionByType(ifstream& elfFile, int type, vector<char>& data) {
int ret;
vector<Elf64_Shdr> shTable;
@@ -289,7 +256,7 @@
{
for (int i = 0; sectionNameTypes[i].type != BPF_PROG_TYPE_UNSPEC; i++)
if (sectionNameTypes[i].type == type)
- return string(sectionNameTypes[i].name);
+ return std::string(sectionNameTypes[i].name);
return NULL;
}
@@ -301,7 +268,7 @@
if (st.type != cs.type) continue;
- if (StartsWith(name, string(".rel") + st.name + "/"))
+ if (StartsWith(name, std::string(".rel") + st.name + "/"))
return true;
else
return false;
@@ -309,36 +276,14 @@
return false;
}
-static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd,
- size_t sizeOfBpfProgDef) {
+static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd) {
vector<char> pdData;
int ret = readSectionByName("progs", elfFile, pdData);
- // Older file formats do not require a 'progs' section at all.
- // (We should probably figure out whether this is behaviour which is safe to remove now.)
if (ret == -2) return 0;
if (ret) return ret;
- if (pdData.size() % sizeOfBpfProgDef) {
- ALOGE("readProgDefs failed due to improper sized progs section, %zu %% %zu != 0\n",
- pdData.size(), sizeOfBpfProgDef);
- return -1;
- };
-
- int progCount = pdData.size() / sizeOfBpfProgDef;
- pd.resize(progCount);
- size_t trimmedSize = std::min(sizeOfBpfProgDef, sizeof(struct bpf_prog_def));
-
- const char* dataPtr = pdData.data();
- for (auto& p : pd) {
- // First we zero initialize
- memset(&p, 0, sizeof(p));
- // Then we set non-zero defaults
- p.bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER; // v1.0
- // Then we copy over the structure prefix from the ELF file.
- memcpy(&p, dataPtr, trimmedSize);
- // Move to next struct in the ELF file
- dataPtr += sizeOfBpfProgDef;
- }
+ pd.resize(pdData.size() / sizeof(struct bpf_prog_def));
+ memcpy(pd.data(), pdData.data(), pdData.size());
return 0;
}
@@ -368,7 +313,7 @@
/* No section found with matching name*/
if (sec_idx == -1) {
- ALOGW("No %s section could be found in elf object\n", sectionName.c_str());
+ ALOGE("No %s section could be found in elf object\n", sectionName.c_str());
return -1;
}
@@ -385,7 +330,7 @@
}
/* Read a section by its index - for ex to get sec hdr strtab blob */
-static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs, size_t sizeOfBpfProgDef) {
+static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs) {
vector<Elf64_Shdr> shTable;
int entries, ret = 0;
@@ -394,7 +339,7 @@
entries = shTable.size();
vector<struct bpf_prog_def> pd;
- ret = readProgDefs(elfFile, pd, sizeOfBpfProgDef);
+ ret = readProgDefs(elfFile, pd);
if (ret) return ret;
vector<string> progDefNames;
ret = getSectionSymNames(elfFile, "progs", progDefNames);
@@ -411,10 +356,7 @@
enum bpf_prog_type ptype = getSectionType(name);
if (ptype != BPF_PROG_TYPE_UNSPEC) {
string oldName = name;
-
- // convert all slashes to underscores
- std::replace(name.begin(), name.end(), '/', '_');
-
+ deslash(name);
cs_temp.type = ptype;
cs_temp.name = name;
@@ -465,8 +407,7 @@
return getSymName(elfFile, symtab[index].st_name, name);
}
-static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
- const char* prefix, size_t sizeOfBpfMapDef) {
+static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds) {
int ret;
vector<char> mdData;
vector<struct bpf_map_def> md;
@@ -476,105 +417,31 @@
ret = readSectionByName("maps", elfFile, mdData);
if (ret == -2) return 0; // no maps to read
if (ret) return ret;
-
- if (mdData.size() % sizeOfBpfMapDef) {
- ALOGE("createMaps failed due to improper sized maps section, %zu %% %zu != 0\n",
- mdData.size(), sizeOfBpfMapDef);
- return -1;
- };
-
- int mapCount = mdData.size() / sizeOfBpfMapDef;
- md.resize(mapCount);
- size_t trimmedSize = std::min(sizeOfBpfMapDef, sizeof(struct bpf_map_def));
-
- const char* dataPtr = mdData.data();
- for (auto& m : md) {
- // First we zero initialize
- memset(&m, 0, sizeof(m));
- // Then we set non-zero defaults
- m.bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER; // v1.0
- m.max_kver = 0xFFFFFFFFu; // matches KVER_INF from bpf_helpers.h
- // Then we copy over the structure prefix from the ELF file.
- memcpy(&m, dataPtr, trimmedSize);
- // Move to next struct in the ELF file
- dataPtr += sizeOfBpfMapDef;
- }
+ md.resize(mdData.size() / sizeof(struct bpf_map_def));
+ memcpy(md.data(), mdData.data(), mdData.size());
ret = getSectionSymNames(elfFile, "maps", mapNames);
if (ret) return ret;
- unsigned kvers = kernelVersion();
-
for (int i = 0; i < (int)mapNames.size(); i++) {
- if (BPFLOADER_VERSION < md[i].bpfloader_min_ver) {
- ALOGI("skipping map %s which requires bpfloader min ver 0x%05x\n", mapNames[i].c_str(),
- md[i].bpfloader_min_ver);
- mapFds.push_back(unique_fd());
- continue;
- }
-
- if (BPFLOADER_VERSION >= md[i].bpfloader_max_ver) {
- ALOGI("skipping map %s which requires bpfloader max ver 0x%05x\n", mapNames[i].c_str(),
- md[i].bpfloader_max_ver);
- mapFds.push_back(unique_fd());
- continue;
- }
-
- if (kvers < md[i].min_kver) {
- ALOGI("skipping map %s which requires kernel version 0x%x >= 0x%x\n",
- mapNames[i].c_str(), kvers, md[i].min_kver);
- mapFds.push_back(unique_fd());
- continue;
- }
-
- if (kvers >= md[i].max_kver) {
- ALOGI("skipping map %s which requires kernel version 0x%x < 0x%x\n",
- mapNames[i].c_str(), kvers, md[i].max_kver);
- mapFds.push_back(unique_fd());
- continue;
- }
-
- // Format of pin location is /sys/fs/bpf/<prefix>map_<filename>_<mapname>
- string mapPinLoc =
- string(BPF_FS_PATH) + prefix + "map_" + fname + "_" + string(mapNames[i]);
- bool reuse = false;
unique_fd fd;
- int saved_errno;
+ // Format of pin location is /sys/fs/bpf/map_<filename>_<mapname>
+ string mapPinLoc;
+ bool reuse = false;
+ mapPinLoc = string(BPF_FS_PATH) + "map_" + fname + "_" + string(mapNames[i]);
if (access(mapPinLoc.c_str(), F_OK) == 0) {
fd.reset(bpf_obj_get(mapPinLoc.c_str()));
- saved_errno = errno;
ALOGD("bpf_create_map reusing map %s, ret: %d\n", mapNames[i].c_str(), fd.get());
reuse = true;
} else {
- enum bpf_map_type type = md[i].type;
- if (type == BPF_MAP_TYPE_DEVMAP && !isAtLeastKernelVersion(4, 14, 0)) {
- // On Linux Kernels older than 4.14 this map type doesn't exist, but it can kind
- // of be approximated: ARRAY has the same userspace api, though it is not usable
- // by the same ebpf programs. However, that's okay because the bpf_redirect_map()
- // helper doesn't exist on 4.9 anyway (so the bpf program would fail to load,
- // and thus needs to be tagged as 4.14+ either way), so there's nothing useful you
- // could do with a DEVMAP anyway (that isn't already provided by an ARRAY)...
- // Hence using an ARRAY instead of a DEVMAP simply makes life easier for userspace.
- type = BPF_MAP_TYPE_ARRAY;
- }
- if (type == BPF_MAP_TYPE_DEVMAP_HASH && !isAtLeastKernelVersion(5, 4, 0)) {
- // On Linux Kernels older than 5.4 this map type doesn't exist, but it can kind
- // of be approximated: HASH has the same userspace visible api.
- // However it cannot be used by ebpf programs in the same way.
- // Since bpf_redirect_map() only requires 4.14, a program using a DEVMAP_HASH map
- // would fail to load (due to trying to redirect to a HASH instead of DEVMAP_HASH).
- // One must thus tag any BPF_MAP_TYPE_DEVMAP_HASH + bpf_redirect_map() using
- // programs as being 5.4+...
- type = BPF_MAP_TYPE_HASH;
- }
- fd.reset(bpf_create_map(type, mapNames[i].c_str(), md[i].key_size, md[i].value_size,
+ fd.reset(bpf_create_map(md[i].type, mapNames[i].c_str(), md[i].key_size, md[i].value_size,
md[i].max_entries, md[i].map_flags));
- saved_errno = errno;
ALOGD("bpf_create_map name %s, ret: %d\n", mapNames[i].c_str(), fd.get());
}
- if (fd < 0) return -saved_errno;
+ if (fd < 0) return fd;
+ if (fd == 0) return -EINVAL;
if (!reuse) {
ret = bpf_obj_pin(fd, mapPinLoc.c_str());
@@ -664,8 +531,7 @@
}
}
-static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const string& license,
- const char* prefix) {
+static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const string& license) {
unsigned kvers = kernelVersion();
int ret, fd;
@@ -675,8 +541,6 @@
for (int i = 0; i < (int)cs.size(); i++) {
string name = cs[i].name;
- unsigned bpfMinVer = DEFAULT_BPFLOADER_MIN_VER; // v0.0
- unsigned bpfMaxVer = DEFAULT_BPFLOADER_MAX_VER; // v1.0
if (cs[i].prog_def.has_value()) {
unsigned min_kver = cs[i].prog_def->min_kver;
@@ -685,16 +549,8 @@
max_kver, kvers);
if (kvers < min_kver) continue;
if (kvers >= max_kver) continue;
-
- bpfMinVer = cs[i].prog_def->bpfloader_min_ver;
- bpfMaxVer = cs[i].prog_def->bpfloader_max_ver;
}
- ALOGD("cs[%d].name:%s requires bpfloader version [0x%05x,0x%05x)\n", i, name.c_str(),
- bpfMinVer, bpfMaxVer);
- if (BPFLOADER_VERSION < bpfMinVer) continue;
- if (BPFLOADER_VERSION >= bpfMaxVer) continue;
-
// strip any potential $foo suffix
// this can be used to provide duplicate programs
// conditionally loaded based on running kernel version
@@ -702,10 +558,8 @@
bool reuse = false;
// Format of pin location is
- // /sys/fs/bpf/<prefix>prog_<filename>_<mapname>
- string progPinLoc = BPF_FS_PATH;
- progPinLoc += prefix;
- progPinLoc += "prog_";
+ // /sys/fs/bpf/prog_<filename>_<mapname>
+ string progPinLoc = BPF_FS_PATH "prog_";
progPinLoc += fname;
progPinLoc += '_';
progPinLoc += name;
@@ -724,7 +578,7 @@
cs[i].name.c_str(), fd, (fd < 0 ? std::strerror(errno) : "no error"));
if (fd < 0) {
- vector<string> lines = android::base::Split(log_buf.data(), "\n");
+ std::vector<std::string> lines = android::base::Split(log_buf.data(), "\n");
ALOGW("bpf_prog_load - BEGIN log_buf contents:");
for (const auto& line : lines) ALOGW("%s", line.c_str());
@@ -759,7 +613,7 @@
return 0;
}
-int loadProg(const char* elfPath, bool* isCritical, const char* prefix) {
+int loadProg(const char* elfPath, bool* isCritical) {
vector<char> license;
vector<char> critical;
vector<codeSection> cs;
@@ -785,46 +639,7 @@
elfPath, (char*)license.data());
}
- // the following default values are for bpfloader V0.0 format which does not include them
- unsigned int bpfLoaderMinVer =
- readSectionUint("bpfloader_min_ver", elfFile, DEFAULT_BPFLOADER_MIN_VER);
- unsigned int bpfLoaderMaxVer =
- readSectionUint("bpfloader_max_ver", elfFile, DEFAULT_BPFLOADER_MAX_VER);
- size_t sizeOfBpfMapDef =
- readSectionUint("size_of_bpf_map_def", elfFile, DEFAULT_SIZEOF_BPF_MAP_DEF);
- size_t sizeOfBpfProgDef =
- readSectionUint("size_of_bpf_prog_def", elfFile, DEFAULT_SIZEOF_BPF_PROG_DEF);
-
- // inclusive lower bound check
- if (BPFLOADER_VERSION < bpfLoaderMinVer) {
- ALOGI("BpfLoader version 0x%05x ignoring ELF object %s with min ver 0x%05x\n",
- BPFLOADER_VERSION, elfPath, bpfLoaderMinVer);
- return 0;
- }
-
- // exclusive upper bound check
- if (BPFLOADER_VERSION >= bpfLoaderMaxVer) {
- ALOGI("BpfLoader version 0x%05x ignoring ELF object %s with max ver 0x%05x\n",
- BPFLOADER_VERSION, elfPath, bpfLoaderMaxVer);
- return 0;
- }
-
- ALOGI("BpfLoader version 0x%05x processing ELF object %s with ver [0x%05x,0x%05x)\n",
- BPFLOADER_VERSION, elfPath, bpfLoaderMinVer, bpfLoaderMaxVer);
-
- if (sizeOfBpfMapDef < DEFAULT_SIZEOF_BPF_MAP_DEF) {
- ALOGE("sizeof(bpf_map_def) of %zu is too small (< %d)\n", sizeOfBpfMapDef,
- DEFAULT_SIZEOF_BPF_MAP_DEF);
- return -1;
- }
-
- if (sizeOfBpfProgDef < DEFAULT_SIZEOF_BPF_PROG_DEF) {
- ALOGE("sizeof(bpf_prog_def) of %zu is too small (< %d)\n", sizeOfBpfProgDef,
- DEFAULT_SIZEOF_BPF_PROG_DEF);
- return -1;
- }
-
- ret = readCodeSections(elfFile, cs, sizeOfBpfProgDef);
+ ret = readCodeSections(elfFile, cs);
if (ret) {
ALOGE("Couldn't read all code sections in %s\n", elfPath);
return ret;
@@ -833,7 +648,7 @@
/* Just for future debugging */
if (0) dumpAllCs(cs);
- ret = createMaps(elfPath, elfFile, mapFds, prefix, sizeOfBpfMapDef);
+ ret = createMaps(elfPath, elfFile, mapFds);
if (ret) {
ALOGE("Failed to create maps: (ret=%d) in %s\n", ret, elfPath);
return ret;
@@ -844,11 +659,28 @@
applyMapRelo(elfFile, mapFds, cs);
- ret = loadCodeSections(elfPath, cs, string(license.data()), prefix);
+ ret = loadCodeSections(elfPath, cs, string(license.data()));
if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d\n", ret);
return ret;
}
+static bool waitSecondsForProgsLoaded(int seconds) {
+ bool ok =
+ android::base::WaitForProperty("bpf.progs_loaded", "1", std::chrono::seconds(seconds));
+ if (!ok) ALOGW("Waited %ds for bpf.progs_loaded, still waiting...", seconds);
+ return ok;
+}
+
+void waitForProgsLoaded() {
+ if (!android::bpf::isBpfSupported()) return;
+
+ if (waitSecondsForProgsLoaded(5)) return;
+ if (waitSecondsForProgsLoaded(10)) return;
+ if (waitSecondsForProgsLoaded(20)) return;
+ while (!waitSecondsForProgsLoaded(60))
+ ; // loop until success
+}
+
} // namespace bpf
} // namespace android
diff --git a/libbpf_android/LoaderUtils.h b/libbpf_android/LoaderUtils.h
new file mode 100644
index 0000000..a5ff7fc
--- /dev/null
+++ b/libbpf_android/LoaderUtils.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+
+static std::string pathToFilename(const std::string& path, bool noext = false) {
+ std::vector<std::string> spath = android::base::Split(path, "/");
+ std::string ret = spath.back();
+
+ if (noext) {
+ size_t lastindex = ret.find_last_of('.');
+ return ret.substr(0, lastindex);
+ }
+ return ret;
+}
+
+static void deslash(std::string& s) {
+ std::replace(s.begin(), s.end(), '/', '_');
+}
diff --git a/libbpf_android/include/bpf/BpfMap.h b/libbpf_android/include/bpf/BpfMap.h
index bdffc0f..3e7413e 100644
--- a/libbpf_android/include/bpf/BpfMap.h
+++ b/libbpf_android/include/bpf/BpfMap.h
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#pragma once
+#ifndef BPF_BPFMAP_H
+#define BPF_BPFMAP_H
#include <linux/bpf.h>
@@ -133,11 +134,10 @@
return *this;
}
- // Move assignment operator
- BpfMap<Key, Value>& operator=(BpfMap<Key, Value>&& other) noexcept {
+ // Move constructor
+ void operator=(BpfMap<Key, Value>&& other) noexcept {
mMapFd = std::move(other.mMapFd);
other.reset(-1);
- return *this;
}
void reset(base::unique_fd fd) = delete;
@@ -258,3 +258,5 @@
} // namespace bpf
} // namespace android
+
+#endif
diff --git a/libbpf_android/include/bpf/BpfUtils.h b/libbpf_android/include/bpf/BpfUtils.h
index 8fd2a4a..6814f94 100644
--- a/libbpf_android/include/bpf/BpfUtils.h
+++ b/libbpf_android/include/bpf/BpfUtils.h
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-#pragma once
+#ifndef BPF_BPFUTILS_H
+#define BPF_BPFUTILS_H
+#include <linux/bpf.h>
#include <linux/if_ether.h>
+#include <linux/unistd.h>
#include <net/if.h>
#include <stdlib.h>
#include <string.h>
@@ -24,60 +27,174 @@
#include <string>
-#include "BpfSyscallWrappers.h"
+#include "android-base/unique_fd.h"
+
+#define ptr_to_u64(x) ((uint64_t)(uintptr_t)(x))
namespace android {
namespace bpf {
+enum class BpfLevel {
+ // Devices shipped before P or kernel version is lower than 4.9 do not
+ // have eBPF enabled.
+ NONE,
+ // Devices shipped in P with android 4.9 kernel only have the basic eBPF
+ // functionalities such as xt_bpf and cgroup skb filter.
+ BASIC_4_9,
+ // For devices that have 4.14 kernel. It supports advanced features like
+ // map_in_map and cgroup socket filter.
+ EXTENDED_4_14,
+ EXTENDED_4_19,
+ EXTENDED_5_4,
+};
+
constexpr const int OVERFLOW_COUNTERSET = 2;
constexpr const uint64_t NONEXISTENT_COOKIE = 0;
+constexpr const int MINIMUM_API_REQUIRED = 28;
+
+/* Note: bpf_attr is a union which might have a much larger size then the anonymous struct portion
+ * of it that we are using. The kernel's bpf() system call will perform a strict check to ensure
+ * all unused portions are zero. It will fail with E2BIG if we don't fully zero bpf_attr.
+ */
+
+inline int bpf(int cmd, const bpf_attr& attr) {
+ return syscall(__NR_bpf, cmd, &attr, sizeof(attr));
+}
+
+inline int createMap(bpf_map_type map_type, uint32_t key_size, uint32_t value_size,
+ uint32_t max_entries, uint32_t map_flags) {
+ return bpf(BPF_MAP_CREATE, {
+ .map_type = map_type,
+ .key_size = key_size,
+ .value_size = value_size,
+ .max_entries = max_entries,
+ .map_flags = map_flags,
+ });
+}
+
+inline int writeToMapEntry(const base::unique_fd& map_fd, const void* key, const void* value,
+ uint64_t flags) {
+ return bpf(BPF_MAP_UPDATE_ELEM, {
+ .map_fd = static_cast<__u32>(map_fd.get()),
+ .key = ptr_to_u64(key),
+ .value = ptr_to_u64(value),
+ .flags = flags,
+ });
+}
+
+inline int findMapEntry(const base::unique_fd& map_fd, const void* key, void* value) {
+ return bpf(BPF_MAP_LOOKUP_ELEM, {
+ .map_fd = static_cast<__u32>(map_fd.get()),
+ .key = ptr_to_u64(key),
+ .value = ptr_to_u64(value),
+ });
+}
+
+inline int deleteMapEntry(const base::unique_fd& map_fd, const void* key) {
+ return bpf(BPF_MAP_DELETE_ELEM, {
+ .map_fd = static_cast<__u32>(map_fd.get()),
+ .key = ptr_to_u64(key),
+ });
+}
+
+inline int getNextMapKey(const base::unique_fd& map_fd, const void* key, void* next_key) {
+ return bpf(BPF_MAP_GET_NEXT_KEY, {
+ .map_fd = static_cast<__u32>(map_fd.get()),
+ .key = ptr_to_u64(key),
+ .next_key = ptr_to_u64(next_key),
+ });
+}
+
+inline int getFirstMapKey(const base::unique_fd& map_fd, void* firstKey) {
+ return getNextMapKey(map_fd, NULL, firstKey);
+}
+
+inline int bpfFdPin(const base::unique_fd& map_fd, const char* pathname) {
+ return bpf(BPF_OBJ_PIN, {
+ .pathname = ptr_to_u64(pathname),
+ .bpf_fd = static_cast<__u32>(map_fd.get()),
+ });
+}
+
+inline int bpfFdGet(const char* pathname, uint32_t flag) {
+ return bpf(BPF_OBJ_GET, {
+ .pathname = ptr_to_u64(pathname),
+ .file_flags = flag,
+ });
+}
+
+inline int mapRetrieve(const char* pathname, uint32_t flag) {
+ return bpfFdGet(pathname, flag);
+}
+
+inline int mapRetrieveRW(const char* pathname) {
+ return mapRetrieve(pathname, 0);
+}
+
+inline int mapRetrieveRO(const char* pathname) {
+ return mapRetrieve(pathname, BPF_F_RDONLY);
+}
+
+inline int mapRetrieveWO(const char* pathname) {
+ return mapRetrieve(pathname, BPF_F_WRONLY);
+}
+
+inline int retrieveProgram(const char* pathname) {
+ return bpfFdGet(pathname, BPF_F_RDONLY);
+}
+
+inline int attachProgram(bpf_attach_type type, const base::unique_fd& prog_fd,
+ const base::unique_fd& cg_fd) {
+ return bpf(BPF_PROG_ATTACH, {
+ .target_fd = static_cast<__u32>(cg_fd.get()),
+ .attach_bpf_fd = static_cast<__u32>(prog_fd.get()),
+ .attach_type = type,
+ });
+}
+
+inline int detachProgram(bpf_attach_type type, const base::unique_fd& cg_fd) {
+ return bpf(BPF_PROG_DETACH, {
+ .target_fd = static_cast<__u32>(cg_fd.get()),
+ .attach_type = type,
+ });
+}
+
uint64_t getSocketCookie(int sockFd);
int synchronizeKernelRCU();
int setrlimitForTest();
-
-#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
-
unsigned kernelVersion();
+std::string BpfLevelToString(BpfLevel BpfLevel);
+BpfLevel getBpfSupportLevel();
-static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
- return kernelVersion() >= KVER(major, minor, sub);
+inline bool isBpfSupported() {
+ return getBpfSupportLevel() != BpfLevel::NONE;
}
-#define SKIP_IF_BPF_SUPPORTED \
- do { \
- if (android::bpf::isAtLeastKernelVersion(4, 9, 0)) { \
- GTEST_LOG_(INFO) << "This test is skipped since bpf is supported\n"; \
- return; \
- } \
- } while (0)
-
#define SKIP_IF_BPF_NOT_SUPPORTED \
do { \
- if (!android::bpf::isAtLeastKernelVersion(4, 9, 0)) { \
- GTEST_LOG_(INFO) << "This test is skipped since bpf is not supported\n"; \
+ if (!android::bpf::isBpfSupported()) { \
+ GTEST_LOG_(INFO) << "This test is skipped since bpf is not available\n"; \
return; \
} \
} while (0)
-#define SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED \
- do { \
- if (!android::bpf::isAtLeastKernelVersion(4, 14, 0)) { \
- GTEST_LOG_(INFO) << "This test is skipped since extended bpf feature" \
- << "not supported\n"; \
- return; \
- } \
+#define SKIP_IF_BPF_SUPPORTED \
+ do { \
+ if (android::bpf::isBpfSupported()) return; \
} while (0)
-#define SKIP_IF_XDP_NOT_SUPPORTED \
- do { \
- if (!android::bpf::isAtLeastKernelVersion(5, 9, 0)) { \
- GTEST_LOG_(INFO) << "This test is skipped since xdp" \
- << "not supported\n"; \
- return; \
- } \
+#define SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED \
+ do { \
+ if (android::bpf::getBpfSupportLevel() < android::bpf::BpfLevel::EXTENDED_4_14) { \
+ GTEST_LOG_(INFO) << "This test is skipped since extended bpf feature" \
+ << "not supported\n"; \
+ return; \
+ } \
} while (0)
} // namespace bpf
} // namespace android
+
+#endif
diff --git a/libbpf_android/include/libbpf_android.h b/libbpf_android/include/libbpf_android.h
index 640f35b..3810d07 100644
--- a/libbpf_android/include/libbpf_android.h
+++ b/libbpf_android/include/libbpf_android.h
@@ -15,33 +15,22 @@
* limitations under the License.
*/
-#pragma once
+#ifndef LIBBPF_SYSTEM_H
+#define LIBBPF_SYSTEM_H
#include <libbpf.h>
#include <linux/bpf.h>
-#include <log/log.h>
-
-#include <android-base/properties.h>
namespace android {
namespace bpf {
// BPF loader implementation. Loads an eBPF ELF object
-int loadProg(const char* elfPath, bool* isCritical, const char* prefix = "");
-
-// Exposed for testing
-unsigned int readSectionUint(const char* name, std::ifstream& elfFile, unsigned int defVal);
+int loadProg(const char* elfPath, bool* isCritical);
// Wait for bpfloader to load BPF programs.
-static inline void waitForProgsLoaded() {
- // infinite loop until success with 5/10/20/40/60/60/60... delay
- for (int delay = 5;; delay *= 2) {
- if (delay > 60) delay = 60;
- if (android::base::WaitForProperty("bpf.progs_loaded", "1", std::chrono::seconds(delay)))
- return;
- ALOGW("Waited %ds for bpf.progs_loaded, still waiting...", delay);
- }
-}
+void waitForProgsLoaded();
} // namespace bpf
} // namespace android
+
+#endif
diff --git a/progs/Android.bp b/progs/Android.bp
index aeb04a7..37feafd 100644
--- a/progs/Android.bp
+++ b/progs/Android.bp
@@ -14,14 +14,6 @@
// limitations under the License.
//
-package {
- // http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // the below license kinds from "system_bpf_license":
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["system_bpf_license"],
-}
-
cc_library_headers {
name: "bpf_prog_headers",
export_include_dirs: ["include"],
diff --git a/progs/include/bpf_helpers.h b/progs/include/bpf_helpers.h
index abd19c6..f09ec83 100644
--- a/progs/include/bpf_helpers.h
+++ b/progs/include/bpf_helpers.h
@@ -6,47 +6,11 @@
#include "bpf_map_def.h"
-/******************************************************************************
- * WARNING: CHANGES TO THIS FILE OUTSIDE OF AOSP/MASTER ARE LIKELY TO BREAK *
- * DEVICE COMPATIBILITY WITH MAINLINE MODULES SHIPPING EBPF CODE. *
- * *
- * THIS WILL LIKELY RESULT IN BRICKED DEVICES AT SOME ARBITRARY FUTURE TIME *
- * *
- * THAT GOES ESPECIALLY FOR THE 'SEC' 'LICENSE' AND 'CRITICAL' MACRO DEFINES *
- * *
- * We strongly suggest that if you need changes to bpfloader functionality *
- * you get your changes reviewed and accepted into aosp/master. *
- * *
- ******************************************************************************/
-
/* place things in different elf sections */
#define SEC(NAME) __attribute__((section(NAME), used))
-/* Must be present in every program, example usage:
- * LICENSE("GPL"); or LICENSE("Apache 2.0");
- *
- * We also take this opportunity to embed a bunch of other useful values in
- * the resulting .o (This is to enable some limited forward compatibility
- * with mainline module shipped ebpf programs)
- *
- * The bpfloader_{min/max}_ver defines the [min, max) range of bpfloader
- * versions that should load this .o file (bpfloaders outside of this range
- * will simply ignore/skip this *entire* .o)
- * The [inclusive,exclusive) matches what we do for kernel ver dependencies.
- *
- * The size_of_bpf_{map,prog}_def allow the bpfloader to load programs where
- * these structures have been extended with additional fields (they will of
- * course simply be ignored then).
- *
- * If missing, bpfloader_{min/max}_ver default to 0/0x10000 ie. [v0.0, v1.0),
- * while size_of_bpf_{map/prog}_def default to 32/20 which are the v0.0 sizes.
- */
-#define LICENSE(NAME) \
- unsigned int _bpfloader_min_ver SEC("bpfloader_min_ver") = DEFAULT_BPFLOADER_MIN_VER; \
- unsigned int _bpfloader_max_ver SEC("bpfloader_max_ver") = DEFAULT_BPFLOADER_MAX_VER; \
- size_t _size_of_bpf_map_def SEC("size_of_bpf_map_def") = sizeof(struct bpf_map_def); \
- size_t _size_of_bpf_prog_def SEC("size_of_bpf_prog_def") = sizeof(struct bpf_prog_def); \
- char _license[] SEC("license") = (NAME)
+/* Example use: LICENSE("GPL"); or LICENSE("Apache 2.0"); */
+#define LICENSE(NAME) char _license[] SEC("license") = (NAME)
/* flag the resulting bpf .o file as critical to system functionality,
* loading all kernel version appropriate programs in it must succeed
@@ -59,10 +23,6 @@
* implemented in the kernel sources.
*/
-#define KVER_NONE 0
-#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
-#define KVER_INF 0xFFFFFFFFu
-
/* generic functions */
/*
@@ -87,18 +47,18 @@
* This will make sure that if you change the type of a map you'll get compile
* errors at any spots you forget to update with the new type.
*
- * Note: these all take pointers to const map because from the C/eBPF point of view
+ * Note: these all take 'const void* map' because from the C/eBPF point of view
* the map struct is really just a readonly map definition of the in kernel object.
* Runtime modification of the map defining struct is meaningless, since
* the contents is only ever used during bpf program loading & map creation
* by the bpf loader, and not by the eBPF program itself.
*/
-static void* (*bpf_map_lookup_elem_unsafe)(const struct bpf_map_def* map,
+static void* (*bpf_map_lookup_elem_unsafe)(const void* map,
const void* key) = (void*)BPF_FUNC_map_lookup_elem;
-static int (*bpf_map_update_elem_unsafe)(const struct bpf_map_def* map, const void* key,
- const void* value, unsigned long long flags) = (void*)
+static int (*bpf_map_update_elem_unsafe)(const void* map, const void* key, const void* value,
+ unsigned long long flags) = (void*)
BPF_FUNC_map_update_elem;
-static int (*bpf_map_delete_elem_unsafe)(const struct bpf_map_def* map,
+static int (*bpf_map_delete_elem_unsafe)(const void* map,
const void* key) = (void*)BPF_FUNC_map_delete_elem;
/* type safe macro to declare a map and related accessor functions */
@@ -108,14 +68,9 @@
.key_size = sizeof(TypeOfKey), \
.value_size = sizeof(TypeOfValue), \
.max_entries = (num_entries), \
- .map_flags = 0, \
.uid = (usr), \
.gid = (grp), \
.mode = (md), \
- .bpfloader_min_ver = DEFAULT_BPFLOADER_MIN_VER, \
- .bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER, \
- .min_kver = KVER_NONE, \
- .max_kver = KVER_INF, \
}; \
\
static inline __always_inline __unused TypeOfValue* bpf_##the_map##_lookup_elem( \
@@ -147,12 +102,17 @@
static int (*bpf_probe_read)(void* dst, int size, void* unsafe_ptr) = (void*) BPF_FUNC_probe_read;
static int (*bpf_probe_read_str)(void* dst, int size, void* unsafe_ptr) = (void*) BPF_FUNC_probe_read_str;
static unsigned long long (*bpf_ktime_get_ns)(void) = (void*) BPF_FUNC_ktime_get_ns;
-static unsigned long long (*bpf_ktime_get_boot_ns)(void) = (void*)BPF_FUNC_ktime_get_boot_ns;
+// TODO: change to BPF_FUNC_ktime_get_boot_ns in sc-mainline-prod.
+static unsigned long long (*bpf_ktime_get_boot_ns)(void) = (void*)125;
static int (*bpf_trace_printk)(const char* fmt, int fmt_size, ...) = (void*) BPF_FUNC_trace_printk;
static unsigned long long (*bpf_get_current_pid_tgid)(void) = (void*) BPF_FUNC_get_current_pid_tgid;
static unsigned long long (*bpf_get_current_uid_gid)(void) = (void*) BPF_FUNC_get_current_uid_gid;
static unsigned long long (*bpf_get_smp_processor_id)(void) = (void*) BPF_FUNC_get_smp_processor_id;
+#define KVER_NONE 0
+#define KVER(a, b, c) ((a)*65536 + (b)*256 + (c))
+#define KVER_INF 0xFFFFFFFF
+
#define DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv, \
opt) \
const struct bpf_prog_def SEC("progs") the_prog##_def = { \
@@ -161,8 +121,6 @@
.min_kver = (min_kv), \
.max_kver = (max_kv), \
.optional = (opt), \
- .bpfloader_min_ver = DEFAULT_BPFLOADER_MIN_VER, \
- .bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER, \
}; \
SEC(SECTION_NAME) \
int the_prog
diff --git a/progs/include/bpf_map_def.h b/progs/include/bpf_map_def.h
index 647c813..452b682 100644
--- a/progs/include/bpf_map_def.h
+++ b/progs/include/bpf_map_def.h
@@ -23,94 +23,9 @@
#include <linux/bpf.h>
// Pull in AID_* constants from //system/core/libcutils/include/private/android_filesystem_config.h
+#define EXCLUDE_FS_CONFIG_STRUCTURES
#include <private/android_filesystem_config.h>
-/******************************************************************************
- * *
- * ! ! ! W A R N I N G ! ! ! *
- * *
- * CHANGES TO THESE STRUCTURE DEFINITIONS OUTSIDE OF AOSP/MASTER *WILL* BREAK *
- * MAINLINE MODULE COMPATIBILITY *
- * *
- * AND THUS MAY RESULT IN YOUR DEVICE BRICKING AT SOME ARBITRARY POINT IN *
- * THE FUTURE *
- * *
- * (and even in aosp/master you may only append new fields at the very end, *
- * you may *never* delete fields, change their types, ordering, insert in *
- * the middle, etc. If a mainline module using the old definition has *
- * already shipped (which happens roughly monthly), then it's set in stone) *
- * *
- ******************************************************************************/
-
-// These are the values used if these fields are missing
-#define DEFAULT_BPFLOADER_MIN_VER 0u // v0.0 (this is inclusive ie. >= v0.0)
-#define DEFAULT_BPFLOADER_MAX_VER 0x10000u // v1.0 (this is exclusive ie. < v1.0)
-#define DEFAULT_SIZEOF_BPF_MAP_DEF 32 // v0.0 struct: enum (uint sized) + 7 uint
-#define DEFAULT_SIZEOF_BPF_PROG_DEF 20 // v0.0 struct: 4 uint + bool + 3 byte alignment pad
-
-/*
- * The bpf_{map,prog}_def structures are compiled for different architectures.
- * Once by the BPF compiler for the BPF architecture, and once by a C++
- * compiler for the native Android architecture for the bpfloader.
- *
- * For things to work, their layout must be the same between the two.
- * The BPF architecture is platform independent ('64-bit LSB bpf').
- * So this effectively means these structures must be the same layout
- * on 5 architectures, all of them little endian:
- * 64-bit BPF, x86_64, arm and 32-bit x86 and arm
- *
- * As such for any types we use inside of these structs we must make sure that
- * the size and alignment are the same, so the same amount of padding is used.
- *
- * Currently we only use: bool, enum bpf_map_type and unsigned int.
- * Additionally we use char for padding.
- *
- * !!! WARNING: HERE BE DRAGONS !!!
- *
- * Be particularly careful with 64-bit integers.
- * You will need to manually override their alignment to 8 bytes.
- *
- * To quote some parts of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69560
- *
- * Some types have weaker alignment requirements when they are structure members.
- *
- * unsigned long long on x86 is such a type.
- *
- * C distinguishes C11 _Alignof (the minimum alignment the type is guaranteed
- * to have in all contexts, so 4, see min_align_of_type) from GNU C __alignof
- * (the normal alignment of the type, so 8).
- *
- * alignof / _Alignof == minimum alignment required by target ABI
- * __alignof / __alignof__ == preferred alignment
- *
- * When in a struct, apparently the minimum alignment is used.
- */
-
-_Static_assert(sizeof(bool) == 1, "sizeof bool != 1");
-_Static_assert(__alignof__(bool) == 1, "__alignof__ bool != 1");
-_Static_assert(_Alignof(bool) == 1, "_Alignof bool != 1");
-
-_Static_assert(sizeof(char) == 1, "sizeof char != 1");
-_Static_assert(__alignof__(char) == 1, "__alignof__ char != 1");
-_Static_assert(_Alignof(char) == 1, "_Alignof char != 1");
-
-// This basically verifies that an enum is 'just' a 32-bit int
-_Static_assert(sizeof(enum bpf_map_type) == 4, "sizeof enum bpf_map_type != 4");
-_Static_assert(__alignof__(enum bpf_map_type) == 4, "__alignof__ enum bpf_map_type != 4");
-_Static_assert(_Alignof(enum bpf_map_type) == 4, "_Alignof enum bpf_map_type != 4");
-
-// Linux kernel requires sizeof(int) == 4, sizeof(void*) == sizeof(long), sizeof(long long) == 8
-_Static_assert(sizeof(unsigned int) == 4, "sizeof unsigned int != 4");
-_Static_assert(__alignof__(unsigned int) == 4, "__alignof__ unsigned int != 4");
-_Static_assert(_Alignof(unsigned int) == 4, "_Alignof unsigned int != 4");
-
-// We don't currently use any 64-bit types in these structs, so this is purely to document issue.
-// Here sizeof & __alignof__ are consistent, but _Alignof is not: compile for 'aosp_cf_x86_phone'
-_Static_assert(sizeof(unsigned long long) == 8, "sizeof unsigned long long != 8");
-_Static_assert(__alignof__(unsigned long long) == 8, "__alignof__ unsigned long long != 8");
-// BPF wants 8, but 32-bit x86 wants 4
-//_Static_assert(_Alignof(unsigned long long) == 8, "_Alignof unsigned long long != 8");
-
/*
* Map structure to be used by Android eBPF C programs. The Android eBPF loader
* uses this structure from eBPF object to create maps at boot time.
@@ -137,41 +52,14 @@
unsigned int uid; // uid_t
unsigned int gid; // gid_t
unsigned int mode; // mode_t
-
- // The following fields were added in version 0.1
- unsigned int bpfloader_min_ver; // if missing, defaults to 0, ie. v0.0
- unsigned int bpfloader_max_ver; // if missing, defaults to 0x10000, ie. v1.0
-
- // The following fields were added in version 0.2
- // kernelVersion() must be >= min_kver and < max_kver
- unsigned int min_kver;
- unsigned int max_kver;
};
-// This needs to be updated whenever the above structure definition is expanded.
-_Static_assert(sizeof(struct bpf_map_def) == 48, "sizeof struct bpf_map_def != 48");
-_Static_assert(__alignof__(struct bpf_map_def) == 4, "__alignof__ struct bpf_map_def != 4");
-_Static_assert(_Alignof(struct bpf_map_def) == 4, "_Alignof struct bpf_map_def != 4");
-
struct bpf_prog_def {
unsigned int uid;
unsigned int gid;
- // kernelVersion() must be >= min_kver and < max_kver
- unsigned int min_kver;
- unsigned int max_kver;
+ unsigned int min_kver; // KERNEL_MAJOR * 65536 + KERNEL_MINOR * 256 + KERNEL_SUB
+ unsigned int max_kver; // ie. 0x40900 for Linux 4.9 - but beware of hexadecimal for >= 10
- bool optional; // program section (ie. function) may fail to load, continue onto next func.
- char pad0[3];
-
- // The following fields were added in version 0.1
- unsigned int bpfloader_min_ver; // if missing, defaults to 0, ie. v0.0
- unsigned int bpfloader_max_ver; // if missing, defaults to 0x10000, ie. v1.0
-
- // No new fields in version 0.2
+ bool optional; // program section (ie. function) may fail to load, continue onto next func.
};
-
-// This needs to be updated whenever the above structure definition is expanded.
-_Static_assert(sizeof(struct bpf_prog_def) == 28, "sizeof struct bpf_prog_def != 28");
-_Static_assert(__alignof__(struct bpf_prog_def) == 4, "__alignof__ struct bpf_prog_def != 4");
-_Static_assert(_Alignof(struct bpf_prog_def) == 4, "_Alignof struct bpf_prog_def != 4");
diff --git a/progs/include/bpf_timeinstate.h b/progs/include/bpf_timeinstate.h
index 1065b6e..6cbc4e1 100644
--- a/progs/include/bpf_timeinstate.h
+++ b/progs/include/bpf_timeinstate.h
@@ -15,14 +15,9 @@
*/
#include <inttypes.h>
-#include <sys/types.h>
#define BPF_FS_PATH "/sys/fs/bpf/"
-// Number of frequencies tracked in the array with total time. If some CPUs have
-// more than 64 freqs
-// // available, the overflow is stored in the last entry.
-#define MAX_FREQS_FOR_TOTAL 64
// Number of frequencies for which a UID's times can be tracked in a single map entry. If some CPUs
// have more than 32 freqs available, a single UID is tracked using 2 or more entries.
#define FREQS_PER_ENTRY 32
@@ -48,27 +43,3 @@
uint32_t policy;
uint32_t freq;
} freq_idx_key_t;
-
-// Maximum number of processes whose thread CPU time-in-state can be tracked simultaneously.
-#define MAX_TRACKED_PIDS 8
-
-// Indicates that the pid_tracked_map item is unused and further items in the array are also
-// unused
-#define TRACKED_PID_STATE_UNUSED 0
-// Indicates that the pid_tracked_map item contains a PID that is currently tracked
-#define TRACKED_PID_STATE_ACTIVE 1
-// Indicates that the pid_tracked_map item is vacant, but further items in the array may
-// contain tracked PIDs
-#define TRACKED_PID_STATE_EXITED 2
-
-typedef struct {
- pid_t pid;
- // TRACKED_PID_STATE_UNUSED, TRACKED_PID_STATE_ACTIVE or TRACKED_PID_STATE_EXITED
- uint8_t state;
-} tracked_pid_t;
-
-typedef struct {
- pid_t tgid;
- uint16_t aggregation_key;
- uint16_t bucket;
-} aggregated_task_tis_key_t;