[Spice-devel] [PATCH 5/6] server/red_worker: (QXLInterface change) add update_mem for S3/S4 support

Alon Levy alevy at redhat.com
Mon Jun 20 04:18:08 PDT 2011


Adds QXLWorker::update_mem to the public api (spice.h) functions, used by qxl
(qemu) for ACPI S3 (suspend to ram) and S4 (hibernate, suspend to disk)
support. This is a helper function, it provides functionality available
otherwise but by adding it the amount of vmexits is reduced. Specifically:

update_mem:
 render all operations onto each surface
  (guest will copy surfaces from pci to guest ram)
 free all resources (pci may be erased and qxl *will* be reset)
  (in particular) destroy all surfaces
 alternative: guest can do an QXL_IO_UPDATE_AREA for each surface.
  This is a vmexit per UPDATE_AREA.
  this only takes care of the rendering. It can then send a destroy surface
  command treating it specially in the driver (not releasing the guest side
  resource). 32 commands in the ring, so an additional number of vmexits
  (windows xp guest has ~40 surfaces after boot).

Cc: Yonit Halperin <yhalperi at redhat.com>
---
 server/red_dispatcher.c |   12 ++++++++++
 server/red_worker.c     |   52 +++++++++++++++++++++++++++++++++++++++++++++-
 server/red_worker.h     |    1 +
 server/spice.h          |    1 +
 4 files changed, 64 insertions(+), 2 deletions(-)

diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c
index 56446ab..22c69b5 100644
--- a/server/red_dispatcher.c
+++ b/server/red_dispatcher.c
@@ -460,6 +460,16 @@ void red_dispatcher_set_mouse_mode(uint32_t mode)
     }
 }
 
+static void qxl_worker_update_mem(QXLWorker *qxl_worker)
+{
+    RedDispatcher *dispatcher = (RedDispatcher *)qxl_worker;
+    RedWorkerMessage message = RED_WORKER_MESSAGE_UPDATE_MEM;
+
+    write_message(dispatcher->channel, &message);
+    read_message(dispatcher->channel, &message);
+    ASSERT(message == RED_WORKER_MESSAGE_READY);
+}
+
 int red_dispatcher_count(void)
 {
     RedDispatcher *now = dispatchers;
@@ -538,6 +548,8 @@ RedDispatcher *red_dispatcher_init(QXLInstance *qxl)
     dispatcher->base.destroy_surface_wait = qxl_worker_destroy_surface_wait;
     dispatcher->base.loadvm_commands = qxl_worker_loadvm_commands;
 
+    dispatcher->base.update_mem = qxl_worker_update_mem;
+
     qxl->st->qif->get_init_info(qxl, &init_info);
 
     init_data.memslot_id_bits = init_info.memslot_id_bits;
diff --git a/server/red_worker.c b/server/red_worker.c
index 38ccb90..f5a2091 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -4290,7 +4290,8 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
 
             red_get_surface_cmd(&worker->mem_slots, ext_cmd.group_id,
                                 surface, ext_cmd.cmd.data);
-            red_process_surface(worker, surface, ext_cmd.group_id, 0);
+            red_process_surface(worker, surface, ext_cmd.group_id,
+                                surface->flags & QXL_SURF_FLAG_KEEP_DATA);
             break;
         }
         default:
@@ -8485,6 +8486,9 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, ui
         }
 
         red_create_surface_item(worker, surface_id);
+        if (data_is_valid) {
+            red_add_surface_image(worker, surface_id);
+        }
         return;
     }
 
@@ -8495,6 +8499,9 @@ static inline void red_create_surface(RedWorker *worker, uint32_t surface_id, ui
         if (surface->context.canvas) { //no need canvas check
             worker->renderer = worker->renderers[i];
             red_create_surface_item(worker, surface_id);
+            if (data_is_valid) {
+                red_add_surface_image(worker, surface_id);
+            }
             return;
         }
     }
@@ -9550,7 +9557,6 @@ static inline void destroy_all_surfaces_starting_from(RedWorker *worker, int fir
 {
     int i;
 
-    red_printf("");
     flush_all_qxl_commands(worker);
     //to handle better
     for (i = first_surface_id; i < NUM_SURFACES; ++i) {
@@ -9590,6 +9596,12 @@ static inline void destroy_all_surfaces_starting_from(RedWorker *worker, int fir
     worker->cursor_trail_length = worker->cursor_trail_frequency = 0;
 }
 
+static void destroy_all_offscreen_surfaces(RedWorker *worker)
+{
+    red_printf("");
+    destroy_all_surfaces_starting_from(worker, 1);
+}
+
 /* called upon device reset */
 static inline void handle_dev_destroy_surfaces(RedWorker *worker)
 {
@@ -9676,6 +9688,38 @@ static inline void handle_dev_destroy_primary_surface(RedWorker *worker)
     write_message(worker->channel, &message);
 }
 
+/*
+ * Render all surfaces
+ * then call to handle_dev_destroy_surface_wait
+ */
+static void red_worker_update_mem(RedWorker *worker)
+{
+    RedWorkerMessage message;
+    SpiceRect area = {0, 0, 0, 0};
+    RedSurface *surface;
+    int surface_id;
+    int count = 0;
+
+    red_cursor_flush(worker);
+    flush_all_qxl_commands(worker);
+    // TODO: go over worker->current_list instead of individually over surfaces currents.
+    for (surface_id = 1 ; surface_id < NUM_SURFACES ; ++surface_id) {
+        if (!worker->surfaces[surface_id].context.canvas) {
+            continue;
+        }
+        count++;
+        surface = &worker->surfaces[surface_id];
+        area.right = surface->context.width;
+        area.bottom = surface->context.height;
+        red_update_area(worker, &area, surface_id);
+    }
+    destroy_all_offscreen_surfaces(worker);
+    red_printf("updated %d surfaces (left with %d/%d/%d)", count,
+        worker->drawable_count, worker->current_size, worker->stream_count);
+    message = RED_WORKER_MESSAGE_READY;
+    write_message(worker->channel, &message);
+}
+
 static void handle_dev_input(EventListener *listener, uint32_t events)
 {
     RedWorker *worker = SPICE_CONTAINEROF(listener, RedWorker, dev_listener);
@@ -9913,6 +9957,10 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
         write_message(worker->channel, &message);
         break;
     }
+    case RED_WORKER_MESSAGE_UPDATE_MEM: {
+        red_worker_update_mem(worker);
+        break;
+    }
     default:
         red_error("message error");
     }
diff --git a/server/red_worker.h b/server/red_worker.h
index b4e2ed2..7dfb433 100644
--- a/server/red_worker.h
+++ b/server/red_worker.h
@@ -70,6 +70,7 @@ enum {
     RED_WORKER_MESSAGE_RESET_IMAGE_CACHE,
     RED_WORKER_MESSAGE_DESTROY_SURFACE_WAIT,
     RED_WORKER_MESSAGE_LOADVM_COMMANDS,
+    RED_WORKER_MESSAGE_UPDATE_MEM,
 };
 
 typedef uint32_t RedWorkerMessage;
diff --git a/server/spice.h b/server/spice.h
index cbb75cc..23e7d54 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -122,6 +122,7 @@ struct QXLWorker {
     void (*reset_cursor)(QXLWorker *worker);
     void (*destroy_surface_wait)(QXLWorker *worker, uint32_t surface_id);
     void (*loadvm_commands)(QXLWorker *worker, struct QXLCommandExt *ext, uint32_t count);
+    void (*update_mem)(QXLWorker *worker);
 };
 
 typedef struct QXLDrawArea {
-- 
1.7.5.2



More information about the Spice-devel mailing list