Skip to content

Commit

Permalink
[Fuchsia] Implement PageAllocator APIs using VMOs instead of mmap().
Browse files Browse the repository at this point in the history
Memory is implicitly allocated/deallocated in response to mmap() and
munmap() calls when working with MAP_ANONYMOUS mmaps.

Fuchsia's Zircon kernel separates reservation of address space through
Virtual Memory Address Ranges (VMARs) from allocation of pages through
Virtual Memory Objects (VMOs).

This implementation creates and maps a new VMO for each call to
SystemAllocPagesInternal(). Each of these VMOs has is named based on
the PageTag of the subsystem (e.g. V8, Blink) making the allocation,
making it easier to identify which subsystems are consuming memory.

DecommitSystemPagesInternal() will be implemented once Fuchsia provides
the vmar_op_range(DECOMMIT) API.

Bug: 927411
Change-Id: Ie7966f9dcf487e308326e940ca3941c0f87487f4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1859677
Reviewed-by: Chris Palmer <palmer@chromium.org>
Reviewed-by: Wez <wez@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Commit-Queue: Wez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#711960}
  • Loading branch information
Wez authored and Commit Bot committed Nov 2, 2019
1 parent c416e43 commit 7fcb6fe
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 9 deletions.
6 changes: 5 additions & 1 deletion base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1795,9 +1795,13 @@ jumbo_component("base") {
if (is_win) {
sources +=
[ "allocator/partition_allocator/page_allocator_internals_win.h" ]
} else if (is_posix || is_fuchsia) {
} else if (is_posix) {
sources +=
[ "allocator/partition_allocator/page_allocator_internals_posix.h" ]
} else if (is_fuchsia) {
sources += [
"allocator/partition_allocator/page_allocator_internals_fuchsia.h",
]
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion base/allocator/partition_allocator/page_allocator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@

#if defined(OS_WIN)
#include "base/allocator/partition_allocator/page_allocator_internals_win.h"
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
#elif defined(OS_POSIX)
#include "base/allocator/partition_allocator/page_allocator_internals_posix.h"
#elif defined(OS_FUCHSIA)
#include "base/allocator/partition_allocator/page_allocator_internals_fuchsia.h"
#else
#error Platform not supported.
#endif
Expand Down
190 changes: 190 additions & 0 deletions base/allocator/partition_allocator/page_allocator_internals_fuchsia.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This file implements memory allocation primitives for PageAllocator using
// Fuchsia's VMOs (Virtual Memory Objects). VMO API is documented in
// https://fuchsia.dev/fuchsia-src/zircon/objects/vm_object . A VMO is a kernel
// object that corresponds to a set of memory pages. VMO pages may be mapped
// to an address space. The code below creates VMOs for each memory allocations
// and maps them to the default address space of the current process.

#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_FUCHSIA_H_
#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_FUCHSIA_H_

#include <lib/zx/vmar.h>
#include <lib/zx/vmo.h>

#include "base/allocator/partition_allocator/page_allocator.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h"

namespace base {

namespace {

// Returns VMO name for a PageTag.
const char* PageTagToName(PageTag tag) {
switch (tag) {
case PageTag::kBlinkGC:
return "cr_blink_gc";
case PageTag::kPartitionAlloc:
return "cr_partition_alloc";
case PageTag::kChromium:
return "cr_chromium";
case PageTag::kV8:
return "cr_v8";
default:
DCHECK(false);
return "";
}
}

zx_vm_option_t PageAccessibilityToZxVmOptions(
PageAccessibilityConfiguration accessibility) {
switch (accessibility) {
case PageRead:
return ZX_VM_PERM_READ;
case PageReadWrite:
return ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
case PageReadExecute:
return ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE;
case PageReadWriteExecute:
return ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE;
default:
NOTREACHED();
FALLTHROUGH;
case PageInaccessible:
return 0;
}
}

} // namespace

// zx_vmar_map() will fail if the VMO cannot be mapped at |vmar_offset|, i.e.
// |hint| is not advisory.
constexpr bool kHintIsAdvisory = false;

std::atomic<int32_t> s_allocPageErrorCode{0};

void* SystemAllocPagesInternal(void* hint,
size_t length,
PageAccessibilityConfiguration accessibility,
PageTag page_tag,
bool commit) {
zx::vmo vmo;
zx_status_t status = zx::vmo::create(length, 0, &vmo);
if (status != ZX_OK) {
ZX_DLOG(INFO, status) << "zx_vmo_create";
return nullptr;
}

const char* vmo_name = PageTagToName(page_tag);
status = vmo.set_property(ZX_PROP_NAME, vmo_name, strlen(vmo_name));

// VMO names are used only for debugging, so failure to set a name is not
// fatal.
ZX_DCHECK(status == ZX_OK, status);

if (page_tag == PageTag::kV8) {
// V8 uses JIT. Call zx_vmo_replace_as_executable() to allow code execution
// in the new VMO.
status = vmo.replace_as_executable(zx::handle(), &vmo);
if (status != ZX_OK) {
ZX_DLOG(INFO, status) << "zx_vmo_replace_as_executable";
return nullptr;
}
}

zx_vm_option_t options = PageAccessibilityToZxVmOptions(accessibility);

uint64_t vmar_offset = 0;
if (hint) {
vmar_offset = reinterpret_cast<uint64_t>(hint);
options |= ZX_VM_SPECIFIC;
}

uint64_t address;
status =
zx::vmar::root_self()->map(vmar_offset, vmo,
/*vmo_offset=*/0, length, options, &address);
if (status != ZX_OK) {
ZX_DLOG(INFO, status) << "zx_vmar_map";
return nullptr;
}

return reinterpret_cast<void*>(address);
}

void* TrimMappingInternal(void* base,
size_t base_length,
size_t trim_length,
PageAccessibilityConfiguration accessibility,
bool commit,
size_t pre_slack,
size_t post_slack) {
DCHECK_EQ(base_length, trim_length + pre_slack + post_slack);

uint64_t base_address = reinterpret_cast<uint64_t>(base);

// Unmap head if necessary.
if (pre_slack) {
zx_status_t status = zx::vmar::root_self()->unmap(base_address, pre_slack);
ZX_CHECK(status == ZX_OK, status);
}

// Unmap tail if necessary.
if (post_slack) {
zx_status_t status = zx::vmar::root_self()->unmap(
base_address + pre_slack + trim_length, post_slack);
ZX_CHECK(status == ZX_OK, status);
}

return reinterpret_cast<void*>(base_address + pre_slack);
}

bool TrySetSystemPagesAccessInternal(
void* address,
size_t length,
PageAccessibilityConfiguration accessibility) {
zx_status_t status = zx::vmar::root_self()->protect(
reinterpret_cast<uint64_t>(address), length,
PageAccessibilityToZxVmOptions(accessibility));
return status == ZX_OK;
}

void SetSystemPagesAccessInternal(
void* address,
size_t length,
PageAccessibilityConfiguration accessibility) {
zx_status_t status = zx::vmar::root_self()->protect(
reinterpret_cast<uint64_t>(address), length,
PageAccessibilityToZxVmOptions(accessibility));
ZX_CHECK(status == ZX_OK, status);
}

void FreePagesInternal(void* address, size_t length) {
uint64_t address_int = reinterpret_cast<uint64_t>(address);
zx_status_t status = zx::vmar::root_self()->unmap(address_int, length);
ZX_CHECK(status == ZX_OK, status);
}

void DecommitSystemPagesInternal(void* address, size_t length) {
DiscardSystemPages(address, length);
SetSystemPagesAccessInternal(address, length, PageInaccessible);
}

bool RecommitSystemPagesInternal(void* address,
size_t length,
PageAccessibilityConfiguration accessibility) {
SetSystemPagesAccessInternal(address, length, accessibility);
return true;
}

void DiscardSystemPagesInternal(void* address, size_t length) {
NOTIMPLEMENTED_LOG_ONCE();
}

} // namespace base

#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_FUCHSIA_H_
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,7 @@ void* SystemAllocPagesInternal(void* hint,
int access_flag = GetAccessFlags(accessibility);
int map_flags = MAP_ANONYMOUS | MAP_PRIVATE;

// TODO(https://crbug.com/927411): Remove OS_FUCHSIA once Fuchsia uses a
// native page allocator, rather than relying on POSIX compatibility.
#if defined(OS_FUCHSIA)
if (page_tag == PageTag::kV8) {
map_flags |= MAP_JIT;
}
#elif defined(OS_MACOSX)
#if defined(OS_MACOSX)
// On macOS 10.14 and higher, executables that are code signed with the
// "runtime" option cannot execute writable memory by default. They can opt
// into this capability by specifying the "com.apple.security.cs.allow-jit"
Expand Down

0 comments on commit 7fcb6fe

Please sign in to comment.