[Mesa-dev] [PATCH 01/10] gallium/pb_cache: add a copy of cache bufmgr independent of pb_manager

Nicolai Hähnle nhaehnle at gmail.com
Tue Dec 8 13:08:40 PST 2015


On 06.12.2015 19:00, Marek Olšák wrote:
> From: Marek Olšák <marek.olsak at amd.com>
>
> This simplified (basically duplicated) version of pb_cache_manager will
> allow removing some ugly hacks from radeon and amdgpu winsyses and
> flatten simplify their design.
>
> The difference is that winsyses must manually add buffers to the cache
> in "destroy" functions and the cache doesn't know about the buffers before
> that. The integration is therefore trivial and the impact on the winsys
> design is negligible.
> ---
>   src/gallium/auxiliary/Makefile.sources      |   1 +
>   src/gallium/auxiliary/pipebuffer/pb_cache.c | 286 ++++++++++++++++++++++++++++
>   src/gallium/auxiliary/pipebuffer/pb_cache.h |  74 +++++++
>   3 files changed, 361 insertions(+)
>   create mode 100644 src/gallium/auxiliary/pipebuffer/pb_cache.c
>   create mode 100644 src/gallium/auxiliary/pipebuffer/pb_cache.h
>
> diff --git a/src/gallium/auxiliary/Makefile.sources b/src/gallium/auxiliary/Makefile.sources
> index 6160192..817308d 100644
> --- a/src/gallium/auxiliary/Makefile.sources
> +++ b/src/gallium/auxiliary/Makefile.sources
> @@ -93,6 +93,7 @@ C_SOURCES := \
>   	pipebuffer/pb_bufmgr_ondemand.c \
>   	pipebuffer/pb_bufmgr_pool.c \
>   	pipebuffer/pb_bufmgr_slab.c \
> +	pipebuffer/pb_cache.c \

I believe pb_cache.h needs to be added as well.

>   	pipebuffer/pb_validate.c \
>   	pipebuffer/pb_validate.h \
>   	postprocess/filters.h \
> diff --git a/src/gallium/auxiliary/pipebuffer/pb_cache.c b/src/gallium/auxiliary/pipebuffer/pb_cache.c
> new file mode 100644
> index 0000000..45f600d
> --- /dev/null
> +++ b/src/gallium/auxiliary/pipebuffer/pb_cache.c
...
> +/**
> + * \return 1   if compatible and can be reclaimed
> + *         0   if incompatible
> + *        -1   if compatible and can't be reclaimed
> + */
> +static int
> +pb_cache_is_buffer_compat(struct pb_cache_entry *entry,
> +                          pb_size size, unsigned alignment, unsigned usage)
> +{
> +   struct pb_buffer *buf = entry->buffer;
> +
> +   if (usage & entry->mgr->bypass_usage)
> +      return 0;

It should be possible to move this test to the top of 
pb_cache_reclaim_buffer, right?

> +   if (buf->size < size)
> +      return 0;
> +
> +   /* be lenient with size */
> +   if (buf->size > (unsigned) (entry->mgr->size_factor * size))
> +      return 0;
> +
> +   if (!pb_check_alignment(alignment, buf->alignment))
> +      return 0;
> +
> +   if (!pb_check_usage(usage, buf->usage))
> +      return 0;
> +
> +   return entry->mgr->can_reclaim(buf) ? 1 : -1;
> +}
> +
> +/**
> + * Find a compatible buffer in the cache, return it, and remove it
> + * from the cache.
> + */
> +struct pb_buffer *
> +pb_cache_reclaim_buffer(struct pb_cache *mgr, pb_size size,
> +                        unsigned alignment, unsigned usage)
> +{
> +   struct pb_cache_entry *entry;
> +   struct pb_cache_entry *cur_entry;
> +   struct list_head *cur, *next;
> +   int64_t now;
> +   int ret = 0;
> +
> +   pipe_mutex_lock(mgr->mutex);
> +
> +   entry = NULL;
> +   cur = mgr->cache.next;
> +   next = cur->next;
> +
> +   /* search in the expired buffers, freeing them in the process */
> +   now = os_time_get();
> +   while (cur != &mgr->cache) {
> +      cur_entry = LIST_ENTRY(struct pb_cache_entry, cur, head);
> +
> +      if (!entry && (ret = pb_cache_is_buffer_compat(cur_entry, size,
> +                                                     alignment, usage) > 0))
> +         entry = cur_entry;
> +      else if (os_time_timeout(cur_entry->start, cur_entry->end, now))
> +         destroy_buffer_locked(cur_entry);
> +      else
> +         /* This buffer (and all hereafter) are still hot in cache */
> +         break;
> +
> +      /* the buffer is busy (and probably all remaining ones too) */
> +      if (ret == -1)
> +         break;
> +
> +      cur = next;
> +      next = cur->next;
> +   }
> +
> +   /* keep searching in the hot buffers */
> +   if (!entry && ret != -1) {
> +      while (cur != &mgr->cache) {
> +         cur_entry = LIST_ENTRY(struct pb_cache_entry, cur, head);
> +         ret = pb_cache_is_buffer_compat(cur_entry, size, alignment, usage);
> +
> +         if (ret > 0) {
> +            entry = cur_entry;
> +            break;
> +         }
> +         if (ret == -1)
> +            break;
> +         /* no need to check the timeout here */
> +         cur = next;
> +         next = cur->next;
> +      }
> +   }
> +
> +   /* found a compatible buffer, return it */
> +   if (entry) {
> +      struct pb_buffer *buf = entry->buffer;
> +
> +      mgr->cache_size -= buf->size;
> +      LIST_DEL(&entry->head);
> +      --mgr->num_buffers;
> +      pipe_mutex_unlock(mgr->mutex);
> +      /* Increase refcount */
> +      pipe_reference_init(&buf->reference, 1);
> +      return buf;
> +   }
> +
> +   pipe_mutex_unlock(mgr->mutex);
> +   return NULL;
> +}
> +
> +/**
> + * Empty the cache. Useful when there is not enough memory.
> + */
> +void
> +pb_cache_release_all_buffers(struct pb_cache *mgr)
> +{
> +   struct list_head *curr, *next;
> +   struct pb_cache_entry *buf;
> +
> +   pipe_mutex_lock(mgr->mutex);
> +   curr = mgr->cache.next;
> +   next = curr->next;
> +   while (curr != &mgr->cache) {
> +      buf = LIST_ENTRY(struct pb_cache_entry, curr, head);
> +      destroy_buffer_locked(buf);
> +      curr = next;
> +      next = curr->next;
> +   }
> +   pipe_mutex_unlock(mgr->mutex);
> +}
> +
> +void
> +pb_cache_init_entry(struct pb_cache *mgr, struct pb_cache_entry *entry,
> +                    struct pb_buffer *buf)
> +{
> +   memset(entry, 0, sizeof(*entry));
> +   entry->buffer = buf;
> +   entry->mgr = mgr;
> +}
> +
> +/**
> + * Initialize a caching buffer manager.
> + *
> + * @param mgr     The cache buffer manager
> + * @param usecs   Unused buffers may be released from the cache after this
> + *                time
> + * @param size_factor  Declare buffers that are size_factor times bigger than
> + *                     the requested size as cache hits.
> + * @param bypass_usage  Bitmask. If (requested usage & bypass_usage) != 0,
> + *                      buffer allocation requests are rejected.
> + * @param maximum_cache_size  Maximum size of all unused buffers the cache can
> + *                            hold.
> + * @param destroy_buffer  Function that destroy a buffer for good.

*destroys

Nicolai

> + * @param can_reclaim     Whether a buffer can be reclaimed (e.g. is not busy)
> + */
> +void
> +pb_cache_init(struct pb_cache *mgr, uint usecs, float size_factor,
> +              unsigned bypass_usage, uint64_t maximum_cache_size,
> +              void (*destroy_buffer)(struct pb_buffer *buf),
> +              bool (*can_reclaim)(struct pb_buffer *buf))
> +{
> +   LIST_INITHEAD(&mgr->cache);
> +   pipe_mutex_init(mgr->mutex);
> +   mgr->cache_size = 0;
> +   mgr->max_cache_size = maximum_cache_size;
> +   mgr->usecs = usecs;
> +   mgr->num_buffers = 0;
> +   mgr->bypass_usage = bypass_usage;
> +   mgr->size_factor = size_factor;
> +   mgr->destroy_buffer = destroy_buffer;
> +   mgr->can_reclaim = can_reclaim;
> +}
> +
> +/**
> + * Deinitialize the manager completely.
> + */
> +void
> +pb_cache_deinit(struct pb_cache *mgr)
> +{
> +   pb_cache_release_all_buffers(mgr);
> +   pipe_mutex_destroy(mgr->mutex);
> +}
> diff --git a/src/gallium/auxiliary/pipebuffer/pb_cache.h b/src/gallium/auxiliary/pipebuffer/pb_cache.h
> new file mode 100644
> index 0000000..f0fa012
> --- /dev/null
> +++ b/src/gallium/auxiliary/pipebuffer/pb_cache.h
> @@ -0,0 +1,74 @@
> +/**************************************************************************
> + *
> + * Copyright 2007-2008 VMware, Inc.
> + * Copyright 2015 Advanced Micro Devices, Inc.
> + * All Rights Reserved.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the
> + * "Software"), to deal in the Software without restriction, including
> + * without limitation the rights to use, copy, modify, merge, publish,
> + * distribute, sub license, and/or sell copies of the Software, and to
> + * permit persons to whom the Software is furnished to do so, subject to
> + * the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the
> + * next paragraph) shall be included in all copies or substantial portions
> + * of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
> + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
> + * IN NO EVENT SHALL AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR
> + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> + *
> + **************************************************************************/
> +
> +#ifndef PB_CACHE_H
> +#define PB_CACHE_H
> +
> +#include "pb_buffer.h"
> +#include "util/list.h"
> +#include "os/os_thread.h"
> +
> +/**
> + * Statically inserted into the driver-specific buffer structure.
> + */
> +struct pb_cache_entry
> +{
> +   struct list_head head;
> +   struct pb_buffer *buffer; /**< Pointer to the structure this is part of. */
> +   struct pb_cache *mgr;
> +   int64_t start, end; /**< Caching time interval */
> +};
> +
> +struct pb_cache
> +{
> +   struct list_head cache;
> +   pipe_mutex mutex;
> +   uint64_t cache_size;
> +   uint64_t max_cache_size;
> +   unsigned usecs;
> +   unsigned num_buffers;
> +   unsigned bypass_usage;
> +   float size_factor;
> +
> +   void (*destroy_buffer)(struct pb_buffer *buf);
> +   bool (*can_reclaim)(struct pb_buffer *buf);
> +};
> +
> +void pb_cache_add_buffer(struct pb_cache_entry *entry);
> +struct pb_buffer *pb_cache_reclaim_buffer(struct pb_cache *mgr, pb_size size,
> +                                       unsigned alignment, unsigned usage);
> +void pb_cache_release_all_buffers(struct pb_cache *mgr);
> +void pb_cache_init_entry(struct pb_cache *mgr, struct pb_cache_entry *entry,
> +                         struct pb_buffer *buf);
> +void pb_cache_init(struct pb_cache *mgr, uint usecs, float size_factor,
> +                   unsigned bypass_usage, uint64_t maximum_cache_size,
> +                   void (*destroy_buffer)(struct pb_buffer *buf),
> +                   bool (*can_reclaim)(struct pb_buffer *buf));
> +void pb_cache_deinit(struct pb_cache *mgr);
> +
> +#endif
>


More information about the mesa-dev mailing list