038adc2f58
There are three page size in qemu: real host page size host page size target page size All of them have dedicate variable to represent. For the last two, we use the same form in the whole qemu project, while for the first one we use two forms: qemu_real_host_page_size and getpagesize(). qemu_real_host_page_size is defined to be a replacement of getpagesize(), so let it serve the role. [Note] Not fully tested for some arch or device. Signed-off-by: Wei Yang <richardw.yang@linux.intel.com> Message-Id: <20191013021145.16011-3-richardw.yang@linux.intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
329 lines
6.2 KiB
C
329 lines
6.2 KiB
C
/*
|
|
* Virtio vhost-user GPU Device
|
|
*
|
|
* DRM helpers
|
|
*
|
|
* 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 "vugbm.h"
|
|
|
|
static bool
|
|
mem_alloc_bo(struct vugbm_buffer *buf)
|
|
{
|
|
buf->mmap = g_malloc(buf->width * buf->height * 4);
|
|
buf->stride = buf->width * 4;
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
mem_free_bo(struct vugbm_buffer *buf)
|
|
{
|
|
g_free(buf->mmap);
|
|
}
|
|
|
|
static bool
|
|
mem_map_bo(struct vugbm_buffer *buf)
|
|
{
|
|
return buf->mmap != NULL;
|
|
}
|
|
|
|
static void
|
|
mem_unmap_bo(struct vugbm_buffer *buf)
|
|
{
|
|
}
|
|
|
|
static void
|
|
mem_device_destroy(struct vugbm_device *dev)
|
|
{
|
|
}
|
|
|
|
#ifdef CONFIG_MEMFD
|
|
struct udmabuf_create {
|
|
uint32_t memfd;
|
|
uint32_t flags;
|
|
uint64_t offset;
|
|
uint64_t size;
|
|
};
|
|
|
|
#define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create)
|
|
|
|
static size_t
|
|
udmabuf_get_size(struct vugbm_buffer *buf)
|
|
{
|
|
return ROUND_UP(buf->width * buf->height * 4, qemu_real_host_page_size);
|
|
}
|
|
|
|
static bool
|
|
udmabuf_alloc_bo(struct vugbm_buffer *buf)
|
|
{
|
|
int ret;
|
|
|
|
buf->memfd = memfd_create("udmabuf-bo", MFD_ALLOW_SEALING);
|
|
if (buf->memfd < 0) {
|
|
return false;
|
|
}
|
|
|
|
ret = ftruncate(buf->memfd, udmabuf_get_size(buf));
|
|
if (ret < 0) {
|
|
close(buf->memfd);
|
|
return false;
|
|
}
|
|
|
|
ret = fcntl(buf->memfd, F_ADD_SEALS, F_SEAL_SHRINK);
|
|
if (ret < 0) {
|
|
close(buf->memfd);
|
|
return false;
|
|
}
|
|
|
|
buf->stride = buf->width * 4;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
udmabuf_free_bo(struct vugbm_buffer *buf)
|
|
{
|
|
close(buf->memfd);
|
|
}
|
|
|
|
static bool
|
|
udmabuf_map_bo(struct vugbm_buffer *buf)
|
|
{
|
|
buf->mmap = mmap(NULL, udmabuf_get_size(buf),
|
|
PROT_READ | PROT_WRITE, MAP_SHARED, buf->memfd, 0);
|
|
if (buf->mmap == MAP_FAILED) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
udmabuf_get_fd(struct vugbm_buffer *buf, int *fd)
|
|
{
|
|
struct udmabuf_create create = {
|
|
.memfd = buf->memfd,
|
|
.offset = 0,
|
|
.size = udmabuf_get_size(buf),
|
|
};
|
|
|
|
*fd = ioctl(buf->dev->fd, UDMABUF_CREATE, &create);
|
|
|
|
return *fd >= 0;
|
|
}
|
|
|
|
static void
|
|
udmabuf_unmap_bo(struct vugbm_buffer *buf)
|
|
{
|
|
munmap(buf->mmap, udmabuf_get_size(buf));
|
|
}
|
|
|
|
static void
|
|
udmabuf_device_destroy(struct vugbm_device *dev)
|
|
{
|
|
close(dev->fd);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_GBM
|
|
static bool
|
|
alloc_bo(struct vugbm_buffer *buf)
|
|
{
|
|
struct gbm_device *dev = buf->dev->dev;
|
|
|
|
assert(!buf->bo);
|
|
|
|
buf->bo = gbm_bo_create(dev, buf->width, buf->height,
|
|
buf->format,
|
|
GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
|
|
|
|
if (buf->bo) {
|
|
buf->stride = gbm_bo_get_stride(buf->bo);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
free_bo(struct vugbm_buffer *buf)
|
|
{
|
|
gbm_bo_destroy(buf->bo);
|
|
}
|
|
|
|
static bool
|
|
map_bo(struct vugbm_buffer *buf)
|
|
{
|
|
uint32_t stride;
|
|
|
|
buf->mmap = gbm_bo_map(buf->bo, 0, 0, buf->width, buf->height,
|
|
GBM_BO_TRANSFER_READ_WRITE, &stride,
|
|
&buf->mmap_data);
|
|
|
|
assert(stride == buf->stride);
|
|
|
|
return buf->mmap != NULL;
|
|
}
|
|
|
|
static void
|
|
unmap_bo(struct vugbm_buffer *buf)
|
|
{
|
|
gbm_bo_unmap(buf->bo, buf->mmap_data);
|
|
}
|
|
|
|
static bool
|
|
get_fd(struct vugbm_buffer *buf, int *fd)
|
|
{
|
|
*fd = gbm_bo_get_fd(buf->bo);
|
|
|
|
return *fd >= 0;
|
|
}
|
|
|
|
static void
|
|
device_destroy(struct vugbm_device *dev)
|
|
{
|
|
gbm_device_destroy(dev->dev);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
vugbm_device_destroy(struct vugbm_device *dev)
|
|
{
|
|
if (!dev->inited) {
|
|
return;
|
|
}
|
|
|
|
dev->device_destroy(dev);
|
|
}
|
|
|
|
bool
|
|
vugbm_device_init(struct vugbm_device *dev, int fd)
|
|
{
|
|
dev->fd = fd;
|
|
|
|
#ifdef CONFIG_GBM
|
|
dev->dev = gbm_create_device(fd);
|
|
#endif
|
|
|
|
if (0) {
|
|
/* nothing */
|
|
}
|
|
#ifdef CONFIG_GBM
|
|
else if (dev->dev != NULL) {
|
|
dev->alloc_bo = alloc_bo;
|
|
dev->free_bo = free_bo;
|
|
dev->get_fd = get_fd;
|
|
dev->map_bo = map_bo;
|
|
dev->unmap_bo = unmap_bo;
|
|
dev->device_destroy = device_destroy;
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_MEMFD
|
|
else if (g_file_test("/dev/udmabuf", G_FILE_TEST_EXISTS)) {
|
|
dev->fd = open("/dev/udmabuf", O_RDWR);
|
|
if (dev->fd < 0) {
|
|
return false;
|
|
}
|
|
g_debug("Using experimental udmabuf backend");
|
|
dev->alloc_bo = udmabuf_alloc_bo;
|
|
dev->free_bo = udmabuf_free_bo;
|
|
dev->get_fd = udmabuf_get_fd;
|
|
dev->map_bo = udmabuf_map_bo;
|
|
dev->unmap_bo = udmabuf_unmap_bo;
|
|
dev->device_destroy = udmabuf_device_destroy;
|
|
}
|
|
#endif
|
|
else {
|
|
g_debug("Using mem fallback");
|
|
dev->alloc_bo = mem_alloc_bo;
|
|
dev->free_bo = mem_free_bo;
|
|
dev->map_bo = mem_map_bo;
|
|
dev->unmap_bo = mem_unmap_bo;
|
|
dev->device_destroy = mem_device_destroy;
|
|
return false;
|
|
}
|
|
|
|
dev->inited = true;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
vugbm_buffer_map(struct vugbm_buffer *buf)
|
|
{
|
|
struct vugbm_device *dev = buf->dev;
|
|
|
|
return dev->map_bo(buf);
|
|
}
|
|
|
|
static void
|
|
vugbm_buffer_unmap(struct vugbm_buffer *buf)
|
|
{
|
|
struct vugbm_device *dev = buf->dev;
|
|
|
|
dev->unmap_bo(buf);
|
|
}
|
|
|
|
bool
|
|
vugbm_buffer_can_get_dmabuf_fd(struct vugbm_buffer *buffer)
|
|
{
|
|
if (!buffer->dev->get_fd) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
vugbm_buffer_get_dmabuf_fd(struct vugbm_buffer *buffer, int *fd)
|
|
{
|
|
if (!vugbm_buffer_can_get_dmabuf_fd(buffer) ||
|
|
!buffer->dev->get_fd(buffer, fd)) {
|
|
g_warning("Failed to get dmabuf");
|
|
return false;
|
|
}
|
|
|
|
if (*fd < 0) {
|
|
g_warning("error: dmabuf_fd < 0");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev,
|
|
uint32_t width, uint32_t height)
|
|
{
|
|
buffer->dev = dev;
|
|
buffer->width = width;
|
|
buffer->height = height;
|
|
buffer->format = GBM_FORMAT_XRGB8888;
|
|
buffer->stride = 0; /* modified during alloc */
|
|
if (!dev->alloc_bo(buffer)) {
|
|
g_warning("alloc_bo failed");
|
|
return false;
|
|
}
|
|
|
|
if (!vugbm_buffer_map(buffer)) {
|
|
g_warning("map_bo failed");
|
|
goto err;
|
|
}
|
|
|
|
return true;
|
|
|
|
err:
|
|
dev->free_bo(buffer);
|
|
return false;
|
|
}
|
|
|
|
void
|
|
vugbm_buffer_destroy(struct vugbm_buffer *buffer)
|
|
{
|
|
struct vugbm_device *dev = buffer->dev;
|
|
|
|
vugbm_buffer_unmap(buffer);
|
|
dev->free_bo(buffer);
|
|
}
|