etnaviv_bo_cache.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*
  2. * Copyright (C) 2016 Etnaviv Project
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice (including the next
  12. * paragraph) shall be included in all copies or substantial portions of the
  13. * Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  18. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. * SOFTWARE.
  22. *
  23. * Authors:
  24. * Christian Gmeiner <christian.gmeiner@gmail.com>
  25. */
  26. #include "etnaviv_priv.h"
  27. #include "etnaviv_drmif.h"
  28. drm_private void bo_del(struct etna_bo *bo);
  29. drm_private extern pthread_mutex_t table_lock;
  30. static void add_bucket(struct etna_bo_cache *cache, int size)
  31. {
  32. unsigned i = cache->num_buckets;
  33. assert(i < ARRAY_SIZE(cache->cache_bucket));
  34. list_inithead(&cache->cache_bucket[i].list);
  35. cache->cache_bucket[i].size = size;
  36. cache->num_buckets++;
  37. }
  38. drm_private void etna_bo_cache_init(struct etna_bo_cache *cache)
  39. {
  40. unsigned long size, cache_max_size = 64 * 1024 * 1024;
  41. /* OK, so power of two buckets was too wasteful of memory.
  42. * Give 3 other sizes between each power of two, to hopefully
  43. * cover things accurately enough. (The alternative is
  44. * probably to just go for exact matching of sizes, and assume
  45. * that for things like composited window resize the tiled
  46. * width/height alignment and rounding of sizes to pages will
  47. * get us useful cache hit rates anyway)
  48. */
  49. add_bucket(cache, 4096);
  50. add_bucket(cache, 4096 * 2);
  51. add_bucket(cache, 4096 * 3);
  52. /* Initialize the linked lists for BO reuse cache. */
  53. for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
  54. add_bucket(cache, size);
  55. add_bucket(cache, size + size * 1 / 4);
  56. add_bucket(cache, size + size * 2 / 4);
  57. add_bucket(cache, size + size * 3 / 4);
  58. }
  59. }
  60. /* Frees older cached buffers. Called under table_lock */
  61. drm_private void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t time)
  62. {
  63. unsigned i;
  64. if (cache->time == time)
  65. return;
  66. for (i = 0; i < cache->num_buckets; i++) {
  67. struct etna_bo_bucket *bucket = &cache->cache_bucket[i];
  68. struct etna_bo *bo;
  69. while (!LIST_IS_EMPTY(&bucket->list)) {
  70. bo = LIST_ENTRY(struct etna_bo, bucket->list.next, list);
  71. /* keep things in cache for at least 1 second: */
  72. if (time && ((time - bo->free_time) <= 1))
  73. break;
  74. list_del(&bo->list);
  75. bo_del(bo);
  76. }
  77. }
  78. cache->time = time;
  79. }
  80. static struct etna_bo_bucket *get_bucket(struct etna_bo_cache *cache, uint32_t size)
  81. {
  82. unsigned i;
  83. /* hmm, this is what intel does, but I suppose we could calculate our
  84. * way to the correct bucket size rather than looping..
  85. */
  86. for (i = 0; i < cache->num_buckets; i++) {
  87. struct etna_bo_bucket *bucket = &cache->cache_bucket[i];
  88. if (bucket->size >= size) {
  89. return bucket;
  90. }
  91. }
  92. return NULL;
  93. }
  94. static int is_idle(struct etna_bo *bo)
  95. {
  96. return etna_bo_cpu_prep(bo,
  97. DRM_ETNA_PREP_READ |
  98. DRM_ETNA_PREP_WRITE |
  99. DRM_ETNA_PREP_NOSYNC) == 0;
  100. }
  101. static struct etna_bo *find_in_bucket(struct etna_bo_bucket *bucket, uint32_t flags)
  102. {
  103. struct etna_bo *bo = NULL, *tmp;
  104. pthread_mutex_lock(&table_lock);
  105. if (LIST_IS_EMPTY(&bucket->list))
  106. goto out_unlock;
  107. LIST_FOR_EACH_ENTRY_SAFE(bo, tmp, &bucket->list, list) {
  108. /* skip BOs with different flags */
  109. if (bo->flags != flags)
  110. continue;
  111. /* check if the first BO with matching flags is idle */
  112. if (is_idle(bo)) {
  113. list_delinit(&bo->list);
  114. goto out_unlock;
  115. }
  116. /* If the oldest BO is still busy, don't try younger ones */
  117. break;
  118. }
  119. /* There was no matching buffer found */
  120. bo = NULL;
  121. out_unlock:
  122. pthread_mutex_unlock(&table_lock);
  123. return bo;
  124. }
  125. /* allocate a new (un-tiled) buffer object
  126. *
  127. * NOTE: size is potentially rounded up to bucket size
  128. */
  129. drm_private struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache, uint32_t *size,
  130. uint32_t flags)
  131. {
  132. struct etna_bo *bo;
  133. struct etna_bo_bucket *bucket;
  134. *size = ALIGN(*size, 4096);
  135. bucket = get_bucket(cache, *size);
  136. /* see if we can be green and recycle: */
  137. if (bucket) {
  138. *size = bucket->size;
  139. bo = find_in_bucket(bucket, flags);
  140. if (bo) {
  141. atomic_set(&bo->refcnt, 1);
  142. etna_device_ref(bo->dev);
  143. return bo;
  144. }
  145. }
  146. return NULL;
  147. }
  148. drm_private int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo *bo)
  149. {
  150. struct etna_bo_bucket *bucket = get_bucket(cache, bo->size);
  151. /* see if we can be green and recycle: */
  152. if (bucket) {
  153. struct timespec time;
  154. clock_gettime(CLOCK_MONOTONIC, &time);
  155. bo->free_time = time.tv_sec;
  156. list_addtail(&bo->list, &bucket->list);
  157. etna_bo_cache_cleanup(cache, time.tv_sec);
  158. /* bo's in the bucket cache don't have a ref and
  159. * don't hold a ref to the dev:
  160. */
  161. etna_device_del_locked(bo->dev);
  162. return 0;
  163. }
  164. return -1;
  165. }