chromium/chrome/renderer/performance_manager/mechanisms/userspace_swap_impl_chromeos.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/renderer/performance_manager/mechanisms/userspace_swap_impl_chromeos.h"

#include <signal.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

#include <vector>

#include "base/threading/scoped_blocking_call.h"
#include "chromeos/ash/components/memory/userspace_swap/userspace_swap.h"
#include "chromeos/ash/components/memory/userspace_swap/userspace_swap.mojom.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"

// MREMAP_DONTUNMAP was added in the 5.7 kernel, but we've backported it to
// earlier CrOS kernels.
#if !defined(MREMAP_DONTUNMAP)
#define MREMAP_DONTUNMAP 4
#endif

namespace performance_manager {
namespace mechanism {

using ::ash::memory::userspace_swap::UserspaceSwapConfig;

UserspaceSwapImpl::UserspaceSwapImpl() {
  CHECK(UserspaceSwapImpl::PlatformSupportsUserspaceSwap());
}

UserspaceSwapImpl::~UserspaceSwapImpl() = default;

// static
void UserspaceSwapImpl::Create(
    mojo::PendingReceiver<::userspace_swap::mojom::UserspaceSwap> receiver) {
  if (!PlatformSupportsUserspaceSwap()) {
    DLOG(WARNING) << "Userspace swap not supported by platform";
    return;
  }
  auto impl = std::make_unique<UserspaceSwapImpl>();
  mojo::MakeSelfOwnedReceiver(std::move(impl), std::move(receiver));
}

// static
bool UserspaceSwapImpl::PlatformSupportsUserspaceSwap() {
  return ash::memory::userspace_swap::UserspaceSwapSupportedAndEnabled();
}

void UserspaceSwapImpl::MovePTEsLeavingMapping(MemoryRegionPtr src,
                                               uint64_t dest) {
  DCHECK(PlatformSupportsUserspaceSwap());

  // We're moving to a known location, the location provided to us here is one
  // that the renderer created and reserved for the browser the renderers
  // address space for moving mappings to. The browser will observe this remap
  // and then it will know it can safely use process_vm_readv() to move over the
  // memory.
  //
  // The MREMAP_DONTUNMAP (which requires MREMAP_MAYMOVE) is used to move just
  // the PTEs leaving the original mapping in place (with the userfaultfd still
  // attached). The MREMAP_FIXED tells the kernel we know where we want the
  // memory to go.
  void* dest_mapping =
      mremap(reinterpret_cast<void*>(src->address), src->length, src->length,
             MREMAP_MAYMOVE | MREMAP_DONTUNMAP | MREMAP_FIXED,
             reinterpret_cast<void*>(dest));
  PCHECK(dest_mapping != MAP_FAILED) << "mremap failed";
}

void UserspaceSwapImpl::MapArea(MemoryRegionPtr area) {
  DCHECK(PlatformSupportsUserspaceSwap());

  // MapArea is used to drop pages after they have been read over to the browser
  // process. We do this rather than MADV_DONTNEED because this will fully drop
  // the VMA and recreate it while holding the MM sem. This means the memory
  // will be unaccounted.
  void* dest_mapping =
      mmap(reinterpret_cast<void*>(area->address), area->length, PROT_NONE,
           MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  PCHECK(dest_mapping != MAP_FAILED) << "Unable to map area";
}

void UserspaceSwapImpl::GetPartitionAllocSuperPagesUsed(
    int32_t max_superpages,
    UserspaceSwapImpl::GetPartitionAllocSuperPagesUsedCallback callback) {
  std::vector<::userspace_swap::mojom::MemoryRegionPtr> areas;
  ash::memory::userspace_swap::GetPartitionAllocSuperPagesInUse(max_superpages,
                                                                areas);
  std::move(callback).Run(std::move(areas));
}

}  // namespace mechanism
}  // namespace performance_manager