• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MakeOwnerTid(uint32_t raw_tid)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  */
TryLock(uint32_t tid,uint32_t * expected_out)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 
UnlockCommon(uint32_t tid)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 
RecoverSingleSided()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 
Lock(RegionView * region)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 
Unlock(RegionView * region)153 void layout::GuestAndHostLock::Unlock(RegionView* region) {
154   region->SendSignal(UnlockCommon(gettid()), &lock_uint32_);
155 }
156 
Recover(RegionView * region)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
181