summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2022-03-11 17:14:17 -0800
committerGravatar bunnei2022-03-14 18:14:54 -0700
commit813b2ef2530433c95abca97254c5b614f3d8c615 (patch)
tree270838870905acc327b9fad6635aca4eb5900b61
parentcore: hle: kernel: k_page_table: Add implementations of MapPages, UnmapPages,... (diff)
downloadyuzu-813b2ef2530433c95abca97254c5b614f3d8c615.tar.gz
yuzu-813b2ef2530433c95abca97254c5b614f3d8c615.tar.xz
yuzu-813b2ef2530433c95abca97254c5b614f3d8c615.zip
core: hle: kernel: k_process: Implement thread local storage accurately.
Diffstat (limited to '')
-rw-r--r--src/core/hle/kernel/k_process.cpp188
-rw-r--r--src/core/hle/kernel/k_process.h18
-rw-r--r--src/core/hle/kernel/k_thread.cpp4
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).
78class TLSPage {
79public:
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
116private:
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
125ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, 73ResultCode 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/** 438ResultCode 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 */
498static 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
503VAddr 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
534void KProcess::FreeTLSRegion(VAddr tls_address) { 489ResultCode 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
549void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { 537void 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
370private: 371private:
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.