nouveau_mm.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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 *rmm, 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 *rmm, 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 == &rmm->nodes) ? NULL : \
  54. list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)
  55. void
  56. nouveau_mm_put(struct nouveau_mm *rmm, 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, &rmm->free);
  61. this->type = 0;
  62. if (prev && prev->type == 0) {
  63. prev->length += this->length;
  64. region_put(rmm, this);
  65. this = prev;
  66. }
  67. if (next && next->type == 0) {
  68. next->offset = this->offset;
  69. next->length += this->length;
  70. region_put(rmm, this);
  71. }
  72. }
  73. int
  74. nouveau_mm_get(struct nouveau_mm *rmm, 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, &rmm->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, rmm->block_size);
  88. next = node(this, next);
  89. if (next && next->type != type)
  90. e = rounddown(e, rmm->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(rmm, this, splitoff))
  97. return -ENOMEM;
  98. this = region_split(rmm, 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 **prmm, u32 offset, u32 length, u32 block)
  110. {
  111. struct nouveau_mm *rmm;
  112. struct nouveau_mm_node *heap;
  113. heap = kzalloc(sizeof(*heap), GFP_KERNEL);
  114. if (!heap)
  115. return -ENOMEM;
  116. heap->offset = roundup(offset, block);
  117. heap->length = rounddown(offset + length, block) - heap->offset;
  118. rmm = kzalloc(sizeof(*rmm), GFP_KERNEL);
  119. if (!rmm) {
  120. kfree(heap);
  121. return -ENOMEM;
  122. }
  123. rmm->block_size = block;
  124. mutex_init(&rmm->mutex);
  125. INIT_LIST_HEAD(&rmm->nodes);
  126. INIT_LIST_HEAD(&rmm->free);
  127. list_add(&heap->nl_entry, &rmm->nodes);
  128. list_add(&heap->fl_entry, &rmm->free);
  129. *prmm = rmm;
  130. return 0;
  131. }
  132. int
  133. nouveau_mm_fini(struct nouveau_mm **prmm)
  134. {
  135. struct nouveau_mm *rmm = *prmm;
  136. struct nouveau_mm_node *node, *heap =
  137. list_first_entry(&rmm->nodes, struct nouveau_mm_node, nl_entry);
  138. if (!list_is_singular(&rmm->nodes)) {
  139. printk(KERN_ERR "nouveau_mm not empty at destroy time!\n");
  140. list_for_each_entry(node, &rmm->nodes, nl_entry) {
  141. printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n",
  142. node->type, node->offset, node->length);
  143. }
  144. WARN_ON(1);
  145. return -EBUSY;
  146. }
  147. kfree(heap);
  148. kfree(rmm);
  149. *prmm = NULL;
  150. return 0;
  151. }