Merge "bpfloader: ignore_on_(arch) support"
diff --git a/staticlibs/native/bpf_headers/Android.bp b/staticlibs/native/bpf_headers/Android.bp
index 31adef9..41184ea 100644
--- a/staticlibs/native/bpf_headers/Android.bp
+++ b/staticlibs/native/bpf_headers/Android.bp
@@ -33,8 +33,10 @@
min_sdk_version: "30",
apex_available: [
"//apex_available:platform",
- "com.android.tethering",
"com.android.art.debug",
+ "com.android.os.statsd",
+ "com.android.resolv",
+ "com.android.tethering",
],
}
diff --git a/staticlibs/native/bpf_headers/BpfRingbufTest.cpp b/staticlibs/native/bpf_headers/BpfRingbufTest.cpp
index d23afae..6c0841c 100644
--- a/staticlibs/native/bpf_headers/BpfRingbufTest.cpp
+++ b/staticlibs/native/bpf_headers/BpfRingbufTest.cpp
@@ -25,6 +25,7 @@
#include "BpfSyscallWrappers.h"
#include "bpf/BpfRingbuf.h"
#include "bpf/BpfUtils.h"
+#include "bpf/KernelUtils.h"
#define TEST_RINGBUF_MAGIC_NUM 12345
@@ -49,10 +50,6 @@
GTEST_SKIP() << "BPF ring buffers not supported below 5.8";
}
- if (sizeof(unsigned long) != 8) {
- GTEST_SKIP() << "BPF ring buffers not supported on 32 bit arch";
- }
-
errno = 0;
mProgram.reset(retrieveProgram(mProgPath.c_str()));
EXPECT_EQ(errno, 0);
diff --git a/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h b/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
index cac1e43..dd1504c 100644
--- a/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
+++ b/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
@@ -24,6 +24,8 @@
#include "bpf/BpfUtils.h"
+#include <atomic>
+
namespace android {
namespace bpf {
@@ -80,8 +82,28 @@
android::base::unique_fd mRingFd;
void* mDataPos = nullptr;
- unsigned long* mConsumerPos = nullptr;
- unsigned long* mProducerPos = nullptr;
+ // The kernel uses an "unsigned long" type for both consumer and producer position.
+ // Unsigned long is a 4 byte value on a 32-bit kernel, and an 8 byte value on a 64-bit kernel.
+ // To support 32-bit kernels, producer pos is capped at 4 bytes (despite it being 8 bytes on
+ // 64-bit kernels) and all comparisons of consumer and producer pos only compare the low-order 4
+ // bytes (an inequality comparison is performed to support overflow).
+ // This solution is bitness agnostic. The consumer only increments the 8 byte consumer pos, which,
+ // in a little-endian architecture, is safe since the entire page is mapped into memory and a
+ // 32-bit kernel will just ignore the high-order bits.
+ std::atomic_uint64_t* mConsumerPos = nullptr;
+ std::atomic_uint32_t* mProducerPos = nullptr;
+
+ // In order to guarantee atomic access in a 32 bit userspace environment, atomic_uint64_t is used
+ // in addition to std::atomic<T>::is_always_lock_free that guarantees that read / write operations
+ // are indeed atomic.
+ // Since std::atomic does not support wrapping preallocated memory, an additional static assert on
+ // the size of the atomic and the underlying type is added to ensure a reinterpret_cast from type
+ // to its atomic version is safe (is_always_lock_free being true should provide additional
+ // confidence).
+ static_assert(std::atomic_uint64_t::is_always_lock_free);
+ static_assert(std::atomic_uint32_t::is_always_lock_free);
+ static_assert(sizeof(std::atomic_uint64_t) == sizeof(uint64_t));
+ static_assert(sizeof(std::atomic_uint32_t) == sizeof(uint32_t));
};
// This is a class wrapper for eBPF ring buffers. An eBPF ring buffer is a
@@ -121,34 +143,8 @@
BpfRingbuf() : BpfRingbufBase(sizeof(Value)) {}
};
-#define ACCESS_ONCE(x) (*(volatile typeof(x)*)&(x))
-
-#if defined(__i386__) || defined(__x86_64__)
-#define smp_sync() asm volatile("" ::: "memory")
-#elif defined(__aarch64__)
-#define smp_sync() asm volatile("dmb ish" ::: "memory")
-#else
-#define smp_sync() __sync_synchronize()
-#endif
-
-#define smp_store_release(p, v) \
- do { \
- smp_sync(); \
- ACCESS_ONCE(*(p)) = (v); \
- } while (0)
-
-#define smp_load_acquire(p) \
- ({ \
- auto ___p = ACCESS_ONCE(*(p)); \
- smp_sync(); \
- ___p; \
- })
inline base::Result<void> BpfRingbufBase::Init(const char* path) {
- if (sizeof(unsigned long) != 8) {
- return android::base::Error()
- << "BpfRingbuf does not support 32 bit architectures";
- }
mRingFd.reset(mapRetrieveRW(path));
if (!mRingFd.ok()) {
return android::base::ErrnoError()
@@ -184,7 +180,7 @@
return android::base::ErrnoError()
<< "failed to mmap ringbuf consumer pages";
}
- mConsumerPos = reinterpret_cast<unsigned long*>(ptr);
+ mConsumerPos = reinterpret_cast<decltype(mConsumerPos)>(ptr);
}
{
@@ -194,7 +190,7 @@
return android::base::ErrnoError()
<< "failed to mmap ringbuf producer page";
}
- mProducerPos = reinterpret_cast<unsigned long*>(ptr);
+ mProducerPos = reinterpret_cast<decltype(mProducerPos)>(ptr);
}
mDataPos = pointerAddBytes<void*>(mProducerPos, getpagesize());
@@ -204,14 +200,19 @@
inline base::Result<int> BpfRingbufBase::ConsumeAll(
const std::function<void(const void*)>& callback) {
int64_t count = 0;
- unsigned long cons_pos = smp_load_acquire(mConsumerPos);
- unsigned long prod_pos = smp_load_acquire(mProducerPos);
- while (cons_pos < prod_pos) {
+ uint32_t prod_pos = mProducerPos->load(std::memory_order_acquire);
+ // Only userspace writes to mConsumerPos, so no need to use std::memory_order_acquire
+ uint64_t cons_pos = mConsumerPos->load(std::memory_order_relaxed);
+ while ((cons_pos & 0xFFFFFFFF) != prod_pos) {
// Find the start of the entry for this read (wrapping is done here).
void* start_ptr = pointerAddBytes<void*>(mDataPos, cons_pos & mPosMask);
// The entry has an 8 byte header containing the sample length.
- uint32_t length = smp_load_acquire(reinterpret_cast<uint32_t*>(start_ptr));
+ // struct bpf_ringbuf_hdr {
+ // u32 len;
+ // u32 pg_off;
+ // };
+ uint32_t length = *reinterpret_cast<volatile uint32_t*>(start_ptr);
// If the sample isn't committed, we're caught up with the producer.
if (length & BPF_RINGBUF_BUSY_BIT) return count;
@@ -220,7 +221,7 @@
if ((length & BPF_RINGBUF_DISCARD_BIT) == 0) {
if (length != mValueSize) {
- smp_store_release(mConsumerPos, cons_pos);
+ mConsumerPos->store(cons_pos, std::memory_order_release);
errno = EMSGSIZE;
return android::base::ErrnoError()
<< "BPF ring buffer message has unexpected size (want "
@@ -230,7 +231,7 @@
count++;
}
- smp_store_release(mConsumerPos, cons_pos);
+ mConsumerPos->store(cons_pos, std::memory_order_release);
}
return count;
@@ -252,10 +253,5 @@
});
}
-#undef ACCESS_ONCE
-#undef smp_sync
-#undef smp_store_release
-#undef smp_load_acquire
-
} // namespace bpf
} // namespace android
diff --git a/staticlibs/native/bpf_headers/include/bpf/KernelUtils.h b/staticlibs/native/bpf_headers/include/bpf/KernelUtils.h
index 3f82deb..b3dd86c 100644
--- a/staticlibs/native/bpf_headers/include/bpf/KernelUtils.h
+++ b/staticlibs/native/bpf_headers/include/bpf/KernelUtils.h
@@ -48,12 +48,26 @@
// Figure out the bitness of userspace.
// Trivial and known at compile time.
-static constexpr bool isUserspace64bit() {
- return sizeof(long) == 8;
+static constexpr bool isUserspace32bit() {
+ return sizeof(void*) == 4;
}
+static constexpr bool isUserspace64bit() {
+ return sizeof(void*) == 8;
+}
+
+#if defined(__LP64__)
+static_assert(isUserspace64bit(), "huh?");
+#elif defined(__ILP32__)
+static_assert(isUserspace32bit(), "huh?");
+#else
+#error "huh?"
+#endif
+
+static_assert(isUserspace32bit() || isUserspace64bit(), "must be either 32 or 64 bit");
+
// Figure out the bitness of the kernel.
-static inline __unused bool isKernel64Bit() {
+static inline bool isKernel64Bit() {
// a 64-bit userspace requires a 64-bit kernel
if (isUserspace64bit()) return true;
@@ -100,5 +114,52 @@
return cache;
}
+static inline __unused bool isKernel32Bit() {
+ return !isKernel64Bit();
+}
+
+static constexpr bool isArm() {
+#if defined(__arm__) || defined(__aarch64__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+static constexpr bool isX86() {
+#if defined(__i386__) || defined(__x86_64__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+static constexpr bool isRiscV() {
+#if defined(__riscv)
+ static_assert(isUserspace64bit(), "riscv must be 64 bit");
+ return true;
+#else
+ return false;
+#endif
+}
+
+static_assert(isArm() || isX86() || isRiscV(), "Unknown architecture");
+
+static __unused const char * describeArch() {
+ // ordered so as to make it easier to compile time optimize,
+ // only thing not known at compile time is isKernel64Bit()
+ if (isUserspace64bit()) {
+ if (isArm()) return "64-on-aarch64";
+ if (isX86()) return "64-on-x86-64";
+ if (isRiscV()) return "64-on-riscv64";
+ } else if (isKernel64Bit()) {
+ if (isArm()) return "32-on-aarch64";
+ if (isX86()) return "32-on-x86-64";
+ } else {
+ if (isArm()) return "32-on-arm32";
+ if (isX86()) return "32-on-x86-32";
+ }
+}
+
} // namespace bpf
} // namespace android
diff --git a/staticlibs/native/bpf_syscall_wrappers/Android.bp b/staticlibs/native/bpf_syscall_wrappers/Android.bp
index 125d631..b3efc21 100644
--- a/staticlibs/native/bpf_syscall_wrappers/Android.bp
+++ b/staticlibs/native/bpf_syscall_wrappers/Android.bp
@@ -33,6 +33,8 @@
"//apex_available:platform",
"com.android.art.debug",
"com.android.mediaprovider",
+ "com.android.os.statsd",
+ "com.android.resolv",
"com.android.tethering",
],
}
diff --git a/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt b/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
index fd837aa..3fc74aa 100644
--- a/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
+++ b/staticlibs/testutils/host/com/android/testutils/ConnectivityTestTargetPreparer.kt
@@ -48,6 +48,11 @@
override fun setUp(testInformation: TestInformation) {
if (isDisabled) return
+ disableGmsUpdate(testInformation)
+ runPreparerApk(testInformation)
+ }
+
+ private fun runPreparerApk(testInformation: TestInformation) {
installer.setCleanApk(true)
installer.addTestFileName(CONNECTIVITY_CHECKER_APK)
installer.setShouldGrantPermission(true)
@@ -90,8 +95,22 @@
testInformation.device.deviceDescriptor)
}
- override fun tearDown(testInformation: TestInformation?, e: Throwable?) {
+ private fun disableGmsUpdate(testInformation: TestInformation) {
+ // This will be a no-op on devices without root (su) or not using gservices, but that's OK.
+ testInformation.device.executeShellCommand("su 0 am broadcast " +
+ "-a com.google.gservices.intent.action.GSERVICES_OVERRIDE " +
+ "-e finsky.play_services_auto_update_enabled false")
+ }
+
+ private fun clearGmsUpdateOverride(testInformation: TestInformation) {
+ testInformation.device.executeShellCommand("su 0 am broadcast " +
+ "-a com.google.gservices.intent.action.GSERVICES_OVERRIDE " +
+ "--esn finsky.play_services_auto_update_enabled")
+ }
+
+ override fun tearDown(testInformation: TestInformation, e: Throwable?) {
if (isTearDownDisabled) return
installer.tearDown(testInformation, e)
+ clearGmsUpdateOverride(testInformation)
}
}