chromium/ui/ozone/platform/drm/gpu/page_flip_watchdog.h

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_OZONE_PLATFORM_DRM_GPU_PAGE_FLIP_WATCHDOG_H_
#define UI_OZONE_PLATFORM_DRM_GPU_PAGE_FLIP_WATCHDOG_H_

#include "base/containers/ring_buffer.h"
#include "base/timer/timer.h"

namespace ui {

// The maximum amount of time we will wait for a new modeset attempt before we
// crash the GPU process.
constexpr base::TimeDelta kWaitForModesetTimeout = base::Seconds(15);

// Number of historical page flip statuses to track.
//
// This number was chosen to collect a large enough sample to detect real
// errors, and with some consideration to the fact that pending page flips can
// be buffered upstream of OzoneDRM.
constexpr size_t kPageFlipWatcherHistorySize = 80;

// Number of times the plane assignment can fail and then succeed within
// |kPageFlipWatcherHistorySize| flip attempts. Exceeding this number will crash
// the process immediately.
//
// For instance, if S is a successful assignment and F is a failure,
// S-S-F-S-S-F-S would count as two flakes.
//
// This number was chosen with consideration of a pending page flip buffer
// upstream that might fill with unflippable plane assignments.
constexpr uint32_t kPlaneAssignmentFlakeThreshold = 3;

// Number of failures permitted by the watchdog, within
// |kPageFlipWatcherHistorySize| flip attempts. Exceeding this number will crash
// the process immediately.
//
// For instance, if S is a successful assignment and F is a failure,
// S-F-S-F-F-F-F-S-S would lead to a crash.
//
// This number was chosen with consideration of a pending page flip buffer
// upstream that might fill with unflippable plane assignments.
constexpr uint32_t kPlaneAssignmentMaximumFailures = 20;

// Tracks the failures and successes of interactions with DRM and handles
// unrecoverable errors by crashing the process.
class PageFlipWatchdog {
 public:
  PageFlipWatchdog();

  PageFlipWatchdog(const PageFlipWatchdog&) = delete;
  PageFlipWatchdog& operator=(const PageFlipWatchdog&) = delete;

  ~PageFlipWatchdog();

  // Notify the watchdog that a successful page flip submission has happened.
  // Ratio of successful and unsuccessful flips are used to determine whether to
  // start the crash timer in |MaybeArmForFailedPlaneAssignment|.
  void OnSuccessfulPageFlip();

  // Called when OzoneDRM can't assign planes for a frame. This may start the
  // crash countdown timer, but this failure is usually intermittent, so
  // we will only start the countdown timer when we see a large percentage of
  // failures.
  void CrashOnFailedPlaneAssignment();
  // Called when an actual DRM commit failed. This will start the crash
  // countdown timer.
  void ArmForFailedCommit();
  // This will reset the destruction countdown timer.
  void Disarm();

 private:
  void StartCrashGpuTimer();

  // Used to crash the GPU process if a page flip commit fails and no new
  // modeset attempts come in.
  base::OneShotTimer crash_gpu_timer_;
  base::RingBuffer<bool, kPageFlipWatcherHistorySize> page_flip_status_tracker_;
  int16_t failed_page_flip_counter_ = 0;
};

}  // namespace ui

#endif  // UI_OZONE_PLATFORM_DRM_GPU_PAGE_FLIP_WATCHDOG_H_