Home | History | Annotate | Download | only in shm
      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 // 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 #if defined(__SSE2__)
     27 #include <x86intrin.h>
     28 #define _pause() _mm_pause()
     29 #elif defined(__arm__) || defined(__aarch64__)
     30 #include <arm_acle.h>
     31 #define _pause() __yield()
     32 #endif
     33 
     34 #include <atomic>
     35 #include <cstdint>
     36 
     37 #include "common/vsoc/shm/base.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.
     44 static_assert(sizeof(std::atomic<uint32_t>) == 4, "std::atomic size mismatch");
     45 
     46 namespace vsoc {
     47 
     48 class RegionView;
     49 
     50 namespace 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  */
     57 class SpinLock {
     58  public:
     59   static constexpr size_t layout_size = 4;
     60 
     61   /**
     62    * Acquire the spinlock on the queue. This will effectively block all
     63    * readers and writers.
     64    */
     65   void Lock() {
     66     while (1) {
     67       uint32_t expected = 0;
     68       if (lock_.compare_exchange_strong(expected, Sides::OurSide)) {
     69         return;
     70       }
     71       _pause();
     72     }
     73   }
     74 
     75   /**
     76    * Drop the lock iff it is currently held by this side. Used by
     77    * recovery code that cleans up regions in the event of a reboot
     78    * (guest side) or a service restart (host side).
     79    *
     80    * The caller must ensure that there are no other threads on its
     81    * side (e.g. guest/host) are using the window.
     82    */
     83   bool Recover() {
     84     uint32_t expected = Sides::OurSide;
     85     return lock_.compare_exchange_strong(expected, 0);
     86   }
     87 
     88   /**
     89    * Release the spinlock.
     90    */
     91   void Unlock() {
     92     lock_ = 0;
     93   }
     94 
     95  protected:
     96   std::atomic<uint32_t> lock_;
     97 };
     98 ASSERT_SHM_COMPATIBLE(SpinLock);
     99 
    100 /**
    101  * This is a generic synchronization primitive that provides space for the
    102  * owner of the lock to write platform-specific information.
    103  */
    104 class WaitingLockBase {
    105  public:
    106   static constexpr size_t layout_size = 40;
    107 
    108  protected:
    109   // Common code to handle locking
    110   // Must be called with the kernel's thread id
    111   // Returns true if the lock was acquired. In this case the value in
    112   // expected_vlaue is undefined.
    113   // Returns false if locking failed. The value discovered in the lock word
    114   // is returned in expected_value, and should probably be used in a conditional
    115   // sleep.
    116   bool TryLock(uint32_t tid, uint32_t* expected_value);
    117 
    118   // Common code to handle unlocking.
    119   // Must be called with the kernel's thread id
    120   // Returns sides that should be signalled or 0
    121   Sides UnlockCommon(uint32_t tid);
    122 
    123   // Common code to recover single-sided locks.
    124   bool RecoverSingleSided();
    125 
    126   // Non-zero values in this word indicate that the lock is in use.
    127   // This is 32 bits for compatibility with futex()
    128   std::atomic<uint32_t> lock_uint32_;
    129 
    130   // Pad so we line up with glib's pthread_mutex_t and can share the same queue.
    131   // These fields may be redefined at any point in the future. They should not
    132   // be used.
    133  private:
    134 // These fields are known to be unused and are provided for compatibility
    135 // with glibc's locks.
    136 #pragma clang diagnostic push
    137 #pragma clang diagnostic ignored "-Wunused-private-field"
    138   uint32_t reserved_1_;
    139   char reserved_2_[16];
    140   // Provide scratch space for the owner of the lock. The content of this space
    141   // is undefined when the lock is acquired. The owner may write to and read
    142   // from it while it holds the lock, but must relinquish control before
    143   // releasing the lock.
    144   //
    145   // This is intended to support Linux robust futexes. See the documentation
    146   // in the kernel tree:
    147   //   Documentation/robust-futex-ABI.txt
    148  public:
    149   int64_t owner_scratch_[2];
    150 #pragma clang diagnostic pop
    151 };
    152 ASSERT_SHM_COMPATIBLE(WaitingLockBase);
    153 
    154 /**
    155  * GuestLocks can be acquired and released only on the guest. They reside
    156  * in the shared memory window because mutiple guest processes may need
    157  * to coordinate activities in certain shared memory regions.
    158  *
    159  * Representing this as a concrete type allows for some optimizations when
    160  * signalling on the lock.
    161  */
    162 class GuestLock : public WaitingLockBase {
    163  public:
    164   static constexpr size_t layout_size = WaitingLockBase::layout_size;
    165 
    166 #ifndef CUTTLEFISH_HOST
    167   void Lock();
    168   void Unlock();
    169   /**
    170    * Drop the lock iff it is currently held. Used by
    171    * recovery code that cleans up regions in the event of a reboot.
    172    *
    173    * The caller must ensure that there are no other threads on its
    174    * side (e.g. guest/host) are using the window.
    175    */
    176   bool Recover();
    177 #endif
    178 };
    179 ASSERT_SHM_COMPATIBLE(GuestLock);
    180 
    181 /**
    182  * HostLocks can be acquired and released only on the host. They reside
    183  * in the shared memory window because mutiple host processes may need
    184  * to coordinate activities in certain shared memory regions.
    185  *
    186  * Representing this as a concrete type allows for some optimizations when
    187  * signalling on the lock.
    188  */
    189 class HostLock : public WaitingLockBase {
    190  public:
    191   static constexpr size_t layout_size = WaitingLockBase::layout_size;
    192 
    193 #ifdef CUTTLEFISH_HOST
    194   void Lock();
    195   void Unlock();
    196   /**
    197    * Drop the lock iff it is currently held. Used by
    198    * recovery code that cleans up regions in the event of a daemon
    199    * restart.
    200    *
    201    * The caller must ensure that there are no other threads on its
    202    * side (e.g. guest/host) are using the window.
    203    */
    204   bool Recover();
    205 #endif
    206 };
    207 ASSERT_SHM_COMPATIBLE(HostLock);
    208 
    209 /**
    210  * GuestAndHostLocks can be acquired and released on either side of the
    211  * shared memory window. The locks attempt to enforce fairness by using
    212  * a round-trip signal:
    213  *
    214  *   When a guest releases a lock this code sends a signal to wake the host,
    215  *   but not other guest waiters.
    216  *
    217  *   The wake handler on the host wakes up and local waiters and then reposts
    218  *   the signal to the guest.
    219  *
    220  *   When the guest receives the signal from the host it then wakes ups
    221  *   any waiters.
    222  *
    223  * A similar scenario applies when the host releases a lock with guest waiters.
    224  *
    225  * Signalling across the shared memory window twice has non-trivial cost.
    226  * There are some optimizations in the code to prevent the full round-trip
    227  * if the process releasing the lock can confirm that there are no waiters on
    228  * the other side.
    229  *
    230  * Representing this as a concrete type allows for some optimizations when
    231  * signalling on the lock.
    232  */
    233 class GuestAndHostLock : public WaitingLockBase {
    234  public:
    235   static constexpr size_t layout_size = WaitingLockBase::layout_size;
    236 
    237   void Lock(RegionView*);
    238   void Unlock(RegionView*);
    239   /**
    240    * Drop the lock iff it is currently held by this side. Used by
    241    * recovery code that cleans up regions in the event of a reboot
    242    * (guest side) or a service restart (host side).
    243    *
    244    * The caller must ensure that there are no other threads on its
    245    * side (e.g. guest/host) are using the window.
    246    */
    247   bool Recover(RegionView*);
    248 };
    249 ASSERT_SHM_COMPATIBLE(GuestAndHostLock);
    250 
    251 }  // namespace layout
    252 }  // namespace vsoc
    253