chromium/chromeos/ash/components/memory/userspace_swap/userspace_swap.h

// 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_