platforms/drm: Avoid re-using blobs

Blobs are not reference counted if used by other drm master, if kwin
re-uses a deleted blob in an atomic commit, it will fail. For example,
on my computer, this happens when kwin starts after xorg.

Besides that, kwin may try to destroy blobs that it doesn't own, which
is not fatal but it's strange to do so.

CCBUG: 442603
CCBUG: 439873
This commit is contained in:
Vlad Zahorodnii 2021-09-27 21:43:26 +03:00 committed by Nate Graham
parent 6513c66ca6
commit eb1daa0aad
4 changed files with 40 additions and 80 deletions

View file

@ -153,18 +153,10 @@ bool DrmObject::updateProperties()
continue;
}
if (def.name == prop->name) {
drmModePropertyBlobRes *blob = nullptr;
if (prop->flags & DRM_MODE_PROP_BLOB) {
blob = drmModeGetPropertyBlob(m_gpu->fd(), properties->prop_values[drmPropIndex]);
}
if (m_props[propIndex]) {
if (prop->flags & DRM_MODE_PROP_BLOB) {
m_props[propIndex]->setCurrentBlob(blob);
} else {
m_props[propIndex]->setCurrent(properties->prop_values[drmPropIndex]);
}
} else {
m_props[propIndex] = new Property(this, prop.data(), properties->prop_values[drmPropIndex], def.enumNames, blob);
m_props[propIndex] = new Property(this, prop.data(), properties->prop_values[drmPropIndex], def.enumNames);
}
found = true;
break;
@ -189,15 +181,12 @@ bool DrmObject::updateProperties()
* Definitions for DrmObject::Property
*/
DrmObject::Property::Property(DrmObject *obj, drmModePropertyRes *prop, uint64_t val, const QVector<QByteArray> &enumNames, drmModePropertyBlobRes *blob)
DrmObject::Property::Property(DrmObject *obj, drmModePropertyRes *prop, uint64_t val, const QVector<QByteArray> &enumNames)
: m_propId(prop->prop_id)
, m_propName(prop->name)
, m_pending(val)
, m_pendingBlob(blob)
, m_next(val)
, m_nextBlob(blob)
, m_current(val)
, m_currentBlob(blob)
, m_immutable(prop->flags & DRM_MODE_PROP_IMMUTABLE)
, m_obj(obj)
{
@ -234,8 +223,8 @@ bool DrmObject::Property::setPendingBlob(void *blob, size_t length)
return false;
}
}
if (m_pending && m_pending != m_current && m_pending != m_next) {
drmModeDestroyPropertyBlob(m_obj->m_gpu->fd(), m_pending);
if (m_pendingBlob && m_pendingBlob != m_currentBlob && m_pendingBlob != m_nextBlob) {
drmModeDestroyPropertyBlob(m_obj->m_gpu->fd(), m_pendingBlob->id);
drmModeFreePropertyBlob(m_pendingBlob);
}
m_pending = id;
@ -243,11 +232,6 @@ bool DrmObject::Property::setPendingBlob(void *blob, size_t length)
return true;
}
drmModePropertyBlobRes *DrmObject::Property::pendingBlob() const
{
return m_pendingBlob;
}
void DrmObject::Property::setCurrent(uint64_t value)
{
m_current = value;
@ -260,40 +244,12 @@ uint64_t DrmObject::Property::current() const
void DrmObject::Property::setCurrentBlob(drmModePropertyBlobRes *blob)
{
if (!blob && !m_currentBlob) {
return;
}
if (!blob && m_pending == m_current) {
// the current blob might have been created by the prior drm master
// if it is, then the kernel has destroyed the blob and we must not continue using it
// -> simply create a copy
bool commit = m_pending == m_next;
setPendingBlob(m_currentBlob->data, m_currentBlob->length);
if (commit) {
commitPending();
}
}
if (!blob && m_next == m_current) {
// same as above, for the "next" blob
uint32_t id = 0;
if (drmModeCreatePropertyBlob(m_obj->m_gpu->fd(), m_currentBlob->data, m_currentBlob->length, &id) != 0) {
qCWarning(KWIN_DRM) << "Creating property blob failed!" << strerror(errno);
}
m_next = id;
m_nextBlob = drmModeGetPropertyBlob(m_obj->m_gpu->fd(), id);
}
uint32_t blobId = blob ? blob->id : 0;
if (m_current && m_current != m_pending && m_current != m_next && m_current != blobId) {
drmModeDestroyPropertyBlob(m_obj->m_gpu->fd(), m_current);
if (m_currentBlob && m_currentBlob != m_pendingBlob && m_currentBlob != m_nextBlob && m_currentBlob != blob) {
drmModeDestroyPropertyBlob(m_obj->m_gpu->fd(), m_currentBlob->id);
drmModeFreePropertyBlob(m_currentBlob);
}
m_currentBlob = blob;
m_current = blobId;
}
drmModePropertyBlobRes *DrmObject::Property::currentBlob() const
{
return m_currentBlob;
m_current = blob ? blob->id : 0;
}
void DrmObject::Property::commit()
@ -314,8 +270,8 @@ void DrmObject::Property::commitPending()
return;
}
if (m_pendingBlob || m_nextBlob) {
if (m_next && m_next != m_current) {
drmModeDestroyPropertyBlob(m_obj->m_gpu->fd(), m_next);
if (m_nextBlob && m_nextBlob != m_currentBlob) {
drmModeDestroyPropertyBlob(m_obj->m_gpu->fd(), m_nextBlob->id);
drmModeFreePropertyBlob(m_nextBlob);
}
m_next = m_pending;
@ -331,8 +287,8 @@ void DrmObject::Property::rollbackPending()
return;
}
if (m_pendingBlob || m_nextBlob) {
if (m_pending && m_pending != m_current) {
drmModeDestroyPropertyBlob(m_obj->m_gpu->fd(), m_pending);
if (m_pendingBlob && m_pendingBlob != m_currentBlob) {
drmModeDestroyPropertyBlob(m_obj->m_gpu->fd(), m_pendingBlob->id);
drmModeFreePropertyBlob(m_pendingBlob);
}
m_pending = m_next;

View file

@ -79,7 +79,7 @@ public:
class Property
{
public:
Property(DrmObject *obj, drmModePropertyRes *prop, uint64_t val, const QVector<QByteArray> &enumNames, drmModePropertyBlobRes *blob);
Property(DrmObject *obj, drmModePropertyRes *prop, uint64_t val, const QVector<QByteArray> &enumNames);
virtual ~Property();
void initEnumMap(drmModePropertyRes *prop);
@ -128,12 +128,10 @@ public:
void setPending(uint64_t value);
uint64_t pending() const;
bool setPendingBlob(void *blob, size_t length);
drmModePropertyBlobRes *pendingBlob() const;
void setCurrent(uint64_t value);
uint64_t current() const;
void setCurrentBlob(drmModePropertyBlobRes *blob);
drmModePropertyBlobRes *currentBlob() const;
void commit();
void commitPending();

View file

@ -100,15 +100,18 @@ bool DrmConnector::init()
// parse edid
auto edidProp = getProp(PropertyIndex::Edid);
if (edidProp && edidProp->currentBlob() && edidProp->currentBlob()->data) {
m_edid = Edid(edidProp->currentBlob()->data, edidProp->currentBlob()->length);
if (edidProp) {
DrmScopedPointer<drmModePropertyBlobRes> blob(drmModeGetPropertyBlob(gpu()->fd(), edidProp->current()));
if (blob && blob->data) {
m_edid = Edid(blob->data, blob->length);
if (!m_edid.isValid()) {
qCWarning(KWIN_DRM, "Couldn't parse EDID for connector with id %d", id());
}
}
deleteProp(PropertyIndex::Edid);
} else {
qCDebug(KWIN_DRM) << "Could not find edid for connector" << this;
}
deleteProp(PropertyIndex::Edid);
// check the physical size
if (m_edid.physicalSize().isEmpty()) {

View file

@ -74,7 +74,9 @@ bool DrmPlane::init()
// read formats from blob if available and if modifiers are supported, and from the plane object if not
if (auto formatProp = getProp(PropertyIndex::In_Formats); formatProp && gpu()->addFB2ModifiersSupported() && qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS") == 1) {
auto blob = static_cast<drm_format_modifier_blob*>(formatProp->currentBlob()->data);
DrmScopedPointer<drmModePropertyBlobRes> propertyBlob(drmModeGetPropertyBlob(gpu()->fd(), formatProp->current()));
if (propertyBlob && propertyBlob->data) {
auto blob = static_cast<drm_format_modifier_blob*>(propertyBlob->data);
auto modifiers = reinterpret_cast<drm_format_modifier*>(reinterpret_cast<uint8_t*>(blob) + blob->modifiers_offset);
uint32_t *formatarr = reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(blob) + blob->formats_offset);
@ -94,6 +96,7 @@ bool DrmPlane::init()
}
m_supportedFormats.insert(format, mods);
}
}
} else {
for (uint32_t i = 0; i < p->count_formats; i++) {
m_supportedFormats.insert(p->formats[i], {});