xref: /openbsd-src/gnu/llvm/compiler-rt/lib/tsan/rtl/tsan_sync.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- tsan_sync.cpp -----------------------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of ThreadSanitizer (TSan), a race detector.
103cab2bb3Spatrick //
113cab2bb3Spatrick //===----------------------------------------------------------------------===//
123cab2bb3Spatrick #include "sanitizer_common/sanitizer_placement_new.h"
133cab2bb3Spatrick #include "tsan_sync.h"
143cab2bb3Spatrick #include "tsan_rtl.h"
153cab2bb3Spatrick #include "tsan_mman.h"
163cab2bb3Spatrick 
173cab2bb3Spatrick namespace __tsan {
183cab2bb3Spatrick 
193cab2bb3Spatrick void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s);
203cab2bb3Spatrick 
SyncVar()21*810390e3Srobert SyncVar::SyncVar() : mtx(MutexTypeSyncVar) { Reset(); }
223cab2bb3Spatrick 
Init(ThreadState * thr,uptr pc,uptr addr,bool save_stack)23*810390e3Srobert void SyncVar::Init(ThreadState *thr, uptr pc, uptr addr, bool save_stack) {
24*810390e3Srobert   Reset();
253cab2bb3Spatrick   this->addr = addr;
26*810390e3Srobert   next = 0;
27*810390e3Srobert   if (save_stack && !SANITIZER_GO)  // Go does not use them
283cab2bb3Spatrick     creation_stack_id = CurrentStackId(thr, pc);
293cab2bb3Spatrick   if (common_flags()->detect_deadlocks)
303cab2bb3Spatrick     DDMutexInit(thr, pc, this);
313cab2bb3Spatrick }
323cab2bb3Spatrick 
Reset()33*810390e3Srobert void SyncVar::Reset() {
34*810390e3Srobert   CHECK(!ctx->resetting);
35*810390e3Srobert   creation_stack_id = kInvalidStackID;
363cab2bb3Spatrick   owner_tid = kInvalidTid;
37*810390e3Srobert   last_lock.Reset();
383cab2bb3Spatrick   recursion = 0;
393cab2bb3Spatrick   atomic_store_relaxed(&flags, 0);
40*810390e3Srobert   Free(clock);
41*810390e3Srobert   Free(read_clock);
423cab2bb3Spatrick }
433cab2bb3Spatrick 
MetaMap()443cab2bb3Spatrick MetaMap::MetaMap()
45*810390e3Srobert     : block_alloc_("heap block allocator"), sync_alloc_("sync allocator") {}
463cab2bb3Spatrick 
AllocBlock(ThreadState * thr,uptr pc,uptr p,uptr sz)473cab2bb3Spatrick void MetaMap::AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz) {
483cab2bb3Spatrick   u32 idx = block_alloc_.Alloc(&thr->proc()->block_cache);
493cab2bb3Spatrick   MBlock *b = block_alloc_.Map(idx);
503cab2bb3Spatrick   b->siz = sz;
513cab2bb3Spatrick   b->tag = 0;
523cab2bb3Spatrick   b->tid = thr->tid;
533cab2bb3Spatrick   b->stk = CurrentStackId(thr, pc);
543cab2bb3Spatrick   u32 *meta = MemToMeta(p);
553cab2bb3Spatrick   DCHECK_EQ(*meta, 0);
563cab2bb3Spatrick   *meta = idx | kFlagBlock;
573cab2bb3Spatrick }
583cab2bb3Spatrick 
FreeBlock(Processor * proc,uptr p,bool reset)59*810390e3Srobert uptr MetaMap::FreeBlock(Processor *proc, uptr p, bool reset) {
603cab2bb3Spatrick   MBlock* b = GetBlock(p);
613cab2bb3Spatrick   if (b == 0)
623cab2bb3Spatrick     return 0;
633cab2bb3Spatrick   uptr sz = RoundUpTo(b->siz, kMetaShadowCell);
64*810390e3Srobert   FreeRange(proc, p, sz, reset);
653cab2bb3Spatrick   return sz;
663cab2bb3Spatrick }
673cab2bb3Spatrick 
FreeRange(Processor * proc,uptr p,uptr sz,bool reset)68*810390e3Srobert bool MetaMap::FreeRange(Processor *proc, uptr p, uptr sz, bool reset) {
693cab2bb3Spatrick   bool has_something = false;
703cab2bb3Spatrick   u32 *meta = MemToMeta(p);
713cab2bb3Spatrick   u32 *end = MemToMeta(p + sz);
723cab2bb3Spatrick   if (end == meta)
733cab2bb3Spatrick     end++;
743cab2bb3Spatrick   for (; meta < end; meta++) {
753cab2bb3Spatrick     u32 idx = *meta;
763cab2bb3Spatrick     if (idx == 0) {
773cab2bb3Spatrick       // Note: don't write to meta in this case -- the block can be huge.
783cab2bb3Spatrick       continue;
793cab2bb3Spatrick     }
803cab2bb3Spatrick     *meta = 0;
813cab2bb3Spatrick     has_something = true;
823cab2bb3Spatrick     while (idx != 0) {
833cab2bb3Spatrick       if (idx & kFlagBlock) {
843cab2bb3Spatrick         block_alloc_.Free(&proc->block_cache, idx & ~kFlagMask);
853cab2bb3Spatrick         break;
863cab2bb3Spatrick       } else if (idx & kFlagSync) {
873cab2bb3Spatrick         DCHECK(idx & kFlagSync);
883cab2bb3Spatrick         SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
893cab2bb3Spatrick         u32 next = s->next;
90*810390e3Srobert         if (reset)
91*810390e3Srobert           s->Reset();
923cab2bb3Spatrick         sync_alloc_.Free(&proc->sync_cache, idx & ~kFlagMask);
933cab2bb3Spatrick         idx = next;
943cab2bb3Spatrick       } else {
953cab2bb3Spatrick         CHECK(0);
963cab2bb3Spatrick       }
973cab2bb3Spatrick     }
983cab2bb3Spatrick   }
993cab2bb3Spatrick   return has_something;
1003cab2bb3Spatrick }
1013cab2bb3Spatrick 
1023cab2bb3Spatrick // ResetRange removes all meta objects from the range.
1033cab2bb3Spatrick // It is called for large mmap-ed regions. The function is best-effort wrt
1043cab2bb3Spatrick // freeing of meta objects, because we don't want to page in the whole range
1053cab2bb3Spatrick // which can be huge. The function probes pages one-by-one until it finds a page
1063cab2bb3Spatrick // without meta objects, at this point it stops freeing meta objects. Because
1073cab2bb3Spatrick // thread stacks grow top-down, we do the same starting from end as well.
ResetRange(Processor * proc,uptr p,uptr sz,bool reset)108*810390e3Srobert void MetaMap::ResetRange(Processor *proc, uptr p, uptr sz, bool reset) {
1093cab2bb3Spatrick   if (SANITIZER_GO) {
1103cab2bb3Spatrick     // UnmapOrDie/MmapFixedNoReserve does not work on Windows,
1113cab2bb3Spatrick     // so we do the optimization only for C/C++.
112*810390e3Srobert     FreeRange(proc, p, sz, reset);
1133cab2bb3Spatrick     return;
1143cab2bb3Spatrick   }
1153cab2bb3Spatrick   const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize;
1163cab2bb3Spatrick   const uptr kPageSize = GetPageSizeCached() * kMetaRatio;
1173cab2bb3Spatrick   if (sz <= 4 * kPageSize) {
1183cab2bb3Spatrick     // If the range is small, just do the normal free procedure.
119*810390e3Srobert     FreeRange(proc, p, sz, reset);
1203cab2bb3Spatrick     return;
1213cab2bb3Spatrick   }
1223cab2bb3Spatrick   // First, round both ends of the range to page size.
1233cab2bb3Spatrick   uptr diff = RoundUp(p, kPageSize) - p;
1243cab2bb3Spatrick   if (diff != 0) {
125*810390e3Srobert     FreeRange(proc, p, diff, reset);
1263cab2bb3Spatrick     p += diff;
1273cab2bb3Spatrick     sz -= diff;
1283cab2bb3Spatrick   }
1293cab2bb3Spatrick   diff = p + sz - RoundDown(p + sz, kPageSize);
1303cab2bb3Spatrick   if (diff != 0) {
131*810390e3Srobert     FreeRange(proc, p + sz - diff, diff, reset);
1323cab2bb3Spatrick     sz -= diff;
1333cab2bb3Spatrick   }
1343cab2bb3Spatrick   // Now we must have a non-empty page-aligned range.
1353cab2bb3Spatrick   CHECK_GT(sz, 0);
1363cab2bb3Spatrick   CHECK_EQ(p, RoundUp(p, kPageSize));
1373cab2bb3Spatrick   CHECK_EQ(sz, RoundUp(sz, kPageSize));
1383cab2bb3Spatrick   const uptr p0 = p;
1393cab2bb3Spatrick   const uptr sz0 = sz;
1403cab2bb3Spatrick   // Probe start of the range.
1413cab2bb3Spatrick   for (uptr checked = 0; sz > 0; checked += kPageSize) {
142*810390e3Srobert     bool has_something = FreeRange(proc, p, kPageSize, reset);
1433cab2bb3Spatrick     p += kPageSize;
1443cab2bb3Spatrick     sz -= kPageSize;
1453cab2bb3Spatrick     if (!has_something && checked > (128 << 10))
1463cab2bb3Spatrick       break;
1473cab2bb3Spatrick   }
1483cab2bb3Spatrick   // Probe end of the range.
1493cab2bb3Spatrick   for (uptr checked = 0; sz > 0; checked += kPageSize) {
150*810390e3Srobert     bool has_something = FreeRange(proc, p + sz - kPageSize, kPageSize, reset);
1513cab2bb3Spatrick     sz -= kPageSize;
1523cab2bb3Spatrick     // Stacks grow down, so sync object are most likely at the end of the region
1533cab2bb3Spatrick     // (if it is a stack). The very end of the stack is TLS and tsan increases
1543cab2bb3Spatrick     // TLS by at least 256K, so check at least 512K.
1553cab2bb3Spatrick     if (!has_something && checked > (512 << 10))
1563cab2bb3Spatrick       break;
1573cab2bb3Spatrick   }
1583cab2bb3Spatrick   // Finally, page out the whole range (including the parts that we've just
1593cab2bb3Spatrick   // freed). Note: we can't simply madvise, because we need to leave a zeroed
1603cab2bb3Spatrick   // range (otherwise __tsan_java_move can crash if it encounters a left-over
1613cab2bb3Spatrick   // meta objects in java heap).
1623cab2bb3Spatrick   uptr metap = (uptr)MemToMeta(p0);
1633cab2bb3Spatrick   uptr metasz = sz0 / kMetaRatio;
1643cab2bb3Spatrick   UnmapOrDie((void*)metap, metasz);
165d89ec533Spatrick   if (!MmapFixedSuperNoReserve(metap, metasz))
1663cab2bb3Spatrick     Die();
1673cab2bb3Spatrick }
1683cab2bb3Spatrick 
ResetClocks()169*810390e3Srobert void MetaMap::ResetClocks() {
170*810390e3Srobert   // This can be called from the background thread
171*810390e3Srobert   // which does not have proc/cache.
172*810390e3Srobert   // The cache is too large for stack.
173*810390e3Srobert   static InternalAllocatorCache cache;
174*810390e3Srobert   internal_memset(&cache, 0, sizeof(cache));
175*810390e3Srobert   internal_allocator()->InitCache(&cache);
176*810390e3Srobert   sync_alloc_.ForEach([&](SyncVar *s) {
177*810390e3Srobert     if (s->clock) {
178*810390e3Srobert       InternalFree(s->clock, &cache);
179*810390e3Srobert       s->clock = nullptr;
180*810390e3Srobert     }
181*810390e3Srobert     if (s->read_clock) {
182*810390e3Srobert       InternalFree(s->read_clock, &cache);
183*810390e3Srobert       s->read_clock = nullptr;
184*810390e3Srobert     }
185*810390e3Srobert     s->last_lock.Reset();
186*810390e3Srobert   });
187*810390e3Srobert   internal_allocator()->DestroyCache(&cache);
188*810390e3Srobert }
189*810390e3Srobert 
GetBlock(uptr p)1903cab2bb3Spatrick MBlock* MetaMap::GetBlock(uptr p) {
1913cab2bb3Spatrick   u32 *meta = MemToMeta(p);
1923cab2bb3Spatrick   u32 idx = *meta;
1933cab2bb3Spatrick   for (;;) {
1943cab2bb3Spatrick     if (idx == 0)
1953cab2bb3Spatrick       return 0;
1963cab2bb3Spatrick     if (idx & kFlagBlock)
1973cab2bb3Spatrick       return block_alloc_.Map(idx & ~kFlagMask);
1983cab2bb3Spatrick     DCHECK(idx & kFlagSync);
1993cab2bb3Spatrick     SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
2003cab2bb3Spatrick     idx = s->next;
2013cab2bb3Spatrick   }
2023cab2bb3Spatrick }
2033cab2bb3Spatrick 
GetSync(ThreadState * thr,uptr pc,uptr addr,bool create,bool save_stack)204*810390e3Srobert SyncVar *MetaMap::GetSync(ThreadState *thr, uptr pc, uptr addr, bool create,
205*810390e3Srobert                           bool save_stack) {
206*810390e3Srobert   DCHECK(!create || thr->slot_locked);
2073cab2bb3Spatrick   u32 *meta = MemToMeta(addr);
2083cab2bb3Spatrick   u32 idx0 = *meta;
2093cab2bb3Spatrick   u32 myidx = 0;
210*810390e3Srobert   SyncVar *mys = nullptr;
2113cab2bb3Spatrick   for (;;) {
212*810390e3Srobert     for (u32 idx = idx0; idx && !(idx & kFlagBlock);) {
2133cab2bb3Spatrick       DCHECK(idx & kFlagSync);
2143cab2bb3Spatrick       SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
215*810390e3Srobert       if (LIKELY(s->addr == addr)) {
216*810390e3Srobert         if (UNLIKELY(myidx != 0)) {
217*810390e3Srobert           mys->Reset();
2183cab2bb3Spatrick           sync_alloc_.Free(&thr->proc()->sync_cache, myidx);
2193cab2bb3Spatrick         }
2203cab2bb3Spatrick         return s;
2213cab2bb3Spatrick       }
2223cab2bb3Spatrick       idx = s->next;
2233cab2bb3Spatrick     }
2243cab2bb3Spatrick     if (!create)
225*810390e3Srobert       return nullptr;
226*810390e3Srobert     if (UNLIKELY(*meta != idx0)) {
2273cab2bb3Spatrick       idx0 = *meta;
2283cab2bb3Spatrick       continue;
2293cab2bb3Spatrick     }
2303cab2bb3Spatrick 
231*810390e3Srobert     if (LIKELY(myidx == 0)) {
2323cab2bb3Spatrick       myidx = sync_alloc_.Alloc(&thr->proc()->sync_cache);
2333cab2bb3Spatrick       mys = sync_alloc_.Map(myidx);
234*810390e3Srobert       mys->Init(thr, pc, addr, save_stack);
2353cab2bb3Spatrick     }
2363cab2bb3Spatrick     mys->next = idx0;
2373cab2bb3Spatrick     if (atomic_compare_exchange_strong((atomic_uint32_t*)meta, &idx0,
2383cab2bb3Spatrick         myidx | kFlagSync, memory_order_release)) {
2393cab2bb3Spatrick       return mys;
2403cab2bb3Spatrick     }
2413cab2bb3Spatrick   }
2423cab2bb3Spatrick }
2433cab2bb3Spatrick 
MoveMemory(uptr src,uptr dst,uptr sz)2443cab2bb3Spatrick void MetaMap::MoveMemory(uptr src, uptr dst, uptr sz) {
2453cab2bb3Spatrick   // src and dst can overlap,
2463cab2bb3Spatrick   // there are no concurrent accesses to the regions (e.g. stop-the-world).
2473cab2bb3Spatrick   CHECK_NE(src, dst);
2483cab2bb3Spatrick   CHECK_NE(sz, 0);
2493cab2bb3Spatrick   uptr diff = dst - src;
2503cab2bb3Spatrick   u32 *src_meta = MemToMeta(src);
2513cab2bb3Spatrick   u32 *dst_meta = MemToMeta(dst);
2523cab2bb3Spatrick   u32 *src_meta_end = MemToMeta(src + sz);
2533cab2bb3Spatrick   uptr inc = 1;
2543cab2bb3Spatrick   if (dst > src) {
2553cab2bb3Spatrick     src_meta = MemToMeta(src + sz) - 1;
2563cab2bb3Spatrick     dst_meta = MemToMeta(dst + sz) - 1;
2573cab2bb3Spatrick     src_meta_end = MemToMeta(src) - 1;
2583cab2bb3Spatrick     inc = -1;
2593cab2bb3Spatrick   }
2603cab2bb3Spatrick   for (; src_meta != src_meta_end; src_meta += inc, dst_meta += inc) {
2613cab2bb3Spatrick     CHECK_EQ(*dst_meta, 0);
2623cab2bb3Spatrick     u32 idx = *src_meta;
2633cab2bb3Spatrick     *src_meta = 0;
2643cab2bb3Spatrick     *dst_meta = idx;
2653cab2bb3Spatrick     // Patch the addresses in sync objects.
2663cab2bb3Spatrick     while (idx != 0) {
2673cab2bb3Spatrick       if (idx & kFlagBlock)
2683cab2bb3Spatrick         break;
2693cab2bb3Spatrick       CHECK(idx & kFlagSync);
2703cab2bb3Spatrick       SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
2713cab2bb3Spatrick       s->addr += diff;
2723cab2bb3Spatrick       idx = s->next;
2733cab2bb3Spatrick     }
2743cab2bb3Spatrick   }
2753cab2bb3Spatrick }
2763cab2bb3Spatrick 
OnProcIdle(Processor * proc)2773cab2bb3Spatrick void MetaMap::OnProcIdle(Processor *proc) {
2783cab2bb3Spatrick   block_alloc_.FlushCache(&proc->block_cache);
2793cab2bb3Spatrick   sync_alloc_.FlushCache(&proc->sync_cache);
2803cab2bb3Spatrick }
2813cab2bb3Spatrick 
GetMemoryStats() const282*810390e3Srobert MetaMap::MemoryStats MetaMap::GetMemoryStats() const {
283*810390e3Srobert   MemoryStats stats;
284*810390e3Srobert   stats.mem_block = block_alloc_.AllocatedMemory();
285*810390e3Srobert   stats.sync_obj = sync_alloc_.AllocatedMemory();
286*810390e3Srobert   return stats;
287*810390e3Srobert }
288*810390e3Srobert 
2893cab2bb3Spatrick }  // namespace __tsan
290