qemu/hw/display/virtio-gpu-udmabuf.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

226 lines
5.9 KiB
C
Raw Normal View History

/*
* Virtio GPU Device
*
* Copyright Red Hat, Inc. 2013-2014
*
* Authors:
* Dave Airlie <airlied@redhat.com>
* Gerd Hoffmann <kraxel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qemu/units.h"
#include "qemu/iov.h"
#include "ui/console.h"
#include "hw/virtio/virtio-gpu.h"
#include "hw/virtio/virtio-gpu-pixman.h"
#include "trace.h"
#include "exec/ramblock.h"
#include "sysemu/hostmem.h"
#include <sys/ioctl.h>
#include <linux/memfd.h>
#include "qemu/memfd.h"
#include "standard-headers/linux/udmabuf.h"
static void virtio_gpu_create_udmabuf(struct virtio_gpu_simple_resource *res)
{
struct udmabuf_create_list *list;
RAMBlock *rb;
ram_addr_t offset;
int udmabuf, i;
udmabuf = udmabuf_fd();
if (udmabuf < 0) {
return;
}
list = g_malloc0(sizeof(struct udmabuf_create_list) +
sizeof(struct udmabuf_create_item) * res->iov_cnt);
for (i = 0; i < res->iov_cnt; i++) {
rcu_read_lock();
rb = qemu_ram_block_from_host(res->iov[i].iov_base, false, &offset);
rcu_read_unlock();
if (!rb || rb->fd < 0) {
g_free(list);
return;
}
list->list[i].memfd = rb->fd;
list->list[i].offset = offset;
list->list[i].size = res->iov[i].iov_len;
}
list->count = res->iov_cnt;
list->flags = UDMABUF_FLAGS_CLOEXEC;
res->dmabuf_fd = ioctl(udmabuf, UDMABUF_CREATE_LIST, list);
if (res->dmabuf_fd < 0) {
warn_report("%s: UDMABUF_CREATE_LIST: %s", __func__,
strerror(errno));
}
g_free(list);
}
static void virtio_gpu_remap_udmabuf(struct virtio_gpu_simple_resource *res)
{
res->remapped = mmap(NULL, res->blob_size, PROT_READ,
MAP_SHARED, res->dmabuf_fd, 0);
if (res->remapped == MAP_FAILED) {
warn_report("%s: dmabuf mmap failed: %s", __func__,
strerror(errno));
res->remapped = NULL;
}
}
static void virtio_gpu_destroy_udmabuf(struct virtio_gpu_simple_resource *res)
{
if (res->remapped) {
munmap(res->remapped, res->blob_size);
res->remapped = NULL;
}
if (res->dmabuf_fd >= 0) {
close(res->dmabuf_fd);
res->dmabuf_fd = -1;
}
}
static int find_memory_backend_type(Object *obj, void *opaque)
{
bool *memfd_backend = opaque;
int ret;
if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) {
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
RAMBlock *rb = backend->mr.ram_block;
if (rb && rb->fd > 0) {
ret = fcntl(rb->fd, F_GET_SEALS);
if (ret > 0) {
*memfd_backend = true;
}
}
}
return 0;
}
bool virtio_gpu_have_udmabuf(void)
{
Object *memdev_root;
int udmabuf;
bool memfd_backend = false;
udmabuf = udmabuf_fd();
if (udmabuf < 0) {
return false;
}
memdev_root = object_resolve_path("/objects", NULL);
object_child_foreach(memdev_root, find_memory_backend_type, &memfd_backend);
return memfd_backend;
}
void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res)
{
void *pdata = NULL;
res->dmabuf_fd = -1;
if (res->iov_cnt == 1 &&
res->iov[0].iov_len < 4096) {
pdata = res->iov[0].iov_base;
} else {
virtio_gpu_create_udmabuf(res);
if (res->dmabuf_fd < 0) {
return;
}
virtio_gpu_remap_udmabuf(res);
if (!res->remapped) {
return;
}
pdata = res->remapped;
}
res->blob = pdata;
}
void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource *res)
{
if (res->remapped) {
virtio_gpu_destroy_udmabuf(res);
}
}
static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf)
{
struct virtio_gpu_scanout *scanout;
scanout = &g->parent_obj.scanout[dmabuf->scanout_id];
ui/console: Use qemu_dmabuf_new() and free() helpers instead This commit introduces utility functions for the creation and deallocation of QemuDmaBuf instances. Additionally, it updates all relevant sections of the codebase to utilize these new utility functions. v7: remove prefix, "dpy_gl_" from all helpers qemu_dmabuf_free() returns without doing anything if input is null (Daniel P. Berrangé <berrange@redhat.com>) call G_DEFINE_AUTOPTR_CLEANUP_FUNC for qemu_dmabuf_free() (Daniel P. Berrangé <berrange@redhat.com>) v8: Introduction of helpers was removed as those were already added by the previous commit v9: set dmabuf->allow_fences to 'true' when dmabuf is created in virtio_gpu_create_dmabuf()/virtio-gpu-udmabuf.c removed unnecessary spaces were accidently added in the patch, 'ui/console: Use qemu_dmabuf_new() a...' v11: Calling qemu_dmabuf_close was removed as closing dmabuf->fd will be done in qemu_dmabuf_free anyway. (Daniel P. Berrangé <berrange@redhat.com>) v12: --- Calling qemu_dmabuf_close separately as qemu_dmabuf_free doesn't do it. --- 'dmabuf' is now allocated space so it should be freed at the end of dbus_scanout_texture v13: --- Immediately free dmabuf after it is released to prevent possible leaking of the ptr (Marc-André Lureau <marcandre.lureau@redhat.com>) --- Use g_autoptr macro to define *dmabuf for auto clean up instead of calling qemu_dmabuf_free (Marc-André Lureau <marcandre.lureau@redhat.com>) v14: --- (vhost-user-gpu) Change qemu_dmabuf_free back to g_clear_pointer as it was done because of some misunderstanding (v13). --- (vhost-user-gpu) g->dmabuf[m->scanout_id] needs to be set to NULL to prevent freed dmabuf to be accessed again in case if(fd==-1)break; happens (before new dmabuf is allocated). Otherwise, it would cause invalid memory access when the same function is executed. Also NULL check should be done before qemu_dmabuf_close (it asserts dmabuf!=NULL.). (Marc-André Lureau <marcandre.lureau@redhat.com>) Suggested-by: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Philippe Mathieu-Daudé <philmd@linaro.org> Cc: Daniel P. Berrangé <berrange@redhat.com> Cc: Vivek Kasireddy <vivek.kasireddy@intel.com> Signed-off-by: Dongwon Kim <dongwon.kim@intel.com> Message-Id: <20240508175403.3399895-6-dongwon.kim@intel.com>
2024-05-08 20:54:02 +03:00
dpy_gl_release_dmabuf(scanout->con, dmabuf->buf);
g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free);
QTAILQ_REMOVE(&g->dmabuf.bufs, dmabuf, next);
g_free(dmabuf);
}
static VGPUDMABuf
*virtio_gpu_create_dmabuf(VirtIOGPU *g,
uint32_t scanout_id,
struct virtio_gpu_simple_resource *res,
struct virtio_gpu_framebuffer *fb,
struct virtio_gpu_rect *r)
{
VGPUDMABuf *dmabuf;
if (res->dmabuf_fd < 0) {
return NULL;
}
dmabuf = g_new0(VGPUDMABuf, 1);
ui/console: Use qemu_dmabuf_new() and free() helpers instead This commit introduces utility functions for the creation and deallocation of QemuDmaBuf instances. Additionally, it updates all relevant sections of the codebase to utilize these new utility functions. v7: remove prefix, "dpy_gl_" from all helpers qemu_dmabuf_free() returns without doing anything if input is null (Daniel P. Berrangé <berrange@redhat.com>) call G_DEFINE_AUTOPTR_CLEANUP_FUNC for qemu_dmabuf_free() (Daniel P. Berrangé <berrange@redhat.com>) v8: Introduction of helpers was removed as those were already added by the previous commit v9: set dmabuf->allow_fences to 'true' when dmabuf is created in virtio_gpu_create_dmabuf()/virtio-gpu-udmabuf.c removed unnecessary spaces were accidently added in the patch, 'ui/console: Use qemu_dmabuf_new() a...' v11: Calling qemu_dmabuf_close was removed as closing dmabuf->fd will be done in qemu_dmabuf_free anyway. (Daniel P. Berrangé <berrange@redhat.com>) v12: --- Calling qemu_dmabuf_close separately as qemu_dmabuf_free doesn't do it. --- 'dmabuf' is now allocated space so it should be freed at the end of dbus_scanout_texture v13: --- Immediately free dmabuf after it is released to prevent possible leaking of the ptr (Marc-André Lureau <marcandre.lureau@redhat.com>) --- Use g_autoptr macro to define *dmabuf for auto clean up instead of calling qemu_dmabuf_free (Marc-André Lureau <marcandre.lureau@redhat.com>) v14: --- (vhost-user-gpu) Change qemu_dmabuf_free back to g_clear_pointer as it was done because of some misunderstanding (v13). --- (vhost-user-gpu) g->dmabuf[m->scanout_id] needs to be set to NULL to prevent freed dmabuf to be accessed again in case if(fd==-1)break; happens (before new dmabuf is allocated). Otherwise, it would cause invalid memory access when the same function is executed. Also NULL check should be done before qemu_dmabuf_close (it asserts dmabuf!=NULL.). (Marc-André Lureau <marcandre.lureau@redhat.com>) Suggested-by: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Philippe Mathieu-Daudé <philmd@linaro.org> Cc: Daniel P. Berrangé <berrange@redhat.com> Cc: Vivek Kasireddy <vivek.kasireddy@intel.com> Signed-off-by: Dongwon Kim <dongwon.kim@intel.com> Message-Id: <20240508175403.3399895-6-dongwon.kim@intel.com>
2024-05-08 20:54:02 +03:00
dmabuf->buf = qemu_dmabuf_new(r->width, r->height, fb->stride,
r->x, r->y, fb->width, fb->height,
qemu_pixman_to_drm_format(fb->format),
0, res->dmabuf_fd, true, false);
dmabuf->scanout_id = scanout_id;
QTAILQ_INSERT_HEAD(&g->dmabuf.bufs, dmabuf, next);
return dmabuf;
}
int virtio_gpu_update_dmabuf(VirtIOGPU *g,
uint32_t scanout_id,
struct virtio_gpu_simple_resource *res,
struct virtio_gpu_framebuffer *fb,
struct virtio_gpu_rect *r)
{
struct virtio_gpu_scanout *scanout = &g->parent_obj.scanout[scanout_id];
VGPUDMABuf *new_primary, *old_primary = NULL;
uint32_t width, height;
new_primary = virtio_gpu_create_dmabuf(g, scanout_id, res, fb, r);
if (!new_primary) {
return -EINVAL;
}
if (g->dmabuf.primary[scanout_id]) {
old_primary = g->dmabuf.primary[scanout_id];
}
ui/console: Use qemu_dmabuf_new() and free() helpers instead This commit introduces utility functions for the creation and deallocation of QemuDmaBuf instances. Additionally, it updates all relevant sections of the codebase to utilize these new utility functions. v7: remove prefix, "dpy_gl_" from all helpers qemu_dmabuf_free() returns without doing anything if input is null (Daniel P. Berrangé <berrange@redhat.com>) call G_DEFINE_AUTOPTR_CLEANUP_FUNC for qemu_dmabuf_free() (Daniel P. Berrangé <berrange@redhat.com>) v8: Introduction of helpers was removed as those were already added by the previous commit v9: set dmabuf->allow_fences to 'true' when dmabuf is created in virtio_gpu_create_dmabuf()/virtio-gpu-udmabuf.c removed unnecessary spaces were accidently added in the patch, 'ui/console: Use qemu_dmabuf_new() a...' v11: Calling qemu_dmabuf_close was removed as closing dmabuf->fd will be done in qemu_dmabuf_free anyway. (Daniel P. Berrangé <berrange@redhat.com>) v12: --- Calling qemu_dmabuf_close separately as qemu_dmabuf_free doesn't do it. --- 'dmabuf' is now allocated space so it should be freed at the end of dbus_scanout_texture v13: --- Immediately free dmabuf after it is released to prevent possible leaking of the ptr (Marc-André Lureau <marcandre.lureau@redhat.com>) --- Use g_autoptr macro to define *dmabuf for auto clean up instead of calling qemu_dmabuf_free (Marc-André Lureau <marcandre.lureau@redhat.com>) v14: --- (vhost-user-gpu) Change qemu_dmabuf_free back to g_clear_pointer as it was done because of some misunderstanding (v13). --- (vhost-user-gpu) g->dmabuf[m->scanout_id] needs to be set to NULL to prevent freed dmabuf to be accessed again in case if(fd==-1)break; happens (before new dmabuf is allocated). Otherwise, it would cause invalid memory access when the same function is executed. Also NULL check should be done before qemu_dmabuf_close (it asserts dmabuf!=NULL.). (Marc-André Lureau <marcandre.lureau@redhat.com>) Suggested-by: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Philippe Mathieu-Daudé <philmd@linaro.org> Cc: Daniel P. Berrangé <berrange@redhat.com> Cc: Vivek Kasireddy <vivek.kasireddy@intel.com> Signed-off-by: Dongwon Kim <dongwon.kim@intel.com> Message-Id: <20240508175403.3399895-6-dongwon.kim@intel.com>
2024-05-08 20:54:02 +03:00
width = qemu_dmabuf_get_width(new_primary->buf);
height = qemu_dmabuf_get_height(new_primary->buf);
g->dmabuf.primary[scanout_id] = new_primary;
qemu_console_resize(scanout->con, width, height);
ui/console: Use qemu_dmabuf_new() and free() helpers instead This commit introduces utility functions for the creation and deallocation of QemuDmaBuf instances. Additionally, it updates all relevant sections of the codebase to utilize these new utility functions. v7: remove prefix, "dpy_gl_" from all helpers qemu_dmabuf_free() returns without doing anything if input is null (Daniel P. Berrangé <berrange@redhat.com>) call G_DEFINE_AUTOPTR_CLEANUP_FUNC for qemu_dmabuf_free() (Daniel P. Berrangé <berrange@redhat.com>) v8: Introduction of helpers was removed as those were already added by the previous commit v9: set dmabuf->allow_fences to 'true' when dmabuf is created in virtio_gpu_create_dmabuf()/virtio-gpu-udmabuf.c removed unnecessary spaces were accidently added in the patch, 'ui/console: Use qemu_dmabuf_new() a...' v11: Calling qemu_dmabuf_close was removed as closing dmabuf->fd will be done in qemu_dmabuf_free anyway. (Daniel P. Berrangé <berrange@redhat.com>) v12: --- Calling qemu_dmabuf_close separately as qemu_dmabuf_free doesn't do it. --- 'dmabuf' is now allocated space so it should be freed at the end of dbus_scanout_texture v13: --- Immediately free dmabuf after it is released to prevent possible leaking of the ptr (Marc-André Lureau <marcandre.lureau@redhat.com>) --- Use g_autoptr macro to define *dmabuf for auto clean up instead of calling qemu_dmabuf_free (Marc-André Lureau <marcandre.lureau@redhat.com>) v14: --- (vhost-user-gpu) Change qemu_dmabuf_free back to g_clear_pointer as it was done because of some misunderstanding (v13). --- (vhost-user-gpu) g->dmabuf[m->scanout_id] needs to be set to NULL to prevent freed dmabuf to be accessed again in case if(fd==-1)break; happens (before new dmabuf is allocated). Otherwise, it would cause invalid memory access when the same function is executed. Also NULL check should be done before qemu_dmabuf_close (it asserts dmabuf!=NULL.). (Marc-André Lureau <marcandre.lureau@redhat.com>) Suggested-by: Marc-André Lureau <marcandre.lureau@redhat.com> Cc: Philippe Mathieu-Daudé <philmd@linaro.org> Cc: Daniel P. Berrangé <berrange@redhat.com> Cc: Vivek Kasireddy <vivek.kasireddy@intel.com> Signed-off-by: Dongwon Kim <dongwon.kim@intel.com> Message-Id: <20240508175403.3399895-6-dongwon.kim@intel.com>
2024-05-08 20:54:02 +03:00
dpy_gl_scanout_dmabuf(scanout->con, new_primary->buf);
if (old_primary) {
virtio_gpu_free_dmabuf(g, old_primary);
}
return 0;
}