blob: b30949fa9f92c7d0d41cf293427612b1e77b210c [file] [log] [blame]
// Copyright 2018 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/fzl/owned-vmo-mapper.h>
#include <lib/fzl/resizeable-vmo-mapper.h>
#include <stdio.h>
#include <string.h>
#include <memory>
#include <utility>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
namespace fzl {
std::unique_ptr<ResizeableVmoMapper> ResizeableVmoMapper::Create(
uint64_t size, const char* name, uint32_t map_options, fbl::RefPtr<VmarManager> vmar_manager,
uint32_t cache_policy) {
fbl::AllocChecker ac;
auto ret = fbl::make_unique_checked<ResizeableVmoMapper>(&ac);
if (!ac.check()) {
return nullptr;
}
zx_status_t res = ret->CreateAndMap(size, name, map_options, vmar_manager, cache_policy);
if (res != ZX_OK) {
return nullptr;
}
return ret;
}
zx_status_t ResizeableVmoMapper::CreateAndMap(uint64_t size, const char* name,
zx_vm_option_t map_options,
fbl::RefPtr<VmarManager> vmar_manager,
uint32_t cache_policy) {
zx::vmo temp;
zx_status_t res = OwnedVmoMapper::CreateAndMap(size, name, map_options, std::move(vmar_manager),
cache_policy, ZX_VMO_RESIZABLE);
if (res == ZX_OK) {
map_options_ = map_options;
}
return res;
}
zx_status_t ResizeableVmoMapper::Map(zx::vmo vmo, uint64_t size, zx_vm_option_t map_options,
fbl::RefPtr<VmarManager> vmar_manager) {
zx_status_t res = OwnedVmoMapper::Map(std::move(vmo), size, map_options, std::move(vmar_manager));
if (res == ZX_OK) {
map_options_ = map_options;
}
return res;
}
zx_status_t ResizeableVmoMapper::Shrink(size_t size) {
if (!vmo().is_valid()) {
return ZX_ERR_BAD_STATE;
} else if (size == 0 || size > size_) {
return ZX_ERR_INVALID_ARGS;
} else if (size == size_) {
return ZX_OK;
}
zx_status_t status;
zx_handle_t vmar_handle = vmar_manager_ ? vmar_manager_->vmar().get() : zx_vmar_root_self();
// Unmap everything after the offset
if ((status = zx_vmar_unmap(vmar_handle, start_ + size, size_ - size)) != ZX_OK) {
return status;
}
size_ = size;
if ((status = vmo().op_range(ZX_VMO_OP_DECOMMIT, size, size_ - size, nullptr, 0)) != ZX_OK) {
// We can tolerate this error; from a client's perspective, the VMO
// still should appear smaller.
fprintf(stderr, "ResizeableVmoMapper::Shrink: VMO Decommit failed: %d\n", status);
}
return ZX_OK;
}
zx_status_t ResizeableVmoMapper::Grow(size_t size) {
if (!vmo().is_valid()) {
return ZX_ERR_BAD_STATE;
} else if (size < size_) {
return ZX_ERR_INVALID_ARGS;
}
size = fbl::round_up<size_t>(size, zx_system_get_page_size());
zx_status_t status;
zx_handle_t vmar_handle = vmar_manager_ ? vmar_manager_->vmar().get() : zx_vmar_root_self();
if ((status = vmo().set_size(size)) != ZX_OK) {
return status;
}
uintptr_t new_start;
// Create entirely new mapping.
if ((status = zx_vmar_map(vmar_handle, map_options_, 0, vmo().get(), 0, size, &new_start)) !=
ZX_OK) {
// If we could not extend the old mapping, and we cannot create a
// new mapping, then we are done. Attempt to shrink the VMO back to
// its original size. This operation should *never* fail. If it
// does, something has gone very wrong and it is time to terminate
// this process.
zx_status_t stat2 = vmo().set_size(size_);
ZX_ASSERT_MSG(stat2 == ZX_OK, "Failed to shrink to original size (0x%zx -> 0x%lx : res %d)\n",
size, this->size(), stat2);
return status;
}
// Now that we have a new mapping, unmap our original mapping. Once
// again, this should *never* fail. Hard assert that this is the case.
status = zx_vmar_unmap(vmar_handle, start_, size_);
ZX_ASSERT_MSG(status == ZX_OK, "Failed to destroy original mapping ([%p, len 0x%lx] : res %d\n",
start(), this->size(), status);
start_ = new_start;
size_ = size;
return ZX_OK;
}
} // namespace fzl