Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8a7d616b authored by Jared Duke's avatar Jared Duke
Browse files

PinnerService: Update support for pinning anon regions

This change does the following:
 * Fixes refresh of anon region pinning - The pinAnonRegion
   method was being called multiple times redundantly (e.g., for
   different users, or during startup). This resulted in duplicate
   pinnings of the same region size, potentially 3-4x the requested
   memory after a single boot. This is not safe even for experiments.
   Update behavior to listen to config changes and refresh accordingly,
   including releasing memory if the experiment flag is reset.
 * Changes the mapping from MAP_PRIVATE to MAP_SHARED - This avoids
   issues w/ downstream telemetry where rss.anon values (as read from
   /proc/*/status) trigger behavior like heap dumps/profiles. The memory
   region is still anonymous, and even appears as private dirty in
   showmap/dumpsys meminfo (due to the invalidating setMemory() call on
   the shared anon buffer), but in perfetto memory accounting, it
   falls under `mem.rss.shmem`.
 * Renames pin_anon_size to pin_shared_anon_size - As it's not safe to
   use the previous flag due to (1), and the semantics are changed
   slightly in (2), introduce a different name.

Bug: 294079161
Test: device_config put runtime_native_boot \
          pin_shared_anon_size 2000000000
Test: device_config put runtime_native_boot \
          pin_shared_anon_size 0
Test: device_config delete runtime_native_boot \
          pin_shared_anon_size
Test: showmap `pidof system_server`
Test: cat /proc/`pidof system_server`/status | grep 'Rss'
Change-Id: I7c7c0dc76b9ff04c9cd05de25bfaed65372a56a0
parent 042df8f0
Loading
Loading
Loading
Loading
+52 −10
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
@@ -153,6 +154,9 @@ public final class PinnerService extends SystemService {
    @GuardedBy("this")
    private ArraySet<Integer> mPinKeys;

    private static final String DEVICE_CONFIG_KEY_ANON_SIZE = "pin_shared_anon_size";
    private static final long DEFAULT_ANON_SIZE =
            SystemProperties.getLong("pinner.pin_shared_anon_size", 0);
    private static final long MAX_ANON_SIZE = 2L * (1L << 30); // 2GB
    private long mPinAnonSize;
    private long mPinAnonAddress;
@@ -180,6 +184,17 @@ public final class PinnerService extends SystemService {
        }
    };

    private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener =
            new DeviceConfig.OnPropertiesChangedListener() {
                @Override
                public void onPropertiesChanged(DeviceConfig.Properties properties) {
                    if (DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT.equals(properties.getNamespace())
                            && properties.getKeyset().contains(DEVICE_CONFIG_KEY_ANON_SIZE)) {
                        refreshPinAnonConfig();
                    }
                }
            };

    public PinnerService(Context context) {
        super(context);

@@ -206,6 +221,11 @@ public final class PinnerService extends SystemService {

        registerUidListener();
        registerUserSetupCompleteListener();

        DeviceConfig.addOnPropertiesChangedListener(
                DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
                new HandlerExecutor(mPinnerHandler),
                mDeviceConfigListener);
    }

    @Override
@@ -344,6 +364,8 @@ public final class PinnerService extends SystemService {
                }
            }
        }

        refreshPinAnonConfig();
    }

    /**
@@ -560,11 +582,6 @@ public final class PinnerService extends SystemService {
            pinKeys.add(KEY_ASSISTANT);
        }

        mPinAnonSize = DeviceConfig.getLong(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
                "pin_anon_size",
                SystemProperties.getLong("pinner.pin_anon_size", 0));
        mPinAnonSize = Math.max(0, Math.min(mPinAnonSize, MAX_ANON_SIZE));

        return pinKeys;
    }

@@ -604,7 +621,6 @@ public final class PinnerService extends SystemService {
            int key = currentPinKeys.valueAt(i);
            pinApp(key, userHandle, true /* force */);
        }
        pinAnonRegion();
    }

    /**
@@ -688,11 +704,29 @@ public final class PinnerService extends SystemService {
        }
    }

    /**
     * Handle any changes in the anon region pinner config.
     */
    private void refreshPinAnonConfig() {
        long newPinAnonSize =
                DeviceConfig.getLong(
                        DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
                        DEVICE_CONFIG_KEY_ANON_SIZE,
                        DEFAULT_ANON_SIZE);
        newPinAnonSize = Math.max(0, Math.min(newPinAnonSize, MAX_ANON_SIZE));
        if (newPinAnonSize != mPinAnonSize) {
            mPinAnonSize = newPinAnonSize;
            pinAnonRegion();
        }
    }

    /**
     * Pin an empty anonymous region. This should only be used for ablation experiments.
     */
    private void pinAnonRegion() {
        if (mPinAnonSize == 0) {
            Slog.d(TAG, "pinAnonRegion: releasing pinned region");
            unpinAnonRegion();
            return;
        }
        long alignedPinSize = mPinAnonSize;
@@ -700,15 +734,21 @@ public final class PinnerService extends SystemService {
            alignedPinSize -= alignedPinSize % PAGE_SIZE;
            Slog.e(TAG, "pinAnonRegion: aligning size to " + alignedPinSize);
        }
        if (mPinAnonAddress != 0
                && mCurrentlyPinnedAnonSize != alignedPinSize) {
        if (mPinAnonAddress != 0) {
            if (mCurrentlyPinnedAnonSize == alignedPinSize) {
                Slog.d(TAG, "pinAnonRegion: already pinned region of size " + alignedPinSize);
                return;
            }
            Slog.d(TAG, "pinAnonRegion: resetting pinned region for new size " + alignedPinSize);
            unpinAnonRegion();
        }
        long address = 0;
        try {
            // Map as SHARED to avoid changing rss.anon for system_server (per /proc/*/status).
            // The mapping is visible in other rss metrics, and as private dirty in smaps/meminfo.
            address = Os.mmap(0, alignedPinSize,
                    OsConstants.PROT_READ | OsConstants.PROT_WRITE,
                    OsConstants.MAP_PRIVATE | OsConstants.MAP_ANONYMOUS,
                    OsConstants.MAP_SHARED | OsConstants.MAP_ANONYMOUS,
                    new FileDescriptor(), /*offset=*/0);

            Unsafe tempUnsafe = null;
@@ -729,7 +769,7 @@ public final class PinnerService extends SystemService {
            mCurrentlyPinnedAnonSize = alignedPinSize;
            mPinAnonAddress = address;
            address = -1;
            Slog.e(TAG, "pinAnonRegion success, size=" + mCurrentlyPinnedAnonSize);
            Slog.w(TAG, "pinAnonRegion success, size=" + mCurrentlyPinnedAnonSize);
        } catch (Exception ex) {
            Slog.e(TAG, "Could not pin anon region of size " + alignedPinSize, ex);
            return;
@@ -744,6 +784,8 @@ public final class PinnerService extends SystemService {
        if (mPinAnonAddress != 0) {
            safeMunmap(mPinAnonAddress, mCurrentlyPinnedAnonSize);
        }
        mPinAnonAddress = 0;
        mCurrentlyPinnedAnonSize = 0;
    }

    /**