kern: use tree for IoPool regions instead of list

This commit is contained in:
Michael Scire 2023-02-21 07:54:57 -07:00
parent b7846247aa
commit 8eef019e3d
3 changed files with 42 additions and 13 deletions

View file

@ -24,10 +24,10 @@ namespace ams::kern {
class KIoPool final : public KAutoObjectWithSlabHeapAndContainer<KIoPool, KAutoObjectWithList> { class KIoPool final : public KAutoObjectWithSlabHeapAndContainer<KIoPool, KAutoObjectWithList> {
MESOSPHERE_AUTOOBJECT_TRAITS(KIoPool, KAutoObject); MESOSPHERE_AUTOOBJECT_TRAITS(KIoPool, KAutoObject);
private: private:
using IoRegionList = util::IntrusiveListMemberTraits<&KIoRegion::m_pool_list_node>::ListType; using IoRegionTree = util::IntrusiveRedBlackTreeBaseTraits<KIoRegion>::TreeType<KIoRegion>;
private: private:
KLightLock m_lock; KLightLock m_lock;
IoRegionList m_io_region_list; IoRegionTree m_io_region_tree;
ams::svc::IoPoolType m_pool_type; ams::svc::IoPoolType m_pool_type;
bool m_is_initialized; bool m_is_initialized;
public: public:

View file

@ -23,11 +23,30 @@ namespace ams::kern {
class KProcess; class KProcess;
class KIoPool; class KIoPool;
class KIoRegion final : public KAutoObjectWithSlabHeapAndContainer<KIoRegion, KAutoObjectWithList> { class KIoRegion final : public KAutoObjectWithSlabHeapAndContainer<KIoRegion, KAutoObjectWithList>, public util::IntrusiveRedBlackTreeBaseNode<KIoRegion> {
MESOSPHERE_AUTOOBJECT_TRAITS(KIoRegion, KAutoObject); MESOSPHERE_AUTOOBJECT_TRAITS(KIoRegion, KAutoObject);
private: private:
friend class KProcess; friend class KProcess;
friend class KIoPool; friend class KIoPool;
public:
using RedBlackKeyType = KPhysicalAddress;
static constexpr ALWAYS_INLINE RedBlackKeyType GetRedBlackKey(const RedBlackKeyType &v) { return v; }
static constexpr ALWAYS_INLINE RedBlackKeyType GetRedBlackKey(const KIoRegion &v) { return v.GetAddress(); }
template<typename T> requires (std::same_as<T, KIoRegion> || std::same_as<T, RedBlackKeyType>)
static constexpr ALWAYS_INLINE int Compare(const T &lhs, const KIoRegion &rhs) {
const RedBlackKeyType lval = GetRedBlackKey(lhs);
const RedBlackKeyType rval = GetRedBlackKey(rhs);
if (lval < rval) {
return -1;
} else if (lval == rval) {
return 0;
} else {
return 1;
}
}
private: private:
KLightLock m_lock; KLightLock m_lock;
KIoPool *m_pool; KIoPool *m_pool;
@ -38,7 +57,6 @@ namespace ams::kern {
bool m_is_initialized; bool m_is_initialized;
bool m_is_mapped; bool m_is_mapped;
util::IntrusiveListNode m_process_list_node; util::IntrusiveListNode m_process_list_node;
util::IntrusiveListNode m_pool_list_node;
public: public:
explicit KIoRegion() : m_pool(nullptr), m_is_initialized(false) { /* ... */ } explicit KIoRegion() : m_pool(nullptr), m_is_initialized(false) { /* ... */ }
@ -51,12 +69,12 @@ namespace ams::kern {
Result Map(KProcessAddress address, size_t size, ams::svc::MemoryPermission map_perm); Result Map(KProcessAddress address, size_t size, ams::svc::MemoryPermission map_perm);
Result Unmap(KProcessAddress address, size_t size); Result Unmap(KProcessAddress address, size_t size);
bool Overlaps(KPhysicalAddress address, size_t size) const { constexpr bool Overlaps(KPhysicalAddress address, size_t size) const {
return m_physical_address <= (address + size - 1) && address <= (m_physical_address + m_size - 1); return m_physical_address <= (address + size - 1) && address <= (m_physical_address + m_size - 1);
} }
ALWAYS_INLINE KPhysicalAddress GetAddress() const { return m_physical_address; } constexpr ALWAYS_INLINE KPhysicalAddress GetAddress() const { return m_physical_address; }
ALWAYS_INLINE size_t GetSize() const { return m_size; } constexpr ALWAYS_INLINE size_t GetSize() const { return m_size; }
}; };
} }

View file

@ -47,7 +47,7 @@ namespace ams::kern {
/* Check if the address/size falls within any allowable extents. */ /* Check if the address/size falls within any allowable extents. */
for (const auto &extents : g_io_region_extents) { for (const auto &extents : g_io_region_extents) {
if (extents.address <= address && address + size - 1 <= extents.address + extents.size - 1) { if (extents.size != 0 && extents.address <= address && address + size - 1 <= extents.address + extents.size - 1) {
return true; return true;
} }
} }
@ -106,12 +106,23 @@ namespace ams::kern {
KScopedLightLock lk(m_lock); KScopedLightLock lk(m_lock);
/* Check that the desired range isn't already in our pool. */ /* Check that the desired range isn't already in our pool. */
for (const auto &region : m_io_region_list) { {
R_UNLESS(!region.Overlaps(new_region->GetAddress(), new_region->GetSize()), svc::ResultBusy()); /* Get the lowest region with address >= the new region that's already in our tree. */
auto lowest_after = m_io_region_tree.nfind_key(new_region->GetAddress());
if (lowest_after != m_io_region_tree.end()) {
R_UNLESS(!lowest_after->Overlaps(new_region->GetAddress(), new_region->GetSize()), svc::ResultBusy());
}
/* There is no region with address >= the new region already in our tree, but we also need to check */
/* for a region with address < the new region already in our tree. */
if (lowest_after != m_io_region_tree.begin()) {
auto highest_before = --lowest_after;
R_UNLESS(!highest_before->Overlaps(new_region->GetAddress(), new_region->GetSize()), svc::ResultBusy());
}
} }
/* Add the region to our pool. */ /* Add the region to our pool. */
m_io_region_list.push_back(*new_region); m_io_region_tree.insert(*new_region);
R_SUCCEED(); R_SUCCEED();
} }
@ -122,8 +133,8 @@ namespace ams::kern {
/* Lock ourselves. */ /* Lock ourselves. */
KScopedLightLock lk(m_lock); KScopedLightLock lk(m_lock);
/* Remove the region from our list. */ /* Remove the region from our tree. */
m_io_region_list.erase(m_io_region_list.iterator_to(*region)); m_io_region_tree.erase(m_io_region_tree.iterator_to(*region));
} }
} }