diff options
Diffstat (limited to '')
| -rw-r--r-- | src/core/hle/kernel/k_process.cpp | 188 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_process.h | 18 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread.cpp | 4 |
3 files changed, 99 insertions, 111 deletions
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 9233261cd..60dc9a048 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -70,58 +70,6 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority | |||
| 70 | } | 70 | } |
| 71 | } // Anonymous namespace | 71 | } // Anonymous namespace |
| 72 | 72 | ||
| 73 | // Represents a page used for thread-local storage. | ||
| 74 | // | ||
| 75 | // Each TLS page contains slots that may be used by processes and threads. | ||
| 76 | // Every process and thread is created with a slot in some arbitrary page | ||
| 77 | // (whichever page happens to have an available slot). | ||
| 78 | class TLSPage { | ||
| 79 | public: | ||
| 80 | static constexpr std::size_t num_slot_entries = | ||
| 81 | Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE; | ||
| 82 | |||
| 83 | explicit TLSPage(VAddr address) : base_address{address} {} | ||
| 84 | |||
| 85 | bool HasAvailableSlots() const { | ||
| 86 | return !is_slot_used.all(); | ||
| 87 | } | ||
| 88 | |||
| 89 | VAddr GetBaseAddress() const { | ||
| 90 | return base_address; | ||
| 91 | } | ||
| 92 | |||
| 93 | std::optional<VAddr> ReserveSlot() { | ||
| 94 | for (std::size_t i = 0; i < is_slot_used.size(); i++) { | ||
| 95 | if (is_slot_used[i]) { | ||
| 96 | continue; | ||
| 97 | } | ||
| 98 | |||
| 99 | is_slot_used[i] = true; | ||
| 100 | return base_address + (i * Core::Memory::TLS_ENTRY_SIZE); | ||
| 101 | } | ||
| 102 | |||
| 103 | return std::nullopt; | ||
| 104 | } | ||
| 105 | |||
| 106 | void ReleaseSlot(VAddr address) { | ||
| 107 | // Ensure that all given addresses are consistent with how TLS pages | ||
| 108 | // are intended to be used when releasing slots. | ||
| 109 | ASSERT(IsWithinPage(address)); | ||
| 110 | ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0); | ||
| 111 | |||
| 112 | const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE; | ||
| 113 | is_slot_used[index] = false; | ||
| 114 | } | ||
| 115 | |||
| 116 | private: | ||
| 117 | bool IsWithinPage(VAddr address) const { | ||
| 118 | return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE; | ||
| 119 | } | ||
| 120 | |||
| 121 | VAddr base_address; | ||
| 122 | std::bitset<num_slot_entries> is_slot_used; | ||
| 123 | }; | ||
| 124 | |||
| 125 | ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, | 73 | ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, |
| 126 | ProcessType type, KResourceLimit* res_limit) { | 74 | ProcessType type, KResourceLimit* res_limit) { |
| 127 | auto& kernel = system.Kernel(); | 75 | auto& kernel = system.Kernel(); |
| @@ -404,7 +352,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, | |||
| 404 | } | 352 | } |
| 405 | 353 | ||
| 406 | // Create TLS region | 354 | // Create TLS region |
| 407 | tls_region_address = CreateTLSRegion(); | 355 | R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address))); |
| 408 | memory_reservation.Commit(); | 356 | memory_reservation.Commit(); |
| 409 | 357 | ||
| 410 | return handle_table.Initialize(capabilities.GetHandleTableSize()); | 358 | return handle_table.Initialize(capabilities.GetHandleTableSize()); |
| @@ -444,7 +392,7 @@ void KProcess::PrepareForTermination() { | |||
| 444 | 392 | ||
| 445 | stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); | 393 | stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); |
| 446 | 394 | ||
| 447 | FreeTLSRegion(tls_region_address); | 395 | this->DeleteThreadLocalRegion(tls_region_address); |
| 448 | tls_region_address = 0; | 396 | tls_region_address = 0; |
| 449 | 397 | ||
| 450 | if (resource_limit) { | 398 | if (resource_limit) { |
| @@ -487,63 +435,103 @@ void KProcess::Finalize() { | |||
| 487 | KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); | 435 | KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); |
| 488 | } | 436 | } |
| 489 | 437 | ||
| 490 | /** | 438 | ResultCode KProcess::CreateThreadLocalRegion(VAddr* out) { |
| 491 | * Attempts to find a TLS page that contains a free slot for | 439 | KThreadLocalPage* tlp = nullptr; |
| 492 | * use by a thread. | 440 | VAddr tlr = 0; |
| 493 | * | ||
| 494 | * @returns If a page with an available slot is found, then an iterator | ||
| 495 | * pointing to the page is returned. Otherwise the end iterator | ||
| 496 | * is returned instead. | ||
| 497 | */ | ||
| 498 | static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) { | ||
| 499 | return std::find_if(tls_pages.begin(), tls_pages.end(), | ||
| 500 | [](const auto& page) { return page.HasAvailableSlots(); }); | ||
| 501 | } | ||
| 502 | 441 | ||
| 503 | VAddr KProcess::CreateTLSRegion() { | 442 | // See if we can get a region from a partially used TLP. |
| 504 | KScopedSchedulerLock lock(kernel); | 443 | { |
| 505 | if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; | 444 | KScopedSchedulerLock sl{kernel}; |
| 506 | tls_page_iter != tls_pages.cend()) { | 445 | |
| 507 | return *tls_page_iter->ReserveSlot(); | 446 | if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) { |
| 508 | } | 447 | tlr = it->Reserve(); |
| 448 | ASSERT(tlr != 0); | ||
| 449 | |||
| 450 | if (it->IsAllUsed()) { | ||
| 451 | tlp = std::addressof(*it); | ||
| 452 | partially_used_tlp_tree.erase(it); | ||
| 453 | fully_used_tlp_tree.insert(*tlp); | ||
| 454 | } | ||
| 509 | 455 | ||
| 510 | Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()}; | 456 | *out = tlr; |
| 511 | ASSERT(tls_page_ptr); | 457 | return ResultSuccess; |
| 458 | } | ||
| 459 | } | ||
| 512 | 460 | ||
| 513 | const VAddr start{page_table->GetKernelMapRegionStart()}; | 461 | // Allocate a new page. |
| 514 | const VAddr size{page_table->GetKernelMapRegionEnd() - start}; | 462 | tlp = KThreadLocalPage::Allocate(kernel); |
| 515 | const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)}; | 463 | R_UNLESS(tlp != nullptr, ResultOutOfMemory); |
| 516 | const VAddr tls_page_addr{page_table | 464 | auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); }); |
| 517 | ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize, | ||
| 518 | KMemoryState::ThreadLocal, | ||
| 519 | KMemoryPermission::UserReadWrite, | ||
| 520 | tls_map_addr) | ||
| 521 | .ValueOr(0)}; | ||
| 522 | 465 | ||
| 523 | ASSERT(tls_page_addr); | 466 | // Initialize the new page. |
| 467 | R_TRY(tlp->Initialize(kernel, this)); | ||
| 524 | 468 | ||
| 525 | std::memset(tls_page_ptr, 0, PageSize); | 469 | // Reserve a TLR. |
| 526 | tls_pages.emplace_back(tls_page_addr); | 470 | tlr = tlp->Reserve(); |
| 471 | ASSERT(tlr != 0); | ||
| 527 | 472 | ||
| 528 | const auto reserve_result{tls_pages.back().ReserveSlot()}; | 473 | // Insert into our tree. |
| 529 | ASSERT(reserve_result.has_value()); | 474 | { |
| 475 | KScopedSchedulerLock sl{kernel}; | ||
| 476 | if (tlp->IsAllUsed()) { | ||
| 477 | fully_used_tlp_tree.insert(*tlp); | ||
| 478 | } else { | ||
| 479 | partially_used_tlp_tree.insert(*tlp); | ||
| 480 | } | ||
| 481 | } | ||
| 530 | 482 | ||
| 531 | return *reserve_result; | 483 | // We succeeded! |
| 484 | tlp_guard.Cancel(); | ||
| 485 | *out = tlr; | ||
| 486 | return ResultSuccess; | ||
| 532 | } | 487 | } |
| 533 | 488 | ||
| 534 | void KProcess::FreeTLSRegion(VAddr tls_address) { | 489 | ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) { |
| 535 | KScopedSchedulerLock lock(kernel); | 490 | KThreadLocalPage* page_to_free = nullptr; |
| 536 | const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); | 491 | |
| 537 | auto iter = | 492 | // Release the region. |
| 538 | std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { | 493 | { |
| 539 | return page.GetBaseAddress() == aligned_address; | 494 | KScopedSchedulerLock sl{kernel}; |
| 540 | }); | 495 | |
| 496 | // Try to find the page in the partially used list. | ||
| 497 | auto it = partially_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize)); | ||
| 498 | if (it == partially_used_tlp_tree.end()) { | ||
| 499 | // If we don't find it, it has to be in the fully used list. | ||
| 500 | it = fully_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize)); | ||
| 501 | R_UNLESS(it != fully_used_tlp_tree.end(), ResultInvalidAddress); | ||
| 502 | |||
| 503 | // Release the region. | ||
| 504 | it->Release(addr); | ||
| 505 | |||
| 506 | // Move the page out of the fully used list. | ||
| 507 | KThreadLocalPage* tlp = std::addressof(*it); | ||
| 508 | fully_used_tlp_tree.erase(it); | ||
| 509 | if (tlp->IsAllFree()) { | ||
| 510 | page_to_free = tlp; | ||
| 511 | } else { | ||
| 512 | partially_used_tlp_tree.insert(*tlp); | ||
| 513 | } | ||
| 514 | } else { | ||
| 515 | // Release the region. | ||
| 516 | it->Release(addr); | ||
| 517 | |||
| 518 | // Handle the all-free case. | ||
| 519 | KThreadLocalPage* tlp = std::addressof(*it); | ||
| 520 | if (tlp->IsAllFree()) { | ||
| 521 | partially_used_tlp_tree.erase(it); | ||
| 522 | page_to_free = tlp; | ||
| 523 | } | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | // If we should free the page it was in, do so. | ||
| 528 | if (page_to_free != nullptr) { | ||
| 529 | page_to_free->Finalize(); | ||
| 541 | 530 | ||
| 542 | // Something has gone very wrong if we're freeing a region | 531 | KThreadLocalPage::Free(kernel, page_to_free); |
| 543 | // with no actual page available. | 532 | } |
| 544 | ASSERT(iter != tls_pages.cend()); | ||
| 545 | 533 | ||
| 546 | iter->ReleaseSlot(tls_address); | 534 | return ResultSuccess; |
| 547 | } | 535 | } |
| 548 | 536 | ||
| 549 | void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { | 537 | void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { |
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index cf1b67428..5ed0f2d83 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/hle/kernel/k_condition_variable.h" | 15 | #include "core/hle/kernel/k_condition_variable.h" |
| 16 | #include "core/hle/kernel/k_handle_table.h" | 16 | #include "core/hle/kernel/k_handle_table.h" |
| 17 | #include "core/hle/kernel/k_synchronization_object.h" | 17 | #include "core/hle/kernel/k_synchronization_object.h" |
| 18 | #include "core/hle/kernel/k_thread_local_page.h" | ||
| 18 | #include "core/hle/kernel/k_worker_task.h" | 19 | #include "core/hle/kernel/k_worker_task.h" |
| 19 | #include "core/hle/kernel/process_capability.h" | 20 | #include "core/hle/kernel/process_capability.h" |
| 20 | #include "core/hle/kernel/slab_helpers.h" | 21 | #include "core/hle/kernel/slab_helpers.h" |
| @@ -362,10 +363,10 @@ public: | |||
| 362 | // Thread-local storage management | 363 | // Thread-local storage management |
| 363 | 364 | ||
| 364 | // Marks the next available region as used and returns the address of the slot. | 365 | // Marks the next available region as used and returns the address of the slot. |
| 365 | [[nodiscard]] VAddr CreateTLSRegion(); | 366 | [[nodiscard]] ResultCode CreateThreadLocalRegion(VAddr* out); |
| 366 | 367 | ||
| 367 | // Frees a used TLS slot identified by the given address | 368 | // Frees a used TLS slot identified by the given address |
| 368 | void FreeTLSRegion(VAddr tls_address); | 369 | ResultCode DeleteThreadLocalRegion(VAddr addr); |
| 369 | 370 | ||
| 370 | private: | 371 | private: |
| 371 | void PinThread(s32 core_id, KThread* thread) { | 372 | void PinThread(s32 core_id, KThread* thread) { |
| @@ -413,13 +414,6 @@ private: | |||
| 413 | /// The ideal CPU core for this process, threads are scheduled on this core by default. | 414 | /// The ideal CPU core for this process, threads are scheduled on this core by default. |
| 414 | u8 ideal_core = 0; | 415 | u8 ideal_core = 0; |
| 415 | 416 | ||
| 416 | /// The Thread Local Storage area is allocated as processes create threads, | ||
| 417 | /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part | ||
| 418 | /// holds the TLS for a specific thread. This vector contains which parts are in use for each | ||
| 419 | /// page as a bitmask. | ||
| 420 | /// This vector will grow as more pages are allocated for new threads. | ||
| 421 | std::vector<TLSPage> tls_pages; | ||
| 422 | |||
| 423 | /// Contains the parsed process capability descriptors. | 417 | /// Contains the parsed process capability descriptors. |
| 424 | ProcessCapabilities capabilities; | 418 | ProcessCapabilities capabilities; |
| 425 | 419 | ||
| @@ -482,6 +476,12 @@ private: | |||
| 482 | KThread* exception_thread{}; | 476 | KThread* exception_thread{}; |
| 483 | 477 | ||
| 484 | KLightLock state_lock; | 478 | KLightLock state_lock; |
| 479 | |||
| 480 | using TLPTree = | ||
| 481 | Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>; | ||
| 482 | using TLPIterator = TLPTree::iterator; | ||
| 483 | TLPTree fully_used_tlp_tree; | ||
| 484 | TLPTree partially_used_tlp_tree; | ||
| 485 | }; | 485 | }; |
| 486 | 486 | ||
| 487 | } // namespace Kernel | 487 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 736f39e91..ba7f72c6b 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -210,7 +210,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s | |||
| 210 | if (owner != nullptr) { | 210 | if (owner != nullptr) { |
| 211 | // Setup the TLS, if needed. | 211 | // Setup the TLS, if needed. |
| 212 | if (type == ThreadType::User) { | 212 | if (type == ThreadType::User) { |
| 213 | tls_address = owner->CreateTLSRegion(); | 213 | R_TRY(owner->CreateThreadLocalRegion(std::addressof(tls_address))); |
| 214 | } | 214 | } |
| 215 | 215 | ||
| 216 | parent = owner; | 216 | parent = owner; |
| @@ -305,7 +305,7 @@ void KThread::Finalize() { | |||
| 305 | 305 | ||
| 306 | // If the thread has a local region, delete it. | 306 | // If the thread has a local region, delete it. |
| 307 | if (tls_address != 0) { | 307 | if (tls_address != 0) { |
| 308 | parent->FreeTLSRegion(tls_address); | 308 | ASSERT(parent->DeleteThreadLocalRegion(tls_address).IsSuccess()); |
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | // Release any waiters. | 311 | // Release any waiters. |