xref: /openbmc/qemu/contrib/vhost-user-gpu/vugbm.c (revision 4c4465ff)
1 /*
2  * Virtio vhost-user GPU Device
3  *
4  * DRM helpers
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7  * See the COPYING file in the top-level directory.
8  */
9 
10 #include "qemu/osdep.h"
11 #include "vugbm.h"
12 
13 static bool
14 mem_alloc_bo(struct vugbm_buffer *buf)
15 {
16     buf->mmap = g_malloc(buf->width * buf->height * 4);
17     buf->stride = buf->width * 4;
18     return true;
19 }
20 
21 static void
22 mem_free_bo(struct vugbm_buffer *buf)
23 {
24     g_free(buf->mmap);
25 }
26 
27 static bool
28 mem_map_bo(struct vugbm_buffer *buf)
29 {
30     return buf->mmap != NULL;
31 }
32 
33 static void
34 mem_unmap_bo(struct vugbm_buffer *buf)
35 {
36 }
37 
38 static void
39 mem_device_destroy(struct vugbm_device *dev)
40 {
41 }
42 
43 #ifdef CONFIG_MEMFD
44 struct udmabuf_create {
45         uint32_t memfd;
46         uint32_t flags;
47         uint64_t offset;
48         uint64_t size;
49 };
50 
51 #define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create)
52 
53 static size_t
54 udmabuf_get_size(struct vugbm_buffer *buf)
55 {
56     return ROUND_UP(buf->width * buf->height * 4, qemu_real_host_page_size);
57 }
58 
59 static bool
60 udmabuf_alloc_bo(struct vugbm_buffer *buf)
61 {
62     int ret;
63 
64     buf->memfd = memfd_create("udmabuf-bo", MFD_ALLOW_SEALING);
65     if (buf->memfd < 0) {
66         return false;
67     }
68 
69     ret = ftruncate(buf->memfd, udmabuf_get_size(buf));
70     if (ret < 0) {
71         close(buf->memfd);
72         return false;
73     }
74 
75     ret = fcntl(buf->memfd, F_ADD_SEALS, F_SEAL_SHRINK);
76     if (ret < 0) {
77         close(buf->memfd);
78         return false;
79     }
80 
81     buf->stride = buf->width * 4;
82 
83     return true;
84 }
85 
86 static void
87 udmabuf_free_bo(struct vugbm_buffer *buf)
88 {
89     close(buf->memfd);
90 }
91 
92 static bool
93 udmabuf_map_bo(struct vugbm_buffer *buf)
94 {
95     buf->mmap = mmap(NULL, udmabuf_get_size(buf),
96                      PROT_READ | PROT_WRITE, MAP_SHARED, buf->memfd, 0);
97     if (buf->mmap == MAP_FAILED) {
98         return false;
99     }
100 
101     return true;
102 }
103 
104 static bool
105 udmabuf_get_fd(struct vugbm_buffer *buf, int *fd)
106 {
107     struct udmabuf_create create = {
108         .memfd = buf->memfd,
109         .offset = 0,
110         .size = udmabuf_get_size(buf),
111     };
112 
113     *fd = ioctl(buf->dev->fd, UDMABUF_CREATE, &create);
114 
115     return *fd >= 0;
116 }
117 
118 static void
119 udmabuf_unmap_bo(struct vugbm_buffer *buf)
120 {
121     munmap(buf->mmap, udmabuf_get_size(buf));
122 }
123 
124 static void
125 udmabuf_device_destroy(struct vugbm_device *dev)
126 {
127     close(dev->fd);
128 }
129 #endif
130 
131 #ifdef CONFIG_GBM
132 static bool
133 alloc_bo(struct vugbm_buffer *buf)
134 {
135     struct gbm_device *dev = buf->dev->dev;
136 
137     assert(!buf->bo);
138 
139     buf->bo = gbm_bo_create(dev, buf->width, buf->height,
140                             buf->format,
141                             GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
142 
143     if (buf->bo) {
144         buf->stride = gbm_bo_get_stride(buf->bo);
145         return true;
146     }
147 
148     return false;
149 }
150 
151 static void
152 free_bo(struct vugbm_buffer *buf)
153 {
154     gbm_bo_destroy(buf->bo);
155 }
156 
157 static bool
158 map_bo(struct vugbm_buffer *buf)
159 {
160     uint32_t stride;
161 
162     buf->mmap = gbm_bo_map(buf->bo, 0, 0, buf->width, buf->height,
163                            GBM_BO_TRANSFER_READ_WRITE, &stride,
164                            &buf->mmap_data);
165 
166     assert(stride == buf->stride);
167 
168     return buf->mmap != NULL;
169 }
170 
171 static void
172 unmap_bo(struct vugbm_buffer *buf)
173 {
174     gbm_bo_unmap(buf->bo, buf->mmap_data);
175 }
176 
177 static bool
178 get_fd(struct vugbm_buffer *buf, int *fd)
179 {
180     *fd = gbm_bo_get_fd(buf->bo);
181 
182     return *fd >= 0;
183 }
184 
185 static void
186 device_destroy(struct vugbm_device *dev)
187 {
188     gbm_device_destroy(dev->dev);
189 }
190 #endif
191 
192 void
193 vugbm_device_destroy(struct vugbm_device *dev)
194 {
195     if (!dev->inited) {
196         return;
197     }
198 
199     dev->device_destroy(dev);
200 }
201 
202 bool
203 vugbm_device_init(struct vugbm_device *dev, int fd)
204 {
205     dev->fd = fd;
206 
207 #ifdef CONFIG_GBM
208     dev->dev = gbm_create_device(fd);
209 #endif
210 
211     if (0) {
212         /* nothing */
213     }
214 #ifdef CONFIG_GBM
215     else if (dev->dev != NULL) {
216         dev->alloc_bo = alloc_bo;
217         dev->free_bo = free_bo;
218         dev->get_fd = get_fd;
219         dev->map_bo = map_bo;
220         dev->unmap_bo = unmap_bo;
221         dev->device_destroy = device_destroy;
222     }
223 #endif
224 #ifdef CONFIG_MEMFD
225     else if (g_file_test("/dev/udmabuf", G_FILE_TEST_EXISTS)) {
226         dev->fd = open("/dev/udmabuf", O_RDWR);
227         if (dev->fd < 0) {
228             return false;
229         }
230         g_debug("Using experimental udmabuf backend");
231         dev->alloc_bo = udmabuf_alloc_bo;
232         dev->free_bo = udmabuf_free_bo;
233         dev->get_fd = udmabuf_get_fd;
234         dev->map_bo = udmabuf_map_bo;
235         dev->unmap_bo = udmabuf_unmap_bo;
236         dev->device_destroy = udmabuf_device_destroy;
237     }
238 #endif
239     else {
240         g_debug("Using mem fallback");
241         dev->alloc_bo = mem_alloc_bo;
242         dev->free_bo = mem_free_bo;
243         dev->map_bo = mem_map_bo;
244         dev->unmap_bo = mem_unmap_bo;
245         dev->device_destroy = mem_device_destroy;
246         return false;
247     }
248 
249     dev->inited = true;
250     return true;
251 }
252 
253 static bool
254 vugbm_buffer_map(struct vugbm_buffer *buf)
255 {
256     struct vugbm_device *dev = buf->dev;
257 
258     return dev->map_bo(buf);
259 }
260 
261 static void
262 vugbm_buffer_unmap(struct vugbm_buffer *buf)
263 {
264     struct vugbm_device *dev = buf->dev;
265 
266     dev->unmap_bo(buf);
267 }
268 
269 bool
270 vugbm_buffer_can_get_dmabuf_fd(struct vugbm_buffer *buffer)
271 {
272     if (!buffer->dev->get_fd) {
273         return false;
274     }
275 
276     return true;
277 }
278 
279 bool
280 vugbm_buffer_get_dmabuf_fd(struct vugbm_buffer *buffer, int *fd)
281 {
282     if (!vugbm_buffer_can_get_dmabuf_fd(buffer) ||
283         !buffer->dev->get_fd(buffer, fd)) {
284         g_warning("Failed to get dmabuf");
285         return false;
286     }
287 
288     if (*fd < 0) {
289         g_warning("error: dmabuf_fd < 0");
290         return false;
291     }
292 
293     return true;
294 }
295 
296 bool
297 vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev,
298                     uint32_t width, uint32_t height)
299 {
300     buffer->dev = dev;
301     buffer->width = width;
302     buffer->height = height;
303     buffer->format = GBM_FORMAT_XRGB8888;
304     buffer->stride = 0; /* modified during alloc */
305     if (!dev->alloc_bo(buffer)) {
306         g_warning("alloc_bo failed");
307         return false;
308     }
309 
310     if (!vugbm_buffer_map(buffer)) {
311         g_warning("map_bo failed");
312         goto err;
313     }
314 
315     return true;
316 
317 err:
318     dev->free_bo(buffer);
319     return false;
320 }
321 
322 void
323 vugbm_buffer_destroy(struct vugbm_buffer *buffer)
324 {
325     struct vugbm_device *dev = buffer->dev;
326 
327     vugbm_buffer_unmap(buffer);
328     dev->free_bo(buffer);
329 }
330