910e62b5创建于 1月15日历史提交
// 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 times the plane assignment can fail and then succeed within
// |kPageFlipWatcherHistorySize| flip attempts on Flex devices.
// 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 is temporarily used as a upper limit while we are conducting
// an experiment to see how often page flips flake on Flex. Flakes tend to
// be more common on Flex as it uses legacy page flips on hardware with
// little-to-no overlap promotion support.
// TODO(crbug.com/371609830): finalize this threshold
// upon experiment completion.
constexpr uint32_t kFlexPlaneAssignmentFlakeThreshold = 10;

// 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;
  uint32_t plane_assignment_flake_threshold = kPlaneAssignmentFlakeThreshold;
};

}  // namespace ui

#endif  // UI_OZONE_PLATFORM_DRM_GPU_PAGE_FLIP_WATCHDOG_H_