From 359a8f6ae5a2e33ffd8dd2a06883fad83e7e09c9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 26 Sep 2017 13:37:40 +0200 Subject: [PATCH] broadcom/vc4: Mark BOs as purgeable when they enter the BO cache 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. v2: - Removed BOs from the cache when they've been purged by the kernel - Check whether the madvise ioctl is supported or not before using it v3: Don't walk the whole list when we find a busy BO (by anholt, acked by Boris) Signed-off-by: Boris Brezillon Reviewed-by: Eric Anholt --- src/gallium/drivers/vc4/vc4_bufmgr.c | 131 +++++++++++++++++---------- src/gallium/drivers/vc4/vc4_screen.c | 2 + src/gallium/drivers/vc4/vc4_screen.h | 1 + 3 files changed, 86 insertions(+), 48 deletions(-) diff --git a/src/gallium/drivers/vc4/vc4_bufmgr.c b/src/gallium/drivers/vc4/vc4_bufmgr.c index d06d55f8645..274c4c3120b 100644 --- a/src/gallium/drivers/vc4/vc4_bufmgr.c +++ b/src/gallium/drivers/vc4/vc4_bufmgr.c @@ -113,35 +113,105 @@ 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 none of the + * other BOs (pushed to the list after later rendering) are + * likely to be idle, either. */ - if (!vc4_bo_wait(bo, 0, NULL)) { - mtx_unlock(&cache->lock); - return NULL; - } + if (!vc4_bo_wait(iter, 0, NULL)) + break; + + 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); vc4_bo_label(screen, bo, "%s", name); bo->name = name; + break; } mtx_unlock(&cache->lock); return bo; @@ -220,42 +290,6 @@ vc4_bo_last_unreference(struct vc4_bo *bo) mtx_unlock(&screen->bo_cache.lock); } -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) { @@ -325,6 +359,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 8128ee6f9d2..7f44fedee80 100644 --- a/src/gallium/drivers/vc4/vc4_screen.c +++ b/src/gallium/drivers/vc4/vc4_screen.c @@ -692,6 +692,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 85108219ee3..09d1c342ed1 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.30.2