[Spice-devel] [spice PATCH] Lock the pixmap image cache for the entiry fill_bits call

Sandy Stutsman sstutsma at redhat.com
Fri Jun 26 09:03:50 PDT 2015


Hi Christophe,

Just sent out a patch with your revisions and a more detailed commit message.

You may be right about race conditions around the lossy state. 

I didn't notice any when testing when testing, but as I don't think it would result
in the client hanging, it doesn't mean it isn't happening.  I'm not sure what
would happen other than some (transient ?) rendering artifacts.

-S
On 6/26/2015 9:14 AM, Christophe Fergeau wrote:
> Hey,
>
> On Tue, Jun 23, 2015 at 05:47:14PM -0400, Sandy Stutsman wrote:
>> Locking the individual calls that access the pixmap cache in fill_bits is
>> not adequately thread safe.  Often a windows guest with multiple monitors
>> will be sending the same image via different threads.  Both threads can
>> be in fill_bits at the same making changes to the cache for the same
>> image.  This can result in images being deleted before all the client
>> channels are finished with them or with the same image being send multiple
>> times.
>>
> A more detailed description of the race in the commit log would be very
> useful, both in terms of reviewing, and for future reference when
> looking at git history.
> The patch in general looks correct to me, but I suspect there are still
> some races around pixmap_cache_locked_hit() in
> is_bitmap_lossy()/is_brush_lossy() as code is doing:
>
> is_lossy = is_bitmap_lossy(); (which calls pixmap_cache_locked_hit())
> [some code]
> if (is_lossy) {
>   do_something();
> } else {
>   do_something_else();
> }
>
> It seems to be that fill_bits could have been called from another thread
> and changed the lossiness in between the is_bitmap_lossy() call and the
> if (is_lossy) test.
>
>> Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1194354
>> ---
>>  server/red_client_shared_cache.h | 20 ++++++++++----------
>>  server/red_worker.c              | 13 ++++++++++---
>>  2 files changed, 20 insertions(+), 13 deletions(-)
>>
>> diff --git a/server/red_client_shared_cache.h b/server/red_client_shared_cache.h
>> index 821ee18..4932f2d 100644
>> --- a/server/red_client_shared_cache.h
>> +++ b/server/red_client_shared_cache.h
>> @@ -42,7 +42,6 @@ static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelC
>>      uint64_t serial;
>>  
>>      serial = red_channel_client_get_message_serial(&dcc->common.base);
>> -    pthread_mutex_lock(&cache->lock);
>>      item = cache->hash_table[CACHE_HASH_KEY(id)];
>>  
>>      while (item) {
>> @@ -57,15 +56,22 @@ static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelC
>>          }
>>          item = item->next;
>>      }
>> -    pthread_mutex_unlock(&cache->lock);
>>  
>>      return !!item;
>>  }
>>  
>> +static int FUNC_NAME(locked_hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelClient *dcc)
> If we do this change, after applying this patch, we've got eg
> pixmap_cache_locked_hit() and pixmap_cache_freeze() both locking the
> mutex appropriately but with inconsistant names. I'd keep the
> pixmap_cache_hit() name for the locked case, and add an _unlocked_
> variant (see attached patch). I also have a patch renaming
> pixmap_cache_set_lossy() for consistency (it's not taking a lock).
>
> Christophe
>
> Patch on top of yours:
>
>
> From 072e9357e858dc51e82f884f8d8cbf45345a9b95 Mon Sep 17 00:00:00 2001
> From: Christophe Fergeau <cfergeau at redhat.com>
> Date: Thu, 25 Jun 2015 14:10:33 +0200
> Subject: [PATCH] fixup! Lock the pixmap cache for the fill_bits function call
>
> ---
>  server/red_client_shared_cache.h |  8 ++++----
>  server/red_worker.c              | 18 +++++++++---------
>  2 files changed, 13 insertions(+), 13 deletions(-)
>
> diff --git a/server/red_client_shared_cache.h b/server/red_client_shared_cache.h
> index 3b56e92..3afed18 100644
> --- a/server/red_client_shared_cache.h
> +++ b/server/red_client_shared_cache.h
> @@ -36,7 +36,7 @@
>  
>  #define CHANNEL_FROM_RCC(rcc) SPICE_CONTAINEROF((rcc)->channel, CHANNEL, common.base);
>  
> -static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelClient *dcc)
> +static int FUNC_NAME(unlocked_hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelClient *dcc)
>  {
>      NewCacheItem *item;
>      uint64_t serial;
> @@ -60,11 +60,11 @@ static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelC
>      return !!item;
>  }
>  
> -static int FUNC_NAME(locked_hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelClient *dcc)
> +static int FUNC_NAME(hit)(CACHE *cache, uint64_t id, int *lossy, DisplayChannelClient *dcc)
>  {
>      int hit;
>      pthread_mutex_lock(&cache->lock);
> -    hit = FUNC_NAME(hit)(cache,id,lossy, dcc);
> +    hit = FUNC_NAME(unlocked_hit)(cache,id,lossy, dcc);
>      pthread_mutex_unlock(&cache->lock);
>      return hit;
>  }
> @@ -85,7 +85,7 @@ static int FUNC_NAME(set_lossy)(CACHE *cache, uint64_t id, int lossy)
>      return !!item;
>  }
>  
> -static int FUNC_NAME(add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, DisplayChannelClient *dcc)
> +static int FUNC_NAME(unlocked_add)(CACHE *cache, uint64_t id, uint32_t size, int lossy, DisplayChannelClient *dcc)
>  {
>      NewCacheItem *item;
>      uint64_t serial;
> diff --git a/server/red_worker.c b/server/red_worker.c
> index dd91179..301b4af 100644
> --- a/server/red_worker.c
> +++ b/server/red_worker.c
> @@ -6686,9 +6686,9 @@ static inline void red_display_add_image_to_pixmap_cache(RedChannelClient *rcc,
>      if ((image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
>          spice_assert(image->descriptor.width * image->descriptor.height > 0);
>          if (!(io_image->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_REPLACE_ME)) {
> -            if (pixmap_cache_add(dcc->pixmap_cache, image->descriptor.id,
> -                                 image->descriptor.width * image->descriptor.height, is_lossy,
> -                                 dcc)) {
> +            if (pixmap_cache_unlocked_add(dcc->pixmap_cache, image->descriptor.id,
> +                                          image->descriptor.width * image->descriptor.height, is_lossy,
> +                                          dcc)) {
>                  io_image->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
>                  dcc->send_data.pixmap_cache_items[dcc->send_data.num_pixmap_cache_items++] =
>                                                                                 image->descriptor.id;
> @@ -6738,8 +6738,8 @@ static FillBitsType fill_bits(DisplayChannelClient *dcc, SpiceMarshaller *m,
>  
>      if ((simage->descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) {
>          int lossy_cache_item;
> -        if (pixmap_cache_hit(dcc->pixmap_cache, image.descriptor.id,
> -                             &lossy_cache_item, dcc)) {
> +        if (pixmap_cache_unlocked_hit(dcc->pixmap_cache, image.descriptor.id,
> +                                      &lossy_cache_item, dcc)) {
>              dcc->send_data.pixmap_cache_items[dcc->send_data.num_pixmap_cache_items++] =
>                                                                                 image.descriptor.id;
>              if (can_lossy || !lossy_cache_item) {
> @@ -6990,8 +6990,8 @@ static int is_bitmap_lossy(RedChannelClient *rcc, SpiceImage *image, SpiceRect *
>          int is_hit_lossy;
>  
>          out_data->id = image->descriptor.id;
> -        if (pixmap_cache_locked_hit(dcc->pixmap_cache, image->descriptor.id,
> -                                    &is_hit_lossy, dcc)) {
> +        if (pixmap_cache_hit(dcc->pixmap_cache, image->descriptor.id,
> +                             &is_hit_lossy, dcc)) {
>              out_data->type = BITMAP_DATA_TYPE_CACHE;
>              if (is_hit_lossy) {
>                  return TRUE;
> @@ -8412,8 +8412,8 @@ static inline void display_channel_send_free_list(RedChannelClient *rcc)
>           * But all this message pixmaps cache references used its old serial.
>           * we use pixmap_cache_items to collect these pixmaps, and we update their serial
>           * by calling pixmap_cache_hit. */
> -        pixmap_cache_locked_hit(dcc->pixmap_cache, dcc->send_data.pixmap_cache_items[i],
> -                                &dummy, dcc);
> +        pixmap_cache_hit(dcc->pixmap_cache, dcc->send_data.pixmap_cache_items[i],
> +                         &dummy, dcc);
>      }
>  
>      if (free_list->wait.header.wait_count) {



More information about the Spice-devel mailing list