Home | History | Annotate | Download | only in lib
      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
     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.
     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>
    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>
    170   LockGuard<T> make_lock_guard(T* lock) {
    171     return LockGuard<T>(lock);
    172   }
    173 
    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>
    181   T* region_offset_to_pointer(uint32_t offset) {
    182     return control_->region_offset_to_pointer<T>(offset);
    183   }
    184 
    185   template <typename T>
    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>
    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