Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -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 | // Object that represents a region on the Host |
| 20 | |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 21 | #include <stdlib.h> |
| 22 | #include <sys/mman.h> |
Greg Hartman | aca3bc7 | 2017-07-17 16:03:45 -0700 | [diff] [blame] | 23 | #include <atomic> |
| 24 | #include <cstdint> |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 25 | |
Jorge E. Moreira | 351f783 | 2017-10-14 13:14:07 -0700 | [diff] [blame] | 26 | #include <functional> |
Jorge E. Moreira | 4b9bbb1 | 2017-12-28 15:49:06 -0800 | [diff] [blame] | 27 | #include <map> |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 28 | #include <thread> |
| 29 | |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 30 | #include "common/libs/fs/shared_fd.h" |
Greg Hartman | 4dd9c42 | 2017-07-19 18:14:59 -0700 | [diff] [blame] | 31 | #include "common/libs/glog/logging.h" |
Greg Hartman | fd32a45 | 2017-11-05 08:11:13 -0800 | [diff] [blame] | 32 | #include "common/vsoc/lib/lock_guard.h" |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 33 | #include "common/vsoc/lib/region_control.h" |
Greg Hartman | fd32a45 | 2017-11-05 08:11:13 -0800 | [diff] [blame] | 34 | #include "common/vsoc/lib/region_signaling_interface.h" |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 35 | #include "common/vsoc/shm/base.h" |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 36 | #include "uapi/vsoc_shm.h" |
| 37 | |
| 38 | namespace vsoc { |
| 39 | |
Jorge E. Moreira | c615b45 | 2017-08-07 17:22:44 -0700 | [diff] [blame] | 40 | class RegionView; |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 41 | |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 42 | /** |
| 43 | * Represents a task that is tied to a RegionView. |
| 44 | * |
| 45 | * This is currently used for the task that forwards futexes across the |
| 46 | * shared memory window. |
| 47 | */ |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 48 | class RegionWorker { |
| 49 | public: |
Jorge E. Moreira | c615b45 | 2017-08-07 17:22:44 -0700 | [diff] [blame] | 50 | explicit RegionWorker(RegionView* region); |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 51 | ~RegionWorker(); |
| 52 | void Work(); |
| 53 | |
| 54 | protected: |
Jorge E. Moreira | c615b45 | 2017-08-07 17:22:44 -0700 | [diff] [blame] | 55 | RegionView* region_; |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 56 | std::thread thread_; |
| 57 | volatile bool stopping_{}; |
| 58 | }; |
| 59 | |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 60 | /** |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 61 | * Base class to access a mapped region in VSoC shared memory. |
| 62 | * This class holds the methods that depends on the region's memory having an |
| 63 | * address. The RegionControl class holds the methods that can be invoked |
| 64 | * without mapping the region. |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 65 | */ |
Ping-Hao Wu | 9eed50a | 2017-10-17 16:57:27 -0700 | [diff] [blame] | 66 | class RegionView : public RegionSignalingInterface { |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 67 | public: |
Jorge E. Moreira | c615b45 | 2017-08-07 17:22:44 -0700 | [diff] [blame] | 68 | virtual ~RegionView(); |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 69 | |
Greg Hartman | 6da4302 | 2017-12-05 11:16:02 -0800 | [diff] [blame] | 70 | #if defined(CUTTLEFISH_HOST) |
| 71 | bool Open(const char* region_name, const char* domain); |
| 72 | #else |
| 73 | bool Open(const char* region_name); |
| 74 | #endif |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 75 | |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 76 | // Returns a pointer to the table that will be scanned for signals |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 77 | const vsoc_signal_table_layout& incoming_signal_table(); |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 78 | |
| 79 | // Returns a pointer to the table that will be used to post signals |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 80 | const vsoc_signal_table_layout& outgoing_signal_table(); |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 81 | |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 82 | // Returns true iff an interrupt is queued in the signal table |
| 83 | bool HasIncomingInterrupt() { |
Greg Hartman | 3ea6c0a | 2017-08-01 19:03:19 -0700 | [diff] [blame] | 84 | return *region_offset_to_pointer<std::atomic<uint32_t>>( |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 85 | incoming_signal_table().interrupt_signalled_offset); |
Greg Hartman | 3ea6c0a | 2017-08-01 19:03:19 -0700 | [diff] [blame] | 86 | } |
| 87 | |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 88 | // Wake any threads waiting for an interrupt. This is generally used during |
| 89 | // shutdown. |
| 90 | void InterruptSelf() { control_->InterruptSelf(); } |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 91 | |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 92 | // Interrupt our peer if an interrupt is not already on the way. |
| 93 | // Returns true if the interrupt was sent, false if an interrupt was already |
| 94 | // pending. |
| 95 | bool MaybeInterruptPeer(); |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 96 | |
| 97 | // Scan in the incoming signal table, issuing futex calls for any posted |
| 98 | // signals and then reposting them to the peer if they were round-trip |
| 99 | // signals. |
| 100 | // |
Jorge E. Moreira | 351f783 | 2017-10-14 13:14:07 -0700 | [diff] [blame] | 101 | // signal_handler: An action to perform on every offset signalled by our |
| 102 | // peer, usually a FUTEX_WAKE call, but can be customized for other |
| 103 | // purposes. |
Greg Hartman | 0249021 | 2017-12-13 19:49:10 -0800 | [diff] [blame] | 104 | void ProcessSignalsFromPeer( |
| 105 | std::function<void(std::atomic<uint32_t>*)> signal_handler); |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 106 | |
| 107 | // Post a signal to the guest, the host, or both. |
| 108 | // See futex(2) FUTEX_WAKE for details. |
| 109 | // |
| 110 | // sides_to_signal: controls where the signal is sent |
| 111 | // |
| 112 | // signal_addr: the memory location to signal. Must be within the region. |
Greg Hartman | 0249021 | 2017-12-13 19:49:10 -0800 | [diff] [blame] | 113 | void SendSignal(layout::Sides sides_to_signal, |
| 114 | std::atomic<uint32_t>* signal_addr); |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 115 | |
| 116 | // Post a signal to our peer for a specific memeory location. |
| 117 | // See futex(2) FUTEX_WAKE for details. |
| 118 | // |
| 119 | // signal_addr: the memory location to signal. Must be within the region. |
| 120 | // |
| 121 | // round_trip: true if there may be waiters on both sides of the shared |
| 122 | // memory. |
Greg Hartman | 0249021 | 2017-12-13 19:49:10 -0800 | [diff] [blame] | 123 | void SendSignalToPeer(std::atomic<uint32_t>* signal_addr, bool round_trip); |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 124 | |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 125 | // Waits until an interrupt appears on this region, then clears the |
| 126 | // interrupted flag and returns. |
| 127 | void WaitForInterrupt(); |
| 128 | |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 129 | // This implements the following: |
| 130 | // if (*signal_addr == last_observed_value) |
| 131 | // wait_for_signal_at(signal_addr); |
| 132 | // |
| 133 | // Note: the caller still needs to check the value at signal_addr because |
| 134 | // this function may return early for reasons that are implementation-defined. |
| 135 | // See futex(2) FUTEX_WAIT for details. |
| 136 | // |
| 137 | // signal_addr: the memory that will be signaled. Must be within the region. |
| 138 | // |
| 139 | // last_observed_value: the value that motivated the calling code to wait. |
Greg Hartman | 0249021 | 2017-12-13 19:49:10 -0800 | [diff] [blame] | 140 | void WaitForSignal(std::atomic<uint32_t>* signal_addr, |
| 141 | uint32_t last_observed_value); |
Greg Hartman | ffe99fa | 2017-07-14 14:46:19 -0700 | [diff] [blame] | 142 | |
| 143 | // Starts the signal table scanner. This must be invoked by subclasses, which |
| 144 | // must store the returned unique_ptr as a class member. |
| 145 | std::unique_ptr<RegionWorker> StartWorker(); |
| 146 | |
Greg Hartman | 86b114a | 2017-11-03 21:04:14 -0700 | [diff] [blame] | 147 | // Returns a pointer to the start of region data that is cast to the given |
Greg Hartman | 6454596 | 2017-12-13 19:37:03 -0800 | [diff] [blame] | 148 | // type. Initializers that run in the launcher use this to get a typed view |
| 149 | // of the region. Most other cases should be handled via TypedRegionView. |
Greg Hartman | 86b114a | 2017-11-03 21:04:14 -0700 | [diff] [blame] | 150 | template <typename LayoutType> |
| 151 | LayoutType* GetLayoutPointer() { |
| 152 | return this->region_offset_to_pointer<LayoutType>( |
| 153 | control_->region_desc().offset_of_region_data); |
| 154 | } |
| 155 | |
Greg Hartman | fd32a45 | 2017-11-05 08:11:13 -0800 | [diff] [blame] | 156 | // Helper functions for lock guards. This approach has two advantages: |
| 157 | // |
| 158 | // o Callers don't have to be refactored when lock types change |
| 159 | // o The region pointer can be provided automatically |
| 160 | template <typename T> |
| 161 | LockGuard<T> make_lock_guard(T* lock) { |
| 162 | return LockGuard<T>(lock); |
| 163 | } |
| 164 | |
| 165 | LockGuard<::vsoc::layout::GuestAndHostLock> make_lock_guard( |
| 166 | ::vsoc::layout::GuestAndHostLock* l) { |
| 167 | return LockGuard<::vsoc::layout::GuestAndHostLock>(l, this); |
| 168 | } |
| 169 | |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 170 | protected: |
Greg Hartman | aca3bc7 | 2017-07-17 16:03:45 -0700 | [diff] [blame] | 171 | template <typename T> |
Jorge E. Moreira | 3781d34 | 2017-07-26 15:06:26 -0700 | [diff] [blame] | 172 | T* region_offset_to_pointer(vsoc_reg_off_t offset) { |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 173 | if (offset > control_->region_size()) { |
Greg Hartman | 4dd9c42 | 2017-07-19 18:14:59 -0700 | [diff] [blame] | 174 | LOG(FATAL) << __FUNCTION__ << ": " << offset << " not in region @" |
| 175 | << region_base_; |
| 176 | } |
Greg Hartman | aca3bc7 | 2017-07-17 16:03:45 -0700 | [diff] [blame] | 177 | return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(region_base_) + |
Greg Hartman | 4dd9c42 | 2017-07-19 18:14:59 -0700 | [diff] [blame] | 178 | offset); |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 179 | } |
| 180 | |
Greg Hartman | aca3bc7 | 2017-07-17 16:03:45 -0700 | [diff] [blame] | 181 | template <typename T> |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 182 | const T& region_offset_to_reference(vsoc_reg_off_t offset) const { |
| 183 | if (offset > control_->region_size()) { |
| 184 | LOG(FATAL) << __FUNCTION__ << ": " << offset << " not in region @" |
| 185 | << region_base_; |
| 186 | } |
| 187 | return *reinterpret_cast<const T*>( |
| 188 | reinterpret_cast<uintptr_t>(region_base_) + offset); |
| 189 | } |
| 190 | |
| 191 | // Calculates an offset based on a pointer in the region. Crashes if the |
| 192 | // pointer isn't in the region. |
| 193 | // This is mostly for the RegionView's internal plumbing. Use TypedRegionView |
| 194 | // and RegionLayout to avoid this in most cases. |
| 195 | template <typename T> |
Jorge E. Moreira | 3781d34 | 2017-07-26 15:06:26 -0700 | [diff] [blame] | 196 | vsoc_reg_off_t pointer_to_region_offset(T* ptr) { |
| 197 | vsoc_reg_off_t rval = reinterpret_cast<uintptr_t>(ptr) - |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 198 | reinterpret_cast<uintptr_t>(region_base_); |
| 199 | if (rval > control_->region_size()) { |
Greg Hartman | 4dd9c42 | 2017-07-19 18:14:59 -0700 | [diff] [blame] | 200 | LOG(FATAL) << __FUNCTION__ << ": " << ptr << " not in region @" |
| 201 | << region_base_; |
| 202 | } |
| 203 | return rval; |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 204 | } |
| 205 | |
Greg Hartman | e08d8b2 | 2017-08-08 16:44:29 -0700 | [diff] [blame] | 206 | std::shared_ptr<RegionControl> control_; |
Greg Hartman | aca3bc7 | 2017-07-17 16:03:45 -0700 | [diff] [blame] | 207 | void* region_base_{}; |
Greg Hartman | 40b88f5 | 2017-06-22 15:34:11 -0700 | [diff] [blame] | 208 | }; |
| 209 | |
Greg Hartman | aca3bc7 | 2017-07-17 16:03:45 -0700 | [diff] [blame] | 210 | } // namespace vsoc |