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