panfrost_gem_shrinker.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Copyright (C) 2019 Arm Ltd.
  3. *
  4. * Based on msm_gem_freedreno.c:
  5. * Copyright (C) 2016 Red Hat
  6. * Author: Rob Clark <robdclark@gmail.com>
  7. */
  8. #include <linux/list.h>
  9. #include <drm/drm_device.h>
  10. #include <drm/drm_gem_shmem_helper.h>
  11. #include "panfrost_device.h"
  12. #include "panfrost_gem.h"
  13. #include "panfrost_mmu.h"
  14. static unsigned long
  15. panfrost_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
  16. {
  17. struct panfrost_device *pfdev = shrinker->private_data;
  18. struct drm_gem_shmem_object *shmem;
  19. unsigned long count = 0;
  20. if (!mutex_trylock(&pfdev->shrinker_lock))
  21. return 0;
  22. list_for_each_entry(shmem, &pfdev->shrinker_list, madv_list) {
  23. if (drm_gem_shmem_is_purgeable(shmem))
  24. count += shmem->base.size >> PAGE_SHIFT;
  25. }
  26. mutex_unlock(&pfdev->shrinker_lock);
  27. return count;
  28. }
  29. static bool panfrost_gem_purge(struct drm_gem_object *obj)
  30. {
  31. struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
  32. struct panfrost_gem_object *bo = to_panfrost_bo(obj);
  33. bool ret = false;
  34. if (atomic_read(&bo->gpu_usecount))
  35. return false;
  36. if (!mutex_trylock(&bo->mappings.lock))
  37. return false;
  38. if (!dma_resv_trylock(shmem->base.resv))
  39. goto unlock_mappings;
  40. panfrost_gem_teardown_mappings_locked(bo);
  41. drm_gem_shmem_purge(&bo->base);
  42. ret = true;
  43. dma_resv_unlock(shmem->base.resv);
  44. unlock_mappings:
  45. mutex_unlock(&bo->mappings.lock);
  46. return ret;
  47. }
  48. static unsigned long
  49. panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
  50. {
  51. struct panfrost_device *pfdev = shrinker->private_data;
  52. struct drm_gem_shmem_object *shmem, *tmp;
  53. unsigned long freed = 0;
  54. if (!mutex_trylock(&pfdev->shrinker_lock))
  55. return SHRINK_STOP;
  56. list_for_each_entry_safe(shmem, tmp, &pfdev->shrinker_list, madv_list) {
  57. if (freed >= sc->nr_to_scan)
  58. break;
  59. if (drm_gem_shmem_is_purgeable(shmem) &&
  60. panfrost_gem_purge(&shmem->base)) {
  61. freed += shmem->base.size >> PAGE_SHIFT;
  62. list_del_init(&shmem->madv_list);
  63. }
  64. }
  65. mutex_unlock(&pfdev->shrinker_lock);
  66. if (freed > 0)
  67. pr_info_ratelimited("Purging %lu bytes\n", freed << PAGE_SHIFT);
  68. return freed;
  69. }
  70. /**
  71. * panfrost_gem_shrinker_init - Initialize panfrost shrinker
  72. * @dev: DRM device
  73. *
  74. * This function registers and sets up the panfrost shrinker.
  75. */
  76. int panfrost_gem_shrinker_init(struct drm_device *dev)
  77. {
  78. struct panfrost_device *pfdev = dev->dev_private;
  79. pfdev->shrinker = shrinker_alloc(0, "drm-panfrost");
  80. if (!pfdev->shrinker)
  81. return -ENOMEM;
  82. pfdev->shrinker->count_objects = panfrost_gem_shrinker_count;
  83. pfdev->shrinker->scan_objects = panfrost_gem_shrinker_scan;
  84. pfdev->shrinker->private_data = pfdev;
  85. shrinker_register(pfdev->shrinker);
  86. return 0;
  87. }
  88. /**
  89. * panfrost_gem_shrinker_cleanup - Clean up panfrost shrinker
  90. * @dev: DRM device
  91. *
  92. * This function unregisters the panfrost shrinker.
  93. */
  94. void panfrost_gem_shrinker_cleanup(struct drm_device *dev)
  95. {
  96. struct panfrost_device *pfdev = dev->dev_private;
  97. if (pfdev->shrinker)
  98. shrinker_free(pfdev->shrinker);
  99. }