1
1
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
2
2
// SPDX-License-Identifier: GPL-2.0-or-later
3
3
4
- #include < thread>
5
- #include < boost/icl/interval_set.hpp>
4
+ #include < boost/container/small_vector.hpp>
6
5
#include " common/assert.h"
7
- #include " common/error .h"
6
+ #include " common/debug .h"
8
7
#include " common/signal_context.h"
9
8
#include " common/spin_lock.h"
10
9
#include " core/memory.h"
16
15
#include < sys/mman.h>
17
16
#include " common/adaptive_mutex.h"
18
17
#ifdef ENABLE_USERFAULTFD
18
+ #include < thread>
19
19
#include < fcntl.h>
20
20
#include < linux/userfaultfd.h>
21
21
#include < poll.h>
22
22
#include < sys/ioctl.h>
23
+ #include " common/error.h"
23
24
#endif
24
25
#else
25
26
#include < windows.h>
27
+ #endif
28
+
29
+ #ifdef __linux__
30
+ #include " common/adaptive_mutex.h"
31
+ #else
26
32
#include " common/spin_lock.h"
27
33
#endif
28
34
29
35
namespace VideoCore {
30
36
37
+ constexpr size_t PAGE_SIZE = 4_KB;
38
+ constexpr size_t PAGE_BITS = 12 ;
39
+
31
40
struct PageManager ::Impl {
41
+ struct PageState {
42
+ u8 num_watchers{};
43
+
44
+ Core::MemoryPermission Perm () const noexcept {
45
+ return num_watchers == 0 ? Core::MemoryPermission::ReadWrite
46
+ : Core::MemoryPermission::Read;
47
+ }
48
+
49
+ template <s32 delta>
50
+ u8 AddDelta () {
51
+ if constexpr (delta == 1 ) {
52
+ return ++num_watchers;
53
+ } else {
54
+ ASSERT_MSG (num_watchers > 0 , " Not enough watchers" );
55
+ return --num_watchers;
56
+ }
57
+ }
58
+ };
59
+
60
+ struct UpdateProtectRange {
61
+ VAddr addr;
62
+ u64 size;
63
+ Core::MemoryPermission perms;
64
+ };
65
+
32
66
static constexpr size_t ADDRESS_BITS = 40 ;
33
67
static constexpr size_t NUM_ADDRESS_PAGES = 1ULL << (40 - PAGE_BITS);
34
68
inline static Vulkan::Rasterizer* rasterizer;
35
-
36
69
#ifdef ENABLE_USERFAULTFD
37
70
Impl (Vulkan::Rasterizer* rasterizer_) {
38
71
rasterizer = rasterizer_;
@@ -67,7 +100,8 @@ struct PageManager::Impl {
67
100
ASSERT_MSG (ret != -1 , " Uffdio unregister failed" );
68
101
}
69
102
70
- void Protect (VAddr address, size_t size, bool allow_write) {
103
+ void Protect (VAddr address, size_t size, Core::MemoryPermission perms) {
104
+ bool allow_write = True (perms & Core::MemoryPermission::Write);
71
105
uffdio_writeprotect wp;
72
106
wp.range .start = address;
73
107
wp.range .len = size;
@@ -143,6 +177,7 @@ struct PageManager::Impl {
143
177
}
144
178
145
179
void Protect (VAddr address, size_t size, Core::MemoryPermission perms) {
180
+ RENDERER_TRACE;
146
181
auto * memory = Core::Memory::Instance ();
147
182
auto & impl = memory->GetAddressSpace ();
148
183
impl.Protect (address, size, perms);
@@ -152,96 +187,75 @@ struct PageManager::Impl {
152
187
const auto addr = reinterpret_cast <VAddr>(fault_address);
153
188
if (Common::IsWriteError (context)) {
154
189
return rasterizer->InvalidateMemory (addr, 1 );
155
- } else {
156
- return rasterizer->ReadMemory (addr, 1 );
157
190
}
158
191
return false ;
159
192
}
160
- #endif
161
193
162
- template <s32 delta, bool is_read>
194
+ #endif
195
+ template <s32 delta>
163
196
void UpdatePageWatchers (VAddr addr, u64 size) {
164
- std::scoped_lock lk{lock};
165
- std::atomic_thread_fence (std::memory_order_acquire);
166
-
167
- size_t page = addr >> PAGE_BITS;
168
- auto perms = cached_pages[page].Perms ();
169
- u64 range_begin = 0 ;
170
- u64 range_bytes = 0 ;
171
-
172
- const auto release_pending = [&] {
173
- if (range_bytes > 0 ) {
174
- Protect (range_begin << PAGE_BITS, range_bytes, perms);
175
- range_bytes = 0 ;
176
- }
177
- };
178
- // Iterate requested pages.
179
- const size_t page_end = Common::DivCeil (addr + size, PAGE_SIZE);
180
- for (; page != page_end; ++page) {
181
- PageState& state = cached_pages[page];
182
-
183
- // Apply the change to the page state.
184
- const auto new_count = state.AddDelta <is_read, delta>();
185
-
186
- // If the protection changed flush pending (un)protect action.
187
- if (auto new_perms = state.Perms (); new_perms != perms) [[unlikely]] {
188
- release_pending ();
189
- perms = new_perms;
190
- }
197
+ RENDERER_TRACE;
198
+ boost::container::small_vector<UpdateProtectRange, 16 > update_ranges;
199
+ {
200
+ std::scoped_lock lk (lock);
201
+
202
+ size_t page = addr >> PAGE_BITS;
203
+ auto perms = cached_pages[page].Perm ();
204
+ u64 range_begin = 0 ;
205
+ u64 range_bytes = 0 ;
206
+
207
+ const auto release_pending = [&] {
208
+ if (range_bytes > 0 ) {
209
+ RENDERER_TRACE;
210
+ // Add pending (un)protect action
211
+ update_ranges.push_back ({range_begin << PAGE_BITS, range_bytes, perms});
212
+ range_bytes = 0 ;
213
+ }
214
+ };
215
+
216
+ // Iterate requested pages
217
+ const u64 page_end = Common::DivCeil (addr + size, PAGE_SIZE);
218
+ const u64 aligned_addr = page << PAGE_BITS;
219
+ const u64 aligned_end = page_end << PAGE_BITS;
220
+ // ASSERT_MSG(rasterizer->IsMapped(aligned_addr, aligned_end - aligned_addr),
221
+ // "Attempted to track non-GPU memory at address {:#x}, size {:#x}.",
222
+ // aligned_addr, aligned_end - aligned_addr);
223
+
224
+ for (; page != page_end; ++page) {
225
+ PageState& state = cached_pages[page];
226
+
227
+ // Apply the change to the page state
228
+ const u8 new_count = state.AddDelta <delta>();
229
+
230
+ // If the protection changed add pending (un)protect action
231
+ if (auto new_perms = state.Perm (); new_perms != perms) [[unlikely]] {
232
+ release_pending ();
233
+ perms = new_perms;
234
+ }
191
235
192
- // If the page must be (un)protected add it to pending range.
193
- if ((new_count == 0 && delta < 0 ) || (new_count == 1 && delta > 0 )) {
194
- if (range_bytes == 0 ) {
195
- range_begin = page;
236
+ // If the page must be (un)protected, add it to the pending range
237
+ if ((new_count == 0 && delta < 0 ) || (new_count == 1 && delta > 0 )) {
238
+ if (range_bytes == 0 ) {
239
+ range_begin = page;
240
+ }
241
+ range_bytes += PAGE_SIZE;
242
+ } else {
243
+ release_pending ();
196
244
}
197
- range_bytes += PAGE_SIZE;
198
- } else {
199
- release_pending ();
200
245
}
201
- }
202
- release_pending ();
203
- }
204
-
205
- struct PageState {
206
- u8 num_write_watchers : 7 ;
207
- // At the moment only buffer cache can request read watchers.
208
- // And buffers cannot overlap, thus only 1 can exist per page.
209
- u8 num_read_watchers : 1 ;
210
-
211
- Core::MemoryPermission WritePerm () const noexcept {
212
- return num_write_watchers == 0 ? Core::MemoryPermission::Write
213
- : Core::MemoryPermission::None;
214
- }
215
246
216
- Core::MemoryPermission ReadPerm () const noexcept {
217
- return num_read_watchers == 0 ? Core::MemoryPermission::Read
218
- : Core::MemoryPermission::None;
247
+ // Add pending (un)protect action
248
+ release_pending ();
219
249
}
220
250
221
- Core::MemoryPermission Perms () const noexcept {
222
- return ReadPerm () | WritePerm ();
251
+ // Flush deferred protects
252
+ for (const auto & range : update_ranges) {
253
+ Protect (range.addr , range.size , range.perms );
223
254
}
224
-
225
- template <bool is_read, s32 delta>
226
- u8 AddDelta () {
227
- if constexpr (is_read) {
228
- if constexpr (delta == 1 ) {
229
- return ++num_read_watchers;
230
- } else {
231
- return --num_read_watchers;
232
- }
233
- } else {
234
- if constexpr (delta == 1 ) {
235
- return ++num_write_watchers;
236
- } else {
237
- return --num_write_watchers;
238
- }
239
- }
240
- }
241
- };
255
+ }
242
256
243
257
std::array<PageState, NUM_ADDRESS_PAGES> cached_pages{};
244
- #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
258
+ #ifdef __linux__
245
259
Common::AdaptiveMutex lock;
246
260
#else
247
261
Common::SpinLock lock;
@@ -263,12 +277,12 @@ void PageManager::OnGpuUnmap(VAddr address, size_t size) {
263
277
264
278
template <s32 delta, bool is_read>
265
279
void PageManager::UpdatePageWatchers (VAddr addr, u64 size) const {
266
- impl->UpdatePageWatchers <delta, is_read >(addr, size);
280
+ impl->UpdatePageWatchers <delta>(addr, size);
267
281
}
268
282
269
283
template void PageManager::UpdatePageWatchers<1 , true >(VAddr addr, u64 size) const ;
270
284
template void PageManager::UpdatePageWatchers<1 , false >(VAddr addr, u64 size) const ;
271
285
template void PageManager::UpdatePageWatchers<-1 , true >(VAddr addr, u64 size) const ;
272
286
template void PageManager::UpdatePageWatchers<-1 , false >(VAddr addr, u64 size) const ;
273
287
274
- } // namespace VideoCore
288
+ } // namespace VideoCore
0 commit comments