serenity
serenity copied to clipboard
Kernel/Memory: Redundant page faults on anonymous mmap regions after fork
If you create an anonymous mmap, and then fork, any writes you make to that mmap will cause 2 page faults instead of 1.
How to reproduce:
Apply the following diff, then run test-redundant-fault from the shell
test-redundant-fault simply creates an anonymous mmap, forks, and then writes to each page of the mmap
diff --git a/Kernel/Memory/Region.cpp b/Kernel/Memory/Region.cpp
index 66648dd735..cb03420931 100644
--- a/Kernel/Memory/Region.cpp
+++ b/Kernel/Memory/Region.cpp
@@ -429,7 +429,7 @@ PageFaultResponse Region::handle_fault(PageFault const& fault)
}
VERIFY(fault.type() == PageFault::Type::ProtectionViolation);
if (fault.access() == PageFault::Access::Write && is_writable() && should_cow(page_index_in_region)) {
- dbgln_if(PAGE_FAULT_DEBUG, "PV(cow) fault in Region({})[{}] at {}", this, page_index_in_region, fault.vaddr());
+ dbgln("PV(cow) fault in Region({})[{}] at {}", this, page_index_in_region, fault.vaddr());
auto phys_page = physical_page(page_index_in_region);
if (phys_page->is_shared_zero_page() || phys_page->is_lazy_committed_page()) {
dbgln_if(PAGE_FAULT_DEBUG, "NP(zero) fault in Region({})[{}] at {}", this, page_index_in_region, fault.vaddr());
diff --git a/Userland/Utilities/test-redundant-fault.cpp b/Userland/Utilities/test-redundant-fault.cpp
new file mode 100644
index 0000000000..733c98adb2
--- /dev/null
+++ b/Userland/Utilities/test-redundant-fault.cpp
@@ -0,0 +1,39 @@
+#include <AK/Assertions.h>
+#include <LibMain/Main.h>
+#include <LibThreading/Thread.h>
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+ErrorOr<int> serenity_main(Main::Arguments)
+{
+ size_t pages = 100;
+ size_t size = pages * 4096;
+
+ char *ptr = (char *)mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (ptr == MAP_FAILED) {
+ perror(nullptr);
+ return 1;
+ }
+
+ pid_t pid = fork();
+ switch (pid) {
+ case -1:
+ perror(nullptr);
+ exit(1);
+ break;
+ case 0:
+ for (size_t i = 0; i < pages; ++i)
+ ptr[i * 4096] = '$';
+ exit(EXIT_SUCCESS);
+ break;
+ default:
+ wait(nullptr);
+ break;
+ }
+
+ return 0;
+}
You will see dbgln output like the following, note how each page has 2 page faults instead of 1:
65.756 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[0] at V0x0000000000010000
65.756 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[0] at V0x0000000000010000
65.756 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[1] at V0x0000000000011000
65.756 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[1] at V0x0000000000011000
65.756 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[2] at V0x0000000000012000
65.756 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[2] at V0x0000000000012000
65.760 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[3] at V0x0000000000013000
65.760 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[3] at V0x0000000000013000
65.760 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[4] at V0x0000000000014000
65.764 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[4] at V0x0000000000014000
65.764 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[5] at V0x0000000000015000
65.764 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[5] at V0x0000000000015000
65.764 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[6] at V0x0000000000016000
65.764 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[6] at V0x0000000000016000
65.764 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[7] at V0x0000000000017000
65.764 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[7] at V0x0000000000017000
65.764 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[8] at V0x0000000000018000
65.764 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[8] at V0x0000000000018000
65.764 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[9] at V0x0000000000019000
65.764 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[9] at V0x0000000000019000
65.772 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[10] at V0x000000000001a000
65.772 [#0 test-redundant-fault(47:47)]: PV(cow) fault in Region(0x0000002000fbc940)[10] at V0x000000000001a000