Home | History | Annotate | Download | only in lib
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 #include "common/vsoc/shm/lock.h"
     17 
     18 #include "common/libs/glog/logging.h"
     19 #include "common/vsoc/lib/compat.h"
     20 #include "common/vsoc/lib/region_view.h"
     21 
     22 #include <stdlib.h>
     23 
     24 using vsoc::layout::Sides;
     25 
     26 namespace {
     27 
     28 const uint32_t LockFree = 0U;
     29 const uint32_t GuestWaitingFlag = (Sides::Guest << 30);
     30 const uint32_t HostWaitingFlag = (Sides::Host << 30);
     31 const uint32_t OurWaitingFlag = (Sides::OurSide << 30);
     32 static_assert(GuestWaitingFlag, "GuestWaitingFlag is 0");
     33 static_assert(HostWaitingFlag, "HostWaitingFlag is 0");
     34 static_assert((GuestWaitingFlag & HostWaitingFlag) == 0,
     35               "Waiting flags should not share bits");
     36 
     37 // Set if the current owner is the host
     38 const uint32_t HostIsOwner = 0x20000000U;
     39 
     40 // PID_MAX_LIMIT appears to be 0x00400000U, so we're probably ok here
     41 const uint32_t OwnerMask = 0x3FFFFFFFU;
     42 
     43 uint32_t MakeOwnerTid(uint32_t raw_tid) {
     44   if (Sides::OurSide == Sides::Host) {
     45     return (raw_tid | HostIsOwner) & OwnerMask;
     46   } else {
     47     return raw_tid & (OwnerMask & ~HostIsOwner);
     48   }
     49 }
     50 
     51 };  // namespace
     52 
     53 namespace vsoc {
     54 /**
     55  * This is a generic synchronization primitive that provides space for the
     56  * owner of the lock to write platform-specific information.
     57  */
     58 bool vsoc::layout::WaitingLockBase::TryLock(uint32_t tid,
     59                                             uint32_t* expected_out) {
     60   uint32_t masked_tid = MakeOwnerTid(tid);
     61   uint32_t expected = LockFree;
     62   while (1) {
     63     // First try to lock assuming that the mutex is free
     64     if (lock_uint32_.compare_exchange_strong(expected, masked_tid)) {
     65       // We got the lock.
     66       return true;
     67     }
     68     // We didn't get the lock and our wait flag is already set. It's safe to
     69     // try to sleep
     70     if (expected & OurWaitingFlag) {
     71       *expected_out = expected;
     72       return false;
     73     }
     74     // Attempt to set the wait flag. This will fail if the lock owner changes.
     75     while (1) {
     76       uint32_t add_wait_flag = expected | OurWaitingFlag;
     77       if (lock_uint32_.compare_exchange_strong(expected, add_wait_flag)) {
     78         // We set the waiting flag. Try to sleep.
     79         *expected_out = expected;
     80         return false;
     81       }
     82       // The owner changed, but we at least we got the wait flag.
     83       // Try sleeping
     84       if (expected & OurWaitingFlag) {
     85         *expected_out = expected;
     86         return false;
     87       }
     88       // Special case: the lock was just freed. Stop trying to set the
     89       // waiting flag and try to grab the lock.
     90       if (expected == LockFree) {
     91         break;
     92       }
     93       // The owner changed and we have no wait flag
     94       // Try to set the wait flag again
     95     }
     96     // This only happens if the lock was freed while we attempt the set the
     97     // wait flag. Try to grab the lock again
     98   }
     99   // Never reached.
    100 }
    101 
    102 layout::Sides vsoc::layout::WaitingLockBase::UnlockCommon(uint32_t tid) {
    103   uint32_t expected_state = lock_uint32_;
    104 
    105   // We didn't hold the lock. This process is insane and must die before it
    106   // does damage.
    107   uint32_t marked_tid = MakeOwnerTid(tid);
    108   if ((marked_tid ^ expected_state) & OwnerMask) {
    109     LOG(FATAL) << tid << " unlocking " << this << " owned by "
    110                << expected_state;
    111   }
    112   // If contention is just starting this may fail twice (once for each bit)
    113   // expected_state updates on each failure. When this finishes we have
    114   // one bit for each waiter
    115   while (1) {
    116     if (lock_uint32_.compare_exchange_strong(expected_state, LockFree)) {
    117       break;
    118     }
    119   }
    120   if ((expected_state ^ marked_tid) & OwnerMask) {
    121     LOG(FATAL) << "Lock owner of " << this << " changed from " << tid << " to "
    122                << expected_state << " during unlock";
    123   }
    124   switch (expected_state & (GuestWaitingFlag | HostWaitingFlag)) {
    125     case 0:
    126       return Sides::NoSides;
    127     case GuestWaitingFlag:
    128       return Sides::Guest;
    129     case HostWaitingFlag:
    130       return Sides::Host;
    131     default:
    132       return Sides::Both;
    133   }
    134 }
    135 
    136 bool vsoc::layout::WaitingLockBase::RecoverSingleSided() {
    137   // No need to signal because the caller ensured that there were no other
    138   // threads...
    139   return lock_uint32_.exchange(LockFree) != LockFree;
    140 }
    141 
    142 void layout::GuestAndHostLock::Lock(RegionView* region) {
    143   uint32_t expected;
    144   uint32_t tid = gettid();
    145   while (1) {
    146     if (TryLock(tid, &expected)) {
    147       return;
    148     }
    149     region->WaitForSignal(&lock_uint32_, expected);
    150   }
    151 }
    152 
    153 void layout::GuestAndHostLock::Unlock(RegionView* region) {
    154   region->SendSignal(UnlockCommon(gettid()), &lock_uint32_);
    155 }
    156 
    157 bool layout::GuestAndHostLock::Recover(RegionView* region) {
    158   uint32_t expected_state = lock_uint32_;
    159   uint32_t expected_owner_bit = (Sides::OurSide == Sides::Host) ? HostIsOwner : 0;
    160   // This avoids check then act by reading exactly once and then
    161   // eliminating the states where Recover should be a noop.
    162   if (expected_state == LockFree) {
    163     return false;
    164   }
    165   // Owned by the other side, do nothing.
    166   if ((expected_state & HostIsOwner) != expected_owner_bit) {
    167     return false;
    168   }
    169   // At this point we know two things:
    170   //   * The lock was held by our side
    171   //   * There are no other threads running on our side (precondition
    172   //     for calling Recover())
    173   // Therefore, we know that the current expected value should still
    174   // be in the lock structure. Use the normal unlock logic, providing
    175   // the tid that we observed in the lock.
    176   region->SendSignal(UnlockCommon(expected_state), &lock_uint32_);
    177   return true;
    178 }
    179 
    180 }  // namespace vsoc
    181