ART: Add wrappers for membarrier and memfd_create()
Adds art::membarrer() and art::memfd_create() as wrappers around the
similarly named linux syscalls. These wrappers can fail due to missing
OS support so code that uses them needs to deal with this.
Updates the JIT code cache to use art::membarrer().
Bug: 111199492
Test: art/test.py --host
Change-Id: I122746e6293e7201077e834d8a5ed7bbba45e283
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 1b603b5..19f1532 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -29,6 +29,8 @@
"base/hex_dump.cc",
"base/logging.cc",
"base/malloc_arena_pool.cc",
+ "base/membarrier.cc",
+ "base/memfd.cc",
"base/memory_region.cc",
"base/mem_map.cc",
// "base/mem_map_fuchsia.cc", put in target when fuchsia supported by soong
@@ -186,6 +188,8 @@
"base/indenter_test.cc",
"base/leb128_test.cc",
"base/logging_test.cc",
+ "base/memfd_test.cc",
+ "base/membarrier_test.cc",
"base/memory_region_test.cc",
"base/mem_map_test.cc",
"base/safe_copy_test.cc",
diff --git a/libartbase/base/membarrier.cc b/libartbase/base/membarrier.cc
new file mode 100644
index 0000000..490dbf3
--- /dev/null
+++ b/libartbase/base/membarrier.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "membarrier.h"
+
+#include <errno.h>
+
+#include <sys/syscall.h>
+#include <unistd.h>
+#include "macros.h"
+
+#if defined(__BIONIC__)
+
+#include <atomic>
+#include <android/get_device_api_level.h>
+#include <linux/membarrier.h>
+
+#define CHECK_MEMBARRIER_CMD(art_value, membarrier_value) \
+ static_assert(static_cast<int>(art_value) == membarrier_value, "Bad value for " # art_value)
+CHECK_MEMBARRIER_CMD(art::MembarrierCommand::kQuery, MEMBARRIER_CMD_QUERY);
+CHECK_MEMBARRIER_CMD(art::MembarrierCommand::kGlobal, MEMBARRIER_CMD_SHARED);
+CHECK_MEMBARRIER_CMD(art::MembarrierCommand::kPrivateExpedited, MEMBARRIER_CMD_PRIVATE_EXPEDITED);
+CHECK_MEMBARRIER_CMD(art::MembarrierCommand::kRegisterPrivateExpedited,
+ MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED);
+CHECK_MEMBARRIER_CMD(art::MembarrierCommand::kPrivateExpedited, MEMBARRIER_CMD_PRIVATE_EXPEDITED);
+#undef CHECK_MEMBARRIER_CMD
+
+#endif // __BIONIC
+
+namespace art {
+
+#if defined(__NR_membarrier)
+
+int membarrier(MembarrierCommand command) {
+#if defined(__BIONIC__)
+ // Avoid calling membarrier on older Android versions where membarrier may be barred by secomp
+ // causing the current process to be killed. The probing here could be considered expensive so
+ // endeavour not to repeat too often.
+ static int api_level = android_get_device_api_level();
+ if (api_level < __ANDROID_API_Q__) {
+ errno = ENOSYS;
+ return -1;
+ }
+#endif // __BIONIC__
+ return syscall(__NR_membarrier, static_cast<int>(command), 0);
+}
+
+#else // __NR_membarrier
+
+int membarrier(MembarrierCommand command ATTRIBUTE_UNUSED) {
+ // In principle this could be supported on linux, but Android's prebuilt glibc does not include
+ // the system call number defintions (b/111199492).
+ errno = ENOSYS;
+ return -1;
+}
+
+#endif // __NR_membarrier
+
+} // namespace art
diff --git a/libartbase/base/membarrier.h b/libartbase/base/membarrier.h
new file mode 100644
index 0000000..f829fc1
--- /dev/null
+++ b/libartbase/base/membarrier.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_MEMBARRIER_H_
+#define ART_LIBARTBASE_BASE_MEMBARRIER_H_
+
+namespace art {
+ // Command types for the linux membarrier system call. Different Linux installation may include
+ // different subsets of these commands (at the same codepoints).
+ //
+ // Hardcoding these values is temporary until bionic and prebuilts glibc have an up to date
+ // linux/membarrier.h. The order and values follow the current linux definitions.
+ enum class MembarrierCommand : int {
+ // MEMBARRIER_CMD_QUERY
+ kQuery = 0,
+ // MEMBARRIER_CMD_GLOBAL
+ kGlobal = (1 << 0),
+ // MEMBARRIER_CMD_GLOBAL_EXPEDITED
+ kGlobalExpedited = (1 << 1),
+ // MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED
+ kRegisterGlobalExpedited = (1 << 2),
+ // MEMBARRIER_CMD_PRIVATE_EXPEDITED
+ kPrivateExpedited = (1 << 3),
+ // MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED
+ kRegisterPrivateExpedited = (1 << 4),
+ // MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE
+ kPrivateExpeditedSyncCore = (1 << 5),
+ // MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE
+ kRegisterPrivateExpeditedSyncCore = (1 << 6)
+ };
+
+ // Call membarrier(2) if available on platform and return result. This method can fail if the
+ // command is not supported by the kernel. The underlying system call is linux specific.
+ int membarrier(MembarrierCommand command);
+
+} // namespace art
+
+#endif // ART_LIBARTBASE_BASE_MEMBARRIER_H_
diff --git a/libartbase/base/membarrier_test.cc b/libartbase/base/membarrier_test.cc
new file mode 100644
index 0000000..3eedf14
--- /dev/null
+++ b/libartbase/base/membarrier_test.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "membarrier.h"
+
+class ScopedErrnoCleaner {
+ public:
+ ScopedErrnoCleaner() { errno = 0; }
+ ~ScopedErrnoCleaner() { errno = 0; }
+};
+
+bool HasMembarrier(art::MembarrierCommand cmd) {
+ ScopedErrnoCleaner errno_cleaner;
+ int supported_cmds = art::membarrier(art::MembarrierCommand::kQuery);
+ return (supported_cmds > 0) && ((supported_cmds & static_cast<int>(cmd)) != 0);
+}
+
+TEST(membarrier, query) {
+ ScopedErrnoCleaner errno_cleaner;
+ int supported = art::membarrier(art::MembarrierCommand::kQuery);
+ if (errno == 0) {
+ ASSERT_LE(0, supported);
+ } else {
+ ASSERT_TRUE(errno == ENOSYS && supported == -1);
+ }
+}
+
+TEST(membarrier, global_barrier) {
+ if (!HasMembarrier(art::MembarrierCommand::kGlobal)) {
+ GTEST_LOG_(INFO) << "MembarrierCommand::kGlobal not supported, skipping test.";
+ return;
+ }
+ ASSERT_EQ(0, art::membarrier(art::MembarrierCommand::kGlobal));
+}
+
+static const char* MembarrierCommandToName(art::MembarrierCommand cmd) {
+#define CASE_VALUE(x) case (x): return #x;
+ switch (cmd) {
+ CASE_VALUE(art::MembarrierCommand::kQuery);
+ CASE_VALUE(art::MembarrierCommand::kGlobal);
+ CASE_VALUE(art::MembarrierCommand::kGlobalExpedited);
+ CASE_VALUE(art::MembarrierCommand::kRegisterGlobalExpedited);
+ CASE_VALUE(art::MembarrierCommand::kPrivateExpedited);
+ CASE_VALUE(art::MembarrierCommand::kRegisterPrivateExpedited);
+ CASE_VALUE(art::MembarrierCommand::kPrivateExpeditedSyncCore);
+ CASE_VALUE(art::MembarrierCommand::kRegisterPrivateExpeditedSyncCore);
+ }
+}
+
+static void TestRegisterAndBarrierCommands(art::MembarrierCommand membarrier_cmd_register,
+ art::MembarrierCommand membarrier_cmd_barrier) {
+ if (!HasMembarrier(membarrier_cmd_register)) {
+ GTEST_LOG_(INFO) << MembarrierCommandToName(membarrier_cmd_register)
+ << " not supported, skipping test.";
+ return;
+ }
+ if (!HasMembarrier(membarrier_cmd_barrier)) {
+ GTEST_LOG_(INFO) << MembarrierCommandToName(membarrier_cmd_barrier)
+ << " not supported, skipping test.";
+ return;
+ }
+
+ ScopedErrnoCleaner errno_cleaner;
+
+ // Check barrier use without prior registration.
+ if (membarrier_cmd_register == art::MembarrierCommand::kRegisterGlobalExpedited) {
+ // Global barrier use is always okay.
+ ASSERT_EQ(0, art::membarrier(membarrier_cmd_barrier));
+ } else {
+ // Private barrier should fail.
+ ASSERT_EQ(-1, art::membarrier(membarrier_cmd_barrier));
+ ASSERT_EQ(EPERM, errno);
+ errno = 0;
+ }
+
+ // Check registration for barrier succeeds.
+ ASSERT_EQ(0, art::membarrier(membarrier_cmd_register));
+
+ // Check barrier use after registration succeeds.
+ ASSERT_EQ(0, art::membarrier(membarrier_cmd_barrier));
+}
+
+TEST(membarrier, global_expedited) {
+ TestRegisterAndBarrierCommands(art::MembarrierCommand::kRegisterGlobalExpedited,
+ art::MembarrierCommand::kGlobalExpedited);
+}
+
+TEST(membarrier, private_expedited) {
+ TestRegisterAndBarrierCommands(art::MembarrierCommand::kRegisterPrivateExpedited,
+ art::MembarrierCommand::kPrivateExpedited);
+}
+
+TEST(membarrier, private_expedited_sync_core) {
+ TestRegisterAndBarrierCommands(art::MembarrierCommand::kRegisterPrivateExpeditedSyncCore,
+ art::MembarrierCommand::kPrivateExpeditedSyncCore);
+}
diff --git a/libartbase/base/memfd.cc b/libartbase/base/memfd.cc
new file mode 100644
index 0000000..1afcd7b
--- /dev/null
+++ b/libartbase/base/memfd.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "memfd.h"
+
+#include <errno.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "macros.h"
+
+// When building for linux host, glibc in prebuilts does not include memfd_create system call
+// number. As a temporary testing measure, we add the definition here.
+#if defined(__linux__) && !defined(__NR_memfd_create)
+#if defined(__x86_64__)
+#define __NR_memfd_create 319
+#elif defined(__i386__)
+#define __NR_memfd_create 356
+#endif // defined(__i386__)
+#endif // defined(__linux__) && !defined(__NR_memfd_create)
+
+namespace art {
+
+#if defined(__NR_memfd_create)
+
+int memfd_create(const char* name, unsigned int flags) {
+ return syscall(__NR_memfd_create, name, flags);
+}
+
+#else // __NR_memfd_create
+
+int memfd_create(const char* name ATTRIBUTE_UNUSED, unsigned int flags ATTRIBUTE_UNUSED) {
+ errno = ENOSYS;
+ return -1;
+}
+
+#endif // __NR_memfd_create
+
+} // namespace art
diff --git a/libartbase/base/memfd.h b/libartbase/base/memfd.h
new file mode 100644
index 0000000..4367198
--- /dev/null
+++ b/libartbase/base/memfd.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_MEMFD_H_
+#define ART_LIBARTBASE_BASE_MEMFD_H_
+
+namespace art {
+
+ // Call memfd(2) if available on platform and return result.
+int memfd_create(const char* name, unsigned int flags);
+
+} // namespace art
+
+#endif // ART_LIBARTBASE_BASE_MEMFD_H_
diff --git a/libartbase/base/memfd_test.cc b/libartbase/base/memfd_test.cc
new file mode 100644
index 0000000..1edf3a1
--- /dev/null
+++ b/libartbase/base/memfd_test.cc
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "memfd.h"
+
+TEST(memfd, basic) {
+ errno = 0;
+ int fd = art::memfd_create("memfd_create_test", 0);
+ if (fd < 0) {
+ ASSERT_EQ(ENOSYS, errno);
+ GTEST_LOG_(INFO) << "memfd_create not supported, skipping test.";
+ return;
+ }
+ ASSERT_TRUE(close(fd) == 0 || errno != EBADF);
+}
diff --git a/libartbase/base/utils.cc b/libartbase/base/utils.cc
index 74cc5b9..2242fe8 100644
--- a/libartbase/base/utils.cc
+++ b/libartbase/base/utils.cc
@@ -213,42 +213,4 @@
}
}
-bool FlushInstructionPipeline() {
- // membarrier(2) is only supported for target builds (b/111199492).
-#if defined(__BIONIC__)
- static constexpr int kSyncCoreMask =
- MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE |
- MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE;
- static bool have_probed = false;
- static bool have_sync_core = false;
-
- if (UNLIKELY(!have_probed)) {
- // Probe membarrier(2) commands supported by kernel.
- int commands = syscall(__NR_membarrier, MEMBARRIER_CMD_QUERY, 0);
- if (commands >= 0) {
- have_sync_core = (commands & kSyncCoreMask) == kSyncCoreMask;
- if (have_sync_core) {
- // Register with kernel that we'll be using the private expedited sync core command.
- CheckedCall(syscall,
- "membarrier register sync core",
- __NR_membarrier,
- MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE,
- 0);
- }
- }
- have_probed = true;
- }
-
- if (have_sync_core) {
- CheckedCall(syscall,
- "membarrier sync core",
- __NR_membarrier,
- MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE,
- 0);
- return true;
- }
-#endif // defined(__BIONIC__)
- return false;
-}
-
} // namespace art
diff --git a/libartbase/base/utils.h b/libartbase/base/utils.h
index d85960d..e6a0459 100644
--- a/libartbase/base/utils.h
+++ b/libartbase/base/utils.h
@@ -162,9 +162,6 @@
__builtin___clear_cache(reinterpret_cast<char*>(begin), reinterpret_cast<char*>(end));
}
-// Flush instruction pipeline. Returns true on success, false if feature is unsupported.
-bool FlushInstructionPipeline();
-
template <typename T>
constexpr PointerSize ConvertToPointerSize(T any) {
if (any == 4 || any == 8) {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index bcbdc3b..191795b 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -23,6 +23,7 @@
#include "base/enums.h"
#include "base/histogram-inl.h"
#include "base/logging.h" // For VLOG.
+#include "base/membarrier.h"
#include "base/mem_map.h"
#include "base/quasi_atomic.h"
#include "base/stl_util.h"
@@ -187,6 +188,11 @@
return nullptr;
}
+ // Register for membarrier expedited sync core if JIT will be generating code.
+ if (!used_only_for_profile_data) {
+ art::membarrier(art::MembarrierCommand::kRegisterPrivateExpeditedSyncCore);
+ }
+
// Decide how we should map the code and data sections.
// If we use the code cache just for profiling we do not need to map the code section as
// executable.
@@ -809,9 +815,8 @@
// shootdown (incidentally invalidating the CPU pipelines by sending an IPI to all cores to
// notify them of the TLB invalidation). Some architectures, notably ARM and ARM64, have
// hardware support that broadcasts TLB invalidations and so their kernels have no software
- // based TLB shootdown. FlushInstructionPipeline() is a wrapper around the Linux
- // membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED) syscall which does the appropriate flushing.
- FlushInstructionPipeline();
+ // based TLB shootdown.
+ art::membarrier(art::MembarrierCommand::kPrivateExpeditedSyncCore);
DCHECK(!Runtime::Current()->IsAotCompiler());
if (has_should_deoptimize_flag) {