Skip to content

Commit 0c486fb

Browse files
committed
rsx/mm: Implement range merging mechanics
1 parent a8bfc50 commit 0c486fb

2 files changed

Lines changed: 103 additions & 9 deletions

File tree

rpcs3/Emu/RSX/Host/MM.cpp

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,32 @@ namespace rsx
1414
rsx::simple_array<MM_block> g_deferred_mprotect_queue;
1515
shared_mutex g_mprotect_queue_lock;
1616

17+
void mm_sanitize_queue_internal()
18+
{
19+
u32 w = 0, r = 0;
20+
for (; r < g_deferred_mprotect_queue.size(); ++r)
21+
{
22+
auto& block = g_deferred_mprotect_queue[r];
23+
if (!block.range.valid())
24+
{
25+
continue;
26+
}
27+
g_deferred_mprotect_queue[w++] = block;
28+
}
29+
g_deferred_mprotect_queue.resize(w);
30+
}
31+
1732
void mm_flush_mprotect_queue_internal(u32 count)
1833
{
1934
AUDIT(count <= g_deferred_mprotect_queue.size());
2035

2136
for (u32 i = 0; i < count; ++i)
2237
{
23-
const auto& block = g_deferred_mprotect_queue[i];
38+
auto& block = g_deferred_mprotect_queue[i];
39+
ensure(block.range.valid());
40+
2441
utils::memory_protect(reinterpret_cast<void*>(block.range.start), block.range.length(), block.prot);
42+
block.range.invalidate();
2543
}
2644

2745
const u32 remaining = g_deferred_mprotect_queue.size() - count;
@@ -31,9 +49,8 @@ namespace rsx
3149
return;
3250
}
3351

34-
// Pop count entries from the queue
35-
std::memmove(g_deferred_mprotect_queue.data(), g_deferred_mprotect_queue.data() + count, remaining * sizeof(MM_block));
36-
g_deferred_mprotect_queue.resize(remaining);
52+
// Pop processed entries from the queue
53+
mm_sanitize_queue_internal();
3754
}
3855

3956
// Reverse scan to find the latest overlapping MM conflict. The result is a count of prefix blocks.
@@ -53,9 +70,57 @@ namespace rsx
5370

5471
void mm_defer_mprotect_internal(u64 start, u64 length, utils::protection prot)
5572
{
56-
// We could stack and merge requests here, but that is more trouble than it is truly worth.
57-
// A fresh call to memory_protect only takes a few nanoseconds of setup overhead, it is not worth the risk of hanging because of conflicts.
58-
g_deferred_mprotect_queue.push_back({ utils::address_range64::start_length(start, length), prot });
73+
const auto range = utils::address_range64::start_length(start, length);
74+
bool has_invalid = false;
75+
bool is_merged = false;
76+
77+
// Attempt a merge first. The queue length is short but the time taken to run mprotect is very high in comparison.
78+
for (auto it = g_deferred_mprotect_queue.rbegin();
79+
it != g_deferred_mprotect_queue.rend();
80+
++it)
81+
{
82+
auto& block = *it;
83+
if (!block.touches(range))
84+
{
85+
continue;
86+
}
87+
88+
if (block.prot != prot)
89+
{
90+
// Optimization. If our new range swallows the old one, replace it.
91+
if (block.range.inside(range))
92+
{
93+
block.range.invalidate();
94+
has_invalid = true;
95+
continue;
96+
}
97+
98+
if (!block.overlaps(range))
99+
{
100+
// Adjacent. Skip.
101+
continue;
102+
}
103+
104+
// Preserve ordering. Do not proceed with merge.
105+
break;
106+
}
107+
108+
block.merge(range);
109+
is_merged = true;
110+
break;
111+
}
112+
113+
if (has_invalid)
114+
{
115+
mm_sanitize_queue_internal();
116+
}
117+
118+
if (is_merged)
119+
{
120+
return;
121+
}
122+
123+
g_deferred_mprotect_queue.push_back({ range, prot });
59124
}
60125

61126
void mm_protect(void* ptr, u64 length, utils::protection prot)
@@ -75,9 +140,24 @@ namespace rsx
75140
if (prot == utils::protection::rw || prot == utils::protection::wx)
76141
{
77142
// Basically an unlock op. Flush the conflicting prefix block if any overlap is detected.
78-
if (const u32 count = mm_find_conflict_internal(FN(x.overlaps(range))))
143+
if (u32 count = mm_find_conflict_internal(FN(x.overlaps(range))))
79144
{
80-
mm_flush_mprotect_queue_internal(count);
145+
// Check for degenerate ranges that we'll be crushing
146+
for (auto pblock = &g_deferred_mprotect_queue[count - 1];
147+
count > 0 && pblock->range.inside(range);
148+
count--, pblock--)
149+
{
150+
pblock->range.invalidate();
151+
}
152+
153+
if (count)
154+
{
155+
mm_flush_mprotect_queue_internal(count);
156+
}
157+
else
158+
{
159+
mm_sanitize_queue_internal();
160+
}
81161
}
82162

83163
utils::memory_protect(ptr, length, prot);

rpcs3/Emu/RSX/Host/MM.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ namespace rsx
1313
utils::address_range64 range;
1414
utils::protection prot;
1515

16+
inline void merge(const utils::address_range64& other)
17+
{
18+
AUDIT(other.valid());
19+
range = utils::address_range64::start_end(
20+
std::min(range.start, other.start),
21+
std::max(range.end, other.end)
22+
);
23+
}
24+
1625
inline bool overlaps(const utils::address_range64& test) const
1726
{
1827
return range.overlaps(test);
@@ -22,6 +31,11 @@ namespace rsx
2231
{
2332
return range.overlaps(addr);
2433
}
34+
35+
inline bool touches(const utils::address_range64& test) const
36+
{
37+
return range.touches(test);
38+
}
2539
};
2640

2741
enum mm_backend_ctrl : u32

0 commit comments

Comments
 (0)