From 14749e91e962935fe9ad6b3358ec720c3645e8ea Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Mon, 5 Feb 2024 19:28:15 +0100 Subject: [PATCH] backends/drm: try to handle page flips timing out While this should really never happen in the first place, if the kernel still accepts atomic commits, this is better than the screen(s) freezing and never recovering. BUG: 480895 --- src/backends/drm/drm_commit_thread.cpp | 32 ++++++++++++++++++++++---- src/backends/drm/drm_commit_thread.h | 2 +- src/backends/drm/drm_pipeline.cpp | 2 +- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/backends/drm/drm_commit_thread.cpp b/src/backends/drm/drm_commit_thread.cpp index 5123d2d7ba..776d0bdbb4 100644 --- a/src/backends/drm/drm_commit_thread.cpp +++ b/src/backends/drm/drm_commit_thread.cpp @@ -17,8 +17,16 @@ using namespace std::chrono_literals; namespace KWin { -DrmCommitThread::DrmCommitThread(const QString &name) +/** + * This should always be longer than any real pageflip can take, even with PSR and modesets + */ +static constexpr auto s_pageflipTimeout = 5s; + +DrmCommitThread::DrmCommitThread(DrmGpu *gpu, const QString &name) { + if (!gpu->atomicModeSetting()) { + return; + } m_thread.reset(QThread::create([this]() { const auto thread = QThread::currentThread(); gainRealTime(); @@ -27,11 +35,23 @@ DrmCommitThread::DrmCommitThread(const QString &name) return; } std::unique_lock lock(m_mutex); - if (m_commits.empty() || m_committed) { + bool timeout = false; + if (m_committed) { + timeout = m_commitPending.wait_for(lock, s_pageflipTimeout) == std::cv_status::timeout; + } else if (m_commits.empty()) { m_commitPending.wait(lock); } if (m_committed) { // the commit would fail with EBUSY, wait until the pageflip is done + if (timeout) { + qCCritical(KWIN_DRM, "Pageflip timed out! This is a kernel bug"); + std::unique_ptr committed(static_cast(m_committed.release())); + const bool cursorOnly = committed->isCursorOnly(); + m_droppedCommits.push_back(std::move(committed)); + if (!cursorOnly) { + QMetaObject::invokeMethod(this, &DrmCommitThread::commitFailed, Qt::ConnectionType::QueuedConnection); + } + } continue; } if (!m_commits.empty()) { @@ -187,9 +207,11 @@ void DrmCommitThread::optimizeCommits() DrmCommitThread::~DrmCommitThread() { - m_thread->requestInterruption(); - m_commitPending.notify_all(); - m_thread->wait(); + if (m_thread) { + m_thread->requestInterruption(); + m_commitPending.notify_all(); + m_thread->wait(); + } } void DrmCommitThread::addCommit(std::unique_ptr &&commit) diff --git a/src/backends/drm/drm_commit_thread.h b/src/backends/drm/drm_commit_thread.h index a348b9bbee..e39a3dd24d 100644 --- a/src/backends/drm/drm_commit_thread.h +++ b/src/backends/drm/drm_commit_thread.h @@ -28,7 +28,7 @@ class DrmCommitThread : public QObject { Q_OBJECT public: - explicit DrmCommitThread(const QString &name); + explicit DrmCommitThread(DrmGpu *gpu, const QString &name); ~DrmCommitThread(); void addCommit(std::unique_ptr &&commit); diff --git a/src/backends/drm/drm_pipeline.cpp b/src/backends/drm/drm_pipeline.cpp index f757d46d42..e69b21bce6 100644 --- a/src/backends/drm/drm_pipeline.cpp +++ b/src/backends/drm/drm_pipeline.cpp @@ -40,7 +40,7 @@ static const QMap> legacyCursorFormats = {{DRM_FORMAT_ DrmPipeline::DrmPipeline(DrmConnector *conn) : m_connector(conn) - , m_commitThread(std::make_unique(conn->connectorName())) + , m_commitThread(std::make_unique(conn->gpu(), conn->connectorName())) { QObject::connect(m_commitThread.get(), &DrmCommitThread::commitFailed, [this]() { if (m_output) {