#include "pb_cache.h"
#include "util/u_memory.h"
-#include "util/u_time.h"
+#include "util/os_time.h"
/**
destroy_buffer_locked(struct pb_cache_entry *entry)
{
struct pb_cache *mgr = entry->mgr;
+ struct pb_buffer *buf = entry->buffer;
- assert(!pipe_is_referenced(&entry->buffer->reference));
+ assert(!pipe_is_referenced(&buf->reference));
if (entry->head.next) {
- LIST_DEL(&entry->head);
+ list_del(&entry->head);
assert(mgr->num_buffers);
--mgr->num_buffers;
- mgr->cache_size -= entry->buffer->size;
+ mgr->cache_size -= buf->size;
}
- entry->mgr->destroy_buffer(entry->buffer);
+ mgr->destroy_buffer(buf);
}
/**
* Free as many cache buffers from the list head as possible.
*/
static void
-release_expired_buffers_locked(struct pb_cache *mgr)
+release_expired_buffers_locked(struct list_head *cache,
+ int64_t current_time)
{
struct list_head *curr, *next;
struct pb_cache_entry *entry;
- int64_t now;
- now = os_time_get();
-
- curr = mgr->cache.next;
+ curr = cache->next;
next = curr->next;
- while (curr != &mgr->cache) {
+ while (curr != cache) {
entry = LIST_ENTRY(struct pb_cache_entry, curr, head);
- if (!os_time_timeout(entry->start, entry->end, now))
+ if (!os_time_timeout(entry->start, entry->end, current_time))
break;
destroy_buffer_locked(entry);
pb_cache_add_buffer(struct pb_cache_entry *entry)
{
struct pb_cache *mgr = entry->mgr;
+ struct list_head *cache = &mgr->buckets[entry->bucket_index];
+ struct pb_buffer *buf = entry->buffer;
+ unsigned i;
- pipe_mutex_lock(mgr->mutex);
- assert(!pipe_is_referenced(&entry->buffer->reference));
+ mtx_lock(&mgr->mutex);
+ assert(!pipe_is_referenced(&buf->reference));
- release_expired_buffers_locked(mgr);
+ int64_t current_time = os_time_get();
+
+ for (i = 0; i < mgr->num_heaps; i++)
+ release_expired_buffers_locked(&mgr->buckets[i], current_time);
/* Directly release any buffer that exceeds the limit. */
- if (mgr->cache_size + entry->buffer->size > mgr->max_cache_size) {
- entry->mgr->destroy_buffer(entry->buffer);
- pipe_mutex_unlock(mgr->mutex);
+ if (mgr->cache_size + buf->size > mgr->max_cache_size) {
+ mgr->destroy_buffer(buf);
+ mtx_unlock(&mgr->mutex);
return;
}
entry->start = os_time_get();
entry->end = entry->start + mgr->usecs;
- LIST_ADDTAIL(&entry->head, &mgr->cache);
+ list_addtail(&entry->head, cache);
++mgr->num_buffers;
- mgr->cache_size += entry->buffer->size;
- pipe_mutex_unlock(mgr->mutex);
+ mgr->cache_size += buf->size;
+ mtx_unlock(&mgr->mutex);
}
/**
pb_cache_is_buffer_compat(struct pb_cache_entry *entry,
pb_size size, unsigned alignment, unsigned usage)
{
+ struct pb_cache *mgr = entry->mgr;
struct pb_buffer *buf = entry->buffer;
- if (usage & entry->mgr->bypass_usage)
- return 0;
-
- if (buf->size < size)
+ if (!pb_check_usage(usage, buf->usage))
return 0;
/* be lenient with size */
- if (buf->size > (unsigned) (entry->mgr->size_factor * size))
+ if (buf->size < size ||
+ buf->size > (unsigned) (mgr->size_factor * size))
return 0;
- if (!pb_check_alignment(alignment, buf->alignment))
+ if (usage & mgr->bypass_usage)
return 0;
- if (!pb_check_usage(usage, buf->usage))
+ if (!pb_check_alignment(alignment, buf->alignment))
return 0;
- return entry->mgr->can_reclaim(buf) ? 1 : -1;
+ return mgr->can_reclaim(buf) ? 1 : -1;
}
/**
*/
struct pb_buffer *
pb_cache_reclaim_buffer(struct pb_cache *mgr, pb_size size,
- unsigned alignment, unsigned usage)
+ unsigned alignment, unsigned usage,
+ unsigned bucket_index)
{
struct pb_cache_entry *entry;
struct pb_cache_entry *cur_entry;
int64_t now;
int ret = 0;
- pipe_mutex_lock(mgr->mutex);
+ assert(bucket_index < mgr->num_heaps);
+ struct list_head *cache = &mgr->buckets[bucket_index];
+
+ mtx_lock(&mgr->mutex);
entry = NULL;
- cur = mgr->cache.next;
+ cur = cache->next;
next = cur->next;
/* search in the expired buffers, freeing them in the process */
now = os_time_get();
- while (cur != &mgr->cache) {
+ while (cur != 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))
+ alignment, usage)) > 0)
entry = cur_entry;
else if (os_time_timeout(cur_entry->start, cur_entry->end, now))
destroy_buffer_locked(cur_entry);
/* keep searching in the hot buffers */
if (!entry && ret != -1) {
- while (cur != &mgr->cache) {
+ while (cur != cache) {
cur_entry = LIST_ENTRY(struct pb_cache_entry, cur, head);
ret = pb_cache_is_buffer_compat(cur_entry, size, alignment, usage);
struct pb_buffer *buf = entry->buffer;
mgr->cache_size -= buf->size;
- LIST_DEL(&entry->head);
+ list_del(&entry->head);
--mgr->num_buffers;
- pipe_mutex_unlock(mgr->mutex);
+ mtx_unlock(&mgr->mutex);
/* Increase refcount */
pipe_reference_init(&buf->reference, 1);
return buf;
}
- pipe_mutex_unlock(mgr->mutex);
+ mtx_unlock(&mgr->mutex);
return NULL;
}
{
struct list_head *curr, *next;
struct pb_cache_entry *buf;
+ unsigned i;
- 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;
+ mtx_lock(&mgr->mutex);
+ for (i = 0; i < mgr->num_heaps; i++) {
+ struct list_head *cache = &mgr->buckets[i];
+
+ curr = cache->next;
next = curr->next;
+ while (curr != cache) {
+ buf = LIST_ENTRY(struct pb_cache_entry, curr, head);
+ destroy_buffer_locked(buf);
+ curr = next;
+ next = curr->next;
+ }
}
- pipe_mutex_unlock(mgr->mutex);
+ mtx_unlock(&mgr->mutex);
}
void
pb_cache_init_entry(struct pb_cache *mgr, struct pb_cache_entry *entry,
- struct pb_buffer *buf)
+ struct pb_buffer *buf, unsigned bucket_index)
{
+ assert(bucket_index < mgr->num_heaps);
+
memset(entry, 0, sizeof(*entry));
entry->buffer = buf;
entry->mgr = mgr;
+ entry->bucket_index = bucket_index;
}
/**
* Initialize a caching buffer manager.
*
* @param mgr The cache buffer manager
+ * @param num_heaps Number of separate caches/buckets indexed by bucket_index
+ * for faster buffer matching (alternative to slower
+ * "usage"-based matching).
* @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
* @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,
+pb_cache_init(struct pb_cache *mgr, uint num_heaps,
+ 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);
+ unsigned i;
+
+ mgr->buckets = CALLOC(num_heaps, sizeof(struct list_head));
+ if (!mgr->buckets)
+ return;
+
+ for (i = 0; i < num_heaps; i++)
+ list_inithead(&mgr->buckets[i]);
+
+ (void) mtx_init(&mgr->mutex, mtx_plain);
mgr->cache_size = 0;
mgr->max_cache_size = maximum_cache_size;
+ mgr->num_heaps = num_heaps;
mgr->usecs = usecs;
mgr->num_buffers = 0;
mgr->bypass_usage = bypass_usage;
pb_cache_deinit(struct pb_cache *mgr)
{
pb_cache_release_all_buffers(mgr);
- pipe_mutex_destroy(mgr->mutex);
+ mtx_destroy(&mgr->mutex);
+ FREE(mgr->buckets);
+ mgr->buckets = NULL;
}