blob: d425b2918cf18d4f41d7b897570d52f8c9706ff5 [file] [log] [blame]
// Copyright 2020 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.
#ifndef SRC_MEDIA_AUDIO_AUDIO_CORE_PIN_EXECUTABLE_MEMORY_H_
#define SRC_MEDIA_AUDIO_AUDIO_CORE_PIN_EXECUTABLE_MEMORY_H_
#include <lib/async-loop/cpp/loop.h>
#include <lib/fzl/vmo-mapper.h>
#include <zircon/syscalls/object.h>
#include <mutex>
#include <vector>
#include "src/lib/fxl/synchronization/thread_annotations.h"
namespace media::audio {
// Spins up a background thread to periodically touch all pages of executable
// memory, which keeps our executable pages on the "recently used" list and prevents
// them from being paged out. This is a hacky implementation of memory pinning.
// We are using this temporarily until Zircon provides a better solution.
// See fxbug.dev/62830.
class PinExecutableMemory {
public:
// Return the singleton object. Executable memory is pinned the first time this
// function is called and periodically thereafter. If on-demand pinning is desired,
// use Singleton().Pin().
static PinExecutableMemory& Singleton();
// Pins all executable memory. Thread-safe.
void Pin();
// Our Pin() implementation assumes that mappings are not concurrently discarded.
// To prevent races, dynamic mappings must be made through the following object, which
// synchronizes with Pin() when the mapping is destructed.
class VmoMapper {
public:
VmoMapper() = default;
VmoMapper(VmoMapper&&) = default;
VmoMapper(VmoMapper&) = delete;
~VmoMapper() {
if (!mapper_.start()) {
return;
}
auto& pinner = PinExecutableMemory::Singleton();
std::lock_guard<std::mutex> lock(pinner.mutex_);
pinner.discarded_mappings_.push_back({
.start = reinterpret_cast<size_t>(start()),
.end = reinterpret_cast<size_t>(start()) + size(),
});
mapper_.Unmap();
}
zx_status_t CreateAndMap(uint64_t size, zx_vm_option_t map_flags,
fbl::RefPtr<fzl::VmarManager> vmar_manager = nullptr,
zx::vmo* vmo_out = nullptr,
zx_rights_t vmo_rights = ZX_RIGHT_SAME_RIGHTS,
uint32_t cache_policy = 0, uint32_t vmo_options = 0) {
return mapper_.CreateAndMap(size, map_flags, vmar_manager, vmo_out, vmo_rights, cache_policy,
vmo_options);
}
zx_status_t Map(const zx::vmo& vmo, uint64_t offset, uint64_t size, zx_vm_option_t map_flags,
fbl::RefPtr<fzl::VmarManager> vmar_manager = nullptr) {
return mapper_.Map(vmo, offset, size, map_flags, vmar_manager);
}
void* start() const { return mapper_.start(); }
uint64_t size() const { return mapper_.size(); }
private:
fzl::VmoMapper mapper_;
};
private:
friend class VmoMapper;
PinExecutableMemory();
PinExecutableMemory(PinExecutableMemory&) = delete;
PinExecutableMemory(PinExecutableMemory&&) = delete;
void PeriodicPin();
std::vector<zx_info_maps_t> ListVMaps();
bool ShouldSkip(size_t addr) const FXL_REQUIRE(mutex_);
// Thread annotation workaround for non-scoped lock acquisitions.
void AssertMutexHeld() __attribute__((assert_capability(mutex_))) {}
struct Range {
size_t start;
size_t end;
};
async::Loop loop_;
std::mutex mutex_;
std::vector<Range> discarded_mappings_ FXL_GUARDED_BY(mutex_);
size_t last_pinned_bytes_ FXL_GUARDED_BY(mutex_) = 0;
};
} // namespace media::audio
#endif // SRC_MEDIA_AUDIO_AUDIO_CORE_PIN_EXECUTABLE_MEMORY_H_