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

Commit eb69f2ea authored by Edgar Arriaga's avatar Edgar Arriaga
Browse files

Fix for compaction bailing out when MAX_RW_COUNT bytes are sent to compaction

Compaction uses process_madvise which internally makes use of iovec
which imposes certain restrictions on the amount of data that can be
sent, previously we had added a constraint of the total vmas sent within
a single syscall determined by UIO_MAXIOV. However, in this CL we also
added a cap on the amount of bytes sent per syscall up to the maximum
supported MAX_RW_COUNT and split the VMA into chunks which end up
being sent in different syscalls.

Test: Verified ZRAM changes during compaction and added logging to
verify that all VMAs where processed
Bug: 205658049

Change-Id: Ib478397a1199d31a85606b0a38ab1b673b2333fc
parent e7781424
Loading
Loading
Loading
Loading
+68 −22
Original line number Diff line number Diff line
@@ -57,6 +57,19 @@ using android::base::unique_fd;
#define ASYNC_RECEIVED_WHILE_FROZEN (2)
#define TXNS_PENDING_WHILE_FROZEN (4)

#define MAX_RW_COUNT (INT_MAX & PAGE_MASK)

// Defines the maximum amount of VMAs we can send per process_madvise syscall.
// Currently this is set to UIO_MAXIOV which is the maximum segments allowed by
// iovec implementation used by process_madvise syscall
#define MAX_VMAS_PER_COMPACTION UIO_MAXIOV

// Maximum bytes that we can send per process_madvise syscall once this limit
// is reached we split the remaining VMAs into another syscall. The MAX_RW_COUNT
// limit is imposed by iovec implementation. However, if you want to use a smaller
// limit, it has to be a page aligned value, otherwise, compaction would fail.
#define MAX_BYTES_PER_COMPACTION MAX_RW_COUNT

namespace android {

static bool cancelRunningCompaction;
@@ -70,12 +83,9 @@ static inline void compactProcessProcfs(int pid, const std::string& compactionTy
}

// Compacts a set of VMAs for pid using an madviseType accepted by process_madvise syscall
// On success returns the total bytes that where compacted. On failure it returns
// a negative error code from the standard linux error codes.
// Returns the total bytes that where madvised.
static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) {
    // UIO_MAXIOV is currently a small value and we might have more addresses
    // we do multiple syscalls if we exceed its maximum
    static struct iovec vmasToKernel[UIO_MAXIOV];
    static struct iovec vmasToKernel[MAX_VMAS_PER_COMPACTION];

    if (vmas.empty()) {
        return 0;
@@ -89,33 +99,64 @@ static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseT
    compactionInProgress = true;
    cancelRunningCompaction = false;

    int64_t totalBytesCompacted = 0;
    for (int iBase = 0; iBase < vmas.size(); iBase += UIO_MAXIOV) {
    int64_t totalBytesProcessed = 0;

    int64_t vmaOffset = 0;
    for (int iVma = 0; iVma < vmas.size();) {
        uint64_t bytesSentToCompact = 0;
        int iVec = 0;
        while (iVec < MAX_VMAS_PER_COMPACTION && iVma < vmas.size()) {
            if (CC_UNLIKELY(cancelRunningCompaction)) {
                // There could be a significant delay between when a compaction
            // is requested and when it is handled during this time
            // our OOM adjust could have improved.
            cancelRunningCompaction = false;
                // is requested and when it is handled during this time our
                // OOM adjust could have improved.
                break;
            }
        int totalVmasToKernel = std::min(UIO_MAXIOV, (int)(vmas.size() - iBase));
        for (int iVec = 0, iVma = iBase; iVec < totalVmasToKernel; ++iVec, ++iVma) {
            vmasToKernel[iVec].iov_base = (void*)vmas[iVma].start;
            vmasToKernel[iVec].iov_len = vmas[iVma].end - vmas[iVma].start;

            uint64_t vmaStart = vmas[iVma].start + vmaOffset;
            uint64_t vmaSize = vmas[iVma].end - vmaStart;
            if (vmaSize == 0) {
                goto next_vma;
            }
            vmasToKernel[iVec].iov_base = (void*)vmaStart;
            if (vmaSize > MAX_BYTES_PER_COMPACTION - bytesSentToCompact) {
                // Exceeded the max bytes that could be sent, so clamp
                // the end to avoid exceeding limit and issue compaction
                vmaSize = MAX_BYTES_PER_COMPACTION - bytesSentToCompact;
            }

        auto bytesCompacted =
                process_madvise(pidfd, vmasToKernel, totalVmasToKernel, madviseType, 0);
        if (CC_UNLIKELY(bytesCompacted == -1)) {
            vmasToKernel[iVec].iov_len = vmaSize;
            bytesSentToCompact += vmaSize;
            ++iVec;
            if (bytesSentToCompact >= MAX_BYTES_PER_COMPACTION) {
                // Ran out of bytes within iovec, dispatch compaction.
                vmaOffset += vmaSize;
                break;
            }

        next_vma:
            // Finished current VMA, and have more bytes remaining
            vmaOffset = 0;
            ++iVma;
        }

        if (cancelRunningCompaction) {
            cancelRunningCompaction = false;
            break;
        }

        auto bytesProcessed = process_madvise(pidfd, vmasToKernel, iVec, madviseType, 0);

        if (CC_UNLIKELY(bytesProcessed == -1)) {
            compactionInProgress = false;
            return -errno;
        }

        totalBytesCompacted += bytesCompacted;
        totalBytesProcessed += bytesProcessed;
    }
    compactionInProgress = false;

    return totalBytesCompacted;
    return totalBytesProcessed;
}

static int getFilePageAdvice(const Vma& vma) {
@@ -138,7 +179,12 @@ static int getAnyPageAdvice(const Vma& vma) {
}

// Perform a full process compaction using process_madvise syscall
// reading all filtering VMAs and filtering pages as specified by pageFilter
// using the madvise behavior defined by vmaToAdviseFunc per VMA.
//
// Currently supported behaviors are MADV_COLD and MADV_PAGEOUT.
//
// Returns the total number of bytes compacted or forwards an
// process_madvise error.
static int64_t compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {
    ProcMemInfo meminfo(pid);
    std::vector<Vma> pageoutVmas, coldVmas;