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:
Maciej Żenczykowski 2020-06-17 17:21:06 +00:00 committed by Maciej Zenczykowski
parent 45f5ffd167
commit 2464ddb0df
5 changed files with 69 additions and 18 deletions

View file

@ -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.");
}
}

View file

@ -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);
}

View file

@ -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, {

View file

@ -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) \
#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_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)

View file

@ -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.
};