1 /**
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "runtime/mem/runslots.h"
17
18 #include <cstring>
19
20 #include "runtime/include/object_header.h"
21
22 namespace ark::mem {
23
24 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
25 #define LOG_RUNSLOTS(level) LOG(level, ALLOC) << "RunSlots: "
26
27 template <typename LockTypeT>
Initialize(size_t slotSize, uintptr_t poolPointer, bool initializeLock)28 void RunSlots<LockTypeT>::Initialize(size_t slotSize, uintptr_t poolPointer, bool initializeLock)
29 {
30 ASAN_UNPOISON_MEMORY_REGION(this, RUNSLOTS_SIZE);
31 LOG_RUNSLOTS(DEBUG) << "Initializing RunSlots:";
32 ASSERT_PRINT((slotSize >= SlotToSize(SlotsSizes::SLOT_MIN_SIZE_BYTES)), "Size of slot in RunSlots is too small");
33 ASSERT_PRINT((slotSize <= SlotToSize(SlotsSizes::SLOT_MAX_SIZE_BYTES)), "Size of slot in RunSlots is too big");
34 ASSERT(poolPointer != 0);
35 poolPointer_ = poolPointer;
36 ASSERT_PRINT(!(ToUintPtr(this) & RUNSLOTS_ALIGNMENT_MASK), "RunSlots object must have alignment");
37 slotSize_ = slotSize;
38 size_t firstSlotOffset = ComputeFirstSlotOffset(slotSize);
39 firstUninitializedSlotOffset_ = firstSlotOffset;
40 ASSERT(firstUninitializedSlotOffset_ != 0);
41 nextFree_ = nullptr;
42 usedSlots_ = 0;
43 nextRunslot_ = nullptr;
44 prevRunslot_ = nullptr;
45 if (initializeLock) {
46 new (&lock_) LockTypeT();
47 }
48 memset_s(bitmap_.data(), BITMAP_ARRAY_SIZE, 0x0, BITMAP_ARRAY_SIZE);
49 LOG_RUNSLOTS(DEBUG) << "- Memory started from = 0x" << std::hex << ToUintPtr(this);
50 LOG_RUNSLOTS(DEBUG) << "- Pool size = " << RUNSLOTS_SIZE << " bytes";
51 LOG_RUNSLOTS(DEBUG) << "- Slots size = " << slotSize_ << " bytes";
52 LOG_RUNSLOTS(DEBUG) << "- First free slot = " << std::hex << static_cast<void *>(nextFree_);
53 LOG_RUNSLOTS(DEBUG) << "- First uninitialized slot offset = " << std::hex
54 << static_cast<void *>(ToVoidPtr(firstUninitializedSlotOffset_));
55 LOG_RUNSLOTS(DEBUG) << "- Pool pointer = " << std::hex << static_cast<void *>(ToVoidPtr(poolPointer_));
56 LOG_RUNSLOTS(DEBUG) << "Successful finished RunSlots init";
57 ASAN_POISON_MEMORY_REGION(this, RUNSLOTS_SIZE);
58 }
59
60 template <typename LockTypeT>
PopFreeSlot()61 FreeSlot *RunSlots<LockTypeT>::PopFreeSlot()
62 {
63 ASAN_UNPOISON_MEMORY_REGION(this, GetHeaderSize());
64 FreeSlot *freeSlot = nullptr;
65 if (nextFree_ == nullptr) {
66 void *uninitializedSlot = PopUninitializedSlot();
67 if (uninitializedSlot == nullptr) {
68 LOG_RUNSLOTS(DEBUG) << "Failed to get free slot - there are no free slots in RunSlots";
69 ASAN_POISON_MEMORY_REGION(this, GetHeaderSize());
70 return nullptr;
71 }
72 freeSlot = static_cast<FreeSlot *>(uninitializedSlot);
73 } else {
74 freeSlot = nextFree_;
75 ASAN_UNPOISON_MEMORY_REGION(freeSlot, sizeof(FreeSlot));
76 nextFree_ = nextFree_->GetNext();
77 ASAN_POISON_MEMORY_REGION(freeSlot, sizeof(FreeSlot));
78 }
79 MarkAsOccupied(freeSlot);
80 usedSlots_++;
81 LOG_RUNSLOTS(DEBUG) << "Successfully get free slot " << std::hex << static_cast<void *>(freeSlot)
82 << ". Used slots in this RunSlots = " << std::dec << usedSlots_;
83 ASAN_POISON_MEMORY_REGION(this, GetHeaderSize());
84 return freeSlot;
85 }
86
87 template <typename LockTypeT>
PushFreeSlot(FreeSlot *memSlot)88 void RunSlots<LockTypeT>::PushFreeSlot(FreeSlot *memSlot)
89 {
90 ASAN_UNPOISON_MEMORY_REGION(this, GetHeaderSize());
91 LOG_RUNSLOTS(DEBUG) << "Free slot in RunSlots at addr " << std::hex << static_cast<void *>(memSlot);
92 // We need to poison/unpoison mem_slot here because we could allocate an object with size less than FreeSlot size
93 ASAN_UNPOISON_MEMORY_REGION(memSlot, sizeof(FreeSlot));
94 memSlot->SetNext(nextFree_);
95 ASAN_POISON_MEMORY_REGION(memSlot, sizeof(FreeSlot));
96 nextFree_ = memSlot;
97 MarkAsFree(memSlot);
98 usedSlots_--;
99 LOG_RUNSLOTS(DEBUG) << "Used slots in RunSlots = " << usedSlots_;
100 ASAN_POISON_MEMORY_REGION(this, GetHeaderSize());
101 }
102
103 template <typename LockTypeT>
ComputeFirstSlotOffset(size_t slotSize)104 size_t RunSlots<LockTypeT>::ComputeFirstSlotOffset(size_t slotSize)
105 {
106 size_t slotsForHeader = (GetHeaderSize() / slotSize);
107 if ((GetHeaderSize() % slotSize) > 0) {
108 slotsForHeader++;
109 }
110 return slotsForHeader * slotSize;
111 }
112
113 template <typename LockTypeT>
PopUninitializedSlot()114 void *RunSlots<LockTypeT>::PopUninitializedSlot()
115 {
116 if (firstUninitializedSlotOffset_ != 0) {
117 ASSERT(RUNSLOTS_SIZE > firstUninitializedSlotOffset_);
118 void *uninitializedSlot = ToVoidPtr(ToUintPtr(this) + firstUninitializedSlotOffset_);
119 firstUninitializedSlotOffset_ += slotSize_;
120 if (firstUninitializedSlotOffset_ >= RUNSLOTS_SIZE) {
121 ASSERT(firstUninitializedSlotOffset_ == RUNSLOTS_SIZE);
122 firstUninitializedSlotOffset_ = 0;
123 }
124 return uninitializedSlot;
125 }
126 return nullptr;
127 }
128
129 template <typename LockTypeT>
MarkAsOccupied(const FreeSlot *slotMem)130 void RunSlots<LockTypeT>::MarkAsOccupied(const FreeSlot *slotMem)
131 {
132 uintptr_t bitIndex =
133 (ToUintPtr(slotMem) & (RUNSLOTS_SIZE - 1U)) >> SlotToSize(SlotsSizes::SLOT_MIN_SIZE_BYTES_POWER_OF_TWO);
134 uintptr_t arrayIndex = bitIndex >> BITS_IN_BYTE_POWER_OF_TWO;
135 uintptr_t bitInArrayElement = bitIndex & ((1U << BITS_IN_BYTE_POWER_OF_TWO) - 1U);
136 ASSERT(!(bitmap_[arrayIndex] & (1U << bitInArrayElement)));
137 bitmap_[arrayIndex] |= 1U << bitInArrayElement;
138 }
139
140 template <typename LockTypeT>
MarkAsFree(const FreeSlot *slotMem)141 void RunSlots<LockTypeT>::MarkAsFree(const FreeSlot *slotMem)
142 {
143 uintptr_t bitIndex =
144 (ToUintPtr(slotMem) & (RUNSLOTS_SIZE - 1U)) >> SlotToSize(SlotsSizes::SLOT_MIN_SIZE_BYTES_POWER_OF_TWO);
145 uintptr_t arrayIndex = bitIndex >> BITS_IN_BYTE_POWER_OF_TWO;
146 uintptr_t bitInArrayElement = bitIndex & ((1U << BITS_IN_BYTE_POWER_OF_TWO) - 1U);
147 ASSERT(bitmap_[arrayIndex] & (1U << bitInArrayElement));
148 bitmap_[arrayIndex] ^= 1U << bitInArrayElement;
149 }
150
151 template <typename LockTypeT>
BitMapToSlot(size_t arrayIndex, size_t bit)152 FreeSlot *RunSlots<LockTypeT>::BitMapToSlot(size_t arrayIndex, size_t bit)
153 {
154 return static_cast<FreeSlot *>(
155 ToVoidPtr(ToUintPtr(this) + (((arrayIndex << BITS_IN_BYTE_POWER_OF_TWO) + bit)
156 << SlotToSize(SlotsSizes::SLOT_MIN_SIZE_BYTES_POWER_OF_TWO))));
157 }
158
159 template <typename LockTypeT>
operator ()(RunSlots *run)160 size_t RunSlots<LockTypeT>::RunVerifier::operator()(RunSlots *run)
161 {
162 // 1. should verify whether run's bracket size is the same as recorded in RunSlotsAllocator, but RunSlotsAllocator
163 // does not record this
164 // 2. should verify thread local run's ownership, but thread local run not implemented yet
165
166 // check alloc'ed size
167 auto sizeCheckFunc = [this, &run](const ObjectHeader *obj) {
168 auto sizePowerOfTwo = ConvertToPowerOfTwoUnsafe(obj->ObjectSize());
169 if ((1U << sizePowerOfTwo) != run->GetSlotsSize()) {
170 ++(this->failCnt_);
171 }
172 };
173 run->IterateOverOccupiedSlots(sizeCheckFunc);
174
175 return failCnt_;
176 }
177
178 template <typename LockTypeT>
IsLive(const ObjectHeader *obj) const179 bool RunSlots<LockTypeT>::IsLive(const ObjectHeader *obj) const
180 {
181 ASAN_UNPOISON_MEMORY_REGION(this, GetHeaderSize());
182 uintptr_t memTailByRunslots = ToUintPtr(obj) & (RUNSLOTS_SIZE - 1U);
183 if ((memTailByRunslots & (static_cast<uintptr_t>(slotSize_) - 1)) != 0) {
184 ASAN_POISON_MEMORY_REGION(this, GetHeaderSize());
185 return false;
186 }
187 uintptr_t bitIndex = memTailByRunslots >> SlotToSize(SlotsSizes::SLOT_MIN_SIZE_BYTES_POWER_OF_TWO);
188 uintptr_t arrayIndex = bitIndex >> BITS_IN_BYTE_POWER_OF_TWO;
189 uintptr_t bitInArrayElement = bitIndex & ((1U << BITS_IN_BYTE_POWER_OF_TWO) - 1U);
190 auto liveWord = bitmap_[arrayIndex] & (1U << bitInArrayElement);
191 ASAN_POISON_MEMORY_REGION(this, GetHeaderSize());
192 return liveWord != 0;
193 }
194
195 template class RunSlots<RunSlotsLockConfig::CommonLock>;
196 template class RunSlots<RunSlotsLockConfig::DummyLock>;
197 } // namespace ark::mem
198