qemu/hw/display/virtio-gpu-udmabuf.c
<<
>>
Prefs
   1/*
   2 * Virtio GPU Device
   3 *
   4 * Copyright Red Hat, Inc. 2013-2014
   5 *
   6 * Authors:
   7 *     Dave Airlie <airlied@redhat.com>
   8 *     Gerd Hoffmann <kraxel@redhat.com>
   9 *
  10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  11 * See the COPYING file in the top-level directory.
  12 */
  13
  14#include "qemu/osdep.h"
  15#include "qemu/units.h"
  16#include "qemu/iov.h"
  17#include "ui/console.h"
  18#include "hw/virtio/virtio-gpu.h"
  19#include "hw/virtio/virtio-gpu-pixman.h"
  20#include "trace.h"
  21#include "exec/ramblock.h"
  22#include "sysemu/hostmem.h"
  23#include <sys/ioctl.h>
  24#include <fcntl.h>
  25#include <linux/memfd.h>
  26#include "qemu/memfd.h"
  27#include "standard-headers/linux/udmabuf.h"
  28
  29static void virtio_gpu_create_udmabuf(struct virtio_gpu_simple_resource *res)
  30{
  31    struct udmabuf_create_list *list;
  32    RAMBlock *rb;
  33    ram_addr_t offset;
  34    int udmabuf, i;
  35
  36    udmabuf = udmabuf_fd();
  37    if (udmabuf < 0) {
  38        return;
  39    }
  40
  41    list = g_malloc0(sizeof(struct udmabuf_create_list) +
  42                     sizeof(struct udmabuf_create_item) * res->iov_cnt);
  43
  44    for (i = 0; i < res->iov_cnt; i++) {
  45        rcu_read_lock();
  46        rb = qemu_ram_block_from_host(res->iov[i].iov_base, false, &offset);
  47        rcu_read_unlock();
  48
  49        if (!rb || rb->fd < 0) {
  50            g_free(list);
  51            return;
  52        }
  53
  54        list->list[i].memfd  = rb->fd;
  55        list->list[i].offset = offset;
  56        list->list[i].size   = res->iov[i].iov_len;
  57    }
  58
  59    list->count = res->iov_cnt;
  60    list->flags = UDMABUF_FLAGS_CLOEXEC;
  61
  62    res->dmabuf_fd = ioctl(udmabuf, UDMABUF_CREATE_LIST, list);
  63    if (res->dmabuf_fd < 0) {
  64        warn_report("%s: UDMABUF_CREATE_LIST: %s", __func__,
  65                    strerror(errno));
  66    }
  67    g_free(list);
  68}
  69
  70static void virtio_gpu_remap_udmabuf(struct virtio_gpu_simple_resource *res)
  71{
  72    res->remapped = mmap(NULL, res->blob_size, PROT_READ,
  73                         MAP_SHARED, res->dmabuf_fd, 0);
  74    if (res->remapped == MAP_FAILED) {
  75        warn_report("%s: dmabuf mmap failed: %s", __func__,
  76                    strerror(errno));
  77        res->remapped = NULL;
  78    }
  79}
  80
  81static void virtio_gpu_destroy_udmabuf(struct virtio_gpu_simple_resource *res)
  82{
  83    if (res->remapped) {
  84        munmap(res->remapped, res->blob_size);
  85        res->remapped = NULL;
  86    }
  87    if (res->dmabuf_fd >= 0) {
  88        close(res->dmabuf_fd);
  89        res->dmabuf_fd = -1;
  90    }
  91}
  92
  93static int find_memory_backend_type(Object *obj, void *opaque)
  94{
  95    bool *memfd_backend = opaque;
  96    int ret;
  97
  98    if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) {
  99        HostMemoryBackend *backend = MEMORY_BACKEND(obj);
 100        RAMBlock *rb = backend->mr.ram_block;
 101
 102        if (rb && rb->fd > 0) {
 103            ret = fcntl(rb->fd, F_GET_SEALS);
 104            if (ret > 0) {
 105                *memfd_backend = true;
 106            }
 107        }
 108    }
 109
 110    return 0;
 111}
 112
 113bool virtio_gpu_have_udmabuf(void)
 114{
 115    Object *memdev_root;
 116    int udmabuf;
 117    bool memfd_backend = false;
 118
 119    udmabuf = udmabuf_fd();
 120    if (udmabuf < 0) {
 121        return false;
 122    }
 123
 124    memdev_root = object_resolve_path("/objects", NULL);
 125    object_child_foreach(memdev_root, find_memory_backend_type, &memfd_backend);
 126
 127    return memfd_backend;
 128}
 129
 130void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res)
 131{
 132    void *pdata = NULL;
 133
 134    res->dmabuf_fd = -1;
 135    if (res->iov_cnt == 1) {
 136        pdata = res->iov[0].iov_base;
 137    } else {
 138        virtio_gpu_create_udmabuf(res);
 139        if (res->dmabuf_fd < 0) {
 140            return;
 141        }
 142        virtio_gpu_remap_udmabuf(res);
 143        if (!res->remapped) {
 144            return;
 145        }
 146        pdata = res->remapped;
 147    }
 148
 149    res->blob = pdata;
 150}
 151
 152void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource *res)
 153{
 154    if (res->remapped) {
 155        virtio_gpu_destroy_udmabuf(res);
 156    }
 157}
 158
 159static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf)
 160{
 161    struct virtio_gpu_scanout *scanout;
 162
 163    scanout = &g->parent_obj.scanout[dmabuf->scanout_id];
 164    dpy_gl_release_dmabuf(scanout->con, &dmabuf->buf);
 165    QTAILQ_REMOVE(&g->dmabuf.bufs, dmabuf, next);
 166    g_free(dmabuf);
 167}
 168
 169static VGPUDMABuf
 170*virtio_gpu_create_dmabuf(VirtIOGPU *g,
 171                          uint32_t scanout_id,
 172                          struct virtio_gpu_simple_resource *res,
 173                          struct virtio_gpu_framebuffer *fb,
 174                          struct virtio_gpu_rect *r)
 175{
 176    VGPUDMABuf *dmabuf;
 177
 178    if (res->dmabuf_fd < 0) {
 179        return NULL;
 180    }
 181
 182    dmabuf = g_new0(VGPUDMABuf, 1);
 183    dmabuf->buf.width = fb->width;
 184    dmabuf->buf.height = fb->height;
 185    dmabuf->buf.stride = fb->stride;
 186    dmabuf->buf.x = r->x;
 187    dmabuf->buf.y = r->y;
 188    dmabuf->buf.scanout_width = r->width;
 189    dmabuf->buf.scanout_height = r->height;
 190    dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format);
 191    dmabuf->buf.fd = res->dmabuf_fd;
 192    dmabuf->buf.allow_fences = true;
 193    dmabuf->buf.draw_submitted = false;
 194    dmabuf->scanout_id = scanout_id;
 195    QTAILQ_INSERT_HEAD(&g->dmabuf.bufs, dmabuf, next);
 196
 197    return dmabuf;
 198}
 199
 200int virtio_gpu_update_dmabuf(VirtIOGPU *g,
 201                             uint32_t scanout_id,
 202                             struct virtio_gpu_simple_resource *res,
 203                             struct virtio_gpu_framebuffer *fb,
 204                             struct virtio_gpu_rect *r)
 205{
 206    struct virtio_gpu_scanout *scanout = &g->parent_obj.scanout[scanout_id];
 207    VGPUDMABuf *new_primary, *old_primary = NULL;
 208
 209    new_primary = virtio_gpu_create_dmabuf(g, scanout_id, res, fb, r);
 210    if (!new_primary) {
 211        return -EINVAL;
 212    }
 213
 214    if (g->dmabuf.primary[scanout_id]) {
 215        old_primary = g->dmabuf.primary[scanout_id];
 216    }
 217
 218    g->dmabuf.primary[scanout_id] = new_primary;
 219    qemu_console_resize(scanout->con,
 220                        new_primary->buf.scanout_width,
 221                        new_primary->buf.scanout_height);
 222    dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf);
 223
 224    if (old_primary) {
 225        virtio_gpu_free_dmabuf(g, old_primary);
 226    }
 227
 228    return 0;
 229}
 230