Merge "bpfloader: load map BTF via btfloader"

This commit is contained in:
Maciej Żenczykowski 2022-01-25 02:31:59 +00:00 committed by Gerrit Code Review
commit 102f93a5da
3 changed files with 119 additions and 3 deletions

View file

@ -55,6 +55,7 @@ cc_binary {
init_rc: ["bpfloader.rc"],
required: [
"btfloader",
"time_in_state.o"
],

View file

@ -85,6 +85,17 @@ class BpfLoadTest : public TestWithParam<std::string> {
EXPECT_RESULT_OK(m.iterateWithValue(iterFunc));
EXPECT_EQ(non_zero, 1);
}
void checkMapBtf() {
// Earlier kernels lack BPF_BTF_LOAD support
if (!isAtLeastKernelVersion(4, 19, 0)) GTEST_SKIP() << "pre-4.19 kernel does not support BTF";
const bool haveBtf = GetParam().find("btf") != std::string::npos;
std::string str;
EXPECT_EQ(android::base::ReadFileToString(mTpMapPath, &str), haveBtf);
if (haveBtf) EXPECT_FALSE(str.empty());
}
};
INSTANTIATE_TEST_SUITE_P(BpfLoadTests, BpfLoadTest,
@ -94,5 +105,9 @@ TEST_P(BpfLoadTest, bpfCheckMap) {
checkMapNonZero();
}
TEST_P(BpfLoadTest, bpfCheckBtf) {
checkMapBtf();
}
} // namespace bpf
} // namespace android

View file

@ -24,8 +24,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <unistd.h>
// This is BpfLoader v0.9
@ -37,13 +39,18 @@
#include "bpf/bpf_map_def.h"
#include "include/libbpf_android.h"
#include <bpf/bpf.h>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include <android-base/cmsg.h>
#include <android-base/file.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@ -472,12 +479,88 @@ static int getSymNameByIdx(ifstream& elfFile, int index, string& name) {
return getSymName(elfFile, symtab[index].st_name, name);
}
static bool waitpidTimeout(pid_t pid, int timeoutMs) {
// Add SIGCHLD to the signal set.
sigset_t child_mask, original_mask;
sigemptyset(&child_mask);
sigaddset(&child_mask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &child_mask, &original_mask) == -1) return false;
// Wait for a SIGCHLD notification.
errno = 0;
timespec ts = {0, timeoutMs * 1000000};
int wait_result = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, nullptr, &ts));
// Restore the original signal set.
sigprocmask(SIG_SETMASK, &original_mask, nullptr);
if (wait_result == -1) return false;
int status;
return TEMP_FAILURE_RETRY(waitpid(pid, &status, WNOHANG)) == pid;
}
static std::optional<unique_fd> getMapBtfInfo(const char* elfPath,
std::unordered_map<string, std::pair<uint32_t, uint32_t>> &btfTypeIds) {
unique_fd bpfloaderSocket, btfloaderSocket;
if (!android::base::Socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, &bpfloaderSocket,
&btfloaderSocket)) {
return {};
}
unique_fd pipeRead, pipeWrite;
if (!android::base::Pipe(&pipeRead, &pipeWrite, O_NONBLOCK)) {
return {};
}
pid_t pid = fork();
if (pid < 0) return {};
if (!pid) {
bpfloaderSocket.reset();
pipeRead.reset();
auto socketFdStr = std::to_string(btfloaderSocket.release());
auto pipeFdStr = std::to_string(pipeWrite.release());
if (execl("/system/bin/btfloader", "/system/bin/btfloader", socketFdStr.c_str(),
pipeFdStr.c_str(), elfPath, NULL) == -1) {
ALOGW("exec btfloader failed with errno %d (%s)\n", errno, strerror(errno));
exit(EX_UNAVAILABLE);
}
}
btfloaderSocket.reset();
pipeWrite.reset();
if (!waitpidTimeout(pid, 100)) {
kill(pid, SIGKILL);
return {};
}
unique_fd btfFd;
if (android::base::ReceiveFileDescriptors(bpfloaderSocket, nullptr, 0, &btfFd)) return {};
std::string btfTypeIdStr;
if (!android::base::ReadFdToString(pipeRead, &btfTypeIdStr)) return {};
if (btfFd.get() < 0) return {};
const auto mapTypeIdLines = android::base::Split(btfTypeIdStr, "\n");
for (const auto &line : mapTypeIdLines) {
const auto vec = android::base::Split(line, " ");
// Splitting on newline will give us one empty line
if (vec.size() != 3) continue;
const int kTid = atoi(vec[1].c_str());
const int vTid = atoi(vec[2].c_str());
if (!kTid || !vTid) return {};
btfTypeIds[vec[0]] = std::make_pair(kTid, vTid);
}
return btfFd;
}
static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
const char* prefix, size_t sizeOfBpfMapDef) {
int ret;
vector<char> mdData;
vector<char> mdData, btfData;
vector<struct bpf_map_def> md;
vector<string> mapNames;
std::unordered_map<string, std::pair<uint32_t, uint32_t>> btfTypeIdMap;
string fname = pathToFilename(string(elfPath), true);
ret = readSectionByName("maps", elfFile, mdData);
@ -510,6 +593,11 @@ static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>&
ret = getSectionSymNames(elfFile, "maps", mapNames);
if (ret) return ret;
std::optional<unique_fd> btfFd;
if (!readSectionByName(".BTF", elfFile, btfData)) {
btfFd = getMapBtfInfo(elfPath, btfTypeIdMap);
}
unsigned kvers = kernelVersion();
for (int i = 0; i < (int)mapNames.size(); i++) {
@ -575,8 +663,20 @@ static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>&
// programs as being 5.4+...
type = BPF_MAP_TYPE_HASH;
}
fd.reset(bcc_create_map(type, mapNames[i].c_str(), md[i].key_size, md[i].value_size,
md[i].max_entries, md[i].map_flags));
struct bpf_create_map_attr attr = {
.name = mapNames[i].c_str(),
.map_type = type,
.map_flags = md[i].map_flags,
.key_size = md[i].key_size,
.value_size = md[i].value_size,
.max_entries = md[i].max_entries,
};
if (btfFd.has_value() && btfTypeIdMap.find(mapNames[i]) != btfTypeIdMap.end()) {
attr.btf_fd = btfFd->get();
attr.btf_key_type_id = btfTypeIdMap.at(mapNames[i]).first;
attr.btf_value_type_id = btfTypeIdMap.at(mapNames[i]).second;
}
fd.reset(bcc_create_map_xattr(&attr, true));
saved_errno = errno;
ALOGD("bpf_create_map name %s, ret: %d\n", mapNames[i].c_str(), fd.get());
}