msm_gem_shrinker.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2016 Red Hat
  4. * Author: Rob Clark <robdclark@gmail.com>
  5. */
  6. #include <linux/vmalloc.h>
  7. #include <linux/sched/mm.h>
  8. #include "msm_drv.h"
  9. #include "msm_gem.h"
  10. #include "msm_gpu.h"
  11. #include "msm_gpu_trace.h"
  12. /* Default disabled for now until it has some more testing on the different
  13. * iommu combinations that can be paired with the driver:
  14. */
  15. static bool enable_eviction = true;
  16. MODULE_PARM_DESC(enable_eviction, "Enable swappable GEM buffers");
  17. module_param(enable_eviction, bool, 0600);
  18. static bool can_swap(void)
  19. {
  20. return enable_eviction && get_nr_swap_pages() > 0;
  21. }
  22. static bool can_block(struct shrink_control *sc)
  23. {
  24. if (!(sc->gfp_mask & __GFP_DIRECT_RECLAIM))
  25. return false;
  26. return current_is_kswapd() || (sc->gfp_mask & __GFP_RECLAIM);
  27. }
  28. static unsigned long
  29. msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
  30. {
  31. struct msm_drm_private *priv = shrinker->private_data;
  32. unsigned count = priv->lru.dontneed.count;
  33. if (can_swap())
  34. count += priv->lru.willneed.count;
  35. return count;
  36. }
  37. static bool
  38. purge(struct drm_gem_object *obj)
  39. {
  40. if (!is_purgeable(to_msm_bo(obj)))
  41. return false;
  42. if (msm_gem_active(obj))
  43. return false;
  44. msm_gem_purge(obj);
  45. return true;
  46. }
  47. static bool
  48. evict(struct drm_gem_object *obj)
  49. {
  50. if (is_unevictable(to_msm_bo(obj)))
  51. return false;
  52. if (msm_gem_active(obj))
  53. return false;
  54. msm_gem_evict(obj);
  55. return true;
  56. }
  57. static bool
  58. wait_for_idle(struct drm_gem_object *obj)
  59. {
  60. enum dma_resv_usage usage = dma_resv_usage_rw(true);
  61. return dma_resv_wait_timeout(obj->resv, usage, false, 10) > 0;
  62. }
  63. static bool
  64. active_purge(struct drm_gem_object *obj)
  65. {
  66. if (!wait_for_idle(obj))
  67. return false;
  68. return purge(obj);
  69. }
  70. static bool
  71. active_evict(struct drm_gem_object *obj)
  72. {
  73. if (!wait_for_idle(obj))
  74. return false;
  75. return evict(obj);
  76. }
  77. static unsigned long
  78. msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
  79. {
  80. struct msm_drm_private *priv = shrinker->private_data;
  81. struct {
  82. struct drm_gem_lru *lru;
  83. bool (*shrink)(struct drm_gem_object *obj);
  84. bool cond;
  85. unsigned long freed;
  86. unsigned long remaining;
  87. } stages[] = {
  88. /* Stages of progressively more aggressive/expensive reclaim: */
  89. { &priv->lru.dontneed, purge, true },
  90. { &priv->lru.willneed, evict, can_swap() },
  91. { &priv->lru.dontneed, active_purge, can_block(sc) },
  92. { &priv->lru.willneed, active_evict, can_swap() && can_block(sc) },
  93. };
  94. long nr = sc->nr_to_scan;
  95. unsigned long freed = 0;
  96. unsigned long remaining = 0;
  97. for (unsigned i = 0; (nr > 0) && (i < ARRAY_SIZE(stages)); i++) {
  98. if (!stages[i].cond)
  99. continue;
  100. stages[i].freed =
  101. drm_gem_lru_scan(stages[i].lru, nr,
  102. &stages[i].remaining,
  103. stages[i].shrink);
  104. nr -= stages[i].freed;
  105. freed += stages[i].freed;
  106. remaining += stages[i].remaining;
  107. }
  108. if (freed) {
  109. trace_msm_gem_shrink(sc->nr_to_scan, stages[0].freed,
  110. stages[1].freed, stages[2].freed,
  111. stages[3].freed);
  112. }
  113. return (freed > 0 && remaining > 0) ? freed : SHRINK_STOP;
  114. }
  115. #ifdef CONFIG_DEBUG_FS
  116. unsigned long
  117. msm_gem_shrinker_shrink(struct drm_device *dev, unsigned long nr_to_scan)
  118. {
  119. struct msm_drm_private *priv = dev->dev_private;
  120. struct shrink_control sc = {
  121. .nr_to_scan = nr_to_scan,
  122. };
  123. unsigned long ret = SHRINK_STOP;
  124. fs_reclaim_acquire(GFP_KERNEL);
  125. if (priv->shrinker)
  126. ret = msm_gem_shrinker_scan(priv->shrinker, &sc);
  127. fs_reclaim_release(GFP_KERNEL);
  128. return ret;
  129. }
  130. #endif
  131. /* since we don't know any better, lets bail after a few
  132. * and if necessary the shrinker will be invoked again.
  133. * Seems better than unmapping *everything*
  134. */
  135. static const int vmap_shrink_limit = 15;
  136. static bool
  137. vmap_shrink(struct drm_gem_object *obj)
  138. {
  139. if (!is_vunmapable(to_msm_bo(obj)))
  140. return false;
  141. msm_gem_vunmap(obj);
  142. return true;
  143. }
  144. static int
  145. msm_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr)
  146. {
  147. struct msm_drm_private *priv =
  148. container_of(nb, struct msm_drm_private, vmap_notifier);
  149. struct drm_gem_lru *lrus[] = {
  150. &priv->lru.dontneed,
  151. &priv->lru.willneed,
  152. &priv->lru.pinned,
  153. NULL,
  154. };
  155. unsigned idx, unmapped = 0;
  156. unsigned long remaining = 0;
  157. for (idx = 0; lrus[idx] && unmapped < vmap_shrink_limit; idx++) {
  158. unmapped += drm_gem_lru_scan(lrus[idx],
  159. vmap_shrink_limit - unmapped,
  160. &remaining,
  161. vmap_shrink);
  162. }
  163. *(unsigned long *)ptr += unmapped;
  164. if (unmapped > 0)
  165. trace_msm_gem_purge_vmaps(unmapped);
  166. return NOTIFY_DONE;
  167. }
  168. /**
  169. * msm_gem_shrinker_init - Initialize msm shrinker
  170. * @dev: drm device
  171. *
  172. * This function registers and sets up the msm shrinker.
  173. */
  174. int msm_gem_shrinker_init(struct drm_device *dev)
  175. {
  176. struct msm_drm_private *priv = dev->dev_private;
  177. priv->shrinker = shrinker_alloc(0, "drm-msm_gem");
  178. if (!priv->shrinker)
  179. return -ENOMEM;
  180. priv->shrinker->count_objects = msm_gem_shrinker_count;
  181. priv->shrinker->scan_objects = msm_gem_shrinker_scan;
  182. priv->shrinker->private_data = priv;
  183. shrinker_register(priv->shrinker);
  184. priv->vmap_notifier.notifier_call = msm_gem_shrinker_vmap;
  185. WARN_ON(register_vmap_purge_notifier(&priv->vmap_notifier));
  186. return 0;
  187. }
  188. /**
  189. * msm_gem_shrinker_cleanup - Clean up msm shrinker
  190. * @dev: drm device
  191. *
  192. * This function unregisters the msm shrinker.
  193. */
  194. void msm_gem_shrinker_cleanup(struct drm_device *dev)
  195. {
  196. struct msm_drm_private *priv = dev->dev_private;
  197. if (priv->shrinker) {
  198. WARN_ON(unregister_vmap_purge_notifier(&priv->vmap_notifier));
  199. shrinker_free(priv->shrinker);
  200. }
  201. }