KWin
Loading...
Searching...
No Matches
shmclientbuffer.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "config-kwin.h"
8
10#include "wayland/display.h"
12
13#include <drm_fourcc.h>
14#include <fcntl.h>
15#include <mutex>
16#include <signal.h>
17#include <sys/mman.h>
18#include <sys/stat.h>
19
20namespace KWin
21{
22
23static constexpr int s_version = 1;
24
25static constexpr uint32_t s_formats[] = {
26 WL_SHM_FORMAT_ARGB8888,
27 WL_SHM_FORMAT_XRGB8888,
28#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
29 WL_SHM_FORMAT_ARGB2101010,
30 WL_SHM_FORMAT_XRGB2101010,
31 WL_SHM_FORMAT_ABGR2101010,
32 WL_SHM_FORMAT_XBGR2101010,
33 WL_SHM_FORMAT_ABGR16161616,
34 WL_SHM_FORMAT_XBGR16161616,
35#endif
36};
37
39{
40public:
41 ShmPool *pool = nullptr;
42 int accessCount = 0;
43};
44
45static thread_local ShmSigbusData sigbusData;
46static struct sigaction prevSigbusAction;
47
48static uint32_t shmFormatToDrmFormat(uint32_t shmFormat)
49{
50 switch (shmFormat) {
51 case WL_SHM_FORMAT_ARGB8888:
52 return DRM_FORMAT_ARGB8888;
53 case WL_SHM_FORMAT_XRGB8888:
54 return DRM_FORMAT_XRGB8888;
55 default:
56 return shmFormat; // other wl_shm formats match the drm formats
57 }
58}
59
60ShmPool::ShmPool(ShmClientBufferIntegration *integration, wl_client *client, int id, uint32_t version, FileDescriptor &&fd, MemoryMap &&mapping)
61 : QtWaylandServer::wl_shm_pool(client, id, version)
62 , integration(integration)
63 , mapping(std::move(mapping))
64 , fd(std::move(fd))
65{
66#if HAVE_MEMFD
67 const int seals = fcntl(this->fd.get(), F_GET_SEALS);
68 if (seals != -1) {
69 struct stat statbuf;
70 if ((seals & F_SEAL_SHRINK) && fstat(this->fd.get(), &statbuf) >= 0) {
71 sigbusImpossible = statbuf.st_size >= this->mapping.size();
72 }
73 }
74#endif
75}
76
78{
79 ++refCount;
80}
81
83{
84 --refCount;
85 if (refCount == 0) {
86 delete this;
87 }
88}
89
90void ShmPool::shm_pool_destroy_resource(Resource *resource)
91{
92 unref();
93}
94
95void ShmPool::shm_pool_destroy(Resource *resource)
96{
97 wl_resource_destroy(resource->handle);
98}
99
100void ShmPool::shm_pool_create_buffer(Resource *resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format)
101{
102 if (std::find(std::begin(s_formats), std::end(s_formats), format) == std::end(s_formats)) {
103 wl_resource_post_error(resource->handle,
104 WL_SHM_ERROR_INVALID_FORMAT,
105 "invalid format 0x%x",
106 format);
107 return;
108 }
109
110 if (offset < 0 || width <= 0 || height <= 0 || stride < width
111 || INT32_MAX / stride < height || offset > mapping.size() - stride * height) {
112 wl_resource_post_error(resource->handle,
113 WL_SHM_ERROR_INVALID_STRIDE,
114 "invalid width, height or stride (%dx%d, %u)",
115 width, height, stride);
116 return;
117 }
118
119 ShmAttributes attributes{
120 .fd = fd.duplicate(),
121 .stride = stride,
122 .offset = offset,
123 .size = QSize(width, height),
124 .format = shmFormatToDrmFormat(format),
125 };
126
127 new ShmClientBuffer(this, std::move(attributes), resource->client(), id);
128}
129
130void ShmPool::shm_pool_resize(Resource *resource, int32_t size)
131{
132 if (size < mapping.size()) {
133 wl_resource_post_error(resource->handle, WL_SHM_ERROR_INVALID_FD, "shrinking pool invalid");
134 return;
135 }
136
137 auto remapping = MemoryMap(size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0);
138 if (remapping.isValid()) {
139 mapping = std::move(remapping);
140 } else {
141 wl_resource_post_error(resource->handle, WL_SHM_ERROR_INVALID_FD, "failed to map shm pool with the new size");
142 }
143}
144
145void ShmClientBuffer::buffer_destroy_resource(wl_resource *resource)
146{
147 if (ShmClientBuffer *buffer = ShmClientBuffer::get(resource)) {
148 buffer->m_resource = nullptr;
149 buffer->drop();
150 }
151}
152
153void ShmClientBuffer::buffer_destroy(wl_client *client, wl_resource *resource)
154{
155 wl_resource_destroy(resource);
156}
157
158const struct wl_buffer_interface ShmClientBuffer::implementation = {
159 .destroy = buffer_destroy,
160};
161
162ShmClientBuffer::ShmClientBuffer(ShmPool *pool, ShmAttributes attributes, wl_client *client, uint32_t id)
163 : m_shmPool(pool)
164 , m_shmAttributes(std::move(attributes))
165{
166 m_shmPool->ref();
167
168 connect(this, &GraphicsBuffer::released, [this]() {
169 wl_buffer_send_release(m_resource);
170 });
171
172 m_resource = wl_resource_create(client, &wl_buffer_interface, 1, id);
173 wl_resource_set_implementation(m_resource, &implementation, this, buffer_destroy_resource);
174}
175
177{
178 m_shmPool->unref();
179}
180
182{
183 return m_shmAttributes.size;
184}
185
187{
188 return alphaChannelFromDrmFormat(m_shmAttributes.format);
189}
190
192{
193 return &m_shmAttributes;
194}
195
197{
198 if (wl_resource_instance_of(resource, &wl_buffer_interface, &implementation)) {
199 return static_cast<ShmClientBuffer *>(wl_resource_get_user_data(resource));
200 }
201 return nullptr;
202}
203
204static void sigbusHandler(int signum, siginfo_t *info, void *context)
205{
206 auto reraise = [&]() {
207 if (prevSigbusAction.sa_flags & SA_SIGINFO) {
208 prevSigbusAction.sa_sigaction(signum, info, context);
209 } else {
210 prevSigbusAction.sa_handler(signum);
211 }
212 };
213
214 const ShmPool *pool = sigbusData.pool;
215 if (!pool) {
216 reraise();
217 return;
218 }
219
220 const uchar *addr = static_cast<uchar *>(info->si_addr);
221 const uchar *mappingStart = static_cast<uchar *>(pool->mapping.data());
222 if (addr < mappingStart || addr >= mappingStart + pool->mapping.size()) {
223 reraise();
224 return;
225 }
226
227 // Replace the faulty mapping with a new one that's filled with zeros.
228 if (mmap(pool->mapping.data(), pool->mapping.size(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) {
229 reraise();
230 return;
231 }
232}
233
235{
236 if (!m_shmPool->sigbusImpossible) {
237 // A SIGBUS signal may be emitted if the backing file is shrinked and we access now
238 // removed pages. Install a signal handler to handle this case. Note that if the
239 // backing file has F_SEAL_SHRINK seal, then we don't need to do anything.
240
241 static std::once_flag sigbusOnce;
242 std::call_once(sigbusOnce, []() {
243 struct sigaction action;
244 memset(&action, 0, sizeof(action));
245 sigemptyset(&action.sa_mask);
246 action.sa_sigaction = sigbusHandler;
247 action.sa_flags = SA_SIGINFO | SA_NODEFER;
248 sigaction(SIGBUS, &action, &prevSigbusAction);
249 });
250
251 Q_ASSERT(!sigbusData.pool || sigbusData.pool == m_shmPool);
252 sigbusData.pool = m_shmPool;
253 ++sigbusData.accessCount;
254 }
255
256 return Map{
257 .data = reinterpret_cast<uchar *>(m_shmPool->mapping.data()) + m_shmAttributes.offset,
258 .stride = uint32_t(m_shmAttributes.stride),
259 };
260}
261
263{
264 if (m_shmPool->sigbusImpossible) {
265 return;
266 }
267
268 Q_ASSERT(sigbusData.accessCount > 0);
269 --sigbusData.accessCount;
270 if (sigbusData.accessCount == 0) {
271 sigbusData.pool = nullptr;
272 }
273}
274
276 : QtWaylandServer::wl_shm(*display, s_version)
277 , q(q)
278{
279}
280
282{
283 for (const uint32_t &format : s_formats) {
284 send_format(resource->handle, format);
285 }
286}
287
288void ShmClientBufferIntegrationPrivate::shm_create_pool(Resource *resource, uint32_t id, int32_t fd, int32_t size)
289{
290 FileDescriptor fileDescriptor{fd};
291
292 if (size <= 0) {
293 wl_resource_post_error(resource->handle, error_invalid_stride, "invalid size (%d)", size);
294 return;
295 }
296
297 auto mapping = MemoryMap(size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
298 if (!mapping.isValid()) {
299 wl_resource_post_error(resource->handle, error_invalid_fd, "failed to map shm pool");
300 return;
301 }
302
303 new ShmPool(q, resource->client(), id, resource->version(), std::move(fileDescriptor), std::move(mapping));
304}
305
307 : QObject(display)
308 , d(new ShmClientBufferIntegrationPrivate(display, this))
309{
310}
311
315
316} // namespace KWin
317
318#include "moc_shmclientbuffer_p.cpp"
319
320#include "moc_shmclientbuffer.cpp"
Class holding the Wayland server display loop.
Definition display.h:34
FileDescriptor duplicate() const
static bool alphaChannelFromDrmFormat(uint32_t format)
void * data() const
Definition memorymap.h:58
int size() const
Definition memorymap.h:63
ShmClientBufferIntegrationPrivate(Display *display, ShmClientBufferIntegration *q)
void shm_bind_resource(Resource *resource) override
void shm_create_pool(Resource *resource, uint32_t id, int32_t fd, int32_t size) override
void shm_pool_destroy_resource(Resource *resource) override
ShmPool(ShmClientBufferIntegration *integration, wl_client *client, int id, uint32_t version, FileDescriptor &&fd, MemoryMap &&mapping)
void shm_pool_resize(Resource *resource, int32_t size) override
FileDescriptor fd
void shm_pool_create_buffer(Resource *resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format) override
void shm_pool_destroy(Resource *resource) override
constexpr int version
GLenum format
Definition gltexture.cpp:49
FileDescriptor fd
ShmClientBuffer(ShmPool *pool, ShmAttributes attributes, wl_client *client, uint32_t id)
Map map(MapFlags flags) override
static ShmClientBuffer * get(wl_resource *resource)
bool hasAlphaChannel() const override
QSize size() const override
const ShmAttributes * shmAttributes() const override