2019-06-03 07:44:50 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2016-05-17 16:19:32 -04:00
/*
* Copyright ( C ) 2016 Red Hat
* Author : Rob Clark < robdclark @ gmail . com >
*/
# include "msm_drv.h"
# include "msm_gem.h"
2020-11-16 09:48:50 -08:00
# include "msm_gpu.h"
2020-09-01 08:41:55 -07:00
# include "msm_gpu_trace.h"
2016-05-17 16:19:32 -04:00
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 ) ;
struct msm_gem_object * msm_obj ;
unsigned long count = 0 ;
2020-10-23 09:51:14 -07:00
mutex_lock ( & priv - > mm_lock ) ;
2016-05-17 16:19:32 -04:00
list_for_each_entry ( msm_obj , & priv - > inactive_list , mm_list ) {
2020-10-23 09:51:07 -07:00
if ( ! msm_gem_trylock ( & msm_obj - > base ) )
continue ;
2016-05-17 16:19:32 -04:00
if ( is_purgeable ( msm_obj ) )
count + = msm_obj - > base . size > > PAGE_SHIFT ;
2020-10-23 09:51:07 -07:00
msm_gem_unlock ( & msm_obj - > base ) ;
2016-05-17 16:19:32 -04:00
}
2020-10-23 09:51:14 -07:00
mutex_unlock ( & priv - > mm_lock ) ;
2016-05-17 16:19:32 -04:00
return count ;
}
static unsigned long
msm_gem_shrinker_scan ( struct shrinker * shrinker , struct shrink_control * sc )
{
struct msm_drm_private * priv =
container_of ( shrinker , struct msm_drm_private , shrinker ) ;
struct msm_gem_object * msm_obj ;
unsigned long freed = 0 ;
2020-10-23 09:51:14 -07:00
mutex_lock ( & priv - > mm_lock ) ;
2016-05-17 16:19:32 -04:00
list_for_each_entry ( msm_obj , & priv - > inactive_list , mm_list ) {
if ( freed > = sc - > nr_to_scan )
break ;
2020-10-23 09:51:07 -07:00
if ( ! msm_gem_trylock ( & msm_obj - > base ) )
continue ;
2016-05-17 16:19:32 -04:00
if ( is_purgeable ( msm_obj ) ) {
2020-10-23 09:51:07 -07:00
msm_gem_purge ( & msm_obj - > base ) ;
2016-05-17 16:19:32 -04:00
freed + = msm_obj - > base . size > > PAGE_SHIFT ;
}
2020-10-23 09:51:07 -07:00
msm_gem_unlock ( & msm_obj - > base ) ;
2016-05-17 16:19:32 -04:00
}
2020-10-23 09:51:14 -07:00
mutex_unlock ( & priv - > mm_lock ) ;
2016-05-17 16:19:32 -04:00
if ( freed > 0 )
2020-09-01 08:41:55 -07:00
trace_msm_gem_purge ( freed < < PAGE_SHIFT ) ;
2016-05-17 16:19:32 -04:00
return freed ;
}
2020-11-16 09:48:50 -08:00
/* since we don't know any better, lets bail after a few
* and if necessary the shrinker will be invoked again .
* Seems better than unmapping * everything *
*/
static const int vmap_shrink_limit = 15 ;
static unsigned
vmap_shrink ( struct list_head * mm_list )
2016-05-27 11:16:28 -04:00
{
struct msm_gem_object * msm_obj ;
unsigned unmapped = 0 ;
2020-11-16 09:48:50 -08:00
list_for_each_entry ( msm_obj , mm_list , mm_list ) {
2020-10-23 09:51:07 -07:00
if ( ! msm_gem_trylock ( & msm_obj - > base ) )
continue ;
2016-05-27 11:16:28 -04:00
if ( is_vunmapable ( msm_obj ) ) {
2020-10-23 09:51:07 -07:00
msm_gem_vunmap ( & msm_obj - > base ) ;
unmapped + + ;
2016-05-27 11:16:28 -04:00
}
2020-10-23 09:51:07 -07:00
msm_gem_unlock ( & msm_obj - > base ) ;
2020-11-16 09:48:50 -08:00
if ( + + unmapped > = vmap_shrink_limit )
break ;
}
return unmapped ;
}
static int
msm_gem_shrinker_vmap ( struct notifier_block * nb , unsigned long event , void * ptr )
{
struct msm_drm_private * priv =
container_of ( nb , struct msm_drm_private , vmap_notifier ) ;
struct list_head * mm_lists [ ] = {
& priv - > inactive_list ,
priv - > gpu ? & priv - > gpu - > active_list : NULL ,
NULL ,
} ;
unsigned idx , unmapped = 0 ;
mutex_lock ( & priv - > mm_lock ) ;
for ( idx = 0 ; mm_lists [ idx ] ; idx + + ) {
unmapped + = vmap_shrink ( mm_lists [ idx ] ) ;
if ( unmapped > = vmap_shrink_limit )
2020-10-23 09:51:07 -07:00
break ;
2016-05-27 11:16:28 -04:00
}
2020-10-23 09:51:14 -07:00
mutex_unlock ( & priv - > mm_lock ) ;
2016-05-27 11:16:28 -04:00
* ( unsigned long * ) ptr + = unmapped ;
if ( unmapped > 0 )
2020-09-01 08:41:55 -07:00
trace_msm_gem_purge_vmaps ( unmapped ) ;
2016-05-27 11:16:28 -04:00
return NOTIFY_DONE ;
}
2016-05-17 16:19:32 -04:00
/**
* msm_gem_shrinker_init - Initialize msm shrinker
* @ dev_priv : msm device
*
* This function registers and sets up the msm shrinker .
*/
void msm_gem_shrinker_init ( struct drm_device * dev )
{
struct msm_drm_private * priv = dev - > dev_private ;
priv - > shrinker . count_objects = msm_gem_shrinker_count ;
priv - > shrinker . scan_objects = msm_gem_shrinker_scan ;
priv - > shrinker . seeks = DEFAULT_SEEKS ;
WARN_ON ( register_shrinker ( & priv - > shrinker ) ) ;
2016-05-27 11:16:28 -04:00
priv - > vmap_notifier . notifier_call = msm_gem_shrinker_vmap ;
WARN_ON ( register_vmap_purge_notifier ( & priv - > vmap_notifier ) ) ;
2016-05-17 16:19:32 -04:00
}
/**
* msm_gem_shrinker_cleanup - Clean up msm shrinker
* @ dev_priv : msm device
*
* This function unregisters the msm shrinker .
*/
void msm_gem_shrinker_cleanup ( struct drm_device * dev )
{
struct msm_drm_private * priv = dev - > dev_private ;
2016-11-03 17:36:18 +05:30
if ( priv - > shrinker . nr_deferred ) {
WARN_ON ( unregister_vmap_purge_notifier ( & priv - > vmap_notifier ) ) ;
unregister_shrinker ( & priv - > shrinker ) ;
}
2016-05-17 16:19:32 -04:00
}