backends/drm: don't remove connectors the kernel doesn't consider removed

Removing connectors that are still powered leads to a mismatch in atomic
commits: the crtc is still powered, but the connector also still there.
If KWin tries to disable the crtc afterwards, the atomic commits fail because
the connector needs to be disabled at the same time and it's missing from the
atomic commit request.

To fix this, whenever we fail to fetch information or get wrong data from
the kernel (like 0 modes), use the cached information instead and keep the
connector.

BUG: 456298
This commit is contained in:
Xaver Hugl 2022-07-01 02:41:06 +02:00
parent 4ced407828
commit a71146c999
2 changed files with 27 additions and 32 deletions

View file

@ -258,44 +258,38 @@ bool DrmGpu::updateOutputs()
return c->id() == currentConnector;
});
DrmConnector *conn = it == m_connectors.constEnd() ? nullptr : *it;
bool updateSuccess = true;
if (!conn) {
conn = new DrmConnector(this, currentConnector);
if (!conn->init()) {
if (!conn->init() || !conn->isConnected()) {
delete conn;
continue;
}
m_connectors << conn;
m_allObjects << conn;
} else if (conn->updateProperties()) {
removedConnectors.removeOne(conn);
} else {
updateSuccess = false;
}
if (conn->isConnected() && updateSuccess) {
if (conn->isNonDesktop() ? !findLeaseOutput(conn->id()) : !findOutput(conn->id())) {
qCDebug(KWIN_DRM, "New %soutput on GPU %s: %s", conn->isNonDesktop() ? "non-desktop " : "", qPrintable(m_devNode), qPrintable(conn->modelName()));
const auto pipeline = conn->pipeline();
m_pipelines << pipeline;
if (conn->isNonDesktop()) {
auto leaseOutput = new DrmLeaseOutput(pipeline, m_leaseDevice);
m_leaseOutputs << leaseOutput;
} else {
auto output = new DrmOutput(pipeline);
m_drmOutputs << output;
m_outputs << output;
addedOutputs << output;
Q_EMIT outputAdded(output);
}
pipeline->setLayers(m_platform->renderBackend()->createPrimaryLayer(pipeline), m_platform->renderBackend()->createCursorLayer(pipeline));
pipeline->setActive(!conn->isNonDesktop());
pipeline->applyPendingChanges();
conn->updateProperties();
if (conn->isConnected()) {
removedConnectors.removeOne(conn);
} else {
continue;
}
} else if (auto output = findOutput(conn->id())) {
removeOutput(output);
} else if (auto leaseOutput = findLeaseOutput(conn->id())) {
removeLeaseOutput(leaseOutput);
}
qCDebug(KWIN_DRM, "New %soutput on GPU %s: %s", conn->isNonDesktop() ? "non-desktop " : "", qPrintable(m_devNode), qPrintable(conn->modelName()));
const auto pipeline = conn->pipeline();
m_pipelines << pipeline;
if (conn->isNonDesktop()) {
auto leaseOutput = new DrmLeaseOutput(pipeline, m_leaseDevice);
m_leaseOutputs << leaseOutput;
} else {
auto output = new DrmOutput(pipeline);
m_drmOutputs << output;
m_outputs << output;
addedOutputs << output;
Q_EMIT outputAdded(output);
}
pipeline->setLayers(m_platform->renderBackend()->createPrimaryLayer(pipeline), m_platform->renderBackend()->createCursorLayer(pipeline));
pipeline->setActive(!conn->isNonDesktop());
pipeline->applyPendingChanges();
}
for (const auto &connector : qAsConst(removedConnectors)) {
if (auto output = findOutput(connector->id())) {

View file

@ -283,11 +283,12 @@ Output::RgbRange DrmConnector::rgbRange() const
bool DrmConnector::updateProperties()
{
if (!DrmObject::updateProperties()) {
if (auto connector = drmModeGetConnector(gpu()->fd(), id())) {
m_conn.reset(connector);
} else if (!m_conn) {
return false;
}
m_conn.reset(drmModeGetConnector(gpu()->fd(), id()));
if (!m_conn) {
if (!DrmObject::updateProperties()) {
return false;
}
if (const auto &dpms = getProp(PropertyIndex::Dpms)) {
@ -337,7 +338,7 @@ bool DrmConnector::updateProperties()
for (int i = 0; equal && i < m_conn->count_modes; i++) {
equal &= checkIfEqual(m_driverModes[i]->nativeMode(), &m_conn->modes[i]);
}
if (!equal) {
if (!equal && (m_driverModes.empty() || m_conn->count_modes > 0)) {
// reload modes
m_driverModes.clear();
for (int i = 0; i < m_conn->count_modes; i++) {