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