blob: 769e5cd47275be4bba20419331d0d8c209ed726b [file] [log] [blame]
// Copyright 2021 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
#include <debug.h>
#include <inttypes.h>
#include <lib/arch/intrin.h>
#include <lib/console.h>
#include <lib/zircon-internal/macros.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <platform.h>
#include <stdio.h>
#include <string.h>
#include <fbl/algorithm.h>
#include <kernel/event.h>
#include <ktl/iterator.h>
#include <vm/loan_sweeper.h>
#include <vm/physical_page_borrowing_config.h>
#include <vm/pmm.h>
#include <ktl/enforce.h>
namespace {
// Singleton
static LoanSweeper loan_sweeper;
bool loan_sweeper_init_called = false;
} // namespace
static int cmd_ppb(int argc, const cmd_args* argv, uint32_t flags);
STATIC_COMMAND_START
STATIC_COMMAND("ppb", "control contiguous physical page borrowing", &cmd_ppb)
STATIC_COMMAND_END(kernel)
DECLARE_SINGLETON_MUTEX(ppb_stats_lock);
static Thread* ppb_stats_thread TA_GUARDED(ppb_stats_lock::Get()) = nullptr;
static Event ppb_stats_thread_stop_event(false);
static void cmd_ppb_borrowing_on() {
pmm_physical_page_borrowing_config()->set_borrowing_in_supplypages_enabled(true);
pmm_physical_page_borrowing_config()->set_borrowing_on_mru_enabled(true);
printf("borrowing enabled\n");
}
static void cmd_ppb_borrowing_off() {
pmm_physical_page_borrowing_config()->set_borrowing_in_supplypages_enabled(false);
pmm_physical_page_borrowing_config()->set_borrowing_on_mru_enabled(false);
printf("borrowing disabled\n");
}
static void cmd_ppb_loaning_on() {
pmm_physical_page_borrowing_config()->set_loaning_enabled(true);
printf("loaning enabled\n");
}
static void cmd_ppb_loaning_off() {
pmm_physical_page_borrowing_config()->set_loaning_enabled(false);
printf("loaning disabled\n");
}
static void cmd_ppb_sweep() {
if (!loan_sweeper_init_called) {
loan_sweeper_init_called = true;
loan_sweeper.Init();
}
uint64_t freed_page_count = loan_sweeper.ForceSynchronousSweep();
printf("freed_page_count: %" PRIu64 " freed MiB: %" PRIu64 "\n", freed_page_count,
freed_page_count * PAGE_SIZE / MB);
}
static void cmd_ppb_stats() { pmm_print_physical_page_borrowing_stats(); }
static void cmd_ppb_stats_on() {
Thread* local_ppb_stats_thread;
{ // scope guard
Guard<Mutex> guard(ppb_stats_lock::Get());
auto stats_thread = [](void* arg) -> int {
while (true) {
cmd_ppb_stats();
zx_status_t status = ppb_stats_thread_stop_event.Wait(Deadline::after(ZX_SEC(1)));
if (status == ZX_OK) {
return 0;
}
DEBUG_ASSERT(status == ZX_ERR_TIMED_OUT);
}
};
ppb_stats_thread = Thread::Create("ppb-stats-thread", stats_thread, nullptr, LOW_PRIORITY);
ASSERT(ppb_stats_thread);
local_ppb_stats_thread = ppb_stats_thread;
} // ~guard
local_ppb_stats_thread->Resume();
}
static void cmd_ppb_stats_off() {
Thread* local_ppb_stats_thread;
{ // scope guard
Guard<Mutex> guard(ppb_stats_lock::Get());
local_ppb_stats_thread = ppb_stats_thread;
ppb_stats_thread = nullptr;
} // ~guard
ppb_stats_thread_stop_event.Signal();
int retcode;
local_ppb_stats_thread->Join(&retcode, ZX_TIME_INFINITE);
DEBUG_ASSERT(!retcode);
ppb_stats_thread_stop_event.Unsignal();
}
using CmdFunc = void (*)();
struct Cmd {
const char* name;
CmdFunc func;
};
static Cmd commands[] = {
{"borrowing_on", cmd_ppb_borrowing_on},
{"borrowing_off", cmd_ppb_borrowing_off},
{"loaning_on", cmd_ppb_loaning_on},
{"loaning_off", cmd_ppb_loaning_off},
{"sweep", cmd_ppb_sweep},
{"stats", cmd_ppb_stats},
{"stats_on", cmd_ppb_stats_on},
{"stats_off", cmd_ppb_stats_off},
};
// k ppb borrowing_on
// * this is the default on boot
// * enables page borrowing for new allocations (does not sweep)
// * see also k ppb borrowing_off
// k ppb borrowing_off
// * disables page borrowing for new allocations (does not sweep)
// * see also k ppb borrowing_on
// k ppb loaning_on
// * enables loaning when a contiguous VMO pages are decommitted
// k ppb loaning_off
// * disables loaning when a contiguous VMO pages are decommitted
// k ppb sweep
// * If ppb is on, borrows as many pages as possible in a single sweep
// * If ppb is off, un-borrows all borrowed pages (may cause OOM, depending)
// * The sweep also respects non_pager_on / non_pager_off, etc
// k ppb stats
// * output ppb-related stats (once)
// k ppb stats_on
// * repeatedly output ppb-relevant stats (fairly frequently, for observing usage scenarios)
// k ppb stats_off
// * stop repeatedly outputting ppb-relevant stats
static int cmd_ppb(int argc, const cmd_args* argv, uint32_t flags) {
if (argc != 2) {
printf("2 arguments expected\n");
printf("usage:\n");
printf("ppb <cmd>\n");
printf("command list:\n");
for (auto& cmd : commands) {
printf("%s\n", cmd.name);
}
return -1;
}
for (auto& cmd : commands) {
if (!strcmp(argv[1].str, cmd.name)) {
cmd.func();
return 0;
}
}
printf("sub-command not found - available sub-commands:\n");
for (auto& cmd : commands) {
printf("%s\n", cmd.name);
}
return -1;
}