AddressSpace.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. /*
  2. * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <Kernel/Locking/Spinlock.h>
  8. #include <Kernel/Memory/AddressSpace.h>
  9. #include <Kernel/Memory/AnonymousVMObject.h>
  10. #include <Kernel/Memory/InodeVMObject.h>
  11. #include <Kernel/Memory/MemoryManager.h>
  12. #include <Kernel/PerformanceManager.h>
  13. #include <Kernel/Process.h>
  14. #include <Kernel/Scheduler.h>
  15. namespace Kernel::Memory {
  16. ErrorOr<NonnullOwnPtr<AddressSpace>> AddressSpace::try_create(AddressSpace const* parent)
  17. {
  18. auto page_directory = TRY(PageDirectory::try_create_for_userspace(parent ? &parent->page_directory().range_allocator() : nullptr));
  19. auto space = TRY(adopt_nonnull_own_or_enomem(new (nothrow) AddressSpace(page_directory)));
  20. space->page_directory().set_space({}, *space);
  21. return space;
  22. }
  23. AddressSpace::AddressSpace(NonnullRefPtr<PageDirectory> page_directory)
  24. : m_page_directory(move(page_directory))
  25. {
  26. }
  27. AddressSpace::~AddressSpace()
  28. {
  29. }
  30. ErrorOr<void> AddressSpace::unmap_mmap_range(VirtualAddress addr, size_t size)
  31. {
  32. if (!size)
  33. return EINVAL;
  34. auto range_to_unmap = TRY(VirtualRange::expand_to_page_boundaries(addr.get(), size));
  35. if (!is_user_range(range_to_unmap))
  36. return EFAULT;
  37. if (auto* whole_region = find_region_from_range(range_to_unmap)) {
  38. if (!whole_region->is_mmap())
  39. return EPERM;
  40. PerformanceManager::add_unmap_perf_event(Process::current(), whole_region->range());
  41. deallocate_region(*whole_region);
  42. return {};
  43. }
  44. if (auto* old_region = find_region_containing(range_to_unmap)) {
  45. if (!old_region->is_mmap())
  46. return EPERM;
  47. // Remove the old region from our regions tree, since were going to add another region
  48. // with the exact same start address, but don't deallocate it yet.
  49. auto region = take_region(*old_region);
  50. // We manually unmap the old region here, specifying that we *don't* want the VM deallocated.
  51. region->unmap(Region::ShouldDeallocateVirtualRange::No);
  52. auto new_regions = TRY(try_split_region_around_range(*region, range_to_unmap));
  53. // Instead we give back the unwanted VM manually.
  54. page_directory().range_allocator().deallocate(range_to_unmap);
  55. // And finally we map the new region(s) using our page directory (they were just allocated and don't have one).
  56. for (auto* new_region : new_regions) {
  57. // TODO: Ideally we should do this in a way that can be rolled back on failure, as failing here
  58. // leaves the caller in an undefined state.
  59. TRY(new_region->map(page_directory()));
  60. }
  61. PerformanceManager::add_unmap_perf_event(Process::current(), range_to_unmap);
  62. return {};
  63. }
  64. // Try again while checking multiple regions at a time.
  65. auto const& regions = TRY(find_regions_intersecting(range_to_unmap));
  66. if (regions.is_empty())
  67. return {};
  68. // Check if any of the regions is not mmap'ed, to not accidentally
  69. // error out with just half a region map left.
  70. for (auto* region : regions) {
  71. if (!region->is_mmap())
  72. return EPERM;
  73. }
  74. Vector<Region*, 2> new_regions;
  75. for (auto* old_region : regions) {
  76. // If it's a full match we can remove the entire old region.
  77. if (old_region->range().intersect(range_to_unmap).size() == old_region->size()) {
  78. deallocate_region(*old_region);
  79. continue;
  80. }
  81. // Remove the old region from our regions tree, since were going to add another region
  82. // with the exact same start address, but don't deallocate it yet.
  83. auto region = take_region(*old_region);
  84. // We manually unmap the old region here, specifying that we *don't* want the VM deallocated.
  85. region->unmap(Region::ShouldDeallocateVirtualRange::No);
  86. // Otherwise, split the regions and collect them for future mapping.
  87. auto split_regions = TRY(try_split_region_around_range(*region, range_to_unmap));
  88. TRY(new_regions.try_extend(split_regions));
  89. }
  90. // Give back any unwanted VM to the range allocator.
  91. page_directory().range_allocator().deallocate(range_to_unmap);
  92. // And finally map the new region(s) into our page directory.
  93. for (auto* new_region : new_regions) {
  94. // TODO: Ideally we should do this in a way that can be rolled back on failure, as failing here
  95. // leaves the caller in an undefined state.
  96. TRY(new_region->map(page_directory()));
  97. }
  98. PerformanceManager::add_unmap_perf_event(Process::current(), range_to_unmap);
  99. return {};
  100. }
  101. ErrorOr<VirtualRange> AddressSpace::try_allocate_range(VirtualAddress vaddr, size_t size, size_t alignment)
  102. {
  103. vaddr.mask(PAGE_MASK);
  104. size = TRY(page_round_up(size));
  105. if (vaddr.is_null())
  106. return page_directory().range_allocator().try_allocate_anywhere(size, alignment);
  107. return page_directory().range_allocator().try_allocate_specific(vaddr, size);
  108. }
  109. ErrorOr<Region*> AddressSpace::try_allocate_split_region(Region const& source_region, VirtualRange const& range, size_t offset_in_vmobject)
  110. {
  111. OwnPtr<KString> region_name;
  112. if (!source_region.name().is_null())
  113. region_name = TRY(KString::try_create(source_region.name()));
  114. auto new_region = TRY(Region::try_create_user_accessible(
  115. range, source_region.vmobject(), offset_in_vmobject, move(region_name), source_region.access(), source_region.is_cacheable() ? Region::Cacheable::Yes : Region::Cacheable::No, source_region.is_shared()));
  116. auto* region = TRY(add_region(move(new_region)));
  117. region->set_syscall_region(source_region.is_syscall_region());
  118. region->set_mmap(source_region.is_mmap());
  119. region->set_stack(source_region.is_stack());
  120. size_t page_offset_in_source_region = (offset_in_vmobject - source_region.offset_in_vmobject()) / PAGE_SIZE;
  121. for (size_t i = 0; i < region->page_count(); ++i) {
  122. if (source_region.should_cow(page_offset_in_source_region + i))
  123. region->set_should_cow(i, true);
  124. }
  125. return region;
  126. }
  127. ErrorOr<Region*> AddressSpace::allocate_region(VirtualRange const& range, StringView name, int prot, AllocationStrategy strategy)
  128. {
  129. VERIFY(range.is_valid());
  130. OwnPtr<KString> region_name;
  131. if (!name.is_null())
  132. region_name = TRY(KString::try_create(name));
  133. auto vmobject = TRY(AnonymousVMObject::try_create_with_size(range.size(), strategy));
  134. auto region = TRY(Region::try_create_user_accessible(range, move(vmobject), 0, move(region_name), prot_to_region_access_flags(prot), Region::Cacheable::Yes, false));
  135. TRY(region->map(page_directory(), ShouldFlushTLB::No));
  136. return add_region(move(region));
  137. }
  138. ErrorOr<Region*> AddressSpace::allocate_region_with_vmobject(VirtualRange const& range, NonnullRefPtr<VMObject> vmobject, size_t offset_in_vmobject, StringView name, int prot, bool shared)
  139. {
  140. VERIFY(range.is_valid());
  141. size_t end_in_vmobject = offset_in_vmobject + range.size();
  142. if (end_in_vmobject <= offset_in_vmobject) {
  143. dbgln("allocate_region_with_vmobject: Overflow (offset + size)");
  144. return EINVAL;
  145. }
  146. if (offset_in_vmobject >= vmobject->size()) {
  147. dbgln("allocate_region_with_vmobject: Attempt to allocate a region with an offset past the end of its VMObject.");
  148. return EINVAL;
  149. }
  150. if (end_in_vmobject > vmobject->size()) {
  151. dbgln("allocate_region_with_vmobject: Attempt to allocate a region with an end past the end of its VMObject.");
  152. return EINVAL;
  153. }
  154. offset_in_vmobject &= PAGE_MASK;
  155. OwnPtr<KString> region_name;
  156. if (!name.is_null())
  157. region_name = TRY(KString::try_create(name));
  158. auto region = TRY(Region::try_create_user_accessible(range, move(vmobject), offset_in_vmobject, move(region_name), prot_to_region_access_flags(prot), Region::Cacheable::Yes, shared));
  159. auto* added_region = TRY(add_region(move(region)));
  160. if (prot == PROT_NONE) {
  161. // For PROT_NONE mappings, we don't have to set up any page table mappings.
  162. // We do still need to attach the region to the page_directory though.
  163. SpinlockLocker mm_locker(s_mm_lock);
  164. added_region->set_page_directory(page_directory());
  165. } else {
  166. TRY(added_region->map(page_directory(), ShouldFlushTLB::No));
  167. }
  168. return added_region;
  169. }
  170. void AddressSpace::deallocate_region(Region& region)
  171. {
  172. (void)take_region(region);
  173. }
  174. NonnullOwnPtr<Region> AddressSpace::take_region(Region& region)
  175. {
  176. SpinlockLocker lock(m_lock);
  177. auto found_region = m_regions.unsafe_remove(region.vaddr().get());
  178. VERIFY(found_region.ptr() == &region);
  179. return found_region;
  180. }
  181. Region* AddressSpace::find_region_from_range(VirtualRange const& range)
  182. {
  183. SpinlockLocker lock(m_lock);
  184. auto* found_region = m_regions.find(range.base().get());
  185. if (!found_region)
  186. return nullptr;
  187. auto& region = *found_region;
  188. auto rounded_range_size = page_round_up(range.size());
  189. if (rounded_range_size.is_error() || region->size() != rounded_range_size.value())
  190. return nullptr;
  191. return region;
  192. }
  193. Region* AddressSpace::find_region_containing(VirtualRange const& range)
  194. {
  195. SpinlockLocker lock(m_lock);
  196. auto* candidate = m_regions.find_largest_not_above(range.base().get());
  197. if (!candidate)
  198. return nullptr;
  199. return (*candidate)->range().contains(range) ? candidate->ptr() : nullptr;
  200. }
  201. ErrorOr<Vector<Region*>> AddressSpace::find_regions_intersecting(VirtualRange const& range)
  202. {
  203. Vector<Region*> regions = {};
  204. size_t total_size_collected = 0;
  205. SpinlockLocker lock(m_lock);
  206. auto* found_region = m_regions.find_largest_not_above(range.base().get());
  207. if (!found_region)
  208. return regions;
  209. for (auto iter = m_regions.begin_from((*found_region)->vaddr().get()); !iter.is_end(); ++iter) {
  210. const auto& iter_range = (*iter)->range();
  211. if (iter_range.base() < range.end() && iter_range.end() > range.base()) {
  212. TRY(regions.try_append(*iter));
  213. total_size_collected += (*iter)->size() - iter_range.intersect(range).size();
  214. if (total_size_collected == range.size())
  215. break;
  216. }
  217. }
  218. return regions;
  219. }
  220. ErrorOr<Region*> AddressSpace::add_region(NonnullOwnPtr<Region> region)
  221. {
  222. auto* ptr = region.ptr();
  223. SpinlockLocker lock(m_lock);
  224. TRY(m_regions.try_insert(region->vaddr().get(), move(region)));
  225. return ptr;
  226. }
  227. // Carve out a virtual address range from a region and return the two regions on either side
  228. ErrorOr<Vector<Region*, 2>> AddressSpace::try_split_region_around_range(const Region& source_region, VirtualRange const& desired_range)
  229. {
  230. VirtualRange old_region_range = source_region.range();
  231. auto remaining_ranges_after_unmap = old_region_range.carve(desired_range);
  232. VERIFY(!remaining_ranges_after_unmap.is_empty());
  233. auto try_make_replacement_region = [&](VirtualRange const& new_range) -> ErrorOr<Region*> {
  234. VERIFY(old_region_range.contains(new_range));
  235. size_t new_range_offset_in_vmobject = source_region.offset_in_vmobject() + (new_range.base().get() - old_region_range.base().get());
  236. return try_allocate_split_region(source_region, new_range, new_range_offset_in_vmobject);
  237. };
  238. Vector<Region*, 2> new_regions;
  239. for (auto& new_range : remaining_ranges_after_unmap) {
  240. auto* new_region = TRY(try_make_replacement_region(new_range));
  241. new_regions.unchecked_append(new_region);
  242. }
  243. return new_regions;
  244. }
  245. void AddressSpace::dump_regions()
  246. {
  247. dbgln("Process regions:");
  248. #if ARCH(I386)
  249. char const* addr_padding = "";
  250. #else
  251. char const* addr_padding = " ";
  252. #endif
  253. dbgln("BEGIN{} END{} SIZE{} ACCESS NAME",
  254. addr_padding, addr_padding, addr_padding);
  255. SpinlockLocker lock(m_lock);
  256. for (auto const& sorted_region : m_regions) {
  257. auto const& region = *sorted_region;
  258. dbgln("{:p} -- {:p} {:p} {:c}{:c}{:c}{:c}{:c}{:c} {}", region.vaddr().get(), region.vaddr().offset(region.size() - 1).get(), region.size(),
  259. region.is_readable() ? 'R' : ' ',
  260. region.is_writable() ? 'W' : ' ',
  261. region.is_executable() ? 'X' : ' ',
  262. region.is_shared() ? 'S' : ' ',
  263. region.is_stack() ? 'T' : ' ',
  264. region.is_syscall_region() ? 'C' : ' ',
  265. region.name());
  266. }
  267. MM.dump_kernel_regions();
  268. }
  269. void AddressSpace::remove_all_regions(Badge<Process>)
  270. {
  271. VERIFY(Thread::current() == g_finalizer);
  272. SpinlockLocker locker(m_lock);
  273. {
  274. SpinlockLocker pd_locker(m_page_directory->get_lock());
  275. SpinlockLocker mm_locker(s_mm_lock);
  276. for (auto& region : m_regions)
  277. (*region).unmap_with_locks_held(Region::ShouldDeallocateVirtualRange::No, ShouldFlushTLB::No, pd_locker, mm_locker);
  278. }
  279. m_regions.clear();
  280. }
  281. size_t AddressSpace::amount_dirty_private() const
  282. {
  283. SpinlockLocker lock(m_lock);
  284. // FIXME: This gets a bit more complicated for Regions sharing the same underlying VMObject.
  285. // The main issue I'm thinking of is when the VMObject has physical pages that none of the Regions are mapping.
  286. // That's probably a situation that needs to be looked at in general.
  287. size_t amount = 0;
  288. for (auto const& region : m_regions) {
  289. if (!region->is_shared())
  290. amount += region->amount_dirty();
  291. }
  292. return amount;
  293. }
  294. ErrorOr<size_t> AddressSpace::amount_clean_inode() const
  295. {
  296. SpinlockLocker lock(m_lock);
  297. HashTable<const InodeVMObject*> vmobjects;
  298. for (auto const& region : m_regions) {
  299. if (region->vmobject().is_inode())
  300. TRY(vmobjects.try_set(&static_cast<const InodeVMObject&>(region->vmobject())));
  301. }
  302. size_t amount = 0;
  303. for (auto& vmobject : vmobjects)
  304. amount += vmobject->amount_clean();
  305. return amount;
  306. }
  307. size_t AddressSpace::amount_virtual() const
  308. {
  309. SpinlockLocker lock(m_lock);
  310. size_t amount = 0;
  311. for (auto const& region : m_regions) {
  312. amount += region->size();
  313. }
  314. return amount;
  315. }
  316. size_t AddressSpace::amount_resident() const
  317. {
  318. SpinlockLocker lock(m_lock);
  319. // FIXME: This will double count if multiple regions use the same physical page.
  320. size_t amount = 0;
  321. for (auto const& region : m_regions) {
  322. amount += region->amount_resident();
  323. }
  324. return amount;
  325. }
  326. size_t AddressSpace::amount_shared() const
  327. {
  328. SpinlockLocker lock(m_lock);
  329. // FIXME: This will double count if multiple regions use the same physical page.
  330. // FIXME: It doesn't work at the moment, since it relies on PhysicalPage ref counts,
  331. // and each PhysicalPage is only reffed by its VMObject. This needs to be refactored
  332. // so that every Region contributes +1 ref to each of its PhysicalPages.
  333. size_t amount = 0;
  334. for (auto const& region : m_regions) {
  335. amount += region->amount_shared();
  336. }
  337. return amount;
  338. }
  339. size_t AddressSpace::amount_purgeable_volatile() const
  340. {
  341. SpinlockLocker lock(m_lock);
  342. size_t amount = 0;
  343. for (auto const& region : m_regions) {
  344. if (!region->vmobject().is_anonymous())
  345. continue;
  346. auto const& vmobject = static_cast<AnonymousVMObject const&>(region->vmobject());
  347. if (vmobject.is_purgeable() && vmobject.is_volatile())
  348. amount += region->amount_resident();
  349. }
  350. return amount;
  351. }
  352. size_t AddressSpace::amount_purgeable_nonvolatile() const
  353. {
  354. SpinlockLocker lock(m_lock);
  355. size_t amount = 0;
  356. for (auto const& region : m_regions) {
  357. if (!region->vmobject().is_anonymous())
  358. continue;
  359. auto const& vmobject = static_cast<AnonymousVMObject const&>(region->vmobject());
  360. if (vmobject.is_purgeable() && !vmobject.is_volatile())
  361. amount += region->amount_resident();
  362. }
  363. return amount;
  364. }
  365. }