qemu/contrib/vhost-user-gpu/vugbm.c
<<
>>
Prefs
   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
  13static bool
  14mem_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
  21static void
  22mem_free_bo(struct vugbm_buffer *buf)
  23{
  24    g_free(buf->mmap);
  25}
  26
  27static bool
  28mem_map_bo(struct vugbm_buffer *buf)
  29{
  30    return buf->mmap != NULL;
  31}
  32
  33static void
  34mem_unmap_bo(struct vugbm_buffer *buf)
  35{
  36}
  37
  38static void
  39mem_device_destroy(struct vugbm_device *dev)
  40{
  41}
  42
  43#ifdef CONFIG_MEMFD
  44struct 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
  53static size_t
  54udmabuf_get_size(struct vugbm_buffer *buf)
  55{
  56    return ROUND_UP(buf->width * buf->height * 4, qemu_real_host_page_size);
  57}
  58
  59static bool
  60udmabuf_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
  86static void
  87udmabuf_free_bo(struct vugbm_buffer *buf)
  88{
  89    close(buf->memfd);
  90}
  91
  92static bool
  93udmabuf_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
 104static bool
 105udmabuf_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
 118static void
 119udmabuf_unmap_bo(struct vugbm_buffer *buf)
 120{
 121    munmap(buf->mmap, udmabuf_get_size(buf));
 122}
 123
 124static void
 125udmabuf_device_destroy(struct vugbm_device *dev)
 126{
 127    close(dev->fd);
 128}
 129#endif
 130
 131#ifdef CONFIG_GBM
 132static bool
 133alloc_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
 151static void
 152free_bo(struct vugbm_buffer *buf)
 153{
 154    gbm_bo_destroy(buf->bo);
 155}
 156
 157static bool
 158map_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
 171static void
 172unmap_bo(struct vugbm_buffer *buf)
 173{
 174    gbm_bo_unmap(buf->bo, buf->mmap_data);
 175}
 176
 177static bool
 178get_fd(struct vugbm_buffer *buf, int *fd)
 179{
 180    *fd = gbm_bo_get_fd(buf->bo);
 181
 182    return *fd >= 0;
 183}
 184
 185static void
 186device_destroy(struct vugbm_device *dev)
 187{
 188    gbm_device_destroy(dev->dev);
 189}
 190#endif
 191
 192void
 193vugbm_device_destroy(struct vugbm_device *dev)
 194{
 195    if (!dev->inited) {
 196        return;
 197    }
 198
 199    dev->device_destroy(dev);
 200}
 201
 202void
 203vugbm_device_init(struct vugbm_device *dev, int fd)
 204{
 205    assert(!dev->inited);
 206
 207#ifdef CONFIG_GBM
 208    if (fd >= 0) {
 209        dev->dev = gbm_create_device(fd);
 210    }
 211    if (dev->dev != NULL) {
 212        dev->fd = fd;
 213        dev->alloc_bo = alloc_bo;
 214        dev->free_bo = free_bo;
 215        dev->get_fd = get_fd;
 216        dev->map_bo = map_bo;
 217        dev->unmap_bo = unmap_bo;
 218        dev->device_destroy = device_destroy;
 219        dev->inited = true;
 220    }
 221#endif
 222#ifdef CONFIG_MEMFD
 223    if (!dev->inited && g_file_test("/dev/udmabuf", G_FILE_TEST_EXISTS)) {
 224        dev->fd = open("/dev/udmabuf", O_RDWR);
 225        if (dev->fd >= 0) {
 226            g_debug("Using experimental udmabuf backend");
 227            dev->alloc_bo = udmabuf_alloc_bo;
 228            dev->free_bo = udmabuf_free_bo;
 229            dev->get_fd = udmabuf_get_fd;
 230            dev->map_bo = udmabuf_map_bo;
 231            dev->unmap_bo = udmabuf_unmap_bo;
 232            dev->device_destroy = udmabuf_device_destroy;
 233            dev->inited = true;
 234        }
 235    }
 236#endif
 237    if (!dev->inited) {
 238        g_debug("Using mem fallback");
 239        dev->alloc_bo = mem_alloc_bo;
 240        dev->free_bo = mem_free_bo;
 241        dev->map_bo = mem_map_bo;
 242        dev->unmap_bo = mem_unmap_bo;
 243        dev->device_destroy = mem_device_destroy;
 244        dev->inited = true;
 245    }
 246    assert(dev->inited);
 247}
 248
 249static bool
 250vugbm_buffer_map(struct vugbm_buffer *buf)
 251{
 252    struct vugbm_device *dev = buf->dev;
 253
 254    return dev->map_bo(buf);
 255}
 256
 257static void
 258vugbm_buffer_unmap(struct vugbm_buffer *buf)
 259{
 260    struct vugbm_device *dev = buf->dev;
 261
 262    dev->unmap_bo(buf);
 263}
 264
 265bool
 266vugbm_buffer_can_get_dmabuf_fd(struct vugbm_buffer *buffer)
 267{
 268    if (!buffer->dev->get_fd) {
 269        return false;
 270    }
 271
 272    return true;
 273}
 274
 275bool
 276vugbm_buffer_get_dmabuf_fd(struct vugbm_buffer *buffer, int *fd)
 277{
 278    if (!vugbm_buffer_can_get_dmabuf_fd(buffer) ||
 279        !buffer->dev->get_fd(buffer, fd)) {
 280        g_warning("Failed to get dmabuf");
 281        return false;
 282    }
 283
 284    if (*fd < 0) {
 285        g_warning("error: dmabuf_fd < 0");
 286        return false;
 287    }
 288
 289    return true;
 290}
 291
 292bool
 293vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev,
 294                    uint32_t width, uint32_t height)
 295{
 296    buffer->dev = dev;
 297    buffer->width = width;
 298    buffer->height = height;
 299    buffer->format = GBM_FORMAT_XRGB8888;
 300    buffer->stride = 0; /* modified during alloc */
 301    if (!dev->alloc_bo(buffer)) {
 302        g_warning("alloc_bo failed");
 303        return false;
 304    }
 305
 306    if (!vugbm_buffer_map(buffer)) {
 307        g_warning("map_bo failed");
 308        goto err;
 309    }
 310
 311    return true;
 312
 313err:
 314    dev->free_bo(buffer);
 315    return false;
 316}
 317
 318void
 319vugbm_buffer_destroy(struct vugbm_buffer *buffer)
 320{
 321    struct vugbm_device *dev = buffer->dev;
 322
 323    vugbm_buffer_unmap(buffer);
 324    dev->free_bo(buffer);
 325}
 326