Greg Hartman | 8efdf09 | 2017-07-14 10:05:48 -0700 | [diff] [blame] | 1 | #pragma once |
| 2 | |
| 3 | /* |
| 4 | * Copyright (C) 2017 The Android Open Source Project |
| 5 | * |
| 6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | * you may not use this file except in compliance with the License. |
| 8 | * You may obtain a copy of the License at |
| 9 | * |
| 10 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | * |
| 12 | * Unless required by applicable law or agreed to in writing, software |
| 13 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | * See the License for the specific language governing permissions and |
| 16 | * limitations under the License. |
| 17 | */ |
| 18 | |
| 19 | // Memory layout for locks of all types. |
| 20 | |
| 21 | // The vsoc::layout namespace indicates that these are shared memory structure |
| 22 | // definitions. The #include's given above are strictly limited, as are the |
| 23 | // types that can be referenced below. |
| 24 | |
| 25 | // For _mm_pause() |
| 26 | #include <x86intrin.h> |
| 27 | |
| 28 | #include <atomic> |
| 29 | #include <cstdint> |
| 30 | |
| 31 | #include <linux/futex.h> |
| 32 | #include <sys/syscall.h> |
| 33 | |
| 34 | #include <unistd.h> |
| 35 | |
| 36 | #include "common/vsoc/shm/base.h" |
| 37 | #include "common/vsoc/shm/version.h" |
| 38 | |
| 39 | // Host userspace, guest userspace, and the guest kernel must all agree on |
| 40 | // the relationship between std::atomic and atomic_t. That's hard to do without |
| 41 | // examining assembly, and we can't really examing atomic_t outside of the |
| 42 | // kernel tree, but we can at least assert that the host and the guest |
| 43 | // agree on a size. |
| 44 | static_assert(sizeof(std::atomic<uint32_t>) == 4, "std::atomic size mismatch"); |
| 45 | |
| 46 | namespace vsoc { |
| 47 | |
Jorge E. Moreira | c615b45 | 2017-08-07 17:22:44 -0700 | [diff] [blame] | 48 | class RegionView; |
Greg Hartman | 8efdf09 | 2017-07-14 10:05:48 -0700 | [diff] [blame] | 49 | |
| 50 | namespace layout { |
| 51 | |
| 52 | /** |
| 53 | * Lock that causes threads to busy loop rather than sleeping. |
| 54 | * This lock should never be used when the amount of work in the critical |
| 55 | * section cannot be bounded. |
| 56 | */ |
| 57 | class SpinLock : public Base { |
| 58 | public: |
| 59 | /** |
| 60 | * Acquire the spinlock on the queue. This will effectively block all |
| 61 | * readers and writers. |
| 62 | */ |
| 63 | void Lock() { |
| 64 | while (lock_.exchange(1)) { |
| 65 | _mm_pause(); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | /** |
| 70 | * Release the spinlock. |
| 71 | */ |
| 72 | void Unlock() { |
| 73 | lock_ = 0; |
| 74 | } |
| 75 | |
| 76 | protected: |
| 77 | std::atomic<uint32_t> lock_; |
| 78 | }; |
| 79 | ASSERT_SHM_COMPATIBLE(SpinLock, multi_region); |
| 80 | |
| 81 | /** |
| 82 | * This is a generic synchronization primitive that provides space for the |
| 83 | * owner of the lock to write platform-specific information. |
| 84 | */ |
| 85 | class WaitingLockBase : public Base { |
| 86 | protected: |
| 87 | // Common code to handle locking |
| 88 | // Must be called with the kernel's thread id |
| 89 | // Returns true if the lock was acquired. In this case the value in |
| 90 | // expected_vlaue is undefined. |
| 91 | // Returns false if locking failed. The value discovered in the lock word |
| 92 | // is returned in expected_value, and should probably be used in a conditional |
| 93 | // sleep. |
| 94 | bool TryLock(uint32_t tid, uint32_t* expected_value); |
| 95 | |
| 96 | // Common code to handle unlocking. |
| 97 | // Must be called with the kernel's thread id |
| 98 | // Returns sides that should be signalled or 0 |
| 99 | Sides UnlockCommon(uint32_t tid); |
| 100 | |
| 101 | // Non-zero values in this word indicate that the lock is in use. |
| 102 | // This is 32 bits for compatibility with futex() |
| 103 | std::atomic<uint32_t> lock_uint32_; |
| 104 | |
| 105 | // Pad so we line up with glib's pthread_mutex_t and can share the same queue. |
| 106 | // These fields may be redefined at any point in the future. They should not |
| 107 | // be used. |
| 108 | private: |
| 109 | // These fields are known to be unused and are provided for compatibility |
| 110 | // with glibc's locks. |
| 111 | #pragma clang diagnostic push |
| 112 | #pragma clang diagnostic ignored "-Wunused-private-field" |
| 113 | uint32_t reserved_1_; |
| 114 | char reserved_2_[16]; |
| 115 | // Provide scratch space for the owner of the lock. The content of this space |
| 116 | // is undefined when the lock is acquired. The owner may write to and read |
| 117 | // from it while it holds the lock, but must relinquish control before |
| 118 | // releasing the lock. |
| 119 | // |
| 120 | // This is intended to support Linux robust futexes. See the documentation |
| 121 | // in the kernel tree: |
| 122 | // Documentation/robust-futex-ABI.txt |
| 123 | public: |
| 124 | int64_t owner_scratch_[2]; |
| 125 | #pragma clang diagnostic pop |
| 126 | }; |
| 127 | ASSERT_SHM_COMPATIBLE(WaitingLockBase, multi_region); |
| 128 | |
| 129 | /** |
| 130 | * GuestLocks can be acquired and released only on the guest. They reside |
| 131 | * in the shared memory window because mutiple guest processes may need |
| 132 | * to coordinate activities in certain shared memory regions. |
| 133 | * |
| 134 | * Representing this as a concrete type allows for some optimizations when |
| 135 | * signalling on the lock. |
| 136 | */ |
| 137 | class GuestLock : public WaitingLockBase { |
| 138 | public: |
Greg Hartman | a4ff248 | 2017-10-03 16:35:00 -0700 | [diff] [blame^] | 139 | #ifndef CUTTLEFISH_HOST |
Greg Hartman | 8efdf09 | 2017-07-14 10:05:48 -0700 | [diff] [blame] | 140 | void Lock(); |
| 141 | void Unlock(); |
| 142 | #endif |
| 143 | }; |
| 144 | ASSERT_SHM_COMPATIBLE(GuestLock, multi_region); |
| 145 | |
| 146 | /** |
| 147 | * HostLocks can be acquired and released only on the host. They reside |
| 148 | * in the shared memory window because mutiple host processes may need |
| 149 | * to coordinate activities in certain shared memory regions. |
| 150 | * |
| 151 | * Representing this as a concrete type allows for some optimizations when |
| 152 | * signalling on the lock. |
| 153 | */ |
| 154 | class HostLock : public WaitingLockBase { |
| 155 | public: |
Greg Hartman | a4ff248 | 2017-10-03 16:35:00 -0700 | [diff] [blame^] | 156 | #ifdef CUTTLEFISH_HOST |
Greg Hartman | 8efdf09 | 2017-07-14 10:05:48 -0700 | [diff] [blame] | 157 | void Lock(); |
| 158 | void Unlock(); |
| 159 | #endif |
| 160 | }; |
| 161 | ASSERT_SHM_COMPATIBLE(HostLock, multi_region); |
| 162 | |
| 163 | /** |
| 164 | * GuestAndHostLocks can be acquired and released on either side of the |
| 165 | * shared memory window. The locks attempt to enforce fairness by using |
| 166 | * a round-trip signal: |
| 167 | * |
| 168 | * When a guest releases a lock this code sends a signal to wake the host, |
| 169 | * but not other guest waiters. |
| 170 | * |
| 171 | * The wake handler on the host wakes up and local waiters and then reposts |
| 172 | * the signal to the guest. |
| 173 | * |
| 174 | * When the guest receives the signal from the host it then wakes ups |
| 175 | * any waiters. |
| 176 | * |
| 177 | * A similar scenario applies when the host releases a lock with guest waiters. |
| 178 | * |
| 179 | * Signalling across the shared memory window twice has non-trivial cost. |
| 180 | * There are some optimizations in the code to prevent the full round-trip |
| 181 | * if the process releasing the lock can confirm that there are no waiters on |
| 182 | * the other side. |
| 183 | * |
| 184 | * Representing this as a concrete type allows for some optimizations when |
| 185 | * signalling on the lock. |
| 186 | */ |
| 187 | class GuestAndHostLock : public WaitingLockBase { |
| 188 | public: |
Jorge E. Moreira | c615b45 | 2017-08-07 17:22:44 -0700 | [diff] [blame] | 189 | void Lock(RegionView*); |
| 190 | void Unlock(RegionView*); |
Greg Hartman | 8efdf09 | 2017-07-14 10:05:48 -0700 | [diff] [blame] | 191 | }; |
| 192 | ASSERT_SHM_COMPATIBLE(GuestAndHostLock, multi_region); |
| 193 | |
| 194 | } // namespace layout |
| 195 | } // namespace vsoc |