|
|
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 |
|