Support java BpfMap in BpfNetMaps#swapActiveStatsMap
Bug: 217624062
Test: atest BpfNetMapsTest
android.net.cts.NetworkStatsManagerTest
android.net.cts.TrafficStatsTest
Change-Id: I9cb673673d2ed1dda8fa14c0877e2fc683267791
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 231a47f..7387483 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -84,6 +84,11 @@
// BpfNetMaps acquires this lock while sequence of read, modify, and write.
private static final Object sUidRulesConfigBpfMapLock = new Object();
+ // Lock for sConfigurationMap entry for CURRENT_STATS_MAP_CONFIGURATION_KEY.
+ // BpfNetMaps acquires this lock while sequence of read, modify, and write.
+ // BpfNetMaps is an only writer of this entry.
+ private static final Object sCurrentStatsMapConfigLock = new Object();
+
private static final String CONFIGURATION_MAP_PATH =
"/sys/fs/bpf/netd_shared/map_netd_configuration_map";
private static final String UID_OWNER_MAP_PATH =
@@ -236,6 +241,13 @@
public int getIfIndex(final String ifName) {
return Os.if_nametoindex(ifName);
}
+
+ /**
+ * Call synchronize_rcu()
+ */
+ public int synchronizeKernelRCU() {
+ return native_synchronizeKernelRCU();
+ }
}
/** Constructor used after T that doesn't need to use netd anymore. */
@@ -723,12 +735,40 @@
/**
* Request netd to change the current active network stats map.
*
+ * @throws UnsupportedOperationException if called on pre-T devices.
* @throws ServiceSpecificException in case of failure, with an error code indicating the
* cause of the failure.
*/
public void swapActiveStatsMap() {
- final int err = native_swapActiveStatsMap();
- maybeThrow(err, "Unable to swap active stats map");
+ throwIfPreT("swapActiveStatsMap is not available on pre-T devices");
+
+ if (sEnableJavaBpfMap) {
+ try {
+ synchronized (sCurrentStatsMapConfigLock) {
+ final long config = sConfigurationMap.getValue(
+ CURRENT_STATS_MAP_CONFIGURATION_KEY).val;
+ final long newConfig = (config == STATS_SELECT_MAP_A)
+ ? STATS_SELECT_MAP_B : STATS_SELECT_MAP_A;
+ sConfigurationMap.updateEntry(CURRENT_STATS_MAP_CONFIGURATION_KEY,
+ new U32(newConfig));
+ }
+ } catch (ErrnoException e) {
+ throw new ServiceSpecificException(e.errno, "Failed to swap active stats map");
+ }
+
+ // After changing the config, it's needed to make sure all the current running eBPF
+ // programs are finished and all the CPUs are aware of this config change before the old
+ // map is modified. So special hack is needed here to wait for the kernel to do a
+ // synchronize_rcu(). Once the kernel called synchronize_rcu(), the updated config will
+ // be available to all cores and the next eBPF programs triggered inside the kernel will
+ // use the new map configuration. So once this function returns it is safe to modify the
+ // old stats map without concerning about race between the kernel and userspace.
+ final int err = mDeps.synchronizeKernelRCU();
+ maybeThrow(err, "synchronizeKernelRCU failed");
+ } else {
+ final int err = native_swapActiveStatsMap();
+ maybeThrow(err, "Unable to swap active stats map");
+ }
}
/**
@@ -804,4 +844,5 @@
private native int native_swapActiveStatsMap();
private native void native_setPermissionForUids(int permissions, int[] uids);
private native void native_dump(FileDescriptor fd, boolean verbose);
+ private static native int native_synchronizeKernelRCU();
}