implement support for functions which may optionally fail to load
This is useful for critical functions with fallbacks, but may even be useful for non-critical functions, where a function in the middle of the file may fail to load, but you still want other (later) functions to be attempted. Critical applies to the entire .c file (or to be more correct to the entire resulting .o). Optional applies to a specific section of that .o (ie. a specific individual function). This new optional attribute is necessary to be able to declare a .c/.o file critical even if *some* of the individual functions might fail to load due to missing kernel patches. (Note: we currently have no way to specify a map as optional) Critical guarantees that all non-optional programs, and all maps, have been created, pinned, chowned, and chmoded successfully (or that they already existed). For an example of use see: system/netd/bpf_progs/offload.c (while at it also add retrieveProgram() and mapRetrieve{RW,RO,WO}() helpers to BpfUtils.h) Test: builds, atest, see paired netd change for extra details Bug: 150040815 Signed-off-by: Maciej Żenczykowski <maze@google.com> Original-Change: https://android-review.googlesource.com/1336225 Merged-In: I50b292c061b05fc8f4b4b8574f128345c45c78db Change-Id: I50b292c061b05fc8f4b4b8574f128345c45c78db
This commit is contained in:
parent
45f5ffd167
commit
2464ddb0df
5 changed files with 69 additions and 18 deletions
|
@ -564,8 +564,9 @@ static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const
|
|||
progPinLoc += '_';
|
||||
progPinLoc += name;
|
||||
if (access(progPinLoc.c_str(), F_OK) == 0) {
|
||||
fd = bpf_obj_get(progPinLoc.c_str());
|
||||
ALOGD("New bpf prog load reusing prog %s, ret: %d\n", progPinLoc.c_str(), fd);
|
||||
fd = retrieveProgram(progPinLoc.c_str());
|
||||
ALOGD("New bpf prog load reusing prog %s, ret: %d (%s)\n", progPinLoc.c_str(), fd,
|
||||
(fd < 0 ? std::strerror(errno) : "no error"));
|
||||
reuse = true;
|
||||
} else {
|
||||
vector<char> log_buf(BPF_LOAD_LOG_SZ, 0);
|
||||
|
@ -579,9 +580,15 @@ static int loadCodeSections(const char* elfPath, vector<codeSection>& cs, const
|
|||
if (fd < 0) {
|
||||
std::vector<std::string> lines = android::base::Split(log_buf.data(), "\n");
|
||||
|
||||
ALOGE("bpf_prog_load - BEGIN log_buf contents:");
|
||||
for (const auto& line : lines) ALOGE("%s", line.c_str());
|
||||
ALOGE("bpf_prog_load - END log_buf contents.");
|
||||
ALOGW("bpf_prog_load - BEGIN log_buf contents:");
|
||||
for (const auto& line : lines) ALOGW("%s", line.c_str());
|
||||
ALOGW("bpf_prog_load - END log_buf contents.");
|
||||
|
||||
if (cs[i].prog_def->optional) {
|
||||
ALOGW("failed program is marked optional - continuing...");
|
||||
continue;
|
||||
}
|
||||
ALOGE("non-optional program failed to load.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -179,7 +179,7 @@ class BpfMap {
|
|||
|
||||
template <class Key, class Value>
|
||||
base::Result<void> BpfMap<Key, Value>::init(const char* path) {
|
||||
mMapFd = base::unique_fd(mapRetrieve(path, 0));
|
||||
mMapFd = base::unique_fd(mapRetrieveRW(path));
|
||||
if (mMapFd == -1) {
|
||||
return ErrnoErrorf("Pinned map not accessible or does not exist: ({})", path);
|
||||
}
|
||||
|
|
|
@ -129,6 +129,22 @@ 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, {
|
||||
|
|
|
@ -111,21 +111,47 @@ static unsigned long long (*bpf_get_smp_processor_id)(void) = (void*) BPF_FUNC_g
|
|||
#define KVER(a, b, c) ((a)*65536 + (b)*256 + (c))
|
||||
#define KVER_INF 0xFFFFFFFF
|
||||
|
||||
// programs requiring a kernel version >= min_kv && < max_kv
|
||||
#define DEFINE_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv) \
|
||||
const struct bpf_prog_def SEC("progs") the_prog##_def = { \
|
||||
.uid = (prog_uid), \
|
||||
.gid = (prog_gid), \
|
||||
.min_kver = (min_kv), \
|
||||
.max_kver = (max_kv), \
|
||||
}; \
|
||||
SEC(SECTION_NAME) \
|
||||
#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 = { \
|
||||
.uid = (prog_uid), \
|
||||
.gid = (prog_gid), \
|
||||
.min_kver = (min_kv), \
|
||||
.max_kver = (max_kv), \
|
||||
.optional = (opt), \
|
||||
}; \
|
||||
SEC(SECTION_NAME) \
|
||||
int the_prog
|
||||
|
||||
// Programs (here used in the sense of functions/sections) marked optional are allowed to fail
|
||||
// to load (for example due to missing kernel patches).
|
||||
// The bpfloader will just ignore these failures and continue processing the next section.
|
||||
//
|
||||
// A non-optional program (function/section) failing to load causes a failure and aborts
|
||||
// processing of the entire .o, if the .o is additionally marked critical, this will result
|
||||
// in the entire bpfloader process terminating with a failure and not setting the bpf.progs_loaded
|
||||
// system property. This in turn results in waitForProgsLoaded() never finishing.
|
||||
//
|
||||
// ie. a non-optional program in a critical .o is mandatory for kernels matching the min/max kver.
|
||||
|
||||
// programs requiring a kernel version >= min_kv && < max_kv
|
||||
#define DEFINE_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv) \
|
||||
DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv, \
|
||||
false)
|
||||
#define DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, \
|
||||
max_kv) \
|
||||
DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv, true)
|
||||
|
||||
// programs requiring a kernel version >= min_kv
|
||||
#define DEFINE_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv) \
|
||||
DEFINE_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, KVER_INF)
|
||||
#define DEFINE_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv) \
|
||||
DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, KVER_INF, \
|
||||
false)
|
||||
#define DEFINE_OPTIONAL_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv) \
|
||||
DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, KVER_INF, \
|
||||
true)
|
||||
|
||||
// programs with no kernel version requirements
|
||||
#define DEFINE_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
|
||||
DEFINE_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, 0, KVER_INF)
|
||||
DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, 0, KVER_INF, false)
|
||||
#define DEFINE_OPTIONAL_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
|
||||
DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, 0, KVER_INF, true)
|
||||
|
|
|
@ -60,4 +60,6 @@ struct bpf_prog_def {
|
|||
|
||||
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.
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue