...
 
Commits (4)
......@@ -2,5 +2,5 @@
clang-format -style=file --assume-filename=C -i \
util.h \
waypipe.c server.c handlers.c client.c util.c parsing.c dmabuf.c shadow.c mainloop.c interval.c video.c \
test/diff_roundtrip.c test/damage_merge.c test/fd_mirror.c
test/diff_roundtrip.c test/damage_merge.c test/fd_mirror.c test/wire_parse.c
black -q test/headless.py test/startup_failure.py
......@@ -63,6 +63,8 @@ void wl_resource_post_event(struct wl_resource *resource, uint32_t opcode, ...);
#include <virtual-keyboard-unstable-v1-server-defs.h>
#include <wayland-drm-client-defs.h>
#include <wayland-drm-server-defs.h>
#include <wlr-data-control-unstable-v1-client-defs.h>
#include <wlr-data-control-unstable-v1-server-defs.h>
#include <wlr-export-dmabuf-unstable-v1-client-defs.h>
#include <wlr-export-dmabuf-unstable-v1-server-defs.h>
#include <wlr-screencopy-unstable-v1-client-defs.h>
......@@ -792,15 +794,17 @@ static void event_wl_keyboard_keymap(void *data,
{
struct context *context = get_context(data, wl_keyboard);
struct shadow_fd *sfd = translate_fd(
&context->g->map, &context->g->render, fd, NULL, false);
if (sfd->type != FDC_FILE || sfd->buffer_size != size) {
size_t fdsz = 0;
fdcat_t fdtype = get_fd_type(fd, &fdsz);
if (fdtype != FDC_FILE || fdsz != size) {
wp_log(WP_ERROR,
"keymap candidate RID=%d was not file-like (type=%d), and with size=%ld did not match %d",
sfd->remote_id, sfd->type, sfd->buffer_size,
size);
"keymap candidate fd %d was not file-like (type=%s), and with size=%ld did not match %d",
fd, fdcat_to_str(fdtype), fdsz, size);
return;
}
struct shadow_fd *sfd = translate_fd(&context->g->map,
&context->g->render, fd, fdtype, fdsz, NULL, false);
struct wp_keyboard *keyboard = (struct wp_keyboard *)context->obj;
keyboard->owned_buffer = shadow_incref_protocol(sfd);
(void)format;
......@@ -813,20 +817,23 @@ static void request_wl_shm_create_pool(struct wl_client *client,
struct context *context = get_context(client, resource);
struct wp_shm_pool *the_shm_pool = (struct wp_shm_pool *)listset_get(
context->obj_list, id);
struct shadow_fd *sfd = translate_fd(
&context->g->map, &context->g->render, fd, NULL, false);
size_t fdsz = 0;
fdcat_t fdtype = get_fd_type(fd, &fdsz);
/* It may be valid for the file descriptor size to be larger than the
* immediately advertised size, since the call to wl_shm.create_pool
* may be followed by wl_shm_pool.resize, which then increases the size
*/
if (sfd->type != FDC_FILE || (int32_t)sfd->buffer_size < size) {
if (fdtype != FDC_FILE || (int32_t)fdsz < size) {
wp_log(WP_ERROR,
"File type or size mismatch for RID=%d with claimed: %d %d | %ld %d",
sfd->remote_id, sfd->type, FDC_FILE,
sfd->buffer_size, size);
"File type or size mismatch for fd %d with claimed: %s %s | %ld %d",
fd, fdcat_to_str(fdtype),
fdcat_to_str(FDC_FILE), fdsz, size);
return;
}
struct shadow_fd *sfd = translate_fd(&context->g->map,
&context->g->render, fd, fdtype, fdsz, NULL, false);
the_shm_pool->owned_buffer = shadow_incref_protocol(sfd);
}
......@@ -1083,15 +1090,18 @@ static void request_wl_drm_create_prime_buffer(struct wl_client *client,
(uint32_t)stride2, 0},
.using_planes = {true, false, false, false},
};
struct shadow_fd *sfd = translate_fd(&context->g->map,
&context->g->render, name, &info, false);
if (sfd->type != FDC_DMABUF) {
size_t fdsz = 0;
fdcat_t fdtype = get_fd_type(name, &fdsz);
if (fdtype != FDC_DMABUF) {
wp_log(WP_ERROR,
"keymap candidate RID=%d was not a dmabuf (type=%d)",
sfd->remote_id, sfd->type);
"create_prime_buffer candidate fd %d was not a dmabuf (type=%s)",
name, fdcat_to_str(fdtype));
return;
}
struct shadow_fd *sfd = translate_fd(&context->g->map,
&context->g->render, name, FDC_DMABUF, 0, &info, false);
buf->type = BUF_DMA;
buf->dmabuf_nplanes = 1;
buf->dmabuf_buffers[0] = shadow_incref_protocol(sfd);
......@@ -1313,15 +1323,18 @@ static void request_zwp_linux_buffer_params_v1_create(struct wl_client *client,
format, info.modifier) &&
context->g->config->video_if_possible;
struct shadow_fd *sfd = translate_fd(&context->g->map,
&context->g->render, params->add[i].fd, &info,
try_video);
if (sfd->type != FDC_DMABUF) {
size_t fdsz = 0;
fdcat_t fdtype = get_fd_type(params->add[i].fd, &fdsz);
if (fdtype != FDC_DMABUF) {
wp_log(WP_ERROR,
"fd #%d for linux-dmabuf request wasn't a dmabuf",
i);
"fd #%d for linux-dmabuf request wasn't a dmabuf, instead %s",
i, fdcat_to_str(fdtype));
continue;
}
struct shadow_fd *sfd = translate_fd(&context->g->map,
&context->g->render, params->add[i].fd,
FDC_DMABUF, 0, &info, try_video);
/* increment for each extra time this fd will be sent */
if (sfd->has_owner) {
shadow_incref_transfer(sfd);
......@@ -1422,14 +1435,17 @@ static void zwlr_export_dmabuf_frame_v1_object(void *data,
.modifier = frame->modifier};
info.using_planes[index] = true;
struct shadow_fd *sfd = translate_fd(&context->g->map,
&context->g->render, fd, &info, false);
if (sfd->type != FDC_DMABUF) {
size_t fdsz = 0;
fdcat_t fdtype = get_fd_type(fd, &fdsz);
if (fdtype != FDC_DMABUF) {
wp_log(WP_ERROR,
"fd #%d for wlr-export-dmabuf frame wasn't a dmabuf",
index);
"fd %d, #%d for wlr-export-dmabuf frame wasn't a dmabuf, instead %s",
fd, index, fdcat_to_str(fdtype));
return;
}
struct shadow_fd *sfd = translate_fd(&context->g->map,
&context->g->render, fd, FDC_DMABUF, 0, &info, false);
if (sfd->buffer_size < size) {
wp_log(WP_ERROR,
"Frame object %u has a dmabuf with less (%u) than the advertised (%u) size",
......@@ -1466,6 +1482,24 @@ static void zwlr_export_dmabuf_frame_v1_ready(void *data,
}
}
}
static void data_source_send(
void *data, void *noop, const char *mime_type, int32_t fd)
{
struct context *context = get_context(data, noop);
/* treat the fd as a one-way pipe, even if it has other properties */
translate_fd(&context->g->map, &context->g->render, fd, FDC_PIPE_IW, 0,
NULL, false);
(void)mime_type;
}
static void data_offer_receive(struct wl_client *client,
struct wl_resource *resource, const char *mime_type, int32_t fd)
{
struct context *context = get_context(client, resource);
/* treat the fd as a one-way pipe, even if it is e.g. a file */
translate_fd(&context->g->map, &context->g->render, fd, FDC_PIPE_IW, 0,
NULL, false);
(void)mime_type;
}
static const struct wl_display_listener wl_display_event_handler = {
.error = event_wl_display_error,
......@@ -1534,6 +1568,30 @@ static const struct zwlr_export_dmabuf_frame_v1_listener
.object = zwlr_export_dmabuf_frame_v1_object,
.ready = zwlr_export_dmabuf_frame_v1_ready,
};
typedef void (*zwlr_data_control_source_v1_send_t)(void *,
struct zwlr_data_control_source_v1 *, const char *, int32_t);
static const struct zwlr_data_control_source_v1_listener
zwlr_data_control_source_v1_event_handler = {
.send = (zwlr_data_control_source_v1_send_t)
data_source_send};
typedef void (*gtk_primary_selection_source_send_t)(void *,
struct gtk_primary_selection_source *, const char *, int32_t);
static const struct gtk_primary_selection_source_listener
gtk_primary_selection_source_event_handler = {
.send = (gtk_primary_selection_source_send_t)
data_source_send};
typedef void (*wl_data_source_send_t)(
void *, struct wl_data_source *, const char *, int32_t);
static const struct wl_data_source_listener wl_data_source_event_handler = {
.send = (wl_data_source_send_t)data_source_send};
static const struct zwlr_data_control_offer_v1_interface
zwlr_data_control_offer_v1_request_handler = {
.receive = data_offer_receive};
static const struct gtk_primary_selection_offer_interface
gtk_primary_selection_offer_request_handler = {
.receive = data_offer_receive};
static const struct wl_data_offer_interface wl_data_offer_request_handler = {
.receive = data_offer_receive};
const struct msg_handler handlers[] = {
{&wl_display_interface, &wl_display_event_handler,
......@@ -1555,6 +1613,23 @@ const struct msg_handler handlers[] = {
&zwlr_export_dmabuf_frame_v1_event_handler,
NULL},
/* Copy-paste protocol handlers, handled near identically */
{&zwlr_data_control_offer_v1_interface, NULL,
&zwlr_data_control_offer_v1_request_handler},
{&gtk_primary_selection_offer_interface, NULL,
&gtk_primary_selection_offer_request_handler},
{&wl_data_offer_interface, NULL,
&wl_data_offer_request_handler},
{&zwlr_data_control_source_v1_interface,
&zwlr_data_control_source_v1_event_handler,
NULL},
{&gtk_primary_selection_source_interface,
&gtk_primary_selection_source_event_handler,
NULL},
{&wl_data_source_interface, &wl_data_source_event_handler,
NULL},
/* List all other known global object interface types, so
* that the parsing code can identify all fd usages */
// wayland
......@@ -1586,6 +1661,8 @@ const struct msg_handler handlers[] = {
&wl_drm_request_handler},
// wlr-export-dmabuf
{&zwlr_export_dmabuf_manager_v1_interface, NULL, NULL},
// wlr-export-dmabuf
{&zwlr_data_control_manager_v1_interface, NULL, NULL},
{NULL, NULL, NULL}};
const struct wl_interface *the_display_interface = &wl_display_interface;
......@@ -143,7 +143,11 @@ static void translate_fds(struct fd_translation_map *map,
int ids[])
{
for (int i = 0; i < nfds; i++) {
ids[i] = translate_fd(map, render, fds[i], NULL, false)
/* Autodetect type */
size_t fdsz = 0;
fdcat_t fdtype = get_fd_type(fds[i], &fdsz);
ids[i] = translate_fd(
map, render, fds[i], fdtype, fdsz, NULL, false)
->remote_id;
}
}
......
......@@ -139,9 +139,18 @@ test('If damage rectangles merge efficiently', test_damage)
test_mirror = executable(
'fd_mirror',
['test/fd_mirror.c'],
link_with: lib_waypipe_src
link_with: lib_waypipe_src,
dependencies: [libgbm]
)
# disable leak checking, because library code is often responsible
test('How well file descriptors are replicated', test_mirror, env: ['ASAN_OPTIONS=detect_leaks=0'])
test_parse = executable(
'wire_parse',
['test/wire_parse.c'],
link_with: lib_waypipe_src,
dependencies: [libffi, wayland_client.partial_dependency(compile_args: true)]
)
test('How well file descriptors are replicated', test_mirror)
test('That protocol parsing fails cleanly', test_parse)
weston_prog = find_program('weston', required: false)
weston_shm_prog = find_program('weston-simple-shm', required: false)
weston_egl_prog = find_program('weston-simple-egl', required: false)
......
......@@ -306,12 +306,12 @@ struct uarg {
/* Parse the message payload and apply it to a function, creating new objects
* and consuming fds */
static void invoke_msg_handler(ffi_cif *cif, const struct wl_interface *intf,
void invoke_msg_handler(ffi_cif *cif, const struct wl_interface *intf,
const struct wl_message *msg, bool is_event,
const uint32_t *const payload, const int paylen,
const int *const fd_list, const int fdlen, int *fds_used,
void (*const func)(void), struct context *ctx,
struct message_tracker *mt)
struct message_tracker *mt, struct fd_translation_map *map)
{
/* The types to match these arguments are set up once in
* `setup_subhandler_cif` */
......@@ -395,6 +395,7 @@ static void invoke_msg_handler(ffi_cif *cif, const struct wl_interface *intf,
call_args_val[nargs].obj = lo;
call_args_ptr[nargs] = &call_args_val[nargs].obj;
nargs++;
} break;
case 'n': {
if (i >= paylen) {
......@@ -406,7 +407,7 @@ static void invoke_msg_handler(ffi_cif *cif, const struct wl_interface *intf,
* the client's events are fed the object pointer. */
struct wp_object *new_obj =
create_wp_object(v, msg->types[k]);
listset_insert(&ctx->g->map, &mt->objects, new_obj);
listset_insert(map, &mt->objects, new_obj);
if (ctx->obj->is_zombie) {
/* todo: handle misc data ? */
......@@ -599,7 +600,7 @@ enum parse_state handle_message(struct globals *g, bool display_side,
invoke_msg_handler(cif, intf, msg, !from_client, payload, len / 4 - 2,
&fds->data[fds->zone_start],
fds->zone_end - fds->zone_start, &fds_used, fn, &ctx,
&g->tracker);
&g->tracker, &g->map);
if (ctx.drop_this_msg) {
wp_log(WP_DEBUG, "Dropping %s.%s, with %d fds", intf->name,
msg->name, fds_used);
......
......@@ -13,6 +13,7 @@ protocols = [
'virtual-keyboard-unstable-v1.xml',
'wlr-screencopy-unstable-v1.xml',
'wlr-export-dmabuf-unstable-v1.xml',
'wlr-data-control-unstable-v1.xml',
'wayland-drm.xml',
]
......
This diff is collapsed.
......@@ -281,6 +281,25 @@ void setup_translation_map(struct fd_translation_map *map, bool display_side,
}
}
const char *fdcat_to_str(fdcat_t cat)
{
switch (cat) {
case FDC_UNKNOWN:
return "FDC_UNKNOWN";
case FDC_FILE:
return "FDC_FILE";
case FDC_PIPE_IR:
return "FDC_PIPE_IR";
case FDC_PIPE_IW:
return "FDC_PIPE_IW";
case FDC_PIPE_RW:
return "FDC_PIPE_RW";
case FDC_DMABUF:
return "FDC_DMABUF";
}
return "<invalid>";
}
fdcat_t get_fd_type(int fd, size_t *size)
{
struct stat fsdata;
......@@ -295,12 +314,18 @@ fdcat_t get_fd_type(int fd, size_t *size)
*size = (size_t)fsdata.st_size;
}
return FDC_FILE;
} else if (S_ISFIFO(fsdata.st_mode) || S_ISCHR(fsdata.st_mode)) {
if (!S_ISFIFO(fsdata.st_mode)) {
} else if (S_ISFIFO(fsdata.st_mode) || S_ISCHR(fsdata.st_mode) ||
S_ISSOCK(fsdata.st_mode)) {
if (S_ISCHR(fsdata.st_mode)) {
wp_log(WP_ERROR,
"The fd %d, size %ld, mode %x is a character device. Proceeding under the assumption that it is pipe-like.",
fd, fsdata.st_size, fsdata.st_mode);
}
if (S_ISSOCK(fsdata.st_mode)) {
wp_log(WP_ERROR,
"The fd %d, size %ld, mode %x is a socket. Proceeding under the assumption that it is pipe-like.",
fd, fsdata.st_size, fsdata.st_mode);
}
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
wp_log(WP_ERROR, "fctnl F_GETFL failed!");
......@@ -471,8 +496,8 @@ static void uncompress_buffer(struct fd_translation_map *map, size_t isize,
}
struct shadow_fd *translate_fd(struct fd_translation_map *map,
struct render_data *render, int fd,
struct dmabuf_slice_data *info, bool use_video)
struct render_data *render, int fd, fdcat_t type,
size_t file_sz, struct dmabuf_slice_data *info, bool use_video)
{
struct shadow_fd *sfd = get_shadow_for_local_fd(map, fd);
if (sfd) {
......@@ -488,7 +513,7 @@ struct shadow_fd *translate_fd(struct fd_translation_map *map,
sfd->mem_mirror = NULL;
sfd->buffer_size = (size_t)-1;
sfd->remote_id = (map->max_local_id++) * map->local_sign;
sfd->type = FDC_UNKNOWN;
sfd->type = type;
// File changes must be propagated
sfd->is_dirty = true;
damage_everything(&sfd->damage);
......@@ -500,12 +525,9 @@ struct shadow_fd *translate_fd(struct fd_translation_map *map,
sfd->refcount_protocol = 0;
wp_log(WP_DEBUG, "Creating new shadow buffer for local fd %d", fd);
size_t fsize = 0;
sfd->type = get_fd_type(fd, &fsize);
if (sfd->type == FDC_FILE) {
// We have a file-like object
sfd->buffer_size = fsize;
sfd->buffer_size = file_sz;
// both r/w permissions, because the size the allocates
// the memory does not always have to be the size that
// modifies it
......@@ -1124,11 +1146,11 @@ void collect_update(struct fd_translation_map *map, struct shadow_fd *sfd,
&diff_space,
&sfd->compress_space);
sfd->diff_buffer = calloc(diff_space, 1);
sfd->compress_buffer =
sfd->compress_space
? calloc(sfd->compress_space,
1)
: NULL;
if (!sfd->compress_buffer &&
sfd->compress_space) {
sfd->compress_buffer = calloc(
sfd->compress_space, 1);
}
}
damage_everything(&sfd->damage);
......
/*
* Copyright © 2019 Manuel Stoeckl
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#define _GNU_SOURCE
#include "util.h"
......@@ -22,8 +47,16 @@ static const enum compression_mode comp_modes[] = {
COMP_NONE,
};
static int update_file(int file_fd, size_t sz, int seqno)
#ifdef HAS_DMABUF
#include <gbm.h>
#define TEST_2CPP_FORMAT GBM_FORMAT_GR88
#else
#define TEST_2CPP_FORMAT 0
#endif
static int update_file(int file_fd, struct gbm_bo *bo, size_t sz, int seqno)
{
(void)bo;
if (rand() % 11 == 0) {
/* no change */
return 0;
......@@ -48,6 +81,33 @@ static int update_file(int file_fd, size_t sz, int seqno)
return end - start;
}
static int update_dmabuf(int file_fd, struct gbm_bo *bo, size_t sz, int seqno)
{
(void)file_fd;
if (rand() % 11 == 0) {
/* no change */
return 0;
}
void *map_handle = NULL;
void *data = map_dmabuf(bo, true, &map_handle);
if (data == MAP_FAILED) {
return -1;
}
size_t start = (size_t)rand() % sz;
size_t end = (size_t)rand() % sz;
if (start > end) {
size_t tmp = start;
start = end;
end = tmp;
}
memset((char *)data + start, seqno, end - start);
unmap_dmabuf(bo, map_handle);
return end - start;
}
static void combine_transfer_blocks(struct transfer *transfer,
struct bytebuf *ret_block, int nblocks, struct bytebuf *blocks)
{
......@@ -66,7 +126,8 @@ static void combine_transfer_blocks(struct transfer *transfer,
transfer->nblocks = 1;
}
static bool check_match(int orig_fd, int copy_fd)
static bool check_match(int orig_fd, int copy_fd, struct gbm_bo *orig_bo,
struct gbm_bo *copy_bo)
{
size_t csz = 0, osz = 0;
fdcat_t ctype = get_fd_type(copy_fd, &csz);
......@@ -78,20 +139,40 @@ static bool check_match(int orig_fd, int copy_fd)
return false;
}
void *cdata = mmap(NULL, csz, PROT_READ, MAP_SHARED, copy_fd, 0);
if (cdata == MAP_FAILED) {
return false;
}
void *odata = mmap(NULL, osz, PROT_READ, MAP_SHARED, orig_fd, 0);
if (odata == MAP_FAILED) {
munmap(cdata, csz);
return false;
void *ohandle = NULL, *chandle = NULL;
void *cdata = NULL, *odata = NULL;
if (otype == FDC_FILE) {
cdata = mmap(NULL, csz, PROT_READ, MAP_SHARED, copy_fd, 0);
if (cdata == MAP_FAILED) {
return false;
}
odata = mmap(NULL, osz, PROT_READ, MAP_SHARED, orig_fd, 0);
if (odata == MAP_FAILED) {
munmap(cdata, csz);
return false;
}
} else if (otype == FDC_DMABUF) {
cdata = map_dmabuf(copy_bo, false, &chandle);
if (cdata == MAP_FAILED) {
return false;
}
odata = map_dmabuf(orig_bo, false, &ohandle);
if (odata == MAP_FAILED) {
unmap_dmabuf(copy_bo, chandle);
return false;
}
}
bool pass = memcmp(cdata, odata, csz) == 0;
munmap(odata, osz);
munmap(cdata, csz);
if (otype == FDC_FILE) {
munmap(odata, osz);
munmap(cdata, csz);
} else if (otype == FDC_DMABUF) {
unmap_dmabuf(orig_bo, ohandle);
unmap_dmabuf(copy_bo, chandle);
}
if (!pass) {
wp_log(WP_ERROR, "Mirrored file descriptor contents differ");
}
......@@ -100,7 +181,8 @@ static bool check_match(int orig_fd, int copy_fd)
}
static bool test_transfer(struct fd_translation_map *src_map,
struct fd_translation_map *dst_map, int rid, int ndiff)
struct fd_translation_map *dst_map, int rid, int ndiff,
struct render_data *render_data)
{
int ntransfers = 0, nblocks = 0;
struct transfer transfers[10];
......@@ -129,20 +211,22 @@ static bool test_transfer(struct fd_translation_map *src_map,
struct transfer res_transfer = transfers[0];
struct bytebuf ret_block;
combine_transfer_blocks(&res_transfer, &ret_block, nblocks, blocks);
apply_update(dst_map, NULL, &res_transfer);
apply_update(dst_map, render_data, &res_transfer);
free(ret_block.data);
/* first round, this only exists after the transfer */
struct shadow_fd *dst_shadow = get_shadow_for_rid(dst_map, rid);
return check_match(src_shadow->fd_local, dst_shadow->fd_local);
return check_match(src_shadow->fd_local, dst_shadow->fd_local,
src_shadow->dmabuf_bo, dst_shadow->dmabuf_bo);
}
/* This test closes the provided file fd */
static bool test_mirror(int new_file_fd, size_t sz,
int (*update)(int fd, size_t sz, int seqno),
int (*update)(int fd, struct gbm_bo *bo, size_t sz, int seqno),
enum compression_mode comp_mode, int n_src_threads,
int n_dst_threads)
int n_dst_threads, struct render_data *rd,
struct dmabuf_slice_data *slice_data)
{
struct fd_translation_map src_map;
setup_translation_map(&src_map, false, comp_mode, n_src_threads);
......@@ -160,8 +244,10 @@ static bool test_mirror(int new_file_fd, size_t sz,
dst_map.comp_thread_threshold = 1000;
}
struct shadow_fd *src_shadow =
translate_fd(&src_map, NULL, new_file_fd, NULL, false);
size_t fdsz = 0;
fdcat_t fdtype = get_fd_type(new_file_fd, &fdsz);
struct shadow_fd *src_shadow = translate_fd(&src_map, rd, new_file_fd,
fdtype, fdsz, slice_data, false);
struct shadow_fd *dst_shadow = NULL;
int rid = src_shadow->remote_id;
......@@ -171,7 +257,10 @@ static bool test_mirror(int new_file_fd, size_t sz,
int target_fd = fwd ? src_shadow->fd_local
: dst_shadow->fd_local;
int ndiff = i > 0 ? (*update)(target_fd, sz, i) : (int)sz;
struct gbm_bo *target_bo = fwd ? src_shadow->dmabuf_bo
: dst_shadow->dmabuf_bo;
int ndiff = i > 0 ? (*update)(target_fd, target_bo, sz, i)
: (int)sz;
if (ndiff == -1) {
pass = false;
break;
......@@ -180,11 +269,13 @@ static bool test_mirror(int new_file_fd, size_t sz,
if (fwd) {
src_shadow->is_dirty = true;
damage_everything(&src_shadow->damage);
subpass = test_transfer(&src_map, &dst_map, rid, ndiff);
subpass = test_transfer(
&src_map, &dst_map, rid, ndiff, rd);
} else {
dst_shadow->is_dirty = true;
damage_everything(&dst_shadow->damage);
subpass = test_transfer(&dst_map, &src_map, rid, ndiff);
subpass = test_transfer(
&dst_map, &src_map, rid, ndiff, rd);
}
pass &= subpass;
if (!pass) {
......@@ -212,19 +303,44 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
// TODO: cycle through display-side and plenty of auxiliary variables
/* to avoid warnings when the driver dmabuf size constraints require
* significant alignment, the width/height are already 64 aligned */
size_t test_width = 256;
size_t test_height = 320;
size_t test_cpp = 2;
size_t test_size = test_width * test_height * test_cpp;
uint8_t *test_pattern = malloc(test_size);
for (size_t i = 0; i < test_size; i++) {
test_pattern[i] = (uint8_t)i;
}
struct render_data rd = {
.drm_node_path = NULL,
.drm_fd = -1,
.dev = NULL,
.disabled = false,
};
bool has_dmabuf = TEST_2CPP_FORMAT != 0;
if (has_dmabuf && init_render_data(&rd) == -1) {
has_dmabuf = false;
}
struct dmabuf_slice_data slice_data = {.width = (uint32_t)test_width,
.height = (uint32_t)test_height,
.format = TEST_2CPP_FORMAT,
.num_planes = 1,
.modifier = 0,
.offsets = {0, 0, 0, 0},
.strides = {(uint32_t)(test_width * test_cpp), 0, 0, 0},
.using_planes = {true, false, false, false}};
bool all_success = true;
srand(0);
for (size_t c = 0; c < sizeof(comp_modes) / sizeof(comp_modes[0]);
c++) {
for (int gt = 1; gt <= 5; gt++) {
for (int rt = 1; rt <= 3; rt++) {
size_t test_size = (1u << 15) + 259;
uint8_t *test_pattern = malloc(test_size);
for (size_t i = 0; i < test_size; i++) {
test_pattern[i] = (uint8_t)i;
}
int file_fd = open("test/file",
O_CREAT | O_RDWR | O_TRUNC,
0644);
......@@ -242,19 +358,50 @@ int main(int argc, char **argv)
close(file_fd);
continue;
}
free(test_pattern);
bool pass = test_mirror(file_fd, test_size,
update_file, comp_modes[c], gt,
rt);
rt, &rd, &slice_data);
printf("Tested comp=%d src_thread=%d dst_thread=%d, %s\n",
printf(" FILE comp=%d src_thread=%d dst_thread=%d, %s\n",
(int)c, gt, rt,
pass ? "pass" : "FAIL");
all_success &= pass;
if (has_dmabuf) {
struct gbm_bo *bo = make_dmabuf(&rd,
(const char *)test_pattern,
test_size, &slice_data);
if (!bo) {
has_dmabuf = false;
continue;
}
int dmafd = export_dmabuf(bo);
if (dmafd == -1) {
has_dmabuf = false;
continue;
}
destroy_dmabuf(bo);
bool dpass = test_mirror(dmafd,
test_size,
update_dmabuf,
comp_modes[c], gt, rt,
&rd, &slice_data);
printf("DMABUF comp=%d src_thread=%d dst_thread=%d, %s\n",
(int)c, gt, rt,
dpass ? "pass"
: "FAIL");
all_success &= dpass;
}
}
}
}
cleanup_render_data(&rd);
free(test_pattern);
printf("All pass: %c\n", all_success ? 'Y' : 'n');
return all_success ? EXIT_SUCCESS : EXIT_FAILURE;
}
/*
* Copyright © 2019 Manuel Stoeckl
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "util.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/mman.h>
#include <ffi.h>
#include <wayland-util.h>
void invoke_msg_handler(ffi_cif *cif, const struct wl_interface *intf,
const struct wl_message *msg, bool is_event,
const uint32_t *const payload, const int paylen,
const int *const fd_list, const int fdlen, int *fds_used,
void (*const func)(void), struct context *ctx,
struct message_tracker *mt, struct fd_translation_map *map);
static bool called = false;
static void testfn_client(struct context *ctx, void *noop, struct wp_object *a0,
int a1, int32_t a2, uint32_t a3, struct wp_object *a4,
const char *a5, uint32_t a6)
{
called = true;
printf("%d called with: a0=%u, a1=%d, a2=%d, a3=%u, a4=%u, a5=\"%s\", a6=%u\n",
ctx->obj->obj_id, a0->obj_id, a1, a2, a3, a4->obj_id,
a5, a6);
(void)noop;
}
static void testfn_server(struct context *ctx, void *noop, uint32_t a0, int a1,
int32_t a2, uint32_t a3, struct wp_object *a4, const char *a5,
uint32_t a6)
{
called = true;
printf("%d called with: a0=%u, a1=%d, a2=%d, a3=%u, a4=%u, a5=\"%s\", a6=%u\n",
ctx->obj->obj_id, a0, a1, a2, a3, a4->obj_id, a5, a6);
(void)noop;
}
log_handler_func_t log_funcs[2] = {test_log_handler, test_log_handler};
int main(int argc, char **argv)
{
(void)argc;
(void)argv;
struct wl_interface dummy_intf = {0};
dummy_intf.name = "test";
struct wl_interface result_intf = {0};
result_intf.name = "result";
struct wl_message msg;
msg.name = "test";
msg.signature = "2nhiu?osu";
const struct wl_interface *typevec[] = {&result_intf, NULL, NULL, NULL,
&result_intf, NULL, NULL};
msg.types = typevec;
ffi_type *types_client[] = {&ffi_type_pointer, &ffi_type_pointer,
&ffi_type_pointer, &ffi_type_sint, &ffi_type_sint32,
&ffi_type_uint32, &ffi_type_pointer, &ffi_type_pointer,
&ffi_type_uint32};
ffi_type *types_server[] = {&ffi_type_pointer, &ffi_type_pointer,
&ffi_type_uint32, &ffi_type_sint, &ffi_type_sint32,
&ffi_type_uint32, &ffi_type_pointer, &ffi_type_pointer,
&ffi_type_uint32};
ffi_cif cif_client;
ffi_cif cif_server;
ffi_prep_cif(&cif_client, FFI_DEFAULT_ABI, 8, &ffi_type_void,
types_client);
ffi_prep_cif(&cif_server, FFI_DEFAULT_ABI, 8, &ffi_type_void,
types_server);
struct message_tracker mt;
init_message_tracker(&mt);
struct wp_object *old_display = listset_get(&mt.objects, 1);
listset_remove(&mt.objects, old_display);
destroy_wp_object(NULL, old_display);
struct fd_translation_map map;
setup_translation_map(&map, false, COMP_NONE, 1);
struct wp_object arg_obj;
arg_obj.type = &dummy_intf;
arg_obj.is_zombie = false;
arg_obj.obj_id = 1;
listset_insert(&map, &mt.objects, &arg_obj);
struct wp_object obj;
obj.type = &dummy_intf;
obj.is_zombie = false;
obj.obj_id = 0;
struct context ctx = {.obj = &obj, .g = NULL};
int actual_fdlen = 1;
int fds[1] = {99};
uint32_t new_id = 51;
uint32_t payload[] = {new_id, 11, (uint32_t)-1, arg_obj.obj_id, 13,
0x61626162, 0x61626162, 0x61626162, 0x00000061, 777};
int actual_length = (int)(sizeof(payload) / sizeof(uint32_t));
bool all_success = true;
int fds_used;
for (int fdlen = actual_fdlen; fdlen >= 0; fdlen--) {
for (int length = actual_length; length >= 0; length--) {
bool expect_success = fdlen == actual_fdlen &&
length == actual_length;
printf("Trying: %d/%d %d/%d\n", length, actual_length,
fdlen, actual_fdlen);
called = false;
fds_used = 0;
invoke_msg_handler(&cif_client, &dummy_intf, &msg, true,
payload, length, fds, fdlen, &fds_used,
(void (*)(void))testfn_client, &ctx,
&mt, &map);
all_success &= called == expect_success;
if (called != expect_success) {
wp_log(WP_ERROR,
"client FAIL at %d/%d chars, %d/%d fds",
length, actual_length, fdlen,
actual_fdlen);
}
struct wp_object *new_obj =
listset_get(&mt.objects, new_id);
if (new_obj) {
listset_remove(&mt.objects, new_obj);
destroy_wp_object(&map, new_obj);
}
called = false;
fds_used = 0;
invoke_msg_handler(&cif_server, &dummy_intf, &msg,
false, payload, length, fds, fdlen,
&fds_used,
(void (*)(void))testfn_server, &ctx,
&mt, &map);
all_success &= called == expect_success;
if (called != expect_success) {
wp_log(WP_ERROR,
"server FAIL at %d/%d chars, %d/%d fds",
length, actual_length, fdlen,
actual_fdlen);
}
new_obj = listset_get(&mt.objects, new_id);
if (new_obj) {
listset_remove(&mt.objects, new_obj);
destroy_wp_object(&map, new_obj);
}
}
}
listset_remove(&mt.objects, &arg_obj);
cleanup_message_tracker(&map, &mt);
cleanup_translation_map(&map);
printf("Net result: %s\n", all_success ? "pass" : "FAIL");
return all_success ? EXIT_SUCCESS : EXIT_FAILURE;
}
......@@ -444,13 +444,15 @@ void cleanup_translation_map(struct fd_translation_map *map);
* shadow entry. (For example, FDC_PIPE_IR for a pipe-like object that can only
* be read.) Sets *size if non-NULL and if the object is an FDC_FILE. */
fdcat_t get_fd_type(int fd, size_t *size);
/** Given a local file descriptor, produce matching global id, and register it
* into the translation map if not already done. The function can also be
* provided with optional extra information.
const char *fdcat_to_str(fdcat_t cat);
/** Given a local file descriptor, type hint, and already computed size,
* produce matching global id, and register it into the translation map if
* not already done. The function can also be provided with optional extra
* information (*info).
*/
struct dmabuf_slice_data;
struct shadow_fd *translate_fd(struct fd_translation_map *map,
struct render_data *render, int fd,
struct render_data *render, int fd, fdcat_t type, size_t sz,
struct dmabuf_slice_data *info, bool try_video);
/** Given a struct shadow_fd, produce some number of corresponding file update
* transfer messages. All pointers will be to existing memory. */
......