diff options
| author | 2018-09-20 05:58:08 +0000 | |
|---|---|---|
| committer | 2018-09-20 05:58:08 +0000 | |
| commit | b32535defe6fcec752f4e78f8cfa2746037a1f70 (patch) | |
| tree | 1bf8605b2a00731c3e20d4b0ca2ee3834bcdfb52 | |
| parent | 144f0826919bf7af915bd165fc67eb12aead843c (diff) | |
| parent | 563ada2e5570d947ee2b96e3651ded3692be33be (diff) | |
Merge "ART: Add wrappers for membarrier and memfd_create()"
| -rw-r--r-- | libartbase/Android.bp | 4 | ||||
| -rw-r--r-- | libartbase/base/membarrier.cc | 72 | ||||
| -rw-r--r-- | libartbase/base/membarrier.h | 51 | ||||
| -rw-r--r-- | libartbase/base/membarrier_test.cc | 111 | ||||
| -rw-r--r-- | libartbase/base/memfd.cc | 52 | ||||
| -rw-r--r-- | libartbase/base/memfd.h | 27 | ||||
| -rw-r--r-- | libartbase/base/memfd_test.cc | 30 | ||||
| -rw-r--r-- | libartbase/base/utils.cc | 38 | ||||
| -rw-r--r-- | libartbase/base/utils.h | 3 | ||||
| -rw-r--r-- | runtime/jit/jit_code_cache.cc | 11 |
10 files changed, 355 insertions, 44 deletions
diff --git a/libartbase/Android.bp b/libartbase/Android.bp index 1b603b51e1..19f15322ef 100644 --- a/libartbase/Android.bp +++ b/libartbase/Android.bp @@ -29,6 +29,8 @@ cc_defaults { "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 @@ art_cc_test { "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 0000000000..490dbf3fa0 --- /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 0000000000..f829fc1f80 --- /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 0000000000..3eedf14dcd --- /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 0000000000..1afcd7b311 --- /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 0000000000..4367198185 --- /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 0000000000..1edf3a11ce --- /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 74cc5b97b2..2242fe877e 100644 --- a/libartbase/base/utils.cc +++ b/libartbase/base/utils.cc @@ -213,42 +213,4 @@ void SleepForever() { } } -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 d85960d811..e6a0459e27 100644 --- a/libartbase/base/utils.h +++ b/libartbase/base/utils.h @@ -162,9 +162,6 @@ inline void FlushInstructionCache(void* begin, void* end) { __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 bcbdc3bece..191795be91 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 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, 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 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, // 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) { |