Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit baee9b6

Browse files
tmdsjkotas
authored andcommitted
Linux: FlushProcessWriteBuffers using membarrier when available (#20949)
* Linux: FlushProcessWriteBuffers using membarrier when available * Ensure __NR_membarrier is defined for x64 portable build. * Define __NR_membarrier on all portable architectures
1 parent dcc47ed commit baee9b6

File tree

1 file changed

+82
-15
lines changed

1 file changed

+82
-15
lines changed

src/pal/src/thread/process.cpp

Lines changed: 82 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,24 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d
6161
#include <stdint.h>
6262
#include <dlfcn.h>
6363

64+
#ifdef __linux__
65+
#include <sys/syscall.h> // __NR_membarrier
66+
// Ensure __NR_membarrier is defined for portable builds.
67+
# if !defined(__NR_membarrier)
68+
# if defined(__amd64__)
69+
# define __NR_membarrier 324
70+
# elif defined(__i386__)
71+
# define __NR_membarrier 375
72+
# elif defined(__arm__)
73+
# define __NR_membarrier 389
74+
# elif defined(__aarch64__)
75+
# define __NR_membarrier 283
76+
# elif
77+
# error Unknown architecture
78+
# endif
79+
# endif
80+
#endif
81+
6482
#ifdef __APPLE__
6583
#include <sys/sysctl.h>
6684
#endif
@@ -97,6 +115,32 @@ CObjectType CorUnix::otProcess(
97115
CObjectType::NoOwner
98116
);
99117

118+
//
119+
// Helper membarrier function
120+
//
121+
#ifdef __NR_membarrier
122+
# define membarrier(...) syscall(__NR_membarrier, __VA_ARGS__)
123+
#else
124+
# define membarrier(...) -ENOSYS
125+
#endif
126+
127+
enum membarrier_cmd
128+
{
129+
MEMBARRIER_CMD_QUERY = 0,
130+
MEMBARRIER_CMD_GLOBAL = (1 << 0),
131+
MEMBARRIER_CMD_GLOBAL_EXPEDITED = (1 << 1),
132+
MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED = (1 << 2),
133+
MEMBARRIER_CMD_PRIVATE_EXPEDITED = (1 << 3),
134+
MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED = (1 << 4),
135+
MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE = (1 << 5),
136+
MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE = (1 << 6)
137+
};
138+
139+
//
140+
// Tracks if the OS supports FlushProcessWriteBuffers using membarrier
141+
//
142+
static int s_flushUsingMemBarrier = 0;
143+
100144
//
101145
// Helper memory page used by the FlushProcessWriteBuffers
102146
//
@@ -3133,12 +3177,27 @@ BOOL
31333177
InitializeFlushProcessWriteBuffers()
31343178
{
31353179
_ASSERTE(s_helperPage == 0);
3180+
_ASSERTE(s_flushUsingMemBarrier == 0);
3181+
3182+
// Starting with Linux kernel 4.14, process memory barriers can be generated
3183+
// using MEMBARRIER_CMD_PRIVATE_EXPEDITED.
3184+
int mask = membarrier(MEMBARRIER_CMD_QUERY, 0);
3185+
if (mask >= 0 &&
3186+
mask & MEMBARRIER_CMD_PRIVATE_EXPEDITED)
3187+
{
3188+
// Register intent to use the private expedited command.
3189+
if (membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0) == 0)
3190+
{
3191+
s_flushUsingMemBarrier = TRUE;
3192+
return TRUE;
3193+
}
3194+
}
31363195

31373196
s_helperPage = static_cast<int*>(mmap(0, GetVirtualPageSize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
31383197

31393198
if(s_helperPage == MAP_FAILED)
31403199
{
3141-
return false;
3200+
return FALSE;
31423201
}
31433202

31443203
// Verify that the s_helperPage is really aligned to the GetVirtualPageSize()
@@ -3184,24 +3243,32 @@ VOID
31843243
PALAPI
31853244
FlushProcessWriteBuffers()
31863245
{
3187-
int status = pthread_mutex_lock(&flushProcessWriteBuffersMutex);
3188-
FATAL_ASSERT(status == 0, "Failed to lock the flushProcessWriteBuffersMutex lock");
3246+
if (s_flushUsingMemBarrier)
3247+
{
3248+
int status = membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0);
3249+
FATAL_ASSERT(status == 0, "Failed to flush using membarrier");
3250+
}
3251+
else
3252+
{
3253+
int status = pthread_mutex_lock(&flushProcessWriteBuffersMutex);
3254+
FATAL_ASSERT(status == 0, "Failed to lock the flushProcessWriteBuffersMutex lock");
31893255

3190-
// Changing a helper memory page protection from read / write to no access
3191-
// causes the OS to issue IPI to flush TLBs on all processors. This also
3192-
// results in flushing the processor buffers.
3193-
status = mprotect(s_helperPage, GetVirtualPageSize(), PROT_READ | PROT_WRITE);
3194-
FATAL_ASSERT(status == 0, "Failed to change helper page protection to read / write");
3256+
// Changing a helper memory page protection from read / write to no access
3257+
// causes the OS to issue IPI to flush TLBs on all processors. This also
3258+
// results in flushing the processor buffers.
3259+
status = mprotect(s_helperPage, GetVirtualPageSize(), PROT_READ | PROT_WRITE);
3260+
FATAL_ASSERT(status == 0, "Failed to change helper page protection to read / write");
31953261

3196-
// Ensure that the page is dirty before we change the protection so that
3197-
// we prevent the OS from skipping the global TLB flush.
3198-
InterlockedIncrement(s_helperPage);
3262+
// Ensure that the page is dirty before we change the protection so that
3263+
// we prevent the OS from skipping the global TLB flush.
3264+
InterlockedIncrement(s_helperPage);
31993265

3200-
status = mprotect(s_helperPage, GetVirtualPageSize(), PROT_NONE);
3201-
FATAL_ASSERT(status == 0, "Failed to change helper page protection to no access");
3266+
status = mprotect(s_helperPage, GetVirtualPageSize(), PROT_NONE);
3267+
FATAL_ASSERT(status == 0, "Failed to change helper page protection to no access");
32023268

3203-
status = pthread_mutex_unlock(&flushProcessWriteBuffersMutex);
3204-
FATAL_ASSERT(status == 0, "Failed to unlock the flushProcessWriteBuffersMutex lock");
3269+
status = pthread_mutex_unlock(&flushProcessWriteBuffersMutex);
3270+
FATAL_ASSERT(status == 0, "Failed to unlock the flushProcessWriteBuffersMutex lock");
3271+
}
32053272
}
32063273

32073274
/*++

0 commit comments

Comments
 (0)