blob: f5939c46068bb166337a25c06f5af8bc91dae701 [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.
#include "pager-watchdog.h"
#include <lib/backtrace-request/backtrace-request.h>
#include <lib/zx/status.h>
#include <fs/trace.h>
namespace blobfs {
namespace pager {
PagerWatchdog::PagerWatchdog(zx::duration duration) : duration_(duration) {
thread_ = std::thread(&PagerWatchdog::Thread, this);
}
PagerWatchdog::~PagerWatchdog() {
{
std::scoped_lock lock(mutex_);
terminate_ = true;
}
condition_.notify_all();
thread_.join();
}
void PagerWatchdog::Thread() {
for (;;) {
int deadline_missed = 0;
{
std::scoped_lock lock(mutex_);
if (terminate_) {
return;
}
// See if any deadlines have been exceeded. The order of the list means we only need to check
// the end.
zx::ticks now = zx::ticks::now();
if (token_ && token_->deadline() <= now) {
++deadline_missed;
token_ = nullptr;
}
// If none, wait for the next deadline to expire.
if (deadline_missed == 0) {
if (token_) {
condition_.wait_for(mutex_,
std::chrono::nanoseconds((token_->deadline() - now) * 1'000'000'000 /
zx::ticks::per_second()));
} else {
condition_.wait(mutex_);
}
}
}
// Handle any deadlines that have been missed (outside of the lock).
if (deadline_missed > 0) {
OnDeadlineMissed(deadline_missed);
condition_.notify_all();
}
}
}
PagerWatchdog::ArmToken PagerWatchdog::ArmWithDuration(zx::duration duration) {
// Called from the pager thread. Should avoid blocking.
return ArmToken(*this, duration);
}
void PagerWatchdog::OnDeadlineMissed(int count) {
if (callback_) {
(*callback_)(count);
} else {
backtrace_request();
FS_TRACE_ERROR(
"blobfs: pager exceeded deadline of %lu s for %u request(s). It is likely that other "
"threads on the system\n"
"are stalled on page fault requests.\n",
duration_.to_secs(), count);
}
}
void PagerWatchdog::RunUntilIdle() {
std::scoped_lock lock(mutex_);
while (token_ != nullptr)
condition_.wait(mutex_);
}
PagerWatchdog::ArmToken::ArmToken(PagerWatchdog& watchdog, zx::duration duration)
: watchdog_(watchdog),
deadline_(zx::ticks::now() + zx::ticks::per_second() * duration.to_nsecs() / 1'000'000'000) {
std::scoped_lock lock(watchdog_.mutex_);
ZX_ASSERT(watchdog_.token_ == nullptr);
watchdog_.token_ = this;
watchdog_.condition_.notify_all();
}
PagerWatchdog::ArmToken::~ArmToken() {
std::scoped_lock lock(watchdog_.mutex_);
watchdog_.token_ = nullptr;
watchdog_.condition_.notify_all();
}
} // namespace pager
} // namespace blobfs