blob: 514cba56dbd224468b257805b74f2ab5bf568c40 [file] [log] [blame]
// Copyright 2020 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_OBJECT_INCLUDE_OBJECT_MEMORY_WATCHDOG_H_
#define ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_MEMORY_WATCHDOG_H_
#include <zircon/boot/crash-reason.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
#include <object/diagnostics.h>
#include <object/event_dispatcher.h>
#include <object/job_dispatcher.h>
#include <object/port_dispatcher.h>
class Executor;
// Object is thread safe.
class MemoryWatchdog {
public:
enum PressureLevel : uint8_t {
kOutOfMemory = 0,
kImminentOutOfMemory,
kCritical,
kWarning,
kNormal,
kNumLevels,
};
// Init must be called before any other methods.
void Init(Executor* executor);
fbl::RefPtr<EventDispatcher> GetMemPressureEvent(uint32_t kind);
uint64_t DebugNumBytesTillPressureLevel(PressureLevel level);
void Dump();
private:
// The callback provided to the |eviction_trigger_| timer.
static void EvictionTriggerCallback(Timer* timer, zx_time_t now, void* arg);
void EvictionTrigger();
void WorkerThread() __NO_RETURN;
// Helper called by the WorkerThread when OOM conditions are hit.
void OnOom();
// Called by the WorkerThread to determine if a kernel event needs to be signaled corresponding to
// pressure change to level |idx|.
inline bool IsSignalDue(PressureLevel idx, zx_time_t time_now) const;
// Called by the WorkerThread to determine if kernel eviction (asynchronous) needs to be triggered
// in response to pressure change to level |idx|.
inline bool IsEvictionRequired(PressureLevel idx) const;
void WaitForMemChange(const Deadline& deadline);
ktl::pair<uint64_t, uint64_t> FreeMemBoundsForLevel(PressureLevel level) const;
PressureLevel CalculatePressureLevel() const;
// Kernel-owned events used to signal userspace at different levels of memory pressure.
ktl::array<fbl::RefPtr<EventDispatcher>, PressureLevel::kNumLevels> mem_pressure_events_;
// Event used for communicating memory state between the mem_avail_state_updated_cb callback and
// the WorkerThread.
AutounsignalEvent mem_state_signal_;
// Relaxed atomic so that debug methods can safely read it.
RelaxedAtomic<PressureLevel> mem_event_idx_ = PressureLevel::kNormal;
PressureLevel prev_mem_event_idx_ = mem_event_idx_;
// Watermark information is not modified after Init and so is safe for multiple threads to access.
static constexpr uint8_t kNumWatermarks = PressureLevel::kNumLevels - 1;
ktl::array<uint64_t, kNumWatermarks> mem_watermarks_;
uint64_t watermark_debounce_ = 0;
// Used to delay signaling memory level transitions in the case of rapid changes.
zx_time_t hysteresis_seconds_ = ZX_SEC(10);
// Tracks last time the memory state was evaluated (and signaled if required).
zx_time_t prev_mem_state_eval_time_ = ZX_TIME_INFINITE_PAST;
// The highest pressure level we trigger eviction at, OOM being the lowest pressure level (0).
PressureLevel max_eviction_level_ = PressureLevel::kCritical;
// The free memory target to aim for when we trigger eviction.
uint64_t free_mem_target_ = 0;
// Current minimum amount of memory we want the triggered eviction to reclaim.
uint64_t min_free_target_ = 0;
// A timer is used to trigger eviction so that user space is given a chance to act upon a memory
// pressure signal first.
Timer eviction_trigger_;
// OneShot eviction strategy only triggers eviction events at memory pressure state transitions.
// Continuous eviction strategy enables continuous background eviction as long the system remains
// under memory pressure (i.e. at a memory pressure level that is eligible for eviction).
enum EvictionStrategy : uint8_t { OneShot, Continuous };
EvictionStrategy eviction_strategy_ = EvictionStrategy::OneShot;
Executor* executor_;
};
MemoryWatchdog& GetMemoryWatchdog();
#endif // ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_MEMORY_WATCHDOG_H_