Blame SOURCES/0014-red_channel-cleanup-of-red_channel_client-blocking-m.patch

e2c81d
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
20333d
From: Yonit Halperin <yhalperi@redhat.com>
20333d
Date: Wed, 11 Sep 2013 13:31:21 -0400
20333d
Subject: [PATCH] red_channel: cleanup of red_channel_client blocking methods
20333d
20333d
(1) receive timeout as a parameter.
20333d
(2) add a return value and pass the handling
20333d
    of failures to the calling routine.
20333d
20333d
https://bugzilla.redhat.com/show_bug.cgi?id=1016795
20333d
(cherry picked from commit bcf9e64f134a6073c1e404efc8892c1cb453bd8a)
20333d
---
20333d
 server/red_channel.c | 73 ++++++++++++++++++++++++++--------------------------
20333d
 server/red_channel.h | 22 ++++++++++------
20333d
 server/red_worker.c  | 55 ++++++++++++++++++++++++++++++---------
20333d
 3 files changed, 93 insertions(+), 57 deletions(-)
20333d
20333d
diff --git a/server/red_channel.c b/server/red_channel.c
20333d
index 6e43e0a..228669b 100644
20333d
--- a/server/red_channel.c
20333d
+++ b/server/red_channel.c
20333d
@@ -48,11 +48,7 @@ typedef struct EmptyMsgPipeItem {
20333d
 #define PING_TEST_TIMEOUT_MS 15000
20333d
 #define PING_TEST_IDLE_NET_TIMEOUT_MS 100
20333d
 
20333d
-#define DETACH_TIMEOUT 15000000000ULL //nano
20333d
-#define DETACH_SLEEP_DURATION 10000 //micro
20333d
-
20333d
-#define CHANNEL_PUSH_TIMEOUT 30000000000ULL //nano
20333d
-#define CHANNEL_PUSH_SLEEP_DURATION 10000 //micro
20333d
+#define CHANNEL_BLOCKED_SLEEP_DURATION 10000 //micro
20333d
 
20333d
 enum QosPingState {
20333d
     PING_STATE_NONE,
20333d
@@ -2191,43 +2187,49 @@ uint32_t red_channel_sum_pipes_size(RedChannel *channel)
20333d
     return sum;
20333d
 }
20333d
 
20333d
-void red_wait_outgoing_item(RedChannelClient *rcc)
20333d
+int red_channel_client_wait_outgoing_item(RedChannelClient *rcc,
20333d
+                                          int64_t timeout)
20333d
 {
20333d
     uint64_t end_time;
20333d
     int blocked;
20333d
 
20333d
     if (!red_channel_client_blocked(rcc)) {
20333d
-        return;
20333d
+        return TRUE;
20333d
+    }
20333d
+    if (timeout != -1) {
20333d
+        end_time = red_now() + timeout;
20333d
     }
20333d
-    end_time = red_now() + DETACH_TIMEOUT;
20333d
     spice_info("blocked");
20333d
 
20333d
     do {
20333d
-        usleep(DETACH_SLEEP_DURATION);
20333d
+        usleep(CHANNEL_BLOCKED_SLEEP_DURATION);
20333d
         red_channel_client_receive(rcc);
20333d
         red_channel_client_send(rcc);
20333d
-    } while ((blocked = red_channel_client_blocked(rcc)) && red_now() < end_time);
20333d
+    } while ((blocked = red_channel_client_blocked(rcc)) &&
20333d
+             (timeout == -1 || red_now() < end_time));
20333d
 
20333d
     if (blocked) {
20333d
         spice_warning("timeout");
20333d
-        // TODO - shutting down the socket but we still need to trigger
20333d
-        // disconnection. Right now we wait for main channel to error for that.
20333d
-        red_channel_client_shutdown(rcc);
20333d
+        return FALSE;
20333d
     } else {
20333d
         spice_assert(red_channel_client_no_item_being_sent(rcc));
20333d
+        return TRUE;
20333d
     }
20333d
 }
20333d
 
20333d
 /* TODO: more evil sync stuff. anything with the word wait in it's name. */
20333d
-void red_channel_client_wait_pipe_item_sent(RedChannelClient *rcc,
20333d
-                                            PipeItem *item)
20333d
+int red_channel_client_wait_pipe_item_sent(RedChannelClient *rcc,
20333d
+                                           PipeItem *item,
20333d
+                                           int64_t timeout)
20333d
 {
20333d
     uint64_t end_time;
20333d
     int item_in_pipe;
20333d
 
20333d
     spice_info(NULL);
20333d
 
20333d
-    end_time = red_now() + CHANNEL_PUSH_TIMEOUT;
20333d
+    if (timeout != -1) {
20333d
+        end_time = red_now() + timeout;
20333d
+    }
20333d
 
20333d
     rcc->channel->channel_cbs.hold_item(rcc, item);
20333d
 
20333d
@@ -2237,55 +2239,52 @@ void red_channel_client_wait_pipe_item_sent(RedChannelClient *rcc,
20333d
     }
20333d
     red_channel_client_push(rcc);
20333d
 
20333d
-    while((item_in_pipe = ring_item_is_linked(&item->link)) && (red_now() < end_time)) {
20333d
-        usleep(CHANNEL_PUSH_SLEEP_DURATION);
20333d
+    while((item_in_pipe = ring_item_is_linked(&item->link)) &&
20333d
+          (timeout == -1 || red_now() < end_time)) {
20333d
+        usleep(CHANNEL_BLOCKED_SLEEP_DURATION);
20333d
         red_channel_client_receive(rcc);
20333d
         red_channel_client_send(rcc);
20333d
         red_channel_client_push(rcc);
20333d
     }
20333d
 
20333d
+    red_channel_client_release_item(rcc, item, TRUE);
20333d
     if (item_in_pipe) {
20333d
         spice_warning("timeout");
20333d
-        red_channel_client_disconnect(rcc);
20333d
-    } else {
20333d
-        red_wait_outgoing_item(rcc);
20333d
-    }
20333d
-    red_channel_client_release_item(rcc, item, TRUE);
20333d
-}
20333d
-
20333d
-static void rcc_shutdown_if_pending_send(RedChannelClient *rcc)
20333d
-{
20333d
-    if (red_channel_client_blocked(rcc) || rcc->pipe_size > 0) {
20333d
-        red_channel_client_shutdown(rcc);
20333d
+        return FALSE;
20333d
     } else {
20333d
-        spice_assert(red_channel_client_no_item_being_sent(rcc));
20333d
+        return red_channel_client_wait_outgoing_item(rcc,
20333d
+                                                     timeout == -1 ? -1 : end_time - red_now());
20333d
     }
20333d
 }
20333d
 
20333d
-void red_channel_wait_all_sent(RedChannel *channel)
20333d
+int red_channel_wait_all_sent(RedChannel *channel,
20333d
+                              int64_t timeout)
20333d
 {
20333d
     uint64_t end_time;
20333d
     uint32_t max_pipe_size;
20333d
     int blocked = FALSE;
20333d
 
20333d
-    end_time = red_now() + DETACH_TIMEOUT;
20333d
+    if (timeout != -1) {
20333d
+        end_time = red_now() + timeout;
20333d
+    }
20333d
 
20333d
     red_channel_push(channel);
20333d
     while (((max_pipe_size = red_channel_max_pipe_size(channel)) ||
20333d
            (blocked = red_channel_any_blocked(channel))) &&
20333d
-           red_now() < end_time) {
20333d
+           (timeout == -1 || red_now() < end_time)) {
20333d
         spice_debug("pipe-size %u blocked %d", max_pipe_size, blocked);
20333d
-        usleep(DETACH_SLEEP_DURATION);
20333d
+        usleep(CHANNEL_BLOCKED_SLEEP_DURATION);
20333d
         red_channel_receive(channel);
20333d
         red_channel_send(channel);
20333d
         red_channel_push(channel);
20333d
     }
20333d
 
20333d
     if (max_pipe_size || blocked) {
20333d
-        spice_printerr("timeout: pending out messages exist (pipe-size %u, blocked %d)",
20333d
-                       max_pipe_size, blocked);
20333d
-        red_channel_apply_clients(channel, rcc_shutdown_if_pending_send);
20333d
+        spice_warning("timeout: pending out messages exist (pipe-size %u, blocked %d)",
20333d
+                      max_pipe_size, blocked);
20333d
+        return FALSE;
20333d
     } else {
20333d
         spice_assert(red_channel_no_item_being_sent(channel));
20333d
+        return TRUE;
20333d
     }
20333d
 }
20333d
diff --git a/server/red_channel.h b/server/red_channel.h
20333d
index 9021b3f..fa11505 100644
20333d
--- a/server/red_channel.h
20333d
+++ b/server/red_channel.h
20333d
@@ -596,14 +596,20 @@ int red_client_during_migrate_at_target(RedClient *client);
20333d
 
20333d
 void red_client_migrate(RedClient *client);
20333d
 
20333d
-/* blocking function */
20333d
-void red_channel_client_wait_pipe_item_sent(RedChannelClient *rcc,
20333d
-                                            PipeItem *item);
20333d
-
20333d
-/* blocking function */
20333d
-void red_wait_outgoing_item(RedChannelClient *rcc);
20333d
+/*
20333d
+ * blocking functions.
20333d
+ *
20333d
+ * timeout is in nano sec. -1 for no timeout.
20333d
+ *
20333d
+ * Return: TRUE if waiting succeeded. FALSE if timeout expired.
20333d
+ */
20333d
 
20333d
-/* blocking function */
20333d
-void red_channel_wait_all_sent(RedChannel *channel);
20333d
+int red_channel_client_wait_pipe_item_sent(RedChannelClient *rcc,
20333d
+                                           PipeItem *item,
20333d
+                                           int64_t timeout);
20333d
+int red_channel_client_wait_outgoing_item(RedChannelClient *rcc,
20333d
+                                          int64_t timeout);
20333d
+int red_channel_wait_all_sent(RedChannel *channel,
20333d
+                              int64_t timeout);
20333d
 
20333d
 #endif
20333d
diff --git a/server/red_worker.c b/server/red_worker.c
20333d
index f2c9220..31f3cbb 100644
20333d
--- a/server/red_worker.c
20333d
+++ b/server/red_worker.c
20333d
@@ -98,6 +98,7 @@
20333d
 #define CMD_RING_POLL_TIMEOUT 10 //milli
20333d
 #define CMD_RING_POLL_RETRIES 200
20333d
 
20333d
+#define DISPLAY_CLIENT_SHORT_TIMEOUT 15000000000ULL //nano
20333d
 #define DISPLAY_CLIENT_TIMEOUT 30000000000ULL //nano
20333d
 #define DISPLAY_CLIENT_MIGRATE_DATA_TIMEOUT 10000000000ULL //nano, 10 sec
20333d
 #define DISPLAY_CLIENT_RETRY_INTERVAL 10000 //micro
20333d
@@ -2031,8 +2032,12 @@ static void red_current_clear(RedWorker *worker, int surface_id)
20333d
     }
20333d
 }
20333d
 
20333d
-static void red_clear_surface_drawables_from_pipe(DisplayChannelClient *dcc, int surface_id,
20333d
-                                                  int wait_if_used)
20333d
+/*
20333d
+ * Return: TRUE if wait_if_used == FALSE, or otherwise, if all of the pipe items that
20333d
+ * are related to the surface have been cleared (or sent) from the pipe.
20333d
+ */
20333d
+static int red_clear_surface_drawables_from_pipe(DisplayChannelClient *dcc, int surface_id,
20333d
+                                                 int wait_if_used)
20333d
 {
20333d
     Ring *ring;
20333d
     PipeItem *item;
20333d
@@ -2040,7 +2045,7 @@ static void red_clear_surface_drawables_from_pipe(DisplayChannelClient *dcc, int
20333d
     RedChannelClient *rcc;
20333d
 
20333d
     if (!dcc) {
20333d
-        return;
20333d
+        return TRUE;
20333d
     }
20333d
 
20333d
     /* removing the newest drawables that their destination is surface_id and
20333d
@@ -2085,24 +2090,27 @@ static void red_clear_surface_drawables_from_pipe(DisplayChannelClient *dcc, int
20333d
             if (wait_if_used) {
20333d
                 break;
20333d
             } else {
20333d
-                return;
20333d
+                return TRUE;
20333d
             }
20333d
         }
20333d
     }
20333d
 
20333d
     if (!wait_if_used) {
20333d
-        return;
20333d
+        return TRUE;
20333d
     }
20333d
 
20333d
     if (item) {
20333d
-        red_channel_client_wait_pipe_item_sent(&dcc->common.base, item);
20333d
+        return red_channel_client_wait_pipe_item_sent(&dcc->common.base, item,
20333d
+                                                      DISPLAY_CLIENT_TIMEOUT);
20333d
     } else {
20333d
         /*
20333d
          * in case that the pipe didn't contain any item that is dependent on the surface, but
20333d
-         * there is one during sending.
20333d
+         * there is one during sending. Use a shorter timeout, since it is just one item
20333d
          */
20333d
-        red_wait_outgoing_item(&dcc->common.base);
20333d
+        return red_channel_client_wait_outgoing_item(&dcc->common.base,
20333d
+                                                     DISPLAY_CLIENT_SHORT_TIMEOUT);
20333d
     }
20333d
+    return TRUE;
20333d
 }
20333d
 
20333d
 static void red_clear_surface_drawables_from_pipes(RedWorker *worker,
20333d
@@ -2113,7 +2121,9 @@ static void red_clear_surface_drawables_from_pipes(RedWorker *worker,
20333d
     DisplayChannelClient *dcc;
20333d
 
20333d
     WORKER_FOREACH_DCC_SAFE(worker, item, next, dcc) {
20333d
-        red_clear_surface_drawables_from_pipe(dcc, surface_id, wait_if_used);
20333d
+        if (!red_clear_surface_drawables_from_pipe(dcc, surface_id, wait_if_used)) {
20333d
+            red_channel_client_disconnect(&dcc->common.base);
20333d
+        }
20333d
     }
20333d
 }
20333d
 
20333d
@@ -11127,6 +11137,15 @@ void handle_dev_destroy_surface_wait(void *opaque, void *payload)
20333d
     dev_destroy_surface_wait(worker, msg->surface_id);
20333d
 }
20333d
 
20333d
+static void rcc_shutdown_if_pending_send(RedChannelClient *rcc)
20333d
+{
20333d
+    if (red_channel_client_blocked(rcc) || rcc->pipe_size > 0) {
20333d
+        red_channel_client_shutdown(rcc);
20333d
+    } else {
20333d
+        spice_assert(red_channel_client_no_item_being_sent(rcc));
20333d
+    }
20333d
+}
20333d
+
20333d
 static inline void red_cursor_reset(RedWorker *worker)
20333d
 {
20333d
     if (worker->cursor) {
20333d
@@ -11144,7 +11163,11 @@ static inline void red_cursor_reset(RedWorker *worker)
20333d
         if (!worker->cursor_channel->common.during_target_migrate) {
20333d
             red_pipes_add_verb(&worker->cursor_channel->common.base, SPICE_MSG_CURSOR_RESET);
20333d
         }
20333d
-        red_channel_wait_all_sent(&worker->cursor_channel->common.base);
20333d
+        if (!red_channel_wait_all_sent(&worker->cursor_channel->common.base,
20333d
+                                       DISPLAY_CLIENT_TIMEOUT)) {
20333d
+            red_channel_apply_clients(&worker->cursor_channel->common.base,
20333d
+                                      rcc_shutdown_if_pending_send);
20333d
+        }
20333d
     }
20333d
 }
20333d
 
20333d
@@ -11427,8 +11450,16 @@ void handle_dev_stop(void *opaque, void *payload)
20333d
      * purge the pipe, send destroy_all_surfaces
20333d
      * to the client (there is no such message right now), and start
20333d
      * from scratch on the destination side */
20333d
-    red_channel_wait_all_sent(&worker->display_channel->common.base);
20333d
-    red_channel_wait_all_sent(&worker->cursor_channel->common.base);
20333d
+    if (!red_channel_wait_all_sent(&worker->display_channel->common.base,
20333d
+                                   DISPLAY_CLIENT_TIMEOUT)) {
20333d
+        red_channel_apply_clients(&worker->display_channel->common.base,
20333d
+                                  rcc_shutdown_if_pending_send);
20333d
+    }
20333d
+    if (!red_channel_wait_all_sent(&worker->cursor_channel->common.base,
20333d
+                                   DISPLAY_CLIENT_TIMEOUT)) {
20333d
+        red_channel_apply_clients(&worker->cursor_channel->common.base,
20333d
+                                  rcc_shutdown_if_pending_send);
20333d
+    }
20333d
 }
20333d
 
20333d
 static int display_channel_wait_for_migrate_data(DisplayChannel *display)