diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index f0cd8b9c189b..b199942266a2 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -759,6 +759,29 @@ void msm_gem_purge(struct drm_gem_object *obj) 0, (loff_t)-1); } +/** + * Unpin the backing pages and make them available to be swapped out. + */ +void msm_gem_evict(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct msm_gem_object *msm_obj = to_msm_bo(obj); + + GEM_WARN_ON(!msm_gem_is_locked(obj)); + GEM_WARN_ON(is_unevictable(msm_obj)); + GEM_WARN_ON(!msm_obj->evictable); + GEM_WARN_ON(msm_obj->active_count); + + /* Get rid of any iommu mapping(s): */ + put_iova_spaces(obj, false); + + drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping); + + put_pages(obj); + + update_inactive(msm_obj); +} + void msm_gem_vunmap(struct drm_gem_object *obj) { struct msm_gem_object *msm_obj = to_msm_bo(obj); diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c index a4d85955cbd9..1187ecf9d647 100644 --- a/drivers/gpu/drm/msm/msm_gem_shrinker.c +++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c @@ -9,12 +9,29 @@ #include "msm_gpu.h" #include "msm_gpu_trace.h" +/* Default disabled for now until it has some more testing on the different + * iommu combinations that can be paired with the driver: + */ +bool enable_eviction = false; +MODULE_PARM_DESC(enable_eviction, "Enable swappable GEM buffers"); +module_param(enable_eviction, bool, 0600); + +static bool can_swap(void) +{ + return enable_eviction && get_nr_swap_pages() > 0; +} + static unsigned long msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc) { struct msm_drm_private *priv = container_of(shrinker, struct msm_drm_private, shrinker); - return priv->shrinkable_count; + unsigned count = priv->shrinkable_count; + + if (can_swap()) + count += priv->evictable_count; + + return count; } static bool @@ -32,6 +49,17 @@ purge(struct msm_gem_object *msm_obj) return true; } +static bool +evict(struct msm_gem_object *msm_obj) +{ + if (is_unevictable(msm_obj)) + return false; + + msm_gem_evict(&msm_obj->base); + + return true; +} + static unsigned long scan(struct msm_drm_private *priv, unsigned nr_to_scan, struct list_head *list, bool (*shrink)(struct msm_gem_object *msm_obj)) @@ -104,6 +132,16 @@ msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) if (freed > 0) trace_msm_gem_purge(freed << PAGE_SHIFT); + if (can_swap() && freed < sc->nr_to_scan) { + int evicted = scan(priv, sc->nr_to_scan - freed, + &priv->inactive_willneed, evict); + + if (evicted > 0) + trace_msm_gem_evict(evicted << PAGE_SHIFT); + + freed += evicted; + } + return (freed > 0) ? freed : SHRINK_STOP; } diff --git a/drivers/gpu/drm/msm/msm_gpu_trace.h b/drivers/gpu/drm/msm/msm_gpu_trace.h index 03e0c2536b94..ca0b08d7875b 100644 --- a/drivers/gpu/drm/msm/msm_gpu_trace.h +++ b/drivers/gpu/drm/msm/msm_gpu_trace.h @@ -128,6 +128,19 @@ TRACE_EVENT(msm_gem_purge, ); +TRACE_EVENT(msm_gem_evict, + TP_PROTO(u32 bytes), + TP_ARGS(bytes), + TP_STRUCT__entry( + __field(u32, bytes) + ), + TP_fast_assign( + __entry->bytes = bytes; + ), + TP_printk("Evicting %u bytes", __entry->bytes) +); + + TRACE_EVENT(msm_gem_purge_vmaps, TP_PROTO(u32 unmapped), TP_ARGS(unmapped),