blob: d03adc7f63904da0c043780f409b825f35e6989d [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/zx/vmar.h>
#include <lib/zx/vmo.h>
#include <limits.h>
#include <zircon/assert.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include <algorithm>
#include <vector>
#include <fbl/algorithm.h>
#include <perftest/perftest.h>
#include "assert.h"
namespace {
#define KB(n) ((n)*1024ull)
#define MB(n) (KB(n) * 1024ull)
#define GB(n) (MB(n) * 1024ull)
constexpr size_t kSize = MB(1);
struct Helper {
Helper() {
ASSERT_OK(zx::vmar::root_self()->allocate(ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_SPECIFIC, 0, GB(1),
&vmar, &vmar_base));
const uint64_t vmo_size = MB(4);
ASSERT_OK(zx::vmo::create(vmo_size, 0, &vmo));
// Write random contents to the VMO to ensure it has backing pages so that mappings will happen.
std::vector<char> buffer(vmo_size);
zx_cprng_draw(buffer.data(), buffer.size());
ASSERT_OK(vmo.write(buffer.data(), 0, buffer.size()));
}
~Helper() { vmar.destroy(); }
// Cyclically maps the first chunk_size bytes of |vmo| into the |length| bytes
// of vmar, starting from offset 0. Mapping is done |chunk_size| bytes at a
// time. |chunk_size| and |length| must be multiples of zx_system_get_page_size(). As a
// precondition, |vmar| should be empty.
zx_status_t MapInChunks(size_t chunk_size, size_t length, bool force_into_mmu);
zx::vmar vmar;
zx::vmo vmo;
uintptr_t vmar_base;
};
zx_status_t Helper::MapInChunks(size_t chunk_size, size_t length, bool force_into_mmu) {
zx_status_t status;
zx_vm_option_t flags = ZX_VM_SPECIFIC | ZX_VM_PERM_READ;
if (force_into_mmu) {
flags |= ZX_VM_MAP_RANGE;
}
for (size_t offset = 0; offset < length; offset += chunk_size) {
uintptr_t addr;
size_t len = std::min(chunk_size, length - offset);
status = vmar.map(flags, offset, vmo, 0, len, &addr);
if (status != ZX_OK) {
return status;
}
}
return ZX_OK;
}
// This attempts to measure the amount of time it takes to add and remove
// mappings through the kernel VM layer and the arch MMU layer.
bool MmuMapUnmapTest(perftest::RepeatState* state) {
state->DeclareStep("map");
state->DeclareStep("unmap");
Helper helper;
while (state->KeepRunning()) {
// Map just under a large page at a time, to force small pages. We map many
// pages at once still, to exercise any optimizations the kernel may perform
// for small contiguous mappings.
ASSERT_OK(helper.MapInChunks(511 * KB(4), kSize,
/* force_into_mmu */ true));
state->NextStep();
ASSERT_OK(helper.vmar.unmap(helper.vmar_base, kSize));
}
return true;
}
// This attempts to measure the amount of time it takes to add mappings in
// the kernel VM layer, page fault the mappings into the arch MMU layer, and
// then remove the mappings from both.
bool MmuMapUnmapWithFaultsTest(perftest::RepeatState* state) {
const size_t kPageSize = zx_system_get_page_size();
state->DeclareStep("map");
state->DeclareStep("fault_in");
state->DeclareStep("unmap");
Helper helper;
while (state->KeepRunning()) {
// Map just under a large page at a time, to force small pages. We map many
// pages at once still, to exercise any optimizations the kernel may perform
// for small contiguous mappings.
ASSERT_OK(helper.MapInChunks(511 * KB(4), kSize,
/* force_into_mmu */ false));
state->NextStep();
// Read fault everything in
auto p = reinterpret_cast<volatile uint8_t*>(helper.vmar_base);
for (size_t offset = 0; offset < kSize; offset += kPageSize) {
p[offset];
}
state->NextStep();
ASSERT_OK(helper.vmar.unmap(helper.vmar_base, kSize));
}
return true;
}
void RegisterTests() {
perftest::RegisterTest("Mmu/MapUnmap", MmuMapUnmapTest);
perftest::RegisterTest("Mmu/MapUnmapWithFaults", MmuMapUnmapWithFaultsTest);
}
PERFTEST_CTOR(RegisterTests)
} // namespace