Cody Schuffelen | 134ff03 | 2019-11-22 00:25:32 -0800 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | #include "common/vsoc/shm/lock.h" |
| 17 | |
| 18 | #include "common/libs/glog/logging.h" |
| 19 | #include "common/vsoc/lib/compat.h" |
| 20 | #include "common/vsoc/lib/region_view.h" |
| 21 | |
| 22 | #include <stdlib.h> |
| 23 | |
| 24 | using vsoc::layout::Sides; |
| 25 | |
| 26 | namespace { |
| 27 | |
| 28 | const uint32_t LockFree = 0U; |
| 29 | const uint32_t GuestWaitingFlag = (Sides::Guest << 30); |
| 30 | const uint32_t HostWaitingFlag = (Sides::Host << 30); |
| 31 | const uint32_t OurWaitingFlag = (Sides::OurSide << 30); |
| 32 | static_assert(GuestWaitingFlag, "GuestWaitingFlag is 0"); |
| 33 | static_assert(HostWaitingFlag, "HostWaitingFlag is 0"); |
| 34 | static_assert((GuestWaitingFlag & HostWaitingFlag) == 0, |
| 35 | "Waiting flags should not share bits"); |
| 36 | |
| 37 | // Set if the current owner is the host |
| 38 | const uint32_t HostIsOwner = 0x20000000U; |
| 39 | |
| 40 | // PID_MAX_LIMIT appears to be 0x00400000U, so we're probably ok here |
| 41 | const uint32_t OwnerMask = 0x3FFFFFFFU; |
| 42 | |
| 43 | uint32_t MakeOwnerTid(uint32_t raw_tid) { |
| 44 | if (Sides::OurSide == Sides::Host) { |
| 45 | return (raw_tid | HostIsOwner) & OwnerMask; |
| 46 | } else { |
| 47 | return raw_tid & (OwnerMask & ~HostIsOwner); |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | }; // namespace |
| 52 | |
| 53 | namespace vsoc { |
| 54 | /** |
| 55 | * This is a generic synchronization primitive that provides space for the |
| 56 | * owner of the lock to write platform-specific information. |
| 57 | */ |
| 58 | bool vsoc::layout::WaitingLockBase::TryLock(uint32_t tid, |
| 59 | uint32_t* expected_out) { |
| 60 | uint32_t masked_tid = MakeOwnerTid(tid); |
| 61 | uint32_t expected = LockFree; |
| 62 | while (1) { |
| 63 | // First try to lock assuming that the mutex is free |
| 64 | if (lock_uint32_.compare_exchange_strong(expected, masked_tid)) { |
| 65 | // We got the lock. |
| 66 | return true; |
| 67 | } |
| 68 | // We didn't get the lock and our wait flag is already set. It's safe to |
| 69 | // try to sleep |
| 70 | if (expected & OurWaitingFlag) { |
| 71 | *expected_out = expected; |
| 72 | return false; |
| 73 | } |
| 74 | // Attempt to set the wait flag. This will fail if the lock owner changes. |
| 75 | while (1) { |
| 76 | uint32_t add_wait_flag = expected | OurWaitingFlag; |
| 77 | if (lock_uint32_.compare_exchange_strong(expected, add_wait_flag)) { |
| 78 | // We set the waiting flag. Try to sleep. |
| 79 | *expected_out = expected; |
| 80 | return false; |
| 81 | } |
| 82 | // The owner changed, but we at least we got the wait flag. |
| 83 | // Try sleeping |
| 84 | if (expected & OurWaitingFlag) { |
| 85 | *expected_out = expected; |
| 86 | return false; |
| 87 | } |
| 88 | // Special case: the lock was just freed. Stop trying to set the |
| 89 | // waiting flag and try to grab the lock. |
| 90 | if (expected == LockFree) { |
| 91 | break; |
| 92 | } |
| 93 | // The owner changed and we have no wait flag |
| 94 | // Try to set the wait flag again |
| 95 | } |
| 96 | // This only happens if the lock was freed while we attempt the set the |
| 97 | // wait flag. Try to grab the lock again |
| 98 | } |
| 99 | // Never reached. |
| 100 | } |
| 101 | |
| 102 | layout::Sides vsoc::layout::WaitingLockBase::UnlockCommon(uint32_t tid) { |
| 103 | uint32_t expected_state = lock_uint32_; |
| 104 | |
| 105 | // We didn't hold the lock. This process is insane and must die before it |
| 106 | // does damage. |
| 107 | uint32_t marked_tid = MakeOwnerTid(tid); |
| 108 | if ((marked_tid ^ expected_state) & OwnerMask) { |
| 109 | LOG(FATAL) << tid << " unlocking " << this << " owned by " |
| 110 | << expected_state; |
| 111 | } |
| 112 | // If contention is just starting this may fail twice (once for each bit) |
| 113 | // expected_state updates on each failure. When this finishes we have |
| 114 | // one bit for each waiter |
| 115 | while (1) { |
| 116 | if (lock_uint32_.compare_exchange_strong(expected_state, LockFree)) { |
| 117 | break; |
| 118 | } |
| 119 | } |
| 120 | if ((expected_state ^ marked_tid) & OwnerMask) { |
| 121 | LOG(FATAL) << "Lock owner of " << this << " changed from " << tid << " to " |
| 122 | << expected_state << " during unlock"; |
| 123 | } |
| 124 | switch (expected_state & (GuestWaitingFlag | HostWaitingFlag)) { |
| 125 | case 0: |
| 126 | return Sides::NoSides; |
| 127 | case GuestWaitingFlag: |
| 128 | return Sides::Guest; |
| 129 | case HostWaitingFlag: |
| 130 | return Sides::Host; |
| 131 | default: |
| 132 | return Sides::Both; |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | bool vsoc::layout::WaitingLockBase::RecoverSingleSided() { |
| 137 | // No need to signal because the caller ensured that there were no other |
| 138 | // threads... |
| 139 | return lock_uint32_.exchange(LockFree) != LockFree; |
| 140 | } |
| 141 | |
| 142 | void layout::GuestAndHostLock::Lock(RegionView* region) { |
| 143 | uint32_t expected; |
| 144 | uint32_t tid = gettid(); |
| 145 | while (1) { |
| 146 | if (TryLock(tid, &expected)) { |
| 147 | return; |
| 148 | } |
| 149 | region->WaitForSignal(&lock_uint32_, expected); |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | void layout::GuestAndHostLock::Unlock(RegionView* region) { |
| 154 | region->SendSignal(UnlockCommon(gettid()), &lock_uint32_); |
| 155 | } |
| 156 | |
| 157 | bool layout::GuestAndHostLock::Recover(RegionView* region) { |
| 158 | uint32_t expected_state = lock_uint32_; |
| 159 | uint32_t expected_owner_bit = (Sides::OurSide == Sides::Host) ? HostIsOwner : 0; |
| 160 | // This avoids check then act by reading exactly once and then |
| 161 | // eliminating the states where Recover should be a noop. |
| 162 | if (expected_state == LockFree) { |
| 163 | return false; |
| 164 | } |
| 165 | // Owned by the other side, do nothing. |
| 166 | if ((expected_state & HostIsOwner) != expected_owner_bit) { |
| 167 | return false; |
| 168 | } |
| 169 | // At this point we know two things: |
| 170 | // * The lock was held by our side |
| 171 | // * There are no other threads running on our side (precondition |
| 172 | // for calling Recover()) |
| 173 | // Therefore, we know that the current expected value should still |
| 174 | // be in the lock structure. Use the normal unlock logic, providing |
| 175 | // the tid that we observed in the lock. |
| 176 | region->SendSignal(UnlockCommon(expected_state), &lock_uint32_); |
| 177 | return true; |
| 178 | } |
| 179 | |
| 180 | } // namespace vsoc |