From d2d26c661acc61fe718874bd7cd58ba283ed54df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=C5=BBenczykowski?= Date: Tue, 12 Aug 2025 14:00:39 -0700 Subject: [PATCH] BpfBitmap: some minor optimizations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test: TreeHugger, atest BpfBitmapTest Flag: EXEMPT mainline Signed-off-by: Maciej Żenczykowski Change-Id: I5acf5bcb31777e503e58cef733d617a53006d2b9 --- Tethering/jni/onload.cpp | 4 ++ .../com_android_net_module_util/onload.cpp | 4 ++ .../android/net/module/util/BpfBitmap.java | 71 ++++++++++--------- .../com/android/net/module/util/BpfMap.java | 5 ++ staticlibs/native/bpfmapjni/Android.bp | 2 +- ...pp => com_android_net_module_util_Bpf.cpp} | 56 +++++++++++++-- tests/common/tethering-jni-jarjar-rules.txt | 1 + .../onload.cpp | 4 ++ 8 files changed, 109 insertions(+), 38 deletions(-) rename staticlibs/native/bpfmapjni/{com_android_net_module_util_BpfMap.cpp => com_android_net_module_util_Bpf.cpp} (77%) diff --git a/Tethering/jni/onload.cpp b/Tethering/jni/onload.cpp index fd40d411ab..606cc44392 100644 --- a/Tethering/jni/onload.cpp +++ b/Tethering/jni/onload.cpp @@ -23,6 +23,7 @@ namespace android { int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name); +int register_com_android_net_module_util_BpfBitmap(JNIEnv* env, char const* class_name); int register_com_android_net_module_util_TcUtils(JNIEnv* env, char const* class_name); int register_com_android_networkstack_tethering_BpfCoordinator(JNIEnv* env); int register_com_android_networkstack_tethering_util_TetheringUtils(JNIEnv* env); @@ -39,6 +40,9 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { if (register_com_android_net_module_util_BpfMap(env, "com/android/networkstack/tethering/util/BpfMap") < 0) return JNI_ERR; + if (register_com_android_net_module_util_BpfBitmap(env, + "com/android/networkstack/tethering/util/BpfBitmap") < 0) return JNI_ERR; + if (register_com_android_net_module_util_TcUtils(env, "com/android/networkstack/tethering/util/TcUtils") < 0) return JNI_ERR; diff --git a/service/jni/com_android_net_module_util/onload.cpp b/service/jni/com_android_net_module_util/onload.cpp index d91eb0305c..f160622f3b 100644 --- a/service/jni/com_android_net_module_util/onload.cpp +++ b/service/jni/com_android_net_module_util/onload.cpp @@ -20,6 +20,7 @@ namespace android { int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name); +int register_com_android_net_module_util_BpfBitmap(JNIEnv* env, char const* class_name); int register_com_android_net_module_util_TcUtils(JNIEnv* env, char const* class_name); int register_com_android_net_module_util_BpfUtils(JNIEnv* env, char const* class_name); @@ -33,6 +34,9 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { if (register_com_android_net_module_util_BpfMap(env, "android/net/connectivity/com/android/net/module/util/BpfMap") < 0) return JNI_ERR; + if (register_com_android_net_module_util_BpfBitmap(env, + "android/net/connectivity/com/android/net/module/util/BpfBitmap") < 0) return JNI_ERR; + if (register_com_android_net_module_util_TcUtils(env, "android/net/connectivity/com/android/net/module/util/TcUtils") < 0) return JNI_ERR; diff --git a/staticlibs/device/com/android/net/module/util/BpfBitmap.java b/staticlibs/device/com/android/net/module/util/BpfBitmap.java index b62a430c7d..8f45c651a8 100644 --- a/staticlibs/device/com/android/net/module/util/BpfBitmap.java +++ b/staticlibs/device/com/android/net/module/util/BpfBitmap.java @@ -22,6 +22,8 @@ import android.system.ErrnoException; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; +import dalvik.annotation.optimization.CriticalNative; + /** * * Generic bitmap class for use with BPF programs. Corresponds to a BpfMap @@ -30,7 +32,12 @@ import androidx.annotation.RequiresApi; */ @RequiresApi(Build.VERSION_CODES.S) public class BpfBitmap { - private BpfMap mBpfMap; + static { + System.loadLibrary(JniUtil.getJniLibraryName(BpfBitmap.class.getPackage())); + } + + private final BpfMap mBpfMap; + private final int mMapFd; /** * Create a BpfBitmap map wrapper with "path" of filesystem. @@ -39,21 +46,16 @@ public class BpfBitmap { */ public BpfBitmap(@NonNull String path) throws ErrnoException { mBpfMap = new BpfMap<>(path, Struct.S32.class, Struct.S64.class); + mMapFd = mBpfMap.getFd(); } - /** - * Retrieves the value from BpfMap for the given key. - * - * @param key The key in the map corresponding to the value to return. - */ - private long getBpfMapValue(Struct.S32 key) throws ErrnoException { - Struct.S64 curVal = mBpfMap.getValue(key); - if (curVal != null) { - return curVal.val; - } else { - return 0; - } - } + // Returns > 0 if bit is set, 0 if not set, < 0 on error (negative errno). + @CriticalNative + private static native int nativeGet(int fd, int index); + + // Returns 0 on success, < 0 on error (negative errno). + @CriticalNative + private static native int nativeSet(int fd, int index, boolean set); /** * Retrieves the bit for the given index in the bitmap. @@ -63,8 +65,26 @@ public class BpfBitmap { public boolean get(int index) throws ErrnoException { if (index < 0) return false; - Struct.S32 key = new Struct.S32(index >> 6); - return ((getBpfMapValue(key) >>> (index & 63)) & 1L) != 0; + final int ret = nativeGet(mMapFd, index); + if (ret < 0) { + throw new ErrnoException("nativeGet", -ret); + } + return ret > 0; + } + + /** + * Change the specified index in the bitmap to set value. + * + * @param index Position to (un)set in bitmap. + * @param set Boolean indicating to set or unset index. + */ + public void set(int index, boolean set) throws ErrnoException { + if (index < 0) throw new IllegalArgumentException("Index out of bounds."); + + final int ret = nativeSet(mMapFd, index, set); + if (ret < 0) { + throw new ErrnoException("nativeSet", -ret); + } } /** @@ -85,22 +105,6 @@ public class BpfBitmap { set(index, false); } - /** - * Change the specified index in the bitmap to set value. - * - * @param index Position to unset in bitmap. - * @param set Boolean indicating to set or unset index. - */ - public void set(int index, boolean set) throws ErrnoException { - if (index < 0) throw new IllegalArgumentException("Index out of bounds."); - - Struct.S32 key = new Struct.S32(index >> 6); - long mask = (1L << (index & 63)); - long val = getBpfMapValue(key); - if (set) val |= mask; else val &= ~mask; - mBpfMap.updateEntry(key, new Struct.S64(val)); - } - /** * Clears the map. The map may already be empty. * @@ -118,7 +122,8 @@ public class BpfBitmap { public boolean isEmpty() throws ErrnoException { Struct.S32 key = mBpfMap.getFirstKey(); while (key != null) { - if (getBpfMapValue(key) != 0) { + Struct.S64 val = mBpfMap.getValue(key); + if (val != null && val.val != 0) { return false; } key = mBpfMap.getNextKey(key); diff --git a/staticlibs/device/com/android/net/module/util/BpfMap.java b/staticlibs/device/com/android/net/module/util/BpfMap.java index da04c732dd..72cb430965 100644 --- a/staticlibs/device/com/android/net/module/util/BpfMap.java +++ b/staticlibs/device/com/android/net/module/util/BpfMap.java @@ -71,6 +71,11 @@ public class BpfMap implements IBpfMap private final int mKeySize; private final int mValueSize; + // The following is (ab)used by BpfBitmap.java + /* package */ int getFd() { + return mMapFd.getFd(); + } + private static ConcurrentHashMap, ParcelFileDescriptor> sFdCache = new ConcurrentHashMap<>(); diff --git a/staticlibs/native/bpfmapjni/Android.bp b/staticlibs/native/bpfmapjni/Android.bp index 9a58a93a8b..2612ec5f66 100644 --- a/staticlibs/native/bpfmapjni/Android.bp +++ b/staticlibs/native/bpfmapjni/Android.bp @@ -20,7 +20,7 @@ package { cc_library_static { name: "libnet_utils_device_common_bpfjni", srcs: [ - "com_android_net_module_util_BpfMap.cpp", + "com_android_net_module_util_Bpf.cpp", "com_android_net_module_util_TcUtils.cpp", ], header_libs: [ diff --git a/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp b/staticlibs/native/bpfmapjni/com_android_net_module_util_Bpf.cpp similarity index 77% rename from staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp rename to staticlibs/native/bpfmapjni/com_android_net_module_util_Bpf.cpp index 38cc92c1e1..f0e724657b 100644 --- a/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp +++ b/staticlibs/native/bpfmapjni/com_android_net_module_util_Bpf.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -143,10 +144,47 @@ static jint com_android_net_module_util_BpfMap_nativeSynchronizeKernelRCU() { return 0; } +static jint com_android_net_module_util_BpfBitmap_nativeGet(jint fd, jint index) { + if (index < 0) return -EINVAL; + + const uint32_t key = index >> 6; + const uint32_t subkey = index & 63; + uint64_t value = 0; + + // findMapEntry returns 0 on success, -1 on error. + // If the entry does not exist, it's not an error, the value is just 0. + if (bpf::findMapEntry(fd, &key, &value) && errno != ENOENT) return -errno; + + return (value >> subkey) & 1; +} + +static jint com_android_net_module_util_BpfBitmap_nativeSet(jint fd, jint index, jboolean set) { + if (index < 0) return -EINVAL; + + const uint32_t key = index >> 6; + const uint32_t subkey = index & 63; + uint64_t value = 0; + + // Read the existing value. It's okay if it doesn't exist, value will be 0. + if (bpf::findMapEntry(fd, &key, &value) && errno != ENOENT) return -errno; + + const uint64_t mask = 1uLL << subkey; + if (set) { + value |= mask; + } else { + value &= ~mask; + } + + // Write the updated value back. BPF_ANY will create or update as needed. + if (bpf::writeToMapEntry(fd, &key, &value, BPF_ANY)) return -errno; + + return 0; +} + /* * JNI registration. */ -static const JNINativeMethod gMethods[] = { +static const JNINativeMethod gBpfMapMethods[] = { /* name, signature, funcPtr */ { "nativeBpfFdGet", "(Ljava/lang/String;III)I", (void*) com_android_net_module_util_BpfMap_nativeBpfFdGet }, @@ -165,9 +203,19 @@ static const JNINativeMethod gMethods[] = { }; int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name) { - return jniRegisterNativeMethods(env, - class_name, - gMethods, NELEM(gMethods)); + return jniRegisterNativeMethods(env, class_name, gBpfMapMethods, NELEM(gBpfMapMethods)); +} + +static const JNINativeMethod gBpfBitmapMethods[] = { + /* name, signature, funcPtr */ + // CriticalNative + { "nativeGet", "(II)I", (void*)com_android_net_module_util_BpfBitmap_nativeGet }, + // CriticalNative + { "nativeSet", "(IIZ)I", (void*)com_android_net_module_util_BpfBitmap_nativeSet }, +}; + +int register_com_android_net_module_util_BpfBitmap(JNIEnv* env, char const* class_name) { + return jniRegisterNativeMethods(env, class_name, gBpfBitmapMethods, NELEM(gBpfBitmapMethods)); } }; // namespace android diff --git a/tests/common/tethering-jni-jarjar-rules.txt b/tests/common/tethering-jni-jarjar-rules.txt index 593ba1497f..3dfd77a244 100644 --- a/tests/common/tethering-jni-jarjar-rules.txt +++ b/tests/common/tethering-jni-jarjar-rules.txt @@ -5,6 +5,7 @@ # jarjar rules the test is using, but this is a bit less realistic (using a different JNI library), # and complicates the test build. It would be necessary if TetheringUtils had a different package # name in test code though, as the JNI library name is deducted from the TetheringUtils package. +rule com.android.net.module.util.BpfBitmap* com.android.networkstack.tethering.util.BpfBitmap@1 rule com.android.net.module.util.BpfMap* com.android.networkstack.tethering.util.BpfMap@1 rule com.android.net.module.util.BpfUtils* com.android.networkstack.tethering.util.BpfUtils@1 rule com.android.net.module.util.TcUtils* com.android.networkstack.tethering.util.TcUtils@1 diff --git a/tests/unit/jni/android_net_frameworktests_util/onload.cpp b/tests/unit/jni/android_net_frameworktests_util/onload.cpp index f70b04bf42..b79a23093c 100644 --- a/tests/unit/jni/android_net_frameworktests_util/onload.cpp +++ b/tests/unit/jni/android_net_frameworktests_util/onload.cpp @@ -23,6 +23,7 @@ namespace android { int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name); +int register_com_android_net_module_util_BpfBitmap(JNIEnv* env, char const* class_name); int register_com_android_net_module_util_TcUtils(JNIEnv* env, char const* class_name); int register_com_android_net_module_util_ServiceConnectivityJni(JNIEnv *env, char const *class_name); @@ -37,6 +38,9 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { if (register_com_android_net_module_util_BpfMap(env, "android/net/frameworktests/util/BpfMap") < 0) return JNI_ERR; + if (register_com_android_net_module_util_BpfBitmap(env, + "android/net/frameworktests/util/BpfBitmap") < 0) return JNI_ERR; + if (register_com_android_net_module_util_TcUtils(env, "android/net/frameworktests/util/TcUtils") < 0) return JNI_ERR;