summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJan Vorlicek <janvorli@microsoft.com>2015-05-22 20:11:32 +0200
committerJan Vorlicek <janvorli@microsoft.com>2015-05-27 21:27:13 +0200
commite79b9ed5cde41c65e37ff4cc8b8a084f1511f9eb (patch)
tree0d9f012e3f22b383f46a5887f0be7a0d3624e0de /src
parent4d931ce9e79349733bb74389089b47d600a52fc3 (diff)
downloadcoreclr-e79b9ed5cde41c65e37ff4cc8b8a084f1511f9eb.tar.gz
coreclr-e79b9ed5cde41c65e37ff4cc8b8a084f1511f9eb.tar.bz2
coreclr-e79b9ed5cde41c65e37ff4cc8b8a084f1511f9eb.zip
Implement FlushProcessWriteBuffers using the page protection mechanism
Diffstat (limited to 'src')
-rw-r--r--src/pal/src/include/pal/process.h11
-rw-r--r--src/pal/src/init/pal.cpp5
-rw-r--r--src/pal/src/thread/process.cpp78
3 files changed, 91 insertions, 3 deletions
diff --git a/src/pal/src/include/pal/process.h b/src/pal/src/include/pal/process.h
index d3a2ad4576..1f299271e4 100644
--- a/src/pal/src/include/pal/process.h
+++ b/src/pal/src/include/pal/process.h
@@ -152,6 +152,17 @@ Function:
--*/
void PROCCleanupProcess(BOOL bTerminateUnconditionally);
+/*++
+Function:
+ InitializeFlushProcessWriteBuffers
+
+Abstract
+ This function initializes data structures needed for the FlushProcessWriteBuffers
+Return
+ TRUE if it succeeded, FALSE otherwise
+--*/
+BOOL InitializeFlushProcessWriteBuffers();
+
#if HAVE_MACH_EXCEPTIONS
/*++
Function:
diff --git a/src/pal/src/init/pal.cpp b/src/pal/src/init/pal.cpp
index 93b6c038f8..275d78dd83 100644
--- a/src/pal/src/init/pal.cpp
+++ b/src/pal/src/init/pal.cpp
@@ -670,6 +670,11 @@ PAL_InitializeCoreCLR(
return ERROR_DLL_INIT_FAILED;
}
+ if (!InitializeFlushProcessWriteBuffers())
+ {
+ return ERROR_GEN_FAILURE;
+ }
+
if (!fStayInPAL)
{
PAL_Leave(PAL_BoundaryTop);
diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp
index 78477008cc..7097e093b5 100644
--- a/src/pal/src/thread/process.cpp
+++ b/src/pal/src/thread/process.cpp
@@ -31,6 +31,7 @@ Abstract:
#include "pal/dbgmsg.h"
#include "pal/utils.h"
#include "pal/misc.h"
+#include "pal/virtual.h"
#include <errno.h>
#if HAVE_POLL
@@ -39,6 +40,7 @@ Abstract:
#include "pal/fakepoll.h"
#endif // HAVE_POLL
+#include <sys/mman.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
@@ -85,6 +87,16 @@ CObjectType CorUnix::otProcess(
CObjectType::NoOwner
);
+//
+// Helper memory page used by the FlushProcessWriteBuffers
+//
+static int s_helperPage[VIRTUAL_PAGE_SIZE / sizeof(int)] __attribute__((aligned(VIRTUAL_PAGE_SIZE)));
+
+//
+// Mutex to make the FlushProcessWriteBuffersMutex thread safe
+//
+pthread_mutex_t flushProcessWriteBuffersMutex;
+
CAllowedObjectTypes aotProcess(otiProcess);
//
@@ -1746,6 +1758,50 @@ OpenProcessExit:
/*++
Function:
+ InitializeFlushProcessWriteBuffers
+
+Abstract
+ This function initializes data structures needed for the FlushProcessWriteBuffers
+Return
+ TRUE if it succeeded, FALSE otherwise
+--*/
+BOOL InitializeFlushProcessWriteBuffers()
+{
+ // Verify that the s_helperPage is really aligned to the VIRTUAL_PAGE_SIZE
+ _ASSERTE((((SIZE_T)s_helperPage) & (VIRTUAL_PAGE_SIZE - 1)) == 0);
+
+ // Locking the page ensures that it stays in memory during the two mprotect
+ // calls in the FlushProcessWriteBuffers below. If the page was unmapped between
+ // those calls, they would not have the expected effect of generating IPI.
+ int status = mlock(s_helperPage, VIRTUAL_PAGE_SIZE);
+
+ if (status != 0)
+ {
+ return FALSE;
+ }
+
+ status = pthread_mutex_init(&flushProcessWriteBuffersMutex, NULL);
+ if (status != 0)
+ {
+ munlock(s_helperPage, VIRTUAL_PAGE_SIZE);
+ }
+
+ return status == 0;
+}
+
+#define FATAL_ASSERT(e, msg) \
+ do \
+ { \
+ if (!(e)) \
+ { \
+ fprintf(stderr, "FATAL ERROR: " msg); \
+ abort(); \
+ } \
+ } \
+ while(0)
+
+/*++
+Function:
FlushProcessWriteBuffers
See MSDN doc.
@@ -1753,9 +1809,25 @@ See MSDN doc.
VOID
PALAPI
FlushProcessWriteBuffers()
-{
- // UNIXTODO: Implement this. There seems to be no equivalent on Linux
- // that could be used in user mode code.
+{
+ int status = pthread_mutex_lock(&flushProcessWriteBuffersMutex);
+ FATAL_ASSERT(status == 0, "Failed to lock the flushProcessWriteBuffersMutex lock");
+
+ // Changing a helper memory page protection from read / write to no access
+ // causes the OS to issue IPI to flush TLBs on all processors. This also
+ // results in flushing the processor buffers.
+ status = mprotect(s_helperPage, VIRTUAL_PAGE_SIZE, PROT_READ | PROT_WRITE);
+ FATAL_ASSERT(status == 0, "Failed to change helper page protection to read / write");
+
+ // Ensure that the page is dirty before we change the protection so that
+ // we prevent the OS from skipping the global TLB flush.
+ InterlockedIncrement(s_helperPage);
+
+ status = mprotect(s_helperPage, VIRTUAL_PAGE_SIZE, PROT_NONE);
+ FATAL_ASSERT(status == 0, "Failed to change helper page protection to no access");
+
+ status = pthread_mutex_unlock(&flushProcessWriteBuffersMutex);
+ FATAL_ASSERT(status == 0, "Failed to unlock the flushProcessWriteBuffersMutex lock");
}
/*++