blob: c1005749faf7eb20f03735da2570795c2d86ebb8 [file] [log] [blame]
Cody Schuffelen134ff032019-11-22 00:25:32 -08001/*
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
24using vsoc::layout::Sides;
25
26namespace {
27
28const uint32_t LockFree = 0U;
29const uint32_t GuestWaitingFlag = (Sides::Guest << 30);
30const uint32_t HostWaitingFlag = (Sides::Host << 30);
31const uint32_t OurWaitingFlag = (Sides::OurSide << 30);
32static_assert(GuestWaitingFlag, "GuestWaitingFlag is 0");
33static_assert(HostWaitingFlag, "HostWaitingFlag is 0");
34static_assert((GuestWaitingFlag & HostWaitingFlag) == 0,
35 "Waiting flags should not share bits");
36
37// Set if the current owner is the host
38const uint32_t HostIsOwner = 0x20000000U;
39
40// PID_MAX_LIMIT appears to be 0x00400000U, so we're probably ok here
41const uint32_t OwnerMask = 0x3FFFFFFFU;
42
43uint32_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
53namespace 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 */
58bool 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
102layout::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
136bool 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
142void 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
153void layout::GuestAndHostLock::Unlock(RegionView* region) {
154 region->SendSignal(UnlockCommon(gettid()), &lock_uint32_);
155}
156
157bool 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