| /* | 
 |  * 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> | 
 |  | 
 | // membarrier(2) is only supported for bionic builds (b/111199492). | 
 | #if defined(__BIONIC__) | 
 |  | 
 | #include <linux/membarrier.h> | 
 | #include <sys/syscall.h> | 
 |  | 
 | class ScopedErrnoCleaner { | 
 |  public: | 
 |   ScopedErrnoCleaner() { errno = 0; } | 
 |   ~ScopedErrnoCleaner() { errno = 0; } | 
 | }; | 
 |  | 
 | bool HasMembarrier(int membarrier_cmd) { | 
 |   ScopedErrnoCleaner errno_cleaner; | 
 |   int supported_cmds = syscall(__NR_membarrier, MEMBARRIER_CMD_QUERY, 0); | 
 |   return (supported_cmds > 0) && ((supported_cmds & membarrier_cmd) != 0); | 
 | } | 
 |  | 
 | TEST(membarrier, query) { | 
 |   ScopedErrnoCleaner errno_cleaner; | 
 |   int supported = syscall(__NR_membarrier, MEMBARRIER_CMD_QUERY, 0); | 
 |   if (errno == 0) { | 
 |     ASSERT_TRUE(supported >= 0); | 
 |   } else { | 
 |     ASSERT_TRUE(errno == ENOSYS && supported == -1); | 
 |   } | 
 | } | 
 |  | 
 | TEST(membarrier, global_barrier) { | 
 |   if (!HasMembarrier(MEMBARRIER_CMD_GLOBAL)) { | 
 |     GTEST_SKIP() << "MEMBARRIER_CMD_GLOBAL not supported"; | 
 |   } | 
 |   ASSERT_EQ(0, syscall(__NR_membarrier, MEMBARRIER_CMD_GLOBAL, 0)); | 
 | } | 
 |  | 
 | static const char* MembarrierCommandToName(int membarrier_cmd) { | 
 |   switch (membarrier_cmd) { | 
 |   case MEMBARRIER_CMD_QUERY: | 
 |     return "MEMBARRIER_CMD_QUERY"; | 
 |   case MEMBARRIER_CMD_GLOBAL: | 
 |     return "MEMBARRIER_CMD_GLOBAL"; | 
 |   case MEMBARRIER_CMD_GLOBAL_EXPEDITED: | 
 |     return "MEMBARRIER_CMD_GLOBAL_EXPEDITED"; | 
 |   case MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED: | 
 |     return "MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED"; | 
 |   case MEMBARRIER_CMD_PRIVATE_EXPEDITED: | 
 |     return "MEMBARRIER_CMD_PRIVATE_EXPEDITED"; | 
 |   case MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED: | 
 |     return "MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED"; | 
 |   case MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE: | 
 |     return "MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE"; | 
 |   case MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE: | 
 |     return "MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE"; | 
 |   default: | 
 |     return "MEMBARRIER_UNKNOWN"; | 
 |   } | 
 | } | 
 |  | 
 | static void TestRegisterAndBarrierCommands(int membarrier_cmd_register, | 
 |                                            int membarrier_cmd_barrier) { | 
 |   if (!HasMembarrier(membarrier_cmd_register)) { | 
 |     GTEST_SKIP() << MembarrierCommandToName(membarrier_cmd_register) << " not supported"; | 
 |   } | 
 |   if (!HasMembarrier(membarrier_cmd_barrier)) { | 
 |     GTEST_SKIP() << MembarrierCommandToName(membarrier_cmd_barrier) << " not supported"; | 
 |   } | 
 |  | 
 |   ScopedErrnoCleaner errno_cleaner; | 
 |  | 
 |   // Check barrier use without prior registration. | 
 |   if (membarrier_cmd_register == MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED) { | 
 |     // Global barrier use is always okay. | 
 |     ASSERT_EQ(0, syscall(__NR_membarrier, membarrier_cmd_barrier, 0)); | 
 |   } else { | 
 |     // Private barrier should fail. | 
 |     ASSERT_EQ(-1, syscall(__NR_membarrier, membarrier_cmd_barrier, 0)); | 
 |     ASSERT_EQ(EPERM, errno); | 
 |     errno = 0; | 
 |   } | 
 |  | 
 |   // Check registration for barrier succeeds. | 
 |   ASSERT_EQ(0, syscall(__NR_membarrier, membarrier_cmd_register, 0)); | 
 |  | 
 |   // Check barrier use after registration succeeds. | 
 |   ASSERT_EQ(0, syscall(__NR_membarrier, membarrier_cmd_barrier, 0)); | 
 | } | 
 |  | 
 | TEST(membarrier, global_expedited) { | 
 |   TestRegisterAndBarrierCommands(MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED, | 
 |                                  MEMBARRIER_CMD_GLOBAL_EXPEDITED); | 
 | } | 
 |  | 
 | TEST(membarrier, private_expedited) { | 
 |   TestRegisterAndBarrierCommands(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, | 
 |                                  MEMBARRIER_CMD_PRIVATE_EXPEDITED); | 
 | } | 
 |  | 
 | TEST(membarrier, private_expedited_sync_core) { | 
 |   TestRegisterAndBarrierCommands(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE, | 
 |                                  MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE); | 
 | } | 
 |  | 
 | #endif  // __BIONIC__ |