# Android Memory Management Daemon

## Overview

The Android Memory Management Daemon (mmd) is a new native daemon designed to
handle Android memory management configuration and tunables.

## Background

Before mmd, Android ZRAM configurations were fragmented and offered limited
customization. mmd addresses this by centralizing ZRAM management, enabling more
complex configuration logic. This unified approach simplifies adding new
features and improvements to ZRAM management. Another motivation for mmd is a
separation of concerns between system server and swap management.

## ZRAM management

On boot complete, mmd will try to set up ZRAM with specified configuration. Once
the ZRAM setup is done, mmd service is enabled to handle ZRAM maintenance tasks.

With mmd ZRAM setup, ZRAM maintenance is initiated from system server by sending
Binder requests to mmd via the
[IMmd interface](https://cs.android.com/android/platform/superproject/main/+/main:system/memory/mmd/aidl/android/os/IMmd.aidl).
mmd handles the actual maintenance tasks of doing ZRAM writeback and
recompression based on its own policy. Both the scheduling from system server
and the ZRAM maintenance policies can be configured via system properties as
mentioned in the ZRAM maintenance section.

### ZRAM setup configuration

mmd ZRAM setup can be configured using following system properties:

*   `mmd.zram.enabled`: whether mmd ZRAM setup is enabled. Default = `false`.
*   `mmd.zram.comp_algorithm`: ZRAM compression algorithm. Kernel default
    compression algorithm will be used if not specified[^zram-comp-algo].
*   `mmd.zram.size`: ZRAM device size in bytes, or a percentage of device RAM
    size (eg: 75%). Default = `50%`.
*   `mmd.zram.writeback.enabled`: whether to enable ZRAM writeback. Default =
    `false`.
*   `mmd.zram.writeback.device_size`: the size of the writeback device in bytes
    or percentage of the data partition. The actual device size can be adjusted
    based on available space of the data partition. Default = `1073741824` (1
    GiB).
*   `mmd.zram.writeback.min_free_space_mib`: minimum free space in MiB that
    needs to be available after the writeback device is set up. Default = `1536`
    (1.5 GiB).
*   `mmd.zram.recompression.enabled`: whether to enable ZRAM recompression
    feature. Default = `false`.
*   `mmd.zram.recompression.algorithm`: ZRAM recompression algorithm. Default =
    `zstd`[^zram-comp-algo].

### ZRAM maintenance configuration

ZRAM maintenance should work out of the box, but it can be finetuned further
using below system properties:

**ZRAM maintenance scheduling:**

*   `mm.zram.maintenance.first_delay_seconds`: the delay before the first ZRAM
    maintenance is initiated. Default = `3600` (1 hour).
*   `mm.zram.maintenance.periodic_delay_seconds`: the delay between subsequent
    ZRAM maintenance scheduling. Default = `3600` (1 hour).
*   `mm.zram.maintenance.require_device_idle`: whether to only initiate ZRAM
    maintenance when the device is idle. Default = `true`.
*   `mm.zram.maintenance.require_battery_not_low`: whether to require battery
    not low before initiating ZRAM maintenance. Default = `true`.

**mmd ZRAM writeback policy:**

*   `mmd.zram.writeback.backoff_seconds`: the backoff time since the last
    writeback. Default = `600` (10 minutes).
*   `mmd.zram.writeback.idle_min_seconds`: minimum seconds to be used for
    calculating idle page age dynamically based on memory utilization. A fixed
    idle age will be used when this is the same as idle_max_seconds system
    property. Default = `72000` (20 hours).
*   `mmd.zram.writeback.idle_max_seconds`: maximum seconds to be used for
    calculating idle page age dynamically based on memory utilization. A fixed
    idle age will be used when this is the same as idle_min_seconds system
    property. Default = `90000` (25 hours).
*   `mmd.zram.writeback.huge_enabled`: whether to enable HUGE page writeback.
    Default = `false`.
*   `mmd.zram.writeback.idle_enabled`: whether to enable IDLE page writeback.
    Default = `true`.
*   `mmd.zram.writeback.huge_idle_enabled`: whether to enable HUGE_IDLE page
    writeback. Default = `true`.
*   `mmd.zram.writeback.min_bytes`: minimum bytes to write back in 1 round.
    Default = `5242880` (5 MiB).
*   `mmd.zram.writeback.max_bytes`: maximum bytes to write back in 1 round.
    Default = `314572800` (300 MiB).
*   `mmd.zram.writeback.max_bytes_per_day`: maximum bytes to write back in 1
    day. Default = `1073741824` (1 GiB).
*   `mmd.zram.writeback.limit.enabled`: whether to enable the ZRAM writeback limit.
    Default = `true`.

**mmd ZRAM recompression policy:**

*   `mmd.zram.recompression.backoff_seconds`: the backoff time since the last
    recompression. Default = `1800` (30 minutes).
*   `mmd.zram.recompression.min_idle_seconds`: minimum seconds to be used for
    calculating idle page age dynamically based on memory utilization. A fixed
    idle age will be used when this is the same as idle_max_seconds system
    property. Default = `7200` (2 hours).
*   `mmd.zram.recompression.max_idle_seconds`: maximum seconds to be used for
    calculating idle page age dynamically based on memory utilization. A fixed
    idle age will be used when this is the same as idle_min_seconds system
    property. Default = `14400` (4 hours).
*   `mmd.zram.recompression.threshold_bytes`: the minimum size in bytes of ZRAM
    pages to be considered for recompression. Default = `1024` (1 KiB).
*   `mmd.zram.recompression.huge_enabled`: whether to enable HUGE page
    recompression. Default = `true`.
*   `mmd.zram.recompression.idle_enabled`:`whether to enable IDLE page
    recompression. Default =`true`.
*   `mmd.zram.recompression.huge_idle_enabled`: whether to enable HUGE_IDLE page
    recompression. Default = `true`.

### Zram idle pages tracking

mmd ZRAM maintenance marks ZRAM pages as idle based on how long it has been
since they were last accessed. This feature requires the
`CONFIG_ZRAM_TRACK_ENTRY_ACTIME` or `CONFIG_ZRAM_MEMORY_TRACKING` kernel configs
to be enabled.

If the kernel config is not enabled, mmd ZRAM maintenance falls back to a
substitute logic to get idle zram pages.

1.  Mark all zram pages as idle when mmd starts.
2.  Skip next zram maintenances until required idle duration has passed.
3.  Zram writeback/recompress idle pages. If there are remaining idle pages due
    to writeback limit, mmd continues to writeback pages on the next zram
    maintenance without marking pages as idle (i.e. without moving to step 4).
4.  If all idle pages are written back, mark all zram pages as idle again and
    move back to the step 2. If zram writeback is disabled, mmd marks all zram
    pages as idle when zram recompression happens after the idle duration of
    recompression.

Note that idle duration of zram writeback and recompression is usually different
and recompression is shorter. Some zram maintenances can just recompress idle
pages and skip writeback until writeback idle duration has passed at the step 3.

### Caveats

mmd ZRAM maintenance is only guaranteed to work with the ZRAM device set up by
`mmd --setup-zram`.

### Existing ZRAM setup deprecation

While swapon_all is still available to set up ZRAM and disk-based swap space,
mmd is the preferred approach for ZRAM management for easier configuration and
additional features like ZRAM recompression.

When mmd ZRAM setup is enabled via mmd.zram.enabled system property:

*   ZRAM setup in swapon_all implementation is a no-op.
*   Existing ZRAM configuration such as config_zramWriteback feature in the
    overlay config.xml file, and ro.zram.* writeback system properties are
    ignored.

## Development guide

### Apply rustfmt

Before upload your changes, please apply rustfmt.

```bash
rustfmt +nightly **/*.rs
```

[^zram-comp-algo] `cat /sys/block/zram0/comp_algorithm` gives the available
compression algorithms (as well as the current one included in brackets).
