Blame SOURCES/0044-Lock-the-pixmap-image-cache-for-the-entire-fill_bits.patch

e2c81d
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2be4b2
From: Sandy Stutsman <sstutsma@redhat.com>
2be4b2
Date: Fri, 26 Jun 2015 11:59:13 -0400
2be4b2
Subject: [PATCH] Lock the pixmap image cache for the entire fill_bits call
2be4b2
2be4b2
Locking the individual calls that access the pixmap cache in fill_bits is
2be4b2
not adequately thread safe.  Often a windows guest with multiple monitors
2be4b2
will be sending the same image via different threads.  Both threads can
2be4b2
be in fill_bits at the same time making changes to the cache for the same
2be4b2
image.  This can result in images being deleted before all the client
2be4b2
channels are finished with them or with the same image being send multiple
2be4b2
times.  Here's what can happen with out the lock in fill_bits
2be4b2
2be4b2
On the server in red_worker.c:fill_bits
2be4b2
 Thread 1 calls pixmap_cache_hit for Image A and finds it isn't in cache
2be4b2
 Thread 2 calls pixmap_cache_hit for Image A and finds it isn't in cache
2be4b2
2be4b2
 Thread 1 adds Image 1 to pixmap_cache (1x)
2be4b2
 Thread 2 adds Image 1 to pixmap_cache (2x)
2be4b2
2be4b2
On the client
2be4b2
 Channel 1 adds Image A to image_cache (1x)
2be4b2
 Channel 2 replaces Image A in image_cache (1x)
2be4b2
2be4b2
On server
2be4b2
 Thread 1 sends Image A rendering commands
2be4b2
 Thread N removes Image A from pixmap_cache (image remains - 1x)
2be4b2
 Thread 2 sends Image A rendering commands
2be4b2
2be4b2
On client
2be4b2
 Channe1 renders from Image A
2be4b2
 Channel N removes Image a from image_cache (image is completely removed)
2be4b2
 Channel2 render command hangs waiting for Image A
2be4b2
---
2be4b2
 server/red_client_shared_cache.h | 24 ++++++++++++------------
2be4b2
 server/red_worker.c              | 23 +++++++++++++++--------
2be4b2
 2 files changed, 27 insertions(+), 20 deletions(-)
2be4b2
2be4b2
diff --git a/server/red_client_shared_cache.h b/server/red_client_shared_cache.h
2be4b2
index 821ee18..7feb28e 100644
2be4b2
--- a/server/red_client_shared_cache.h
2be4b2
+++ b/server/red_client_shared_cache.h
2be4b2
@@ -36,13 +36,12 @@
2be4b2
 
2be4b2
 #define CHANNEL_FROM_RCC(rcc) SPICE_CONTAINEROF((rcc)->channel, CHANNEL, common.base);
2be4b2
 
2be4b2
-static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelClient *dcc)
2be4b2
+static int FUNC_NAME(unlocked_hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelClient *dcc)
2be4b2
 {
2be4b2
     NewCacheItem *item;
2be4b2
     uint64_t serial;
2be4b2
 
2be4b2
     serial = red_channel_client_get_message_serial(&dcc->common.base);
2be4b2
-    pthread_mutex_lock(&cache->lock);
2be4b2
     item = cache->hash_table[CACHE_HASH_KEY(id)];
2be4b2
 
2be4b2
     while (item) {
2be4b2
@@ -57,15 +56,22 @@ static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelC
2be4b2
         }
2be4b2
         item = item->next;
2be4b2
     }
2be4b2
-    pthread_mutex_unlock(&cache->lock);
2be4b2
 
2be4b2
     return !!item;
2be4b2
 }
2be4b2
 
2be4b2
-static int FUNC_NAME(set_lossy)(CACHE *cache, uint64_t id, int lossy)
2be4b2
+static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelClient *dcc)
2be4b2
 {
2be4b2
-    NewCacheItem *item;
2be4b2
+    int hit;
2be4b2
     pthread_mutex_lock(&cache->lock);
2be4b2
+    hit = FUNC_NAME(unlocked_hit)(cache,id,lossy, dcc);
2be4b2
+    pthread_mutex_unlock(&cache->lock);
2be4b2
+    return hit;
2be4b2
+}
2be4b2
+
2be4b2
+static int FUNC_NAME(unlocked_set_lossy)(CACHE *cache, uint64_t id, int lossy)
2be4b2
+{
2be4b2
+    NewCacheItem *item;
2be4b2
 
2be4b2
     item = cache->hash_table[CACHE_HASH_KEY(id)];
2be4b2
 
2be4b2
@@ -76,11 +82,10 @@ static int FUNC_NAME(set_lossy)(CACHE *cache, uint64_t id, int lossy)
2be4b2
         }
2be4b2
         item = item->next;
2be4b2
     }
2be4b2
-    pthread_mutex_unlock(&cache->lock);
2be4b2
     return !!item;
2be4b2
 }
2be4b2
 
2be4b2
-static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, DisplayChannelClient *dcc)
2be4b2
+static int FUNC_NAME(unlocked_add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, DisplayChannelClient *dcc)
2be4b2
 {
2be4b2
     NewCacheItem *item;
2be4b2
     uint64_t serial;
2be4b2
@@ -91,15 +96,12 @@ static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, D
2be4b2
     item = spice_new(NewCacheItem, 1);
2be4b2
     serial = red_channel_client_get_message_serial(&dcc->common.base);
2be4b2
 
2be4b2
-    pthread_mutex_lock(&cache->lock);
2be4b2
-
2be4b2
     if (cache->generation != dcc->CACH_GENERATION) {
2be4b2
         if (!dcc->pending_pixmaps_sync) {
2be4b2
             red_channel_client_pipe_add_type(
2be4b2
                 &dcc->common.base, PIPE_ITEM_TYPE_PIXMAP_SYNC);
2be4b2
             dcc->pending_pixmaps_sync = TRUE;
2be4b2
         }
2be4b2
-        pthread_mutex_unlock(&cache->lock);
2be4b2
         free(item);
2be4b2
         return FALSE;
2be4b2
     }
2be4b2
@@ -112,7 +114,6 @@ static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, D
2be4b2
         if (!(tail = (NewCacheItem *)ring_get_tail(&cache->lru)) ||
2be4b2
                                                    tail->sync[dcc->common.id] == serial) {
2be4b2
             cache->available += size;
2be4b2
-            pthread_mutex_unlock(&cache->lock);
2be4b2
             free(item);
2be4b2
             return FALSE;
2be4b2
         }
2be4b2
@@ -144,7 +145,6 @@ static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, D
2be4b2
     memset(item->sync, 0, sizeof(item->sync));
2be4b2
     item->sync[dcc->common.id] = serial;
2be4b2
     cache->sync[dcc->common.id] = serial;
2be4b2
-    pthread_mutex_unlock(&cache->lock);
2be4b2
     return TRUE;
2be4b2
 }
2be4b2
 
2be4b2
diff --git a/server/red_worker.c b/server/red_worker.c
2be4b2
index 955cac2..93e3398 100644
2be4b2
--- a/server/red_worker.c
2be4b2
+++ b/server/red_worker.c
2be4b2
@@ -6750,9 +6750,9 @@ static inline void red_display_add_image_to_pixmap_cache(RedChannelClient *rcc,
2be4b2
     if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
2be4b2
         spice_assert(image->descriptor.width * image->descriptor.height > 0);
2be4b2
         if (!(io_image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME)) {
2be4b2
-            if (pixmap_cache_add(dcc->pixmap_cache, image->descriptor.id,
2be4b2
-                                 image->descriptor.width * image->descriptor.height, is_lossy,
2be4b2
-                                 dcc)) {
2be4b2
+            if (pixmap_cache_unlocked_add(dcc->pixmap_cache, image->descriptor.id,
2be4b2
+                                          image->descriptor.width * image->descriptor.height, is_lossy,
2be4b2
+                                          dcc)) {
2be4b2
                 io_image->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
2be4b2
                 dcc->send_data.pixmap_cache_items[dcc->send_data.num_pixmap_cache_items++] =
2be4b2
                                                                                image->descriptor.id;
2be4b2
@@ -6797,11 +6797,12 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m,
2be4b2
     if (simage->descriptor.flags & SPICE_IMAGE_FLAGS_HIGH_BITS_SET) {
2be4b2
         image.descriptor.flags = SPICE_IMAGE_FLAGS_HIGH_BITS_SET;
2be4b2
     }
2be4b2
+    pthread_mutex_lock(&dcc->pixmap_cache->lock);
2be4b2
 
2be4b2
     if ((simage->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
2be4b2
         int lossy_cache_item;
2be4b2
-        if (pixmap_cache_hit(dcc->pixmap_cache, image.descriptor.id,
2be4b2
-                             &lossy_cache_item, dcc)) {
2be4b2
+        if (pixmap_cache_unlocked_hit(dcc->pixmap_cache, image.descriptor.id,
2be4b2
+                                      &lossy_cache_item, dcc)) {
2be4b2
             dcc->send_data.pixmap_cache_items[dcc->send_data.num_pixmap_cache_items++] =
2be4b2
                                                                                image.descriptor.id;
2be4b2
             if (can_lossy || !lossy_cache_item) {
2be4b2
@@ -6818,10 +6819,11 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m,
2be4b2
                 spice_assert(bitmap_palette_out == NULL);
2be4b2
                 spice_assert(lzplt_palette_out == NULL);
2be4b2
                 stat_inc_counter(display_channel->cache_hits_counter, 1);
2be4b2
+                pthread_mutex_unlock(&dcc->pixmap_cache->lock);
2be4b2
                 return FILL_BITS_TYPE_CACHE;
2be4b2
             } else {
2be4b2
-                pixmap_cache_set_lossy(dcc->pixmap_cache, simage->descriptor.id,
2be4b2
-                                       FALSE);
2be4b2
+                pixmap_cache_unlocked_set_lossy(dcc->pixmap_cache, simage->descriptor.id,
2be4b2
+                                                FALSE);
2be4b2
                 image.descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME;
2be4b2
             }
2be4b2
         }
2be4b2
@@ -6835,6 +6837,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m,
2be4b2
         surface_id = simage->u.surface.surface_id;
2be4b2
         if (!validate_surface(worker, surface_id)) {
2be4b2
             rendering_incorrect("SPICE_IMAGE_TYPE_SURFACE");
2be4b2
+            pthread_mutex_unlock(&dcc->pixmap_cache->lock);
2be4b2
             return FILL_BITS_TYPE_SURFACE;
2be4b2
         }
2be4b2
 
2be4b2
@@ -6849,6 +6852,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m,
2be4b2
                              &bitmap_palette_out, &lzplt_palette_out);
2be4b2
         spice_assert(bitmap_palette_out == NULL);
2be4b2
         spice_assert(lzplt_palette_out == NULL);
2be4b2
+        pthread_mutex_unlock(&dcc->pixmap_cache->lock);
2be4b2
         return FILL_BITS_TYPE_SURFACE;
2be4b2
     }
2be4b2
     case SPICE_IMAGE_TYPE_BITMAP: {
2be4b2
@@ -6879,6 +6883,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m,
2be4b2
             }
2be4b2
 
2be4b2
             spice_marshaller_add_ref_chunks(m, bitmap->data);
2be4b2
+            pthread_mutex_unlock(&dcc->pixmap_cache->lock);
2be4b2
             return FILL_BITS_TYPE_BITMAP;
2be4b2
         } else {
2be4b2
             red_display_add_image_to_pixmap_cache(rcc, simage, &image,
2be4b2
@@ -6896,6 +6901,7 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m,
2be4b2
             }
2be4b2
 
2be4b2
             spice_assert(!comp_send_data.is_lossy || can_lossy);
2be4b2
+            pthread_mutex_unlock(&dcc->pixmap_cache->lock);
2be4b2
             return (comp_send_data.is_lossy ? FILL_BITS_TYPE_COMPRESS_LOSSY :
2be4b2
                                               FILL_BITS_TYPE_COMPRESS_LOSSLESS);
2be4b2
         }
2be4b2
@@ -6909,11 +6915,12 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m,
2be4b2
         spice_assert(bitmap_palette_out == NULL);
2be4b2
         spice_assert(lzplt_palette_out == NULL);
2be4b2
         spice_marshaller_add_ref_chunks(m, image.u.quic.data);
2be4b2
+        pthread_mutex_unlock(&dcc->pixmap_cache->lock);
2be4b2
         return FILL_BITS_TYPE_COMPRESS_LOSSLESS;
2be4b2
     default:
2be4b2
         spice_error("invalid image type %u", image.descriptor.type);
2be4b2
     }
2be4b2
-
2be4b2
+    pthread_mutex_unlock(&dcc->pixmap_cache->lock);
2be4b2
     return 0;
2be4b2
 }
2be4b2