/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define SPICE_LOG_DOMAIN "SpiceWorker"
/* Common variable abbreviations:
*
* rcc - RedChannelClient
* ccc - CursorChannelClient (not to be confused with common_cc)
* common_cc - CommonChannelClient
* dcc - DisplayChannelClient
* cursor_red_channel - downcast of CursorChannel to RedChannel
* display_red_channel - downcast of DisplayChannel to RedChannel
*/
#include <stdio.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <pthread.h>
#include <netinet/tcp.h>
#include <openssl/ssl.h>
#include <inttypes.h>
#include <glib.h>
#include <spice/protocol.h>
#include <spice/qxl_dev.h>
#include "common/lz.h"
#include "common/marshaller.h"
#include "common/quic.h"
#include "common/rect.h"
#include "common/region.h"
#include "common/ring.h"
#include "common/generated_server_marshallers.h"
#include "display-channel.h"
#include "stream.h"
#include "spice.h"
#include "red_worker.h"
#include "spice_timer_queue.h"
#include "cursor-channel.h"
#include "tree.h"
//#define COMPRESS_STAT
//#define DUMP_BITMAP
//#define COMPRESS_DEBUG
#define CMD_RING_POLL_TIMEOUT 10 //milli
#define CMD_RING_POLL_RETRIES 200
#define DISPLAY_CLIENT_SHORT_TIMEOUT 15000000000ULL //nano
#define DISPLAY_CLIENT_MIGRATE_DATA_TIMEOUT 10000000000ULL //nano, 10 sec
#define DISPLAY_CLIENT_RETRY_INTERVAL 10000 //micro
#define DISPLAY_FREE_LIST_DEFAULT_SIZE 128
#define FPS_TEST_INTERVAL 1
#define ZLIB_DEFAULT_COMPRESSION_LEVEL 3
#define MIN_GLZ_SIZE_FOR_ZLIB 100
#define VALIDATE_SURFACE_RET(worker, surface_id) \
if (!validate_surface(worker, surface_id)) { \
rendering_incorrect(__func__); \
return; \
}
#define VALIDATE_SURFACE_RETVAL(worker, surface_id, ret) \
if (!validate_surface(worker, surface_id)) { \
rendering_incorrect(__func__); \
return ret; \
}
#define VALIDATE_SURFACE_BREAK(worker, surface_id) \
if (!validate_surface(worker, surface_id)) { \
rendering_incorrect(__func__); \
break; \
}
static void rendering_incorrect(const char *msg)
{
spice_warning("rendering incorrect from now on: %s", msg);
}
#define MAX_EVENT_SOURCES 20
#define INF_EVENT_WAIT ~0
struct SpiceWatch {
struct RedWorker *worker;
SpiceWatchFunc watch_func;
void *watch_func_opaque;
};
#define MAX_LZ_ENCODERS MAX_CACHE_CLIENTS
#define MAX_PIPE_SIZE 50
#define WIDE_CLIENT_ACK_WINDOW 40
#define NARROW_CLIENT_ACK_WINDOW 20
typedef struct ImageItem {
PipeItem link;
int refs;
SpicePoint pos;
int width;
int height;
int stride;
int top_down;
int surface_id;
int image_format;
uint32_t image_flags;
int can_lossy;
uint8_t data[0];
} ImageItem;
typedef struct {
QuicUsrContext usr;
EncoderData data;
} QuicData;
typedef struct {
LzUsrContext usr;
EncoderData data;
} LzData;
typedef struct {
JpegEncoderUsrContext usr;
EncoderData data;
} JpegData;
#ifdef USE_LZ4
typedef struct {
Lz4EncoderUsrContext usr;
EncoderData data;
} Lz4Data;
#endif
typedef struct {
ZlibEncoderUsrContext usr;
EncoderData data;
} ZlibData;
/**********************************/
/* LZ dictionary related entities */
/**********************************/
#define MAX_GLZ_DRAWABLE_INSTANCES 2
typedef struct RedGlzDrawable RedGlzDrawable;
/* for each qxl drawable, there may be several instances of lz drawables */
/* TODO - reuse this stuff for the top level. I just added a second level of multiplicity
* at the Drawable by keeping a ring, so:
* Drawable -> (ring of) RedGlzDrawable -> (up to 2) GlzDrawableInstanceItem
* and it should probably (but need to be sure...) be
* Drawable -> ring of GlzDrawableInstanceItem.
*/
typedef struct GlzDrawableInstanceItem {
RingItem glz_link;
RingItem free_link;
GlzEncDictImageContext *glz_instance;
RedGlzDrawable *red_glz_drawable;
} GlzDrawableInstanceItem;
struct RedGlzDrawable {
RingItem link; // ordered by the time it was encoded
RingItem drawable_link;
RedDrawable *red_drawable;
Drawable *drawable;
uint32_t group_id;
GlzDrawableInstanceItem instances_pool[MAX_GLZ_DRAWABLE_INSTANCES];
Ring instances;
uint8_t instances_count;
DisplayChannelClient *dcc;
};
pthread_mutex_t glz_dictionary_list_lock = PTHREAD_MUTEX_INITIALIZER;
Ring glz_dictionary_list = {&glz_dictionary_list, &glz_dictionary_list};
typedef struct UpgradeItem {
PipeItem base;
int refs;
Drawable *drawable;
SpiceClipRects *rects;
} UpgradeItem;
typedef struct DrawContext {
SpiceCanvas *canvas;
int canvas_draws_on_surface;
int top_down;
uint32_t width;
uint32_t height;
int32_t stride;
uint32_t format;
void *line_0;
} DrawContext;
typedef struct RedSurface {
uint32_t refs;
Ring current;
Ring current_list;
DrawContext context;
Ring depend_on_me;
QRegion draw_dirty_region;
//fix me - better handling here
QXLReleaseInfoExt create, destroy;
} RedSurface;
struct RedWorker {
pthread_t thread;
clockid_t clockid;
QXLInstance *qxl;
RedDispatcher *red_dispatcher;
int running;
struct pollfd poll_fds[MAX_EVENT_SOURCES];
struct SpiceWatch watches[MAX_EVENT_SOURCES];
unsigned int event_timeout;
DisplayChannel *display_channel;
uint32_t display_poll_tries;
CursorChannel *cursor_channel;
uint32_t cursor_poll_tries;
RedSurface surfaces[NUM_SURFACES];
uint32_t n_surfaces;
SpiceImageSurfaces image_surfaces;
uint32_t red_drawable_count;
uint32_t glz_drawable_count;
uint32_t bits_unique;
RedMemSlotInfo mem_slots;
SpiceImageCompression image_compression;
spice_wan_compression_t jpeg_state;
spice_wan_compression_t zlib_glz_state;
QuicData quic_data;
QuicContext *quic;
LzData lz_data;
LzContext *lz;
JpegData jpeg_data;
JpegEncoderContext *jpeg;
#ifdef USE_LZ4
Lz4Data lz4_data;
Lz4EncoderContext *lz4;
#endif
ZlibData zlib_data;
ZlibEncoder *zlib;
uint32_t process_commands_generation;
#ifdef RED_STATISTICS
StatNodeRef stat;
uint64_t *wakeup_counter;
uint64_t *command_counter;
#endif
int driver_cap_monitors_config;
FILE *record_fd;
};
typedef enum {
BITMAP_DATA_TYPE_INVALID,
BITMAP_DATA_TYPE_CACHE,
BITMAP_DATA_TYPE_SURFACE,
BITMAP_DATA_TYPE_BITMAP,
BITMAP_DATA_TYPE_BITMAP_TO_CACHE,
} BitmapDataType;
typedef struct BitmapData {
BitmapDataType type;
uint64_t id; // surface id or cache item id
SpiceRect lossy_rect;
} BitmapData;
static inline int validate_surface(RedWorker *worker, uint32_t surface_id);
static void red_draw_qxl_drawable(RedWorker *worker, Drawable *drawable);
static void red_current_flush(RedWorker *worker, int surface_id);
static void red_draw_drawable(RedWorker *worker, Drawable *item);
static void red_update_area(RedWorker *worker, const SpiceRect *area, int surface_id);
static void red_update_area_till(RedWorker *worker, const SpiceRect *area, int surface_id,
Drawable *last);
static void detach_stream(DisplayChannel *display, Stream *stream, int detach_sized);
static inline void display_channel_stream_maintenance(DisplayChannel *display, Drawable *candidate, Drawable *sect);
static inline void display_begin_send_message(RedChannelClient *rcc);
static void red_release_glz(DisplayChannelClient *dcc);
static void red_freeze_glz(DisplayChannelClient *dcc);
static void display_channel_push_release(DisplayChannelClient *dcc, uint8_t type, uint64_t id,
uint64_t* sync_data);
static int red_display_free_some_independent_glz_drawables(DisplayChannelClient *dcc);
static void red_display_free_glz_drawable(DisplayChannelClient *dcc, RedGlzDrawable *drawable);
static ImageItem *red_add_surface_area_image(DisplayChannelClient *dcc, int surface_id,
SpiceRect *area, PipeItem *pos, int can_lossy);
static void display_channel_client_release_item_before_push(DisplayChannelClient *dcc,
PipeItem *item);
static void display_channel_client_release_item_after_push(DisplayChannelClient *dcc,
PipeItem *item);
#define LINK_TO_DPI(ptr) SPICE_CONTAINEROF((ptr), DrawablePipeItem, base)
#define DRAWABLE_FOREACH_DPI_SAFE(drawable, link, next, dpi) \
SAFE_FOREACH(link, next, drawable, &(drawable)->pipes, dpi, LINK_TO_DPI(link))
#define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \
drawable_link)
#define DRAWABLE_FOREACH_GLZ_SAFE(drawable, link, next, glz) \
SAFE_FOREACH(link, next, drawable, &(drawable)->glz_ring, glz, LINK_TO_GLZ(link))
|