nouveau_mm.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /*
  2. * Copyright 2010 Red Hat Inc.
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  17. * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18. * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19. * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20. * OTHER DEALINGS IN THE SOFTWARE.
  21. *
  22. * Authors: Ben Skeggs
  23. */
  24. #include "drmP.h"
  25. #include "nouveau_drv.h"
  26. #include "nouveau_mm.h"
  27. static inline void
  28. region_put(struct nouveau_mm *mm, struct nouveau_mm_node *a)
  29. {
  30. list_del(&a->nl_entry);
  31. list_del(&a->fl_entry);
  32. kfree(a);
  33. }
  34. static struct nouveau_mm_node *
  35. region_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
  36. {
  37. struct nouveau_mm_node *b;
  38. if (a->length == size)
  39. return a;
  40. b = kmalloc(sizeof(*b), GFP_KERNEL);
  41. if (unlikely(b == NULL))
  42. return NULL;
  43. b->offset = a->offset;
  44. b->length = size;
  45. b->type = a->type;
  46. a->offset += size;
  47. a->length -= size;
  48. list_add_tail(&b->nl_entry, &a->nl_entry);
  49. if (b->type == 0)
  50. list_add_tail(&b->fl_entry, &a->fl_entry);
  51. return b;
  52. }
  53. #define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \
  54. list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)
  55. void
  56. nouveau_mm_put(struct nouveau_mm *mm, struct nouveau_mm_node *this)
  57. {
  58. struct nouveau_mm_node *prev = node(this, prev);
  59. struct nouveau_mm_node *next = node(this, next);
  60. list_add(&this->fl_entry, &mm->free);
  61. this->type = 0;
  62. if (prev && prev->type == 0) {
  63. prev->length += this->length;
  64. region_put(mm, this);
  65. this = prev;
  66. }
  67. if (next && next->type == 0) {
  68. next->offset = this->offset;
  69. next->length += this->length;
  70. region_put(mm, this);
  71. }
  72. }
  73. int
  74. nouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc,
  75. u32 align, struct nouveau_mm_node **pnode)
  76. {
  77. struct nouveau_mm_node *prev, *this, *next;
  78. u32 min = size_nc ? size_nc : size;
  79. u32 align_mask = align - 1;
  80. u32 splitoff;
  81. u32 s, e;
  82. list_for_each_entry(this, &mm->free, fl_entry) {
  83. e = this->offset + this->length;
  84. s = this->offset;
  85. prev = node(this, prev);
  86. if (prev && prev->type != type)
  87. s = roundup(s, mm->block_size);
  88. next = node(this, next);
  89. if (next && next->type != type)
  90. e = rounddown(e, mm->block_size);
  91. s = (s + align_mask) & ~align_mask;
  92. e &= ~align_mask;
  93. if (s > e || e - s < min)
  94. continue;
  95. splitoff = s - this->offset;
  96. if (splitoff && !region_split(mm, this, splitoff))
  97. return -ENOMEM;
  98. this = region_split(mm, this, min(size, e - s));
  99. if (!this)
  100. return -ENOMEM;
  101. this->type = type;
  102. list_del(&this->fl_entry);
  103. *pnode = this;
  104. return 0;
  105. }
  106. return -ENOSPC;
  107. }
  108. int
  109. nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
  110. {
  111. struct nouveau_mm_node *node;
  112. if (block) {
  113. mutex_init(&mm->mutex);
  114. INIT_LIST_HEAD(&mm->nodes);
  115. INIT_LIST_HEAD(&mm->free);
  116. mm->block_size = block;
  117. mm->heap_nodes = 0;
  118. }
  119. node = kzalloc(sizeof(*node), GFP_KERNEL);
  120. if (!node)
  121. return -ENOMEM;
  122. node->offset = roundup(offset, mm->block_size);
  123. node->length = rounddown(offset + length, mm->block_size) - node->offset;
  124. list_add_tail(&node->nl_entry, &mm->nodes);
  125. list_add_tail(&node->fl_entry, &mm->free);
  126. mm->heap_nodes++;
  127. return 0;
  128. }
  129. int
  130. nouveau_mm_fini(struct nouveau_mm *mm)
  131. {
  132. struct nouveau_mm_node *node, *heap =
  133. list_first_entry(&mm->nodes, struct nouveau_mm_node, nl_entry);
  134. int nodes = 0;
  135. list_for_each_entry(node, &mm->nodes, nl_entry) {
  136. if (nodes++ == mm->heap_nodes) {
  137. printk(KERN_ERR "nouveau_mm in use at destroy time!\n");
  138. list_for_each_entry(node, &mm->nodes, nl_entry) {
  139. printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n",
  140. node->type, node->offset, node->length);
  141. }
  142. WARN_ON(1);
  143. return -EBUSY;
  144. }
  145. }
  146. kfree(heap);
  147. return 0;
  148. }