ttm_memory.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /**************************************************************************
  2. *
  3. * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
  4. * All Rights Reserved.
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a
  7. * copy of this software and associated documentation files (the
  8. * "Software"), to deal in the Software without restriction, including
  9. * without limitation the rights to use, copy, modify, merge, publish,
  10. * distribute, sub license, and/or sell copies of the Software, and to
  11. * permit persons to whom the Software is furnished to do so, subject to
  12. * the following conditions:
  13. *
  14. * The above copyright notice and this permission notice (including the
  15. * next paragraph) shall be included in all copies or substantial portions
  16. * of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
  21. * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
  22. * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  23. * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  24. * USE OR OTHER DEALINGS IN THE SOFTWARE.
  25. *
  26. **************************************************************************/
  27. #include "ttm/ttm_memory.h"
  28. #include <linux/spinlock.h>
  29. #include <linux/sched.h>
  30. #include <linux/wait.h>
  31. #include <linux/mm.h>
  32. #include <linux/module.h>
  33. #define TTM_PFX "[TTM] "
  34. #define TTM_MEMORY_ALLOC_RETRIES 4
  35. /**
  36. * At this point we only support a single shrink callback.
  37. * Extend this if needed, perhaps using a linked list of callbacks.
  38. * Note that this function is reentrant:
  39. * many threads may try to swap out at any given time.
  40. */
  41. static void ttm_shrink(struct ttm_mem_global *glob, bool from_workqueue,
  42. uint64_t extra)
  43. {
  44. int ret;
  45. struct ttm_mem_shrink *shrink;
  46. uint64_t target;
  47. uint64_t total_target;
  48. spin_lock(&glob->lock);
  49. if (glob->shrink == NULL)
  50. goto out;
  51. if (from_workqueue) {
  52. target = glob->swap_limit;
  53. total_target = glob->total_memory_swap_limit;
  54. } else if (capable(CAP_SYS_ADMIN)) {
  55. total_target = glob->emer_total_memory;
  56. target = glob->emer_memory;
  57. } else {
  58. total_target = glob->max_total_memory;
  59. target = glob->max_memory;
  60. }
  61. total_target = (extra >= total_target) ? 0 : total_target - extra;
  62. target = (extra >= target) ? 0 : target - extra;
  63. while (glob->used_memory > target ||
  64. glob->used_total_memory > total_target) {
  65. shrink = glob->shrink;
  66. spin_unlock(&glob->lock);
  67. ret = shrink->do_shrink(shrink);
  68. spin_lock(&glob->lock);
  69. if (unlikely(ret != 0))
  70. goto out;
  71. }
  72. out:
  73. spin_unlock(&glob->lock);
  74. }
  75. static void ttm_shrink_work(struct work_struct *work)
  76. {
  77. struct ttm_mem_global *glob =
  78. container_of(work, struct ttm_mem_global, work);
  79. ttm_shrink(glob, true, 0ULL);
  80. }
  81. int ttm_mem_global_init(struct ttm_mem_global *glob)
  82. {
  83. struct sysinfo si;
  84. uint64_t mem;
  85. spin_lock_init(&glob->lock);
  86. glob->swap_queue = create_singlethread_workqueue("ttm_swap");
  87. INIT_WORK(&glob->work, ttm_shrink_work);
  88. init_waitqueue_head(&glob->queue);
  89. si_meminfo(&si);
  90. mem = si.totalram - si.totalhigh;
  91. mem *= si.mem_unit;
  92. glob->max_memory = mem >> 1;
  93. glob->emer_memory = (mem >> 1) + (mem >> 2);
  94. glob->swap_limit = glob->max_memory - (mem >> 3);
  95. glob->used_memory = 0;
  96. glob->used_total_memory = 0;
  97. glob->shrink = NULL;
  98. mem = si.totalram;
  99. mem *= si.mem_unit;
  100. glob->max_total_memory = mem >> 1;
  101. glob->emer_total_memory = (mem >> 1) + (mem >> 2);
  102. glob->total_memory_swap_limit = glob->max_total_memory - (mem >> 3);
  103. printk(KERN_INFO TTM_PFX "TTM available graphics memory: %llu MiB\n",
  104. glob->max_total_memory >> 20);
  105. printk(KERN_INFO TTM_PFX "TTM available object memory: %llu MiB\n",
  106. glob->max_memory >> 20);
  107. return 0;
  108. }
  109. EXPORT_SYMBOL(ttm_mem_global_init);
  110. void ttm_mem_global_release(struct ttm_mem_global *glob)
  111. {
  112. printk(KERN_INFO TTM_PFX "Used total memory is %llu bytes.\n",
  113. (unsigned long long)glob->used_total_memory);
  114. flush_workqueue(glob->swap_queue);
  115. destroy_workqueue(glob->swap_queue);
  116. glob->swap_queue = NULL;
  117. }
  118. EXPORT_SYMBOL(ttm_mem_global_release);
  119. static inline void ttm_check_swapping(struct ttm_mem_global *glob)
  120. {
  121. bool needs_swapping;
  122. spin_lock(&glob->lock);
  123. needs_swapping = (glob->used_memory > glob->swap_limit ||
  124. glob->used_total_memory >
  125. glob->total_memory_swap_limit);
  126. spin_unlock(&glob->lock);
  127. if (unlikely(needs_swapping))
  128. (void)queue_work(glob->swap_queue, &glob->work);
  129. }
  130. void ttm_mem_global_free(struct ttm_mem_global *glob,
  131. uint64_t amount, bool himem)
  132. {
  133. spin_lock(&glob->lock);
  134. glob->used_total_memory -= amount;
  135. if (!himem)
  136. glob->used_memory -= amount;
  137. wake_up_all(&glob->queue);
  138. spin_unlock(&glob->lock);
  139. }
  140. static int ttm_mem_global_reserve(struct ttm_mem_global *glob,
  141. uint64_t amount, bool himem, bool reserve)
  142. {
  143. uint64_t limit;
  144. uint64_t lomem_limit;
  145. int ret = -ENOMEM;
  146. spin_lock(&glob->lock);
  147. if (capable(CAP_SYS_ADMIN)) {
  148. limit = glob->emer_total_memory;
  149. lomem_limit = glob->emer_memory;
  150. } else {
  151. limit = glob->max_total_memory;
  152. lomem_limit = glob->max_memory;
  153. }
  154. if (unlikely(glob->used_total_memory + amount > limit))
  155. goto out_unlock;
  156. if (unlikely(!himem && glob->used_memory + amount > lomem_limit))
  157. goto out_unlock;
  158. if (reserve) {
  159. glob->used_total_memory += amount;
  160. if (!himem)
  161. glob->used_memory += amount;
  162. }
  163. ret = 0;
  164. out_unlock:
  165. spin_unlock(&glob->lock);
  166. ttm_check_swapping(glob);
  167. return ret;
  168. }
  169. int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory,
  170. bool no_wait, bool interruptible, bool himem)
  171. {
  172. int count = TTM_MEMORY_ALLOC_RETRIES;
  173. while (unlikely(ttm_mem_global_reserve(glob, memory, himem, true)
  174. != 0)) {
  175. if (no_wait)
  176. return -ENOMEM;
  177. if (unlikely(count-- == 0))
  178. return -ENOMEM;
  179. ttm_shrink(glob, false, memory + (memory >> 2) + 16);
  180. }
  181. return 0;
  182. }
  183. size_t ttm_round_pot(size_t size)
  184. {
  185. if ((size & (size - 1)) == 0)
  186. return size;
  187. else if (size > PAGE_SIZE)
  188. return PAGE_ALIGN(size);
  189. else {
  190. size_t tmp_size = 4;
  191. while (tmp_size < size)
  192. tmp_size <<= 1;
  193. return tmp_size;
  194. }
  195. return 0;
  196. }