xref: /openbmc/linux/drivers/gpu/drm/msm/msm_gem_shrinker.c (revision 26d0dfbb16fcb17d128a79dc70f3020ea6992af0)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
268209390SRob Clark /*
368209390SRob Clark  * Copyright (C) 2016 Red Hat
468209390SRob Clark  * Author: Rob Clark <robdclark@gmail.com>
568209390SRob Clark  */
668209390SRob Clark 
789e56d5eSYanteng Si #include <linux/vmalloc.h>
88581fd40SJakub Kicinski #include <linux/sched/mm.h>
989e56d5eSYanteng Si 
1068209390SRob Clark #include "msm_drv.h"
1168209390SRob Clark #include "msm_gem.h"
12fcd371c2SRob Clark #include "msm_gpu.h"
13fdf38426SRob Clark #include "msm_gpu_trace.h"
1468209390SRob Clark 
1563f17ef8SRob Clark /* Default disabled for now until it has some more testing on the different
1663f17ef8SRob Clark  * iommu combinations that can be paired with the driver:
1763f17ef8SRob Clark  */
18e8b8feb5SRob Clark static bool enable_eviction = true;
1963f17ef8SRob Clark MODULE_PARM_DESC(enable_eviction, "Enable swappable GEM buffers");
2063f17ef8SRob Clark module_param(enable_eviction, bool, 0600);
2163f17ef8SRob Clark 
can_swap(void)2263f17ef8SRob Clark static bool can_swap(void)
2363f17ef8SRob Clark {
2463f17ef8SRob Clark 	return enable_eviction && get_nr_swap_pages() > 0;
2563f17ef8SRob Clark }
2663f17ef8SRob Clark 
can_block(struct shrink_control * sc)27025d2723SRob Clark static bool can_block(struct shrink_control *sc)
28025d2723SRob Clark {
297860d720SRob Clark 	if (!(sc->gfp_mask & __GFP_DIRECT_RECLAIM))
30025d2723SRob Clark 		return false;
31025d2723SRob Clark 	return current_is_kswapd() || (sc->gfp_mask & __GFP_RECLAIM);
32025d2723SRob Clark }
33025d2723SRob Clark 
3468209390SRob Clark static unsigned long
msm_gem_shrinker_count(struct shrinker * shrinker,struct shrink_control * sc)3568209390SRob Clark msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
3668209390SRob Clark {
3768209390SRob Clark 	struct msm_drm_private *priv =
3868209390SRob Clark 		container_of(shrinker, struct msm_drm_private, shrinker);
39b352ba54SRob Clark 	unsigned count = priv->lru.dontneed.count;
4063f17ef8SRob Clark 
4163f17ef8SRob Clark 	if (can_swap())
42b352ba54SRob Clark 		count += priv->lru.willneed.count;
4363f17ef8SRob Clark 
4463f17ef8SRob Clark 	return count;
4568209390SRob Clark }
4668209390SRob Clark 
476afb0750SRob Clark static bool
purge(struct drm_gem_object * obj)48b352ba54SRob Clark purge(struct drm_gem_object *obj)
4968209390SRob Clark {
50b352ba54SRob Clark 	if (!is_purgeable(to_msm_bo(obj)))
516afb0750SRob Clark 		return false;
526afb0750SRob Clark 
53b352ba54SRob Clark 	if (msm_gem_active(obj))
5401780d02SRob Clark 		return false;
5501780d02SRob Clark 
56b352ba54SRob Clark 	msm_gem_purge(obj);
576afb0750SRob Clark 
586afb0750SRob Clark 	return true;
596afb0750SRob Clark }
606afb0750SRob Clark 
6163f17ef8SRob Clark static bool
evict(struct drm_gem_object * obj)62b352ba54SRob Clark evict(struct drm_gem_object *obj)
6363f17ef8SRob Clark {
64b352ba54SRob Clark 	if (is_unevictable(to_msm_bo(obj)))
6563f17ef8SRob Clark 		return false;
6663f17ef8SRob Clark 
67b352ba54SRob Clark 	if (msm_gem_active(obj))
6801780d02SRob Clark 		return false;
6901780d02SRob Clark 
70b352ba54SRob Clark 	msm_gem_evict(obj);
7163f17ef8SRob Clark 
7263f17ef8SRob Clark 	return true;
7363f17ef8SRob Clark }
7463f17ef8SRob Clark 
75025d2723SRob Clark static bool
wait_for_idle(struct drm_gem_object * obj)76025d2723SRob Clark wait_for_idle(struct drm_gem_object *obj)
77025d2723SRob Clark {
78025d2723SRob Clark 	enum dma_resv_usage usage = dma_resv_usage_rw(true);
79*9064a70eSRob Clark 	return dma_resv_wait_timeout(obj->resv, usage, false, 10) > 0;
80025d2723SRob Clark }
81025d2723SRob Clark 
82025d2723SRob Clark static bool
active_purge(struct drm_gem_object * obj)83025d2723SRob Clark active_purge(struct drm_gem_object *obj)
84025d2723SRob Clark {
85025d2723SRob Clark 	if (!wait_for_idle(obj))
86025d2723SRob Clark 		return false;
87025d2723SRob Clark 
88025d2723SRob Clark 	return purge(obj);
89025d2723SRob Clark }
90025d2723SRob Clark 
91025d2723SRob Clark static bool
active_evict(struct drm_gem_object * obj)92025d2723SRob Clark active_evict(struct drm_gem_object *obj)
93025d2723SRob Clark {
94025d2723SRob Clark 	if (!wait_for_idle(obj))
95025d2723SRob Clark 		return false;
96025d2723SRob Clark 
97025d2723SRob Clark 	return evict(obj);
98025d2723SRob Clark }
99025d2723SRob Clark 
1006afb0750SRob Clark static unsigned long
msm_gem_shrinker_scan(struct shrinker * shrinker,struct shrink_control * sc)1016afb0750SRob Clark msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
1026afb0750SRob Clark {
1036afb0750SRob Clark 	struct msm_drm_private *priv =
1046afb0750SRob Clark 		container_of(shrinker, struct msm_drm_private, shrinker);
105025d2723SRob Clark 	struct {
106025d2723SRob Clark 		struct drm_gem_lru *lru;
107025d2723SRob Clark 		bool (*shrink)(struct drm_gem_object *obj);
108025d2723SRob Clark 		bool cond;
109025d2723SRob Clark 		unsigned long freed;
1109630b585SDmitry Osipenko 		unsigned long remaining;
111025d2723SRob Clark 	} stages[] = {
112025d2723SRob Clark 		/* Stages of progressively more aggressive/expensive reclaim: */
113025d2723SRob Clark 		{ &priv->lru.dontneed, purge,        true },
114025d2723SRob Clark 		{ &priv->lru.willneed, evict,        can_swap() },
115025d2723SRob Clark 		{ &priv->lru.dontneed, active_purge, can_block(sc) },
116025d2723SRob Clark 		{ &priv->lru.willneed, active_evict, can_swap() && can_block(sc) },
117025d2723SRob Clark 	};
118b352ba54SRob Clark 	long nr = sc->nr_to_scan;
119025d2723SRob Clark 	unsigned long freed = 0;
1209630b585SDmitry Osipenko 	unsigned long remaining = 0;
1216afb0750SRob Clark 
122025d2723SRob Clark 	for (unsigned i = 0; (nr > 0) && (i < ARRAY_SIZE(stages)); i++) {
123025d2723SRob Clark 		if (!stages[i].cond)
124025d2723SRob Clark 			continue;
125025d2723SRob Clark 		stages[i].freed =
1269630b585SDmitry Osipenko 			drm_gem_lru_scan(stages[i].lru, nr,
1279630b585SDmitry Osipenko 					&stages[i].remaining,
1289630b585SDmitry Osipenko 					 stages[i].shrink);
129025d2723SRob Clark 		nr -= stages[i].freed;
130025d2723SRob Clark 		freed += stages[i].freed;
1319630b585SDmitry Osipenko 		remaining += stages[i].remaining;
13263f17ef8SRob Clark 	}
13363f17ef8SRob Clark 
134025d2723SRob Clark 	if (freed) {
135025d2723SRob Clark 		trace_msm_gem_shrink(sc->nr_to_scan, stages[0].freed,
136025d2723SRob Clark 				     stages[1].freed, stages[2].freed,
137025d2723SRob Clark 				     stages[3].freed);
138025d2723SRob Clark 	}
139dd2f0d78SRob Clark 
1409630b585SDmitry Osipenko 	return (freed > 0 && remaining > 0) ? freed : SHRINK_STOP;
14168209390SRob Clark }
14268209390SRob Clark 
1435434941fSRob Clark #ifdef CONFIG_DEBUG_FS
1445434941fSRob Clark unsigned long
msm_gem_shrinker_shrink(struct drm_device * dev,unsigned long nr_to_scan)1455434941fSRob Clark msm_gem_shrinker_shrink(struct drm_device *dev, unsigned long nr_to_scan)
1465434941fSRob Clark {
1475434941fSRob Clark 	struct msm_drm_private *priv = dev->dev_private;
1485434941fSRob Clark 	struct shrink_control sc = {
1495434941fSRob Clark 		.nr_to_scan = nr_to_scan,
1505434941fSRob Clark 	};
1515434941fSRob Clark 	int ret;
1525434941fSRob Clark 
1535434941fSRob Clark 	fs_reclaim_acquire(GFP_KERNEL);
1545434941fSRob Clark 	ret = msm_gem_shrinker_scan(&priv->shrinker, &sc);
1555434941fSRob Clark 	fs_reclaim_release(GFP_KERNEL);
1565434941fSRob Clark 
1575434941fSRob Clark 	return ret;
1585434941fSRob Clark }
1595434941fSRob Clark #endif
1605434941fSRob Clark 
161fcd371c2SRob Clark /* since we don't know any better, lets bail after a few
162fcd371c2SRob Clark  * and if necessary the shrinker will be invoked again.
163fcd371c2SRob Clark  * Seems better than unmapping *everything*
164fcd371c2SRob Clark  */
165fcd371c2SRob Clark static const int vmap_shrink_limit = 15;
166fcd371c2SRob Clark 
1676afb0750SRob Clark static bool
vmap_shrink(struct drm_gem_object * obj)168b352ba54SRob Clark vmap_shrink(struct drm_gem_object *obj)
169e1e9db2cSRob Clark {
170b352ba54SRob Clark 	if (!is_vunmapable(to_msm_bo(obj)))
1716afb0750SRob Clark 		return false;
172e1e9db2cSRob Clark 
173b352ba54SRob Clark 	msm_gem_vunmap(obj);
174599089c6SRob Clark 
1756afb0750SRob Clark 	return true;
176fcd371c2SRob Clark }
177fcd371c2SRob Clark 
178fcd371c2SRob Clark static int
msm_gem_shrinker_vmap(struct notifier_block * nb,unsigned long event,void * ptr)179fcd371c2SRob Clark msm_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr)
180fcd371c2SRob Clark {
181fcd371c2SRob Clark 	struct msm_drm_private *priv =
182fcd371c2SRob Clark 		container_of(nb, struct msm_drm_private, vmap_notifier);
183b352ba54SRob Clark 	struct drm_gem_lru *lrus[] = {
184b352ba54SRob Clark 		&priv->lru.dontneed,
185b352ba54SRob Clark 		&priv->lru.willneed,
186b352ba54SRob Clark 		&priv->lru.pinned,
187fcd371c2SRob Clark 		NULL,
188fcd371c2SRob Clark 	};
189fcd371c2SRob Clark 	unsigned idx, unmapped = 0;
1909630b585SDmitry Osipenko 	unsigned long remaining = 0;
191fcd371c2SRob Clark 
192b352ba54SRob Clark 	for (idx = 0; lrus[idx] && unmapped < vmap_shrink_limit; idx++) {
193b352ba54SRob Clark 		unmapped += drm_gem_lru_scan(lrus[idx],
194b352ba54SRob Clark 					     vmap_shrink_limit - unmapped,
1959630b585SDmitry Osipenko 					     &remaining,
196b352ba54SRob Clark 					     vmap_shrink);
197e1e9db2cSRob Clark 	}
198e1e9db2cSRob Clark 
199e1e9db2cSRob Clark 	*(unsigned long *)ptr += unmapped;
200e1e9db2cSRob Clark 
201e1e9db2cSRob Clark 	if (unmapped > 0)
202fdf38426SRob Clark 		trace_msm_gem_purge_vmaps(unmapped);
203e1e9db2cSRob Clark 
204e1e9db2cSRob Clark 	return NOTIFY_DONE;
205e1e9db2cSRob Clark }
206e1e9db2cSRob Clark 
20768209390SRob Clark /**
20868209390SRob Clark  * msm_gem_shrinker_init - Initialize msm shrinker
209324dca17SLee Jones  * @dev: drm device
21068209390SRob Clark  *
21168209390SRob Clark  * This function registers and sets up the msm shrinker.
21268209390SRob Clark  */
msm_gem_shrinker_init(struct drm_device * dev)21368209390SRob Clark void msm_gem_shrinker_init(struct drm_device *dev)
21468209390SRob Clark {
21568209390SRob Clark 	struct msm_drm_private *priv = dev->dev_private;
21668209390SRob Clark 	priv->shrinker.count_objects = msm_gem_shrinker_count;
21768209390SRob Clark 	priv->shrinker.scan_objects = msm_gem_shrinker_scan;
21868209390SRob Clark 	priv->shrinker.seeks = DEFAULT_SEEKS;
219e33c267aSRoman Gushchin 	WARN_ON(register_shrinker(&priv->shrinker, "drm-msm_gem"));
220e1e9db2cSRob Clark 
221e1e9db2cSRob Clark 	priv->vmap_notifier.notifier_call = msm_gem_shrinker_vmap;
222e1e9db2cSRob Clark 	WARN_ON(register_vmap_purge_notifier(&priv->vmap_notifier));
22368209390SRob Clark }
22468209390SRob Clark 
22568209390SRob Clark /**
22668209390SRob Clark  * msm_gem_shrinker_cleanup - Clean up msm shrinker
227324dca17SLee Jones  * @dev: drm device
22868209390SRob Clark  *
22968209390SRob Clark  * This function unregisters the msm shrinker.
23068209390SRob Clark  */
msm_gem_shrinker_cleanup(struct drm_device * dev)23168209390SRob Clark void msm_gem_shrinker_cleanup(struct drm_device *dev)
23268209390SRob Clark {
23368209390SRob Clark 	struct msm_drm_private *priv = dev->dev_private;
23416976085SArchit Taneja 
23516976085SArchit Taneja 	if (priv->shrinker.nr_deferred) {
236e1e9db2cSRob Clark 		WARN_ON(unregister_vmap_purge_notifier(&priv->vmap_notifier));
23768209390SRob Clark 		unregister_shrinker(&priv->shrinker);
23868209390SRob Clark 	}
23916976085SArchit Taneja }
240