object.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /*
  2. * Copyright (c) 2017 Richard Braun.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. *
  18. * This implementation is based on the paper "A lockless pagecache in Linux"
  19. * by Nick Piggin. It allows looking up pages without contention on VM objects.
  20. */
  21. #include <assert.h>
  22. #include <errno.h>
  23. #include <stddef.h>
  24. #include <stdint.h>
  25. #include <kern/init.h>
  26. #include <kern/kmem.h>
  27. #include <kern/mutex.h>
  28. #include <kern/rcu.h>
  29. #include <kern/rdxtree.h>
  30. #include <vm/object.h>
  31. #include <vm/page.h>
  32. #include <machine/page.h>
  33. struct vm_object vm_object_kernel_object;
  34. static struct kmem_cache vm_object_cache;
  35. static int __init
  36. vm_object_bootstrap (void)
  37. {
  38. return (0);
  39. }
  40. INIT_OP_DEFINE (vm_object_bootstrap,
  41. INIT_OP_DEP (mutex_setup, true),
  42. INIT_OP_DEP (rdxtree_setup, true),
  43. INIT_OP_DEP (vm_page_setup, true));
  44. static int __init
  45. vm_object_setup (void)
  46. {
  47. kmem_cache_init (&vm_object_cache, "vm_object",
  48. sizeof (struct vm_object), 0, NULL, 0);
  49. return (0);
  50. }
  51. INIT_OP_DEFINE (vm_object_setup,
  52. INIT_OP_DEP (kmem_setup, true));
  53. void __init
  54. vm_object_init (struct vm_object *object, uint32_t flags, const void *ctx)
  55. {
  56. mutex_init (&object->lock);
  57. rdxtree_init (&object->pages, RDXTREE_ALLOC_SLEEP);
  58. object->nr_pages = 0;
  59. object->refcount = 1;
  60. if (flags & VM_OBJECT_EXTERNAL)
  61. // XXX: Maybe acquire a reference here ?
  62. object->capability = (void *)ctx;
  63. else
  64. object->pager = ctx;
  65. object->flags = flags;
  66. }
  67. int
  68. vm_object_create (struct vm_object **objp, uint32_t flags, const void *ctx)
  69. {
  70. struct vm_object *ret = kmem_cache_alloc (&vm_object_cache);
  71. if (! ret)
  72. return (ENOMEM);
  73. vm_object_init (ret, flags, ctx);
  74. *objp = ret;
  75. return (0);
  76. }
  77. void
  78. vm_object_destroy (struct vm_object *object)
  79. {
  80. assert (object->nr_pages == 0);
  81. rdxtree_remove_all (&object->pages);
  82. kmem_cache_free (&vm_object_cache, object);
  83. }
  84. int
  85. vm_object_swap (struct vm_object *object, struct vm_page *page,
  86. uint64_t offset, struct vm_page *expected)
  87. {
  88. assert (vm_page_aligned (offset));
  89. /*
  90. * The page may have no references. Add one before publishing
  91. * so that concurrent lookups succeed.
  92. */
  93. vm_page_ref (page);
  94. mutex_lock (&object->lock);
  95. struct vm_page *prev = NULL;
  96. void **slot;
  97. int error = rdxtree_insert_slot (&object->pages, vm_page_btop (offset),
  98. page, &slot);
  99. if (error)
  100. {
  101. if (error != EBUSY || atomic_load_rlx (slot) != expected)
  102. goto skip;
  103. /*
  104. * Replace the page slot. Also, if this is the page's last
  105. * reference, free it after the critical section.
  106. */
  107. prev = rdxtree_replace_slot (slot, page);
  108. if (!vm_page_unref_nofree (prev))
  109. prev = NULL;
  110. }
  111. vm_page_link (page, object, offset);
  112. ++object->nr_pages;
  113. assert (object->nr_pages != 0);
  114. vm_object_ref (object);
  115. mutex_unlock (&object->lock);
  116. if (prev)
  117. vm_page_free (prev, 0, VM_PAGE_SLEEP);
  118. return (0);
  119. skip:
  120. mutex_unlock (&object->lock);
  121. vm_page_unref (page);
  122. return (error);
  123. }
  124. void
  125. vm_object_remove (struct vm_object *object, uint64_t start, uint64_t end)
  126. {
  127. assert (vm_page_aligned (start));
  128. assert (vm_page_aligned (end));
  129. assert (start <= end);
  130. struct list pages;
  131. list_init (&pages);
  132. size_t cnt = 0;
  133. {
  134. MUTEX_GUARD (&object->lock);
  135. for (uint64_t offset = start; offset < end; offset += PAGE_SIZE)
  136. {
  137. void *node;
  138. int idx;
  139. void **slot = rdxtree_lookup_common (&object->pages,
  140. vm_page_btop (offset), true,
  141. &node, &idx);
  142. if (! slot)
  143. continue;
  144. struct vm_page *page = atomic_load_rlx (slot);
  145. if (!vm_page_unref_nofree (page))
  146. continue;
  147. rdxtree_remove_node_idx (&object->pages, slot, node, idx);
  148. vm_page_unlink (page);
  149. list_insert_tail (&pages, &page->node);
  150. assert (object->nr_pages != 0);
  151. ++cnt;
  152. }
  153. object->nr_pages -= cnt;
  154. }
  155. vm_object_unref_many (object, cnt);
  156. vm_page_list_free (&pages);
  157. }
  158. void
  159. vm_object_detach (struct vm_object *object, struct vm_page *page)
  160. {
  161. MUTEX_GUARD (&object->lock);
  162. void *node;
  163. int idx;
  164. void **slot = rdxtree_lookup_common (&object->pages,
  165. vm_page_btop (page->offset), true,
  166. &node, &idx);
  167. if (!slot || atomic_load_rlx (slot) != page)
  168. return;
  169. rdxtree_remove_node_idx (&object->pages, slot, node, idx);
  170. --object->nr_pages;
  171. vm_object_unref (object);
  172. }
  173. struct vm_page*
  174. vm_object_lookup (struct vm_object *object, uint64_t offset)
  175. {
  176. RCU_GUARD ();
  177. while (1)
  178. {
  179. struct vm_page *page = rdxtree_lookup (&object->pages,
  180. vm_page_btop (offset));
  181. if (!page || vm_page_tryref (page) == 0)
  182. return (page);
  183. }
  184. }
  185. static int
  186. vm_object_anon_pager_get (struct vm_object *ap __unused, uint64_t off __unused,
  187. size_t size, int prot __unused, void *dst)
  188. {
  189. memset (dst, 0, size);
  190. return (size >> PAGE_SHIFT);
  191. }
  192. static const struct vm_object_pager vm_object_anon_pager =
  193. {
  194. .get = vm_object_anon_pager_get
  195. };
  196. int
  197. vm_object_anon_create (struct vm_object **outp)
  198. {
  199. return (vm_object_create (outp, 0, &vm_object_anon_pager));
  200. }