blob: b25d2e6b6a39d98c7a54cc0a817295ec8d5a30e1 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef ZIRCON_KERNEL_VM_INCLUDE_VM_DEBUG_COMPRESSOR_H_
#define ZIRCON_KERNEL_VM_INCLUDE_VM_DEBUG_COMPRESSOR_H_
#include <zircon/types.h>
#include <fbl/array.h>
#include <fbl/macros.h>
#include <fbl/ring_buffer.h>
#include <kernel/mutex.h>
#include <kernel/thread.h>
#include <vm/page.h>
class VmCowPages;
// A debug compressor that can be given references to pages in VMOs and will randomly compress a
// subset of them. The compression will be performed in a difference Zircon thread, so the pages
// can be given with arbitrary locks held.
class VmDebugCompressor {
public:
VmDebugCompressor() = default;
~VmDebugCompressor();
// Initializes the debug compressor. This method may acquire Mutexes, and so must not be called
// with any spinlocks held. Other methods should not be called before |Init| is called and returns
// ZX_OK.
zx_status_t Init();
// Adds the specified |page| at |offset| in |object| to the debug compressor as a candidate for
// compression. The |page| and |object| must remain valid until |Add| returns. This implies that
// |object| lock must be held, however this cannot be stated with an TA_REQ statement since
// VmCowPages is not declared yet.
void Add(vm_page_t* page, VmCowPages* object, uint64_t offset);
// Pauses the debug compressor such that all future |Add| calls will be ignored. It is an error to
// call |Pause| twice without calling |Resume| in between. Pause might acquire arbitrary VMO and
// other locks and should not be called with other locks held.
void Pause();
// Resumes from a |Pause|, causing calls to |Add| to no longer be ignored. It is an error to call
// |Resume| except after having called |Pause|.
void Resume();
private:
struct Entry {
fbl::RefPtr<VmCowPages> cow;
vm_page_t* page = nullptr;
uint64_t offset = 0;
};
// Entry point for the |thread_| that performs the actual compression.
void CompressThread();
// Helper that Pops an entry from the list_. Will return an entry with a null cow if the list is
// empty.
Entry Pop() TA_EXCL(lock_);
// Shuts down and cleans up |thread_|.
void Shutdown() TA_EXCL(lock_);
// Size of the |list_| that will be allocated.
static constexpr size_t kArraySize = 128;
DECLARE_SPINLOCK(VmDebugCompressor) lock_;
// Reference to the thread that does compression so we can shut it down later.
Thread* thread_ TA_GUARDED(lock_) = nullptr;
// The array of entries is used to transfer pages from the synchronous |Add| call to the
// compression thread. The list is finite and if full pages will be silently dropped, which is
// equivalent to as if those pages were randomly chosen to not be added.
ktl::unique_ptr<fbl::RingBuffer<Entry, kArraySize>> list_ TA_GUARDED(lock_);
enum class State {
Shutdown,
Running,
Paused,
};
// State is initially shutdown to require |Init| to be called.
State state_ TA_GUARDED(lock_) = State::Shutdown;
// Private rng state to avoid further synchronization overhead with the global rand().
uintptr_t rng_state_ TA_GUARDED(lock_) = 0;
// Used to signal the compression thread if the list_ goes from empty->non-empty.
AutounsignalEvent event_;
};
#endif // ZIRCON_KERNEL_VM_INCLUDE_VM_DEBUG_COMPRESSOR_H_