// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_ASH_COMPONENTS_MEMORY_USERSPACE_SWAP_USERSPACE_SWAP_H_
#define CHROMEOS_ASH_COMPONENTS_MEMORY_USERSPACE_SWAP_USERSPACE_SWAP_H_
#include <sys/mman.h>
#include <cstdint>
#include <vector>
#include "base/component_export.h"
#include "base/process/process_handle.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "chromeos/ash/components/memory/userspace_swap/region.h"
#include "chromeos/ash/components/memory/userspace_swap/userspace_swap.mojom.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h"
#ifndef MREMAP_DONTUNMAP
#define MREMAP_DONTUNMAP 4
#endif
namespace userspace_swap {
namespace mojom {
class UserspaceSwap;
} // namespace mojom
} // namespace userspace_swap
// This file is for containing the browser and renderer common userspace swap
// components such as helper functions and structures.
namespace ash {
namespace memory {
namespace userspace_swap {
class UserfaultFD;
class SwapFile;
// UserspaceSwapConfig is a structure which contains all configuration values
// for userspace swap.
struct COMPONENT_EXPORT(USERSPACE_SWAP) UserspaceSwapConfig {
UserspaceSwapConfig();
UserspaceSwapConfig(const UserspaceSwapConfig& other);
// Returns the Current UserspaceSwapConfig.
static const UserspaceSwapConfig& Get();
friend std::ostream& operator<<(std::ostream& out,
const UserspaceSwapConfig& c);
// enabled is true if the userspace swap feature is enabled.
bool enabled;
// Number of pages per region represents the number of pages we will use for
// each chunk we attempt to swap at a time.
uint16_t number_of_pages_per_region;
// If true the swap file will be compressed on disk.
bool use_compressed_swap_file;
// Minimum disk space swap available is the lower limit of free disk space on
// the swap device. If the available space on the device backing storage
// is lower than this value no further swapping is allowed, this prevents
// userspace swap from exhausting disk space.
uint64_t minimum_swap_disk_space_available;
// Maximum swap disk space represents the maxmium disk space userspace swap is
// allowed to use across all renderers.
uint64_t maximum_swap_disk_space_bytes;
// Renderer maximum disk file size represents the maximum size a swap file may
// for an individual renderer.
uint64_t renderer_maximum_disk_swap_file_size_bytes;
// Renderer region limit per swap limits the number of regions that that an
// individual renderer can swap on each swap, note that each region is
// configured by the number of pages per region so these two together limit
// the total number of pages per swap round of a process.
uint32_t renderer_region_limit_per_swap;
// The blocked refault time is the minimum time a region must be swapped out
// without being blocked. This prevents disk thrashing where if a region
// is immediately refaulted we don't want to swap it again as it'll likely be
// needed in the future, for example, if a region has a blocked refault time
// of 30s if it is refaulted in less than 30s it will never be swapped again.
base::TimeDelta blocked_refault_time;
// Graph walk frequency represents the (shortest) duration in which you can
// walk the graph, that is, a graph walk frequency of 60s means that you will
// not walk the graph more than once every 60s.
base::TimeDelta graph_walk_frequency;
// The process Swap frequency limits the frequency on which a process may be
// swapped, for example 60s means that a process will not be swapped more than
// once every 60s.
base::TimeDelta process_swap_frequency;
// Invisible Time Before Swap is the amount of time a renderer must be
// invisible before it can be considered for swap.
base::TimeDelta invisible_time_before_swap;
// Swap on freeze, if true will swap a process when all frames are frozen.
bool swap_on_freeze;
// Swap on moderate pressure will walk the graph (based on the frequency of
// graph walk frequency) looking for renderers to swap based on visibility
// state.
bool swap_on_moderate_pressure;
// Shuffle maps order will randomly shuffle the processes maps ordering before
// swapping, it does this so that subsequent swaps can start from different
// memory regions.
bool shuffle_maps_on_swap;
};
// Returns true if the kernel supports all the features necessary for userspace
// swap. These features are userfaultfd(2) and mremap(2) with MREMAP_DONTUNMAP
// support this method is the source of truth for the browser UserspaceSwap and
// the rendererer UserspaceSwapImpl.
COMPONENT_EXPORT(USERSPACE_SWAP) bool KernelSupportsUserspaceSwap();
// Returns true if there is kernel support for userspace swap and the feature is
// enabled.
COMPONENT_EXPORT(USERSPACE_SWAP) bool UserspaceSwapSupportedAndEnabled();
// GetGlobalSwapDiskspaceUsed returns the number of bytes currently on disk for
// ALL renderers.
COMPONENT_EXPORT(USERSPACE_SWAP) uint64_t GetGlobalSwapDiskspaceUsed();
// GetGlobalMemoryReclaimed returns the number of bytes (physical memory) which
// has been reclaimed by userspace swap. This number may not match what is on
// disk due to encryption and compression.
COMPONENT_EXPORT(USERSPACE_SWAP) uint64_t GetGlobalMemoryReclaimed();
// DisableSwapGlobally is the global swap kill switch, it prevents any further
// swapping.
COMPONENT_EXPORT(USERSPACE_SWAP) void DisableSwapGlobally();
// Returns true if swap is allowed (globally).
COMPONENT_EXPORT(USERSPACE_SWAP) bool IsSwapAllowedGlobally();
// RendererSwapData is attached to a ProcessNode and owned by the ProcessNode on
// the PerformanceManager graph.
class COMPONENT_EXPORT(USERSPACE_SWAP) RendererSwapData {
public:
virtual ~RendererSwapData();
static std::unique_ptr<RendererSwapData> Create(
int render_process_host_id,
base::ProcessId pid,
std::unique_ptr<UserfaultFD> uffd,
std::unique_ptr<SwapFile> swap_file,
const Region& swap_remap_area,
mojo::PendingRemote<::userspace_swap::mojom::UserspaceSwap> remote);
// Returns the Render Process Host ID associated with this RendererSwapData.
virtual int render_process_host_id() const = 0;
// If true this renderer has not been disallowed swap.
virtual bool SwapAllowed() const = 0;
// DisallowSwap prevents further swapping of this renderer. This cannot be
// unset.
virtual void DisallowSwap() = 0;
// There is a subtle difference between SwapdiskspaceWrittenBytes and
// SwapDiskspaceUsedBytes. Because punching a hole in a file may not reclaim a
// block on disk only after the entire block has been punched will the space
// actually be reclaimed on disk. However, SwapDiskspaceWrittenBytes will
// contain the total number of bytes that we think are on disk, these numbers
// will be equal when there is no waste of block space on disk.
virtual uint64_t SwapDiskspaceWrittenBytes() const = 0;
virtual uint64_t SwapDiskspaceUsedBytes() const = 0;
virtual uint64_t ReclaimedBytes() const = 0;
protected:
RendererSwapData();
};
// SwapRenderer will initiate a swap on the renderer belonging to the
// RendererSwapData |data|. |size_limit_bytes| is a limit imposed by the system
// based on settings.
COMPONENT_EXPORT(USERSPACE_SWAP)
bool SwapRenderer(RendererSwapData* data, size_t size_limit_bytes);
// GetPartitionAllocSuperPagesInUse will return |max_superpages| worth of
// regions that are currently allocated by PartitionAlloc.
COMPONENT_EXPORT(USERSPACE_SWAP)
bool GetPartitionAllocSuperPagesInUse(
int32_t max_superpages,
std::vector<::userspace_swap::mojom::MemoryRegionPtr>& regions);
} // namespace userspace_swap
} // namespace memory
} // namespace ash
#endif // CHROMEOS_ASH_COMPONENTS_MEMORY_USERSPACE_SWAP_USERSPACE_SWAP_H_