|
@@ -70,58 +70,6 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
|
|
|
}
|
|
}
|
|
|
} // Anonymous namespace
|
|
} // Anonymous namespace
|
|
|
|
|
|
|
|
-// Represents a page used for thread-local storage.
|
|
|
|
|
-//
|
|
|
|
|
-// Each TLS page contains slots that may be used by processes and threads.
|
|
|
|
|
-// Every process and thread is created with a slot in some arbitrary page
|
|
|
|
|
-// (whichever page happens to have an available slot).
|
|
|
|
|
-class TLSPage {
|
|
|
|
|
-public:
|
|
|
|
|
- static constexpr std::size_t num_slot_entries =
|
|
|
|
|
- Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE;
|
|
|
|
|
-
|
|
|
|
|
- explicit TLSPage(VAddr address) : base_address{address} {}
|
|
|
|
|
-
|
|
|
|
|
- bool HasAvailableSlots() const {
|
|
|
|
|
- return !is_slot_used.all();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- VAddr GetBaseAddress() const {
|
|
|
|
|
- return base_address;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- std::optional<VAddr> ReserveSlot() {
|
|
|
|
|
- for (std::size_t i = 0; i < is_slot_used.size(); i++) {
|
|
|
|
|
- if (is_slot_used[i]) {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- is_slot_used[i] = true;
|
|
|
|
|
- return base_address + (i * Core::Memory::TLS_ENTRY_SIZE);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return std::nullopt;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- void ReleaseSlot(VAddr address) {
|
|
|
|
|
- // Ensure that all given addresses are consistent with how TLS pages
|
|
|
|
|
- // are intended to be used when releasing slots.
|
|
|
|
|
- ASSERT(IsWithinPage(address));
|
|
|
|
|
- ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0);
|
|
|
|
|
-
|
|
|
|
|
- const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE;
|
|
|
|
|
- is_slot_used[index] = false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-private:
|
|
|
|
|
- bool IsWithinPage(VAddr address) const {
|
|
|
|
|
- return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- VAddr base_address;
|
|
|
|
|
- std::bitset<num_slot_entries> is_slot_used;
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
|
|
ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
|
|
|
ProcessType type, KResourceLimit* res_limit) {
|
|
ProcessType type, KResourceLimit* res_limit) {
|
|
|
auto& kernel = system.Kernel();
|
|
auto& kernel = system.Kernel();
|
|
@@ -404,7 +352,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Create TLS region
|
|
// Create TLS region
|
|
|
- tls_region_address = CreateTLSRegion();
|
|
|
|
|
|
|
+ R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address)));
|
|
|
memory_reservation.Commit();
|
|
memory_reservation.Commit();
|
|
|
|
|
|
|
|
return handle_table.Initialize(capabilities.GetHandleTableSize());
|
|
return handle_table.Initialize(capabilities.GetHandleTableSize());
|
|
@@ -444,7 +392,7 @@ void KProcess::PrepareForTermination() {
|
|
|
|
|
|
|
|
stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList());
|
|
stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList());
|
|
|
|
|
|
|
|
- FreeTLSRegion(tls_region_address);
|
|
|
|
|
|
|
+ this->DeleteThreadLocalRegion(tls_region_address);
|
|
|
tls_region_address = 0;
|
|
tls_region_address = 0;
|
|
|
|
|
|
|
|
if (resource_limit) {
|
|
if (resource_limit) {
|
|
@@ -487,63 +435,103 @@ void KProcess::Finalize() {
|
|
|
KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
|
|
KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/**
|
|
|
|
|
- * Attempts to find a TLS page that contains a free slot for
|
|
|
|
|
- * use by a thread.
|
|
|
|
|
- *
|
|
|
|
|
- * @returns If a page with an available slot is found, then an iterator
|
|
|
|
|
- * pointing to the page is returned. Otherwise the end iterator
|
|
|
|
|
- * is returned instead.
|
|
|
|
|
- */
|
|
|
|
|
-static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
|
|
|
|
|
- return std::find_if(tls_pages.begin(), tls_pages.end(),
|
|
|
|
|
- [](const auto& page) { return page.HasAvailableSlots(); });
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ResultCode KProcess::CreateThreadLocalRegion(VAddr* out) {
|
|
|
|
|
+ KThreadLocalPage* tlp = nullptr;
|
|
|
|
|
+ VAddr tlr = 0;
|
|
|
|
|
|
|
|
-VAddr KProcess::CreateTLSRegion() {
|
|
|
|
|
- KScopedSchedulerLock lock(kernel);
|
|
|
|
|
- if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)};
|
|
|
|
|
- tls_page_iter != tls_pages.cend()) {
|
|
|
|
|
- return *tls_page_iter->ReserveSlot();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // See if we can get a region from a partially used TLP.
|
|
|
|
|
+ {
|
|
|
|
|
+ KScopedSchedulerLock sl{kernel};
|
|
|
|
|
+
|
|
|
|
|
+ if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) {
|
|
|
|
|
+ tlr = it->Reserve();
|
|
|
|
|
+ ASSERT(tlr != 0);
|
|
|
|
|
+
|
|
|
|
|
+ if (it->IsAllUsed()) {
|
|
|
|
|
+ tlp = std::addressof(*it);
|
|
|
|
|
+ partially_used_tlp_tree.erase(it);
|
|
|
|
|
+ fully_used_tlp_tree.insert(*tlp);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()};
|
|
|
|
|
- ASSERT(tls_page_ptr);
|
|
|
|
|
|
|
+ *out = tlr;
|
|
|
|
|
+ return ResultSuccess;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- const VAddr start{page_table->GetKernelMapRegionStart()};
|
|
|
|
|
- const VAddr size{page_table->GetKernelMapRegionEnd() - start};
|
|
|
|
|
- const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)};
|
|
|
|
|
- const VAddr tls_page_addr{page_table
|
|
|
|
|
- ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize,
|
|
|
|
|
- KMemoryState::ThreadLocal,
|
|
|
|
|
- KMemoryPermission::UserReadWrite,
|
|
|
|
|
- tls_map_addr)
|
|
|
|
|
- .ValueOr(0)};
|
|
|
|
|
|
|
+ // Allocate a new page.
|
|
|
|
|
+ tlp = KThreadLocalPage::Allocate(kernel);
|
|
|
|
|
+ R_UNLESS(tlp != nullptr, ResultOutOfMemory);
|
|
|
|
|
+ auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); });
|
|
|
|
|
|
|
|
- ASSERT(tls_page_addr);
|
|
|
|
|
|
|
+ // Initialize the new page.
|
|
|
|
|
+ R_TRY(tlp->Initialize(kernel, this));
|
|
|
|
|
|
|
|
- std::memset(tls_page_ptr, 0, PageSize);
|
|
|
|
|
- tls_pages.emplace_back(tls_page_addr);
|
|
|
|
|
|
|
+ // Reserve a TLR.
|
|
|
|
|
+ tlr = tlp->Reserve();
|
|
|
|
|
+ ASSERT(tlr != 0);
|
|
|
|
|
|
|
|
- const auto reserve_result{tls_pages.back().ReserveSlot()};
|
|
|
|
|
- ASSERT(reserve_result.has_value());
|
|
|
|
|
|
|
+ // Insert into our tree.
|
|
|
|
|
+ {
|
|
|
|
|
+ KScopedSchedulerLock sl{kernel};
|
|
|
|
|
+ if (tlp->IsAllUsed()) {
|
|
|
|
|
+ fully_used_tlp_tree.insert(*tlp);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ partially_used_tlp_tree.insert(*tlp);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return *reserve_result;
|
|
|
|
|
|
|
+ // We succeeded!
|
|
|
|
|
+ tlp_guard.Cancel();
|
|
|
|
|
+ *out = tlr;
|
|
|
|
|
+ return ResultSuccess;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void KProcess::FreeTLSRegion(VAddr tls_address) {
|
|
|
|
|
- KScopedSchedulerLock lock(kernel);
|
|
|
|
|
- const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE);
|
|
|
|
|
- auto iter =
|
|
|
|
|
- std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
|
|
|
|
|
- return page.GetBaseAddress() == aligned_address;
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) {
|
|
|
|
|
+ KThreadLocalPage* page_to_free = nullptr;
|
|
|
|
|
+
|
|
|
|
|
+ // Release the region.
|
|
|
|
|
+ {
|
|
|
|
|
+ KScopedSchedulerLock sl{kernel};
|
|
|
|
|
+
|
|
|
|
|
+ // Try to find the page in the partially used list.
|
|
|
|
|
+ auto it = partially_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
|
|
|
|
|
+ if (it == partially_used_tlp_tree.end()) {
|
|
|
|
|
+ // If we don't find it, it has to be in the fully used list.
|
|
|
|
|
+ it = fully_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
|
|
|
|
|
+ R_UNLESS(it != fully_used_tlp_tree.end(), ResultInvalidAddress);
|
|
|
|
|
+
|
|
|
|
|
+ // Release the region.
|
|
|
|
|
+ it->Release(addr);
|
|
|
|
|
+
|
|
|
|
|
+ // Move the page out of the fully used list.
|
|
|
|
|
+ KThreadLocalPage* tlp = std::addressof(*it);
|
|
|
|
|
+ fully_used_tlp_tree.erase(it);
|
|
|
|
|
+ if (tlp->IsAllFree()) {
|
|
|
|
|
+ page_to_free = tlp;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ partially_used_tlp_tree.insert(*tlp);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Release the region.
|
|
|
|
|
+ it->Release(addr);
|
|
|
|
|
+
|
|
|
|
|
+ // Handle the all-free case.
|
|
|
|
|
+ KThreadLocalPage* tlp = std::addressof(*it);
|
|
|
|
|
+ if (tlp->IsAllFree()) {
|
|
|
|
|
+ partially_used_tlp_tree.erase(it);
|
|
|
|
|
+ page_to_free = tlp;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If we should free the page it was in, do so.
|
|
|
|
|
+ if (page_to_free != nullptr) {
|
|
|
|
|
+ page_to_free->Finalize();
|
|
|
|
|
|
|
|
- // Something has gone very wrong if we're freeing a region
|
|
|
|
|
- // with no actual page available.
|
|
|
|
|
- ASSERT(iter != tls_pages.cend());
|
|
|
|
|
|
|
+ KThreadLocalPage::Free(kernel, page_to_free);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- iter->ReleaseSlot(tls_address);
|
|
|
|
|
|
|
+ return ResultSuccess;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
|
|
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
|