[Mesa-dev] [PATCH v2] vc4: Mark BOs as purgeable when they enter the BO cache

Boris Brezillon boris.brezillon at free-electrons.com
Wed Oct 4 10:05:55 UTC 2017


This patch makes use of the DRM_IOCTL_VC4_GEM_MADVISE ioctl to mark all
BOs placed in the mesa BO cache as purgeable so that the system can
reclaim this memory under memory pressure.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
Hello,

Note that this series depends on kernel code that has not been accepted
yet and is just provided to show reviewers how the ioctl can be used
and what to expect from it.

Please do not consider this for inclusion in MESA until the kernel part
has been accepted.

Thanks,

Boris
---
Changes in v2:
- Remove BOs from the cache when they've been purged by the kernel
- Check whether the madvise ioctl is supported or not before using it
---
 src/gallium/drivers/vc4/vc4_bufmgr.c | 135 ++++++++++++++++++++++-------------
 src/gallium/drivers/vc4/vc4_screen.c |   2 +
 src/gallium/drivers/vc4/vc4_screen.h |   1 +
 3 files changed, 89 insertions(+), 49 deletions(-)

diff --git a/src/gallium/drivers/vc4/vc4_bufmgr.c b/src/gallium/drivers/vc4/vc4_bufmgr.c
index 0653f8823232..0c13ad683b6d 100644
--- a/src/gallium/drivers/vc4/vc4_bufmgr.c
+++ b/src/gallium/drivers/vc4/vc4_bufmgr.c
@@ -87,34 +87,106 @@ vc4_bo_remove_from_cache(struct vc4_bo_cache *cache, struct vc4_bo *bo)
         cache->bo_size -= bo->size;
 }
 
+static void vc4_bo_purgeable(struct vc4_bo *bo)
+{
+        struct drm_vc4_gem_madvise arg = {
+                .handle = bo->handle,
+                .madv = VC4_MADV_DONTNEED,
+        };
+
+	if (bo->screen->has_madvise)
+		vc4_ioctl(bo->screen->fd, DRM_IOCTL_VC4_GEM_MADVISE, &arg);
+}
+
+static bool vc4_bo_unpurgeable(struct vc4_bo *bo)
+{
+        struct drm_vc4_gem_madvise arg = {
+                .handle = bo->handle,
+                .madv = VC4_MADV_WILLNEED,
+        };
+
+	if (!bo->screen->has_madvise)
+		return true;
+
+	if (vc4_ioctl(bo->screen->fd, DRM_IOCTL_VC4_GEM_MADVISE, &arg))
+		return false;
+
+	return arg.retained;
+}
+
+static void
+vc4_bo_free(struct vc4_bo *bo)
+{
+        struct vc4_screen *screen = bo->screen;
+
+        if (bo->map) {
+                if (using_vc4_simulator && bo->name &&
+                    strcmp(bo->name, "winsys") == 0) {
+                        free(bo->map);
+                } else {
+                        munmap(bo->map, bo->size);
+                        VG(VALGRIND_FREELIKE_BLOCK(bo->map, 0));
+                }
+        }
+
+        struct drm_gem_close c;
+        memset(&c, 0, sizeof(c));
+        c.handle = bo->handle;
+        int ret = vc4_ioctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &c);
+        if (ret != 0)
+                fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno));
+
+        screen->bo_count--;
+        screen->bo_size -= bo->size;
+
+        if (dump_stats) {
+                fprintf(stderr, "Freed %s%s%dkb:\n",
+                        bo->name ? bo->name : "",
+                        bo->name ? " " : "",
+                        bo->size / 1024);
+                vc4_bo_dump_stats(screen);
+        }
+
+        free(bo);
+}
+
 static struct vc4_bo *
 vc4_bo_from_cache(struct vc4_screen *screen, uint32_t size, const char *name)
 {
         struct vc4_bo_cache *cache = &screen->bo_cache;
         uint32_t page_index = size / 4096 - 1;
+        struct vc4_bo *iter, *tmp, *bo = NULL;
 
         if (cache->size_list_size <= page_index)
                 return NULL;
 
-        struct vc4_bo *bo = NULL;
         mtx_lock(&cache->lock);
-        if (!list_empty(&cache->size_list[page_index])) {
-                bo = LIST_ENTRY(struct vc4_bo, cache->size_list[page_index].next,
-                                size_list);
-
-                /* Check that the BO has gone idle.  If not, then we want to
-                 * allocate something new instead, since we assume that the
-                 * user will proceed to CPU map it and fill it with stuff.
+	LIST_FOR_EACH_ENTRY_SAFE(iter, tmp, &cache->size_list[page_index],
+				 size_list) {
+                /* Check that the BO has gone idle. If not, then we try the
+                 * next one in the list, and if none of them are idle then
+                 * we want to allocate something new instead, since we assume
+                 * that the user will proceed to CPU map it and fill it with
+                 * stuff.
                  */
-                if (!vc4_bo_wait(bo, 0, NULL)) {
-                        mtx_unlock(&cache->lock);
-                        return NULL;
-                }
-
+                if (!vc4_bo_wait(iter, 0, NULL))
+                        continue;
+
+                if (!vc4_bo_unpurgeable(iter)) {
+                        /* The BO has been purged. Free it and try to find
+                         * another one in the cache.
+                         */
+                        vc4_bo_remove_from_cache(cache, iter);
+                        vc4_bo_free(iter);
+                        continue;
+		}
+
+                bo = iter;
                 pipe_reference_init(&bo->reference, 1);
                 vc4_bo_remove_from_cache(cache, bo);
 
                 bo->name = name;
+                break;
         }
         mtx_unlock(&cache->lock);
         return bo;
@@ -192,42 +264,6 @@ vc4_bo_last_unreference(struct vc4_bo *bo)
 }
 
 static void
-vc4_bo_free(struct vc4_bo *bo)
-{
-        struct vc4_screen *screen = bo->screen;
-
-        if (bo->map) {
-                if (using_vc4_simulator && bo->name &&
-                    strcmp(bo->name, "winsys") == 0) {
-                        free(bo->map);
-                } else {
-                        munmap(bo->map, bo->size);
-                        VG(VALGRIND_FREELIKE_BLOCK(bo->map, 0));
-                }
-        }
-
-        struct drm_gem_close c;
-        memset(&c, 0, sizeof(c));
-        c.handle = bo->handle;
-        int ret = vc4_ioctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &c);
-        if (ret != 0)
-                fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno));
-
-        screen->bo_count--;
-        screen->bo_size -= bo->size;
-
-        if (dump_stats) {
-                fprintf(stderr, "Freed %s%s%dkb:\n",
-                        bo->name ? bo->name : "",
-                        bo->name ? " " : "",
-                        bo->size / 1024);
-                vc4_bo_dump_stats(screen);
-        }
-
-        free(bo);
-}
-
-static void
 free_stale_bos(struct vc4_screen *screen, time_t time)
 {
         struct vc4_bo_cache *cache = &screen->bo_cache;
@@ -296,6 +332,7 @@ vc4_bo_last_unreference_locked_timed(struct vc4_bo *bo, time_t time)
                 cache->size_list_size = page_index + 1;
         }
 
+        vc4_bo_purgeable(bo);
         bo->free_time = time;
         list_addtail(&bo->size_list, &cache->size_list[page_index]);
         list_addtail(&bo->time_list, &cache->time_list);
diff --git a/src/gallium/drivers/vc4/vc4_screen.c b/src/gallium/drivers/vc4/vc4_screen.c
index 4f38346d838e..01cff55075c2 100644
--- a/src/gallium/drivers/vc4/vc4_screen.c
+++ b/src/gallium/drivers/vc4/vc4_screen.c
@@ -677,6 +677,8 @@ vc4_screen_create(int fd, struct renderonly *ro)
                 vc4_has_feature(screen, DRM_VC4_PARAM_SUPPORTS_ETC1);
         screen->has_threaded_fs =
                 vc4_has_feature(screen, DRM_VC4_PARAM_SUPPORTS_THREADED_FS);
+        screen->has_madvise =
+                vc4_has_feature(screen, DRM_VC4_PARAM_SUPPORTS_MADVISE);
 
         if (!vc4_get_chip_info(screen))
                 goto fail;
diff --git a/src/gallium/drivers/vc4/vc4_screen.h b/src/gallium/drivers/vc4/vc4_screen.h
index 85108219ee3b..09d1c342ed19 100644
--- a/src/gallium/drivers/vc4/vc4_screen.h
+++ b/src/gallium/drivers/vc4/vc4_screen.h
@@ -95,6 +95,7 @@ struct vc4_screen {
         bool has_control_flow;
         bool has_etc1;
         bool has_threaded_fs;
+        bool has_madvise;
         bool has_tiling_ioctl;
 
         struct vc4_simulator_file *sim_file;
-- 
2.11.0



More information about the mesa-dev mailing list