• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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