llvmpipe: Implement dmabuf handling

For export this is handled through the use of the udmabuf driver to
allocate a dmabuf we can control from userspace. For import this is
handled through mmap-ing a dmabuf handle. Please note that you can
only mmap a dmabuf handle if its linear and the dmabuf handle was
created with matching read and write permissions.

Co-authored-by: Mike Blumenkrantz <michael.blumenkrantz@gmail.com>

Reviewed-By: Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/27805>
This commit is contained in:
Lucas Fryzek 2024-02-26 18:01:34 -05:00 committed by Marge Bot
parent 7e5c5d313b
commit d74ea2c117
4 changed files with 321 additions and 22 deletions

View File

@ -59,6 +59,9 @@
#include "nir.h"
#ifdef PIPE_MEMORY_FD
#include <fcntl.h>
#endif
int LP_DEBUG = 0;
@ -902,6 +905,10 @@ llvmpipe_destroy_screen(struct pipe_screen *_screen)
glsl_type_singleton_decref();
#ifdef HAVE_LIBDRM
close(screen->udmabuf_fd);
#endif
mtx_destroy(&screen->rast_mutex);
mtx_destroy(&screen->cs_mutex);
FREE(screen);
@ -1139,6 +1146,9 @@ llvmpipe_create_screen(struct sw_winsys *winsys)
screen->num_threads);
screen->num_threads = MIN2(screen->num_threads, LP_MAX_THREADS);
#ifdef HAVE_LIBDRM
screen->udmabuf_fd = open("/dev/udmabuf", O_RDWR);
#endif
snprintf(screen->renderer_string, sizeof(screen->renderer_string),
"llvmpipe (LLVM " MESA_LLVM_VERSION_STRING ", %u bits)",

View File

@ -73,6 +73,10 @@ struct llvmpipe_screen
char renderer_string[100];
struct disk_cache *disk_shader_cache;
#ifdef HAVE_LIBDRM
int udmabuf_fd;
#endif
};

View File

@ -58,6 +58,13 @@
#include "drm-uapi/drm_fourcc.h"
#endif
#ifdef HAVE_LIBDRM
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/udmabuf.h>
#include <util/os_file.h>
#endif
#if MESA_DEBUG
static struct llvmpipe_resource resource_list;
@ -66,6 +73,12 @@ static simple_mtx_t resource_list_mutex = SIMPLE_MTX_INITIALIZER;
static unsigned id_counter = 0;
#ifdef PIPE_MEMORY_FD
static const char *driver_id = "llvmpipe" MESA_GIT_SHA1;
#endif
/**
* Conventional allocation path for non-display textures:
* Compute strides and allocate data (unless asked not to).
@ -250,6 +263,10 @@ llvmpipe_resource_create_all(struct pipe_screen *_screen,
pipe_reference_init(&lpr->base.reference, 1);
lpr->base.screen = &screen->base;
#ifdef HAVE_LIBDRM
lpr->dmabuf_alloc = NULL;
#endif
/* assert(lpr->base.bind); */
if (llvmpipe_resource_is_texture(&lpr->base)) {
@ -334,6 +351,21 @@ llvmpipe_resource_create(struct pipe_screen *_screen,
return llvmpipe_resource_create_front(_screen, templat, NULL);
}
#ifdef HAVE_LIBDRM
static struct pipe_resource *
llvmpipe_resource_create_with_modifiers(struct pipe_screen *_screen,
const struct pipe_resource *templat,
const uint64_t *modifiers, int count)
{
bool has_linear = false;
for (unsigned i = 0; i < count; i++)
if (modifiers[i] == DRM_FORMAT_MOD_LINEAR)
has_linear = true;
if (!has_linear)
return NULL;
return llvmpipe_resource_create_front(_screen, templat, NULL);
}
#endif
static struct pipe_resource *
llvmpipe_resource_create_unbacked(struct pipe_screen *_screen,
@ -474,6 +506,12 @@ llvmpipe_resource_destroy(struct pipe_screen *pscreen,
align_free(lpr->data);
}
}
#ifdef HAVE_LIBDRM
if (lpr->dmabuf_alloc)
pscreen->free_memory_fd(pscreen, (struct pipe_memory_allocation*)lpr->dmabuf_alloc);
#endif
#if MESA_DEBUG
simple_mtx_lock(&resource_list_mutex);
if (!list_is_empty(&lpr->list))
@ -505,6 +543,8 @@ llvmpipe_resource_map(struct pipe_resource *resource,
tex_usage == LP_TEX_USAGE_WRITE_ALL);
if (lpr->dt) {
if (lpr->dmabuf)
return lpr->tex_data;
/* display target */
struct llvmpipe_screen *screen = lpr->screen;
struct sw_winsys *winsys = screen->winsys;
@ -547,6 +587,8 @@ llvmpipe_resource_unmap(struct pipe_resource *resource,
struct llvmpipe_resource *lpr = llvmpipe_resource(resource);
if (lpr->dt) {
if (lpr->dmabuf)
return;
/* display target */
struct llvmpipe_screen *lp_screen = lpr->screen;
struct sw_winsys *winsys = lp_screen->winsys;
@ -580,7 +622,10 @@ llvmpipe_resource_from_handle(struct pipe_screen *_screen,
struct sw_winsys *winsys = screen->winsys;
struct llvmpipe_resource *lpr;
/* XXX Seems like from_handled depth textures doesn't work that well */
/* no multisampled */
assert(template->nr_samples < 2);
/* no miplevels */
assert(template->last_level == 0);
lpr = CALLOC_STRUCT(llvmpipe_resource);
if (!lpr) {
@ -589,6 +634,7 @@ llvmpipe_resource_from_handle(struct pipe_screen *_screen,
lpr->base = *template;
lpr->screen = screen;
lpr->dt_format = whandle->format;
pipe_reference_init(&lpr->base.reference, 1);
lpr->base.screen = _screen;
@ -601,15 +647,44 @@ llvmpipe_resource_from_handle(struct pipe_screen *_screen,
assert(lpr->base.height0 == height);
#endif
lpr->dt = winsys->displaytarget_from_handle(winsys,
template,
whandle,
&lpr->row_stride[0]);
if (!lpr->dt) {
void *data;
#ifdef HAVE_LIBDRM
struct llvmpipe_memory_fd_alloc *alloc;
uint64_t size;
if(_screen->import_memory_fd(_screen, whandle->handle, (struct pipe_memory_allocation**)&alloc, &size, true)) {
data = alloc->data;
lpr->dt = winsys->displaytarget_create_mapped(winsys, template->bind,
template->format, template->width0, template->height0,
whandle->stride, data);
if (!lpr->dt)
goto no_dt;
lpr->dmabuf_alloc = alloc;
} else
#endif
{
lpr->dt = winsys->displaytarget_from_handle(winsys,
template,
whandle,
&lpr->row_stride[0]);
if (!lpr->dt)
goto no_dt;
data = winsys->displaytarget_map(winsys, lpr->dt, PIPE_MAP_READ_WRITE);
}
if (!data) {
winsys->displaytarget_destroy(winsys, lpr->dt);
goto no_dt;
}
lpr->row_stride[0] = whandle->stride;
unsigned nblocksy = util_format_get_nblocksy(template->format, align(template->height0, LP_RASTER_BLOCK_SIZE));
lpr->img_stride[0] = whandle->stride * nblocksy;
lpr->sample_stride = lpr->img_stride[0];
lpr->size_required = lpr->sample_stride;
assert(llvmpipe_resource_is_texture(&lpr->base));
lpr->tex_data = data;
lpr->id = id_counter++;
lpr->dmabuf = true;
#if MESA_DEBUG
simple_mtx_lock(&resource_list_mutex);
@ -627,15 +702,52 @@ no_lpr:
static bool
llvmpipe_resource_get_handle(struct pipe_screen *screen,
llvmpipe_resource_get_handle(struct pipe_screen *_screen,
struct pipe_context *ctx,
struct pipe_resource *pt,
struct winsys_handle *whandle,
unsigned usage)
{
struct sw_winsys *winsys = llvmpipe_screen(screen)->winsys;
struct llvmpipe_screen *screen = llvmpipe_screen(_screen);
struct sw_winsys *winsys = screen->winsys;
struct llvmpipe_resource *lpr = llvmpipe_resource(pt);
whandle->stride = lpr->row_stride[0];
#ifdef HAVE_LIBDRM
whandle->modifier = DRM_FORMAT_MOD_LINEAR;
if (!lpr->dt && whandle->type == WINSYS_HANDLE_TYPE_FD) {
if (!lpr->dmabuf_alloc) {
lpr->dmabuf_alloc = (struct llvmpipe_memory_fd_alloc*)_screen->allocate_memory_fd(_screen, lpr->size_required, (int*)&whandle->handle, true);
if (!lpr->dmabuf_alloc)
return false;
/* replace existing backing with fd backing */
bool is_tex = llvmpipe_resource_is_texture(pt);
if (is_tex) {
if (lpr->tex_data)
memcpy(lpr->dmabuf_alloc->data, lpr->tex_data, lpr->size_required);
} else {
if (lpr->data)
memcpy(lpr->dmabuf_alloc->data, lpr->data, lpr->size_required);
}
if (!lpr->imported_memory)
align_free(is_tex ? lpr->tex_data : lpr->data);
if (is_tex)
lpr->tex_data = lpr->dmabuf_alloc;
else
lpr->data = lpr->dmabuf_alloc->data;
/* reuse lavapipe codepath to handle destruction */
lpr->backable = true;
}
whandle->handle = lpr->dmabuf_alloc->dmabuf_fd;
return true;
} else if (whandle->type == WINSYS_HANDLE_TYPE_KMS) {
/* dri winsys code will use this to query the drm modifiers
* We can just return an null handle and return DRM_FORMAT_MOD_LINEAR */
whandle->handle = 0;
return true;
}
#endif
assert(lpr->dt);
if (!lpr->dt)
return false;
@ -983,22 +1095,105 @@ llvmpipe_free_memory(struct pipe_screen *screen,
}
#ifdef HAVE_LIBDRM
static void*
llvmpipe_resource_alloc_udmabuf(struct llvmpipe_screen *screen,
struct llvmpipe_memory_fd_alloc *alloc,
size_t size)
{
int mem_fd = -1;
int dmabuf_fd = -1;
if (screen->udmabuf_fd != -1) {
uint64_t alignment;
if (!os_get_page_size(&alignment))
alignment = 256;
size = align(size, alignment);
int mem_fd = memfd_create("lp_dma_buf", MFD_ALLOW_SEALING);
if (mem_fd == -1)
goto fail;
int res = ftruncate(mem_fd, size);
if (res == -1)
goto fail;
/* udmabuf create requires that the memfd have
* have the F_SEAL_SHRINK seal added and must not
* have the F_SEAL_WRITE seal added */
if (fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK) < 0)
goto fail;
struct udmabuf_create create = {
.memfd = mem_fd,
.flags = UDMABUF_FLAGS_CLOEXEC,
.offset = 0,
.size = size
};
int dmabuf_fd = ioctl(screen->udmabuf_fd, UDMABUF_CREATE, &create);
if (dmabuf_fd < 0)
goto fail;
struct pipe_memory_allocation *data =
mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, mem_fd, 0);
if (!data)
goto fail;
alloc->mem_fd = mem_fd;
alloc->dmabuf_fd = dmabuf_fd;
alloc->size = size;
return data;
}
fail:
if (mem_fd != -1)
close(mem_fd);
if (dmabuf_fd != -1)
close(dmabuf_fd);
/* If we don't have access to the udmabuf device
* or something else fails we return NULL */
return NULL;
}
#endif
#ifdef PIPE_MEMORY_FD
static const char *driver_id = "llvmpipe" MESA_GIT_SHA1;
static struct pipe_memory_allocation *
llvmpipe_allocate_memory_fd(struct pipe_screen *screen,
llvmpipe_allocate_memory_fd(struct pipe_screen *pscreen,
uint64_t size,
int *fd,
bool dmabuf)
{
uint64_t alignment;
if (!os_get_page_size(&alignment))
alignment = 256;
return os_malloc_aligned_fd(size, alignment, fd,
"llvmpipe memory fd", driver_id);
struct llvmpipe_screen *screen = llvmpipe_screen(pscreen);
struct llvmpipe_memory_fd_alloc *alloc = CALLOC_STRUCT(llvmpipe_memory_fd_alloc);
if (!alloc)
goto fail;
alloc->mem_fd = -1;
alloc->dmabuf_fd = -1;
if (dmabuf) {
alloc->type = LLVMPIPE_MEMORY_FD_TYPE_DMA_BUF;
alloc->data = llvmpipe_resource_alloc_udmabuf(screen, alloc, size);
if (alloc->data)
*fd = os_dupfd_cloexec(alloc->dmabuf_fd);
} else {
alloc->type = LLVMPIPE_MEMORY_FD_TYPE_OPAQUE;
uint64_t alignment;
if (!os_get_page_size(&alignment))
alignment = 256;
alloc->data = os_malloc_aligned_fd(size, alignment, fd,
"llvmpipe memory fd", driver_id);
}
if(alloc && !alloc->data) {
free(alloc);
alloc = NULL;
}
fail:
return (struct pipe_memory_allocation*)alloc;
}
@ -1009,7 +1204,40 @@ llvmpipe_import_memory_fd(struct pipe_screen *screen,
uint64_t *size,
bool dmabuf)
{
return os_import_memory_fd(fd, (void**)ptr, size, driver_id);
struct llvmpipe_memory_fd_alloc *alloc = CALLOC_STRUCT(llvmpipe_memory_fd_alloc);
alloc->mem_fd = -1;
alloc->dmabuf_fd = -1;
if (dmabuf) {
off_t mmap_size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
void *data = mmap(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
free(alloc);
*ptr = NULL;
return false;
}
alloc->type = LLVMPIPE_MEMORY_FD_TYPE_DMA_BUF;
alloc->data = data;
alloc->size = mmap_size;
alloc->dmabuf_fd = os_dupfd_cloexec(fd);
*ptr = (struct pipe_memory_allocation*)alloc;
*size = mmap_size;
return true;
} else {
bool ret = os_import_memory_fd(fd, (void**)&alloc->data, size, driver_id);
if (!ret) {
free(alloc);
*ptr = NULL;
} else {
*ptr = (struct pipe_memory_allocation*)alloc;
}
alloc->type = LLVMPIPE_MEMORY_FD_TYPE_OPAQUE;
return ret;
}
}
@ -1017,7 +1245,18 @@ static void
llvmpipe_free_memory_fd(struct pipe_screen *screen,
struct pipe_memory_allocation *pmem)
{
os_free_fd(pmem);
struct llvmpipe_memory_fd_alloc *alloc = (struct llvmpipe_memory_fd_alloc*)pmem;
if (alloc->type == LLVMPIPE_MEMORY_FD_TYPE_OPAQUE) {
os_free_fd(alloc->data);
} else {
munmap(alloc->data, alloc->size);
if (alloc->dmabuf_fd >= 0)
close(alloc->dmabuf_fd);
if (alloc->mem_fd >= 0)
close(alloc->mem_fd);
}
free(alloc);
}
#endif
@ -1115,7 +1354,7 @@ llvmpipe_resource_get_param(struct pipe_screen *screen,
switch (param) {
case PIPE_RESOURCE_PARAM_NPLANES:
*value = 1;
*value = lpr->dt ? util_format_get_num_planes(lpr->dt_format) : 1;
return true;
case PIPE_RESOURCE_PARAM_STRIDE:
*value = lpr->row_stride[level];
@ -1128,7 +1367,7 @@ llvmpipe_resource_get_param(struct pipe_screen *screen,
return true;
#ifndef _WIN32
case PIPE_RESOURCE_PARAM_MODIFIER:
*value = DRM_FORMAT_MOD_INVALID;
*value = lpr->dt ? DRM_FORMAT_MOD_LINEAR : DRM_FORMAT_MOD_INVALID;
return true;
#endif
case PIPE_RESOURCE_PARAM_HANDLE_TYPE_SHARED:
@ -1159,6 +1398,28 @@ llvmpipe_resource_get_param(struct pipe_screen *screen,
return false;
}
#ifdef HAVE_LIBDRM
static void
llvmpipe_query_dmabuf_modifiers(struct pipe_screen *pscreen, enum pipe_format format, int max, uint64_t *modifiers, unsigned int *external_only, int *count)
{
if (max) {
*count = 1;
*modifiers = DRM_FORMAT_MOD_LINEAR;
}
}
static bool
llvmpipe_is_dmabuf_modifier_supported(struct pipe_screen *pscreen, uint64_t modifier, enum pipe_format format, bool *external_only)
{
return modifier == DRM_FORMAT_MOD_LINEAR;
}
static unsigned
llvmpipe_get_dmabuf_modifier_planes(struct pipe_screen *pscreen, uint64_t modifier, enum pipe_format format)
{
return modifier == DRM_FORMAT_MOD_LINEAR;
}
#endif
void
llvmpipe_init_screen_resource_funcs(struct pipe_screen *screen)
@ -1197,6 +1458,12 @@ llvmpipe_init_screen_resource_funcs(struct pipe_screen *screen)
screen->allocate_memory_fd = llvmpipe_allocate_memory_fd;
screen->import_memory_fd = llvmpipe_import_memory_fd;
screen->free_memory_fd = llvmpipe_free_memory_fd;
#endif
#ifdef HAVE_LIBDRM
screen->query_dmabuf_modifiers = llvmpipe_query_dmabuf_modifiers;
screen->is_dmabuf_modifier_supported = llvmpipe_is_dmabuf_modifier_supported;
screen->get_dmabuf_modifier_planes = llvmpipe_get_dmabuf_modifier_planes;
screen->resource_create_with_modifiers = llvmpipe_resource_create_with_modifiers;
#endif
screen->map_memory = llvmpipe_map_memory;
screen->unmap_memory = llvmpipe_unmap_memory;

View File

@ -44,6 +44,12 @@ enum lp_texture_usage
LP_TEX_USAGE_WRITE_ALL
};
enum llvmpipe_memory_fd_type
{
LLVMPIPE_MEMORY_FD_TYPE_OPAQUE,
LLVMPIPE_MEMORY_FD_TYPE_DMA_BUF,
};
struct pipe_context;
struct pipe_screen;
@ -52,6 +58,13 @@ struct llvmpipe_screen;
struct sw_displaytarget;
struct llvmpipe_memory_fd_alloc {
void *data;
enum llvmpipe_memory_fd_type type;
int mem_fd;
int dmabuf_fd;
size_t size;
};
/**
* llvmpipe subclass of pipe_resource. A texture, drawing surface,
@ -81,6 +94,7 @@ struct llvmpipe_resource
* usage.
*/
struct sw_displaytarget *dt;
enum pipe_format dt_format;
/**
* Malloc'ed data for regular textures, or a mapping to dt above.
@ -101,8 +115,12 @@ struct llvmpipe_resource
uint64_t size_required;
uint64_t backing_offset;
#ifdef HAVE_LIBDRM
struct llvmpipe_memory_fd_alloc *dmabuf_alloc;
#endif
bool backable;
bool imported_memory;
bool dmabuf;
#if MESA_DEBUG
struct list_head list;
#endif