blob: 433714f505e6bd464a06e32c0365ca852a81b9ae [file] [log] [blame]
Greg Hartman8efdf092017-07-14 10:05:48 -07001#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.
44static_assert(sizeof(std::atomic<uint32_t>) == 4, "std::atomic size mismatch");
45
46namespace vsoc {
47
Jorge E. Moreirac615b452017-08-07 17:22:44 -070048class RegionView;
Greg Hartman8efdf092017-07-14 10:05:48 -070049
50namespace 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 */
57class 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};
79ASSERT_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 */
85class 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};
127ASSERT_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 */
137class GuestLock : public WaitingLockBase {
138 public:
Greg Hartmana4ff2482017-10-03 16:35:00 -0700139#ifndef CUTTLEFISH_HOST
Greg Hartman8efdf092017-07-14 10:05:48 -0700140 void Lock();
141 void Unlock();
142#endif
143};
144ASSERT_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 */
154class HostLock : public WaitingLockBase {
155 public:
Greg Hartmana4ff2482017-10-03 16:35:00 -0700156#ifdef CUTTLEFISH_HOST
Greg Hartman8efdf092017-07-14 10:05:48 -0700157 void Lock();
158 void Unlock();
159#endif
160};
161ASSERT_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 */
187class GuestAndHostLock : public WaitingLockBase {
188 public:
Jorge E. Moreirac615b452017-08-07 17:22:44 -0700189 void Lock(RegionView*);
190 void Unlock(RegionView*);
Greg Hartman8efdf092017-07-14 10:05:48 -0700191};
192ASSERT_SHM_COMPATIBLE(GuestAndHostLock, multi_region);
193
194} // namespace layout
195} // namespace vsoc