| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 1062 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1073 size_t copySize = actualOldSize; | 1073 size_t copySize = actualOldSize; |
| 1074 if (newSize < copySize) | 1074 if (newSize < copySize) |
| 1075 copySize = newSize; | 1075 copySize = newSize; |
| 1076 | 1076 |
| 1077 memcpy(ret, ptr, copySize); | 1077 memcpy(ret, ptr, copySize); |
| 1078 partitionFreeGeneric(root, ptr); | 1078 partitionFreeGeneric(root, ptr); |
| 1079 return ret; | 1079 return ret; |
| 1080 #endif | 1080 #endif |
| 1081 } | 1081 } |
| 1082 | 1082 |
| 1083 static size_t partitionPurgePage(const PartitionPage* page, bool discard) | 1083 static size_t partitionPurgePage(PartitionPage* page, bool discard) |
| 1084 { | 1084 { |
| 1085 const PartitionBucket* bucket = page->bucket; | 1085 const PartitionBucket* bucket = page->bucket; |
| 1086 if (bucket->slotSize < kSystemPageSize || !page->numAllocatedSlots) | 1086 size_t slotSize = bucket->slotSize; |
| 1087 if (slotSize < kSystemPageSize || !page->numAllocatedSlots) |
| 1087 return 0; | 1088 return 0; |
| 1088 | 1089 |
| 1089 size_t bucketNumSlots = partitionBucketSlots(bucket); | 1090 size_t bucketNumSlots = partitionBucketSlots(bucket); |
| 1090 size_t discardableBytes = 0; | 1091 size_t discardableBytes = 0; |
| 1091 | 1092 |
| 1092 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); | 1093 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); |
| 1093 if (rawSize) { | 1094 if (rawSize) { |
| 1094 uint32_t usedBytes = static_cast<uint32_t>(partitionRoundUpToSystemPage(
rawSize)); | 1095 uint32_t usedBytes = static_cast<uint32_t>(partitionRoundUpToSystemPage(
rawSize)); |
| 1095 discardableBytes = bucket->slotSize - usedBytes; | 1096 discardableBytes = bucket->slotSize - usedBytes; |
| 1096 if (discardableBytes && discard) { | 1097 if (discardableBytes && discard) { |
| 1097 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); | 1098 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); |
| 1098 ptr += usedBytes; | 1099 ptr += usedBytes; |
| 1099 discardSystemPages(ptr, discardableBytes); | 1100 discardSystemPages(ptr, discardableBytes); |
| 1100 } | 1101 } |
| 1101 return discardableBytes; | 1102 return discardableBytes; |
| 1102 } | 1103 } |
| 1103 | 1104 |
| 1104 const size_t maxSlotCount = (kPartitionPageSize * kMaxPartitionPagesPerSlotS
pan) / kSystemPageSize; | 1105 const size_t maxSlotCount = (kPartitionPageSize * kMaxPartitionPagesPerSlotS
pan) / kSystemPageSize; |
| 1105 ASSERT(bucketNumSlots <= maxSlotCount); | 1106 ASSERT(bucketNumSlots <= maxSlotCount); |
| 1107 ASSERT(page->numUnprovisionedSlots < bucketNumSlots); |
| 1108 size_t numSlots = bucketNumSlots - page->numUnprovisionedSlots; |
| 1106 char slotUsage[maxSlotCount]; | 1109 char slotUsage[maxSlotCount]; |
| 1107 size_t lastSlot = static_cast<size_t>(-1); | 1110 size_t lastSlot = static_cast<size_t>(-1); |
| 1108 memset(slotUsage, 1, sizeof(slotUsage)); | 1111 memset(slotUsage, 1, numSlots); |
| 1109 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); | 1112 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); |
| 1110 PartitionFreelistEntry* entry = page->freelistHead; | 1113 PartitionFreelistEntry* entry = page->freelistHead; |
| 1111 // First, walk the freelist for this page and make a bitmap of which slots | 1114 // First, walk the freelist for this page and make a bitmap of which slots |
| 1112 // are not in use. | 1115 // are not in use. |
| 1113 while (entry) { | 1116 while (entry) { |
| 1114 size_t slotIndex = (reinterpret_cast<char*>(entry) - ptr) / bucket->slot
Size; | 1117 size_t slotIndex = (reinterpret_cast<char*>(entry) - ptr) / slotSize; |
| 1115 ASSERT(slotIndex < bucketNumSlots); | 1118 ASSERT(slotIndex < numSlots); |
| 1116 slotUsage[slotIndex] = 0; | 1119 slotUsage[slotIndex] = 0; |
| 1117 entry = partitionFreelistMask(entry->next); | 1120 entry = partitionFreelistMask(entry->next); |
| 1118 // If we have a slot where the masked freelist entry is 0, we can | 1121 // If we have a slot where the masked freelist entry is 0, we can |
| 1119 // actually discard that freelist entry because touching a discarded | 1122 // actually discard that freelist entry because touching a discarded |
| 1120 // page is guaranteed to return original content or 0. | 1123 // page is guaranteed to return original content or 0. |
| 1121 // (Note that this optimization won't fire on big endian machines | 1124 // (Note that this optimization won't fire on big endian machines |
| 1122 // because the masking function is negation.) | 1125 // because the masking function is negation.) |
| 1123 if (!partitionFreelistMask(entry)) | 1126 if (!partitionFreelistMask(entry)) |
| 1124 lastSlot = slotIndex; | 1127 lastSlot = slotIndex; |
| 1125 } | 1128 } |
| 1129 |
| 1130 // If the slot(s) at the end of the slot span are not in used, we can |
| 1131 // truncate them entirely and rewrite the freelist. |
| 1132 size_t truncatedSlots = 0; |
| 1133 while (!slotUsage[numSlots - 1]) { |
| 1134 truncatedSlots++; |
| 1135 numSlots--; |
| 1136 ASSERT(numSlots); |
| 1137 } |
| 1138 // First, do the work of calculating the discardable bytes. Don't actually |
| 1139 // discard anything unless the discard flag was passed in. |
| 1140 char* beginPtr = nullptr; |
| 1141 char* endPtr = nullptr; |
| 1142 size_t unprovisionedBytes = 0; |
| 1143 if (truncatedSlots) { |
| 1144 beginPtr = ptr + (numSlots * slotSize); |
| 1145 endPtr = beginPtr + (slotSize * truncatedSlots); |
| 1146 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp
ret_cast<size_t>(beginPtr))); |
| 1147 // We round the end pointer here up and not down because we're at the |
| 1148 // end of a slot span, so we "own" all the way up the page boundary. |
| 1149 endPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterpre
t_cast<size_t>(endPtr))); |
| 1150 ASSERT(endPtr <= ptr + partitionBucketBytes(bucket)); |
| 1151 if (beginPtr < endPtr) { |
| 1152 unprovisionedBytes = endPtr - beginPtr; |
| 1153 discardableBytes += unprovisionedBytes; |
| 1154 } |
| 1155 } |
| 1156 if (unprovisionedBytes && discard) { |
| 1157 ASSERT(truncatedSlots > 0); |
| 1158 size_t numNewEntries = 0; |
| 1159 page->numUnprovisionedSlots += truncatedSlots; |
| 1160 // Rewrite the freelist. |
| 1161 PartitionFreelistEntry** entryPtr = &page->freelistHead; |
| 1162 for (size_t slotIndex = 0; slotIndex < numSlots; ++slotIndex) { |
| 1163 if (slotUsage[slotIndex]) |
| 1164 continue; |
| 1165 PartitionFreelistEntry* entry = reinterpret_cast<PartitionFreelistEn
try*>(ptr + (slotSize * slotIndex)); |
| 1166 *entryPtr = partitionFreelistMask(entry); |
| 1167 entryPtr = reinterpret_cast<PartitionFreelistEntry**>(entry); |
| 1168 numNewEntries++; |
| 1169 } |
| 1170 // Terminate the freelist chain. |
| 1171 *entryPtr = nullptr; |
| 1172 // The freelist head is stored unmasked. |
| 1173 page->freelistHead = partitionFreelistMask(page->freelistHead); |
| 1174 ASSERT(numNewEntries == numSlots - page->numAllocatedSlots); |
| 1175 // Discard the memory. |
| 1176 discardSystemPages(beginPtr, unprovisionedBytes); |
| 1177 } |
| 1178 |
| 1126 // Next, walk the slots and for any not in use, consider where the system | 1179 // Next, walk the slots and for any not in use, consider where the system |
| 1127 // page boundaries occur. We can release any system pages back to the | 1180 // page boundaries occur. We can release any system pages back to the |
| 1128 // system as long as we don't interfere with a freelist pointer or an | 1181 // system as long as we don't interfere with a freelist pointer or an |
| 1129 // adjacent slot. | 1182 // adjacent slot. |
| 1130 // TODO(cevans): I think we can "truncate" the page, i.e. increase the | 1183 for (size_t i = 0; i < numSlots; ++i) { |
| 1131 // value of page->numUnprovisionedSlots and rewrite(!) the freelist, if | |
| 1132 // we find that to be a win too. | |
| 1133 for (size_t i = 0; i < bucketNumSlots; ++i) { | |
| 1134 if (slotUsage[i]) | 1184 if (slotUsage[i]) |
| 1135 continue; | 1185 continue; |
| 1136 // The first address we can safely discard is just after the freelist | 1186 // The first address we can safely discard is just after the freelist |
| 1137 // pointer. There's one quirk: if the freelist pointer is actually a | 1187 // pointer. There's one quirk: if the freelist pointer is actually a |
| 1138 // null, we can discard that pointer value too. | 1188 // null, we can discard that pointer value too. |
| 1139 char* beginPtr = ptr + (i * bucket->slotSize); | 1189 char* beginPtr = ptr + (i * slotSize); |
| 1140 char* endPtr = beginPtr + bucket->slotSize; | 1190 char* endPtr = beginPtr + slotSize; |
| 1141 if (i != lastSlot) | 1191 if (i != lastSlot) |
| 1142 beginPtr += sizeof(PartitionFreelistEntry); | 1192 beginPtr += sizeof(PartitionFreelistEntry); |
| 1143 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp
ret_cast<size_t>(beginPtr))); | 1193 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp
ret_cast<size_t>(beginPtr))); |
| 1144 endPtr = reinterpret_cast<char*>(partitionRoundDownToSystemPage(reinterp
ret_cast<size_t>(endPtr))); | 1194 endPtr = reinterpret_cast<char*>(partitionRoundDownToSystemPage(reinterp
ret_cast<size_t>(endPtr))); |
| 1145 if (beginPtr < endPtr) { | 1195 if (beginPtr < endPtr) { |
| 1146 size_t partialSlotBytes = endPtr - beginPtr; | 1196 size_t partialSlotBytes = endPtr - beginPtr; |
| 1147 discardableBytes += partialSlotBytes; | 1197 discardableBytes += partialSlotBytes; |
| 1148 if (discard) | 1198 if (discard) |
| 1149 discardSystemPages(beginPtr, partialSlotBytes); | 1199 discardSystemPages(beginPtr, partialSlotBytes); |
| 1150 } | 1200 } |
| 1151 } | 1201 } |
| 1152 return discardableBytes; | 1202 return discardableBytes; |
| 1153 } | 1203 } |
| 1154 | 1204 |
| 1155 static void partitionPurgeBucket(const PartitionBucket* bucket) | 1205 static void partitionPurgeBucket(PartitionBucket* bucket) |
| 1156 { | 1206 { |
| 1157 if (bucket->activePagesHead != &PartitionRootGeneric::gSeedPage) { | 1207 if (bucket->activePagesHead != &PartitionRootGeneric::gSeedPage) { |
| 1158 for (const PartitionPage* page = bucket->activePagesHead; page; page = p
age->nextPage) { | 1208 for (PartitionPage* page = bucket->activePagesHead; page; page = page->n
extPage) { |
| 1159 ASSERT(page != &PartitionRootGeneric::gSeedPage); | 1209 ASSERT(page != &PartitionRootGeneric::gSeedPage); |
| 1160 (void) partitionPurgePage(page, true); | 1210 (void) partitionPurgePage(page, true); |
| 1161 } | 1211 } |
| 1162 } | 1212 } |
| 1163 } | 1213 } |
| 1164 | 1214 |
| 1165 void partitionPurgeMemory(PartitionRoot* root, int flags) | 1215 void partitionPurgeMemory(PartitionRoot* root, int flags) |
| 1166 { | 1216 { |
| 1167 if (flags & PartitionPurgeDecommitEmptyPages) | 1217 if (flags & PartitionPurgeDecommitEmptyPages) |
| 1168 partitionDecommitEmptyPages(root); | 1218 partitionDecommitEmptyPages(root); |
| 1169 // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages | 1219 // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages |
| 1170 // here because that flag is only useful for allocations >= system page | 1220 // here because that flag is only useful for allocations >= system page |
| 1171 // size. We only have allocations that large inside generic partitions | 1221 // size. We only have allocations that large inside generic partitions |
| 1172 // at the moment. | 1222 // at the moment. |
| 1173 } | 1223 } |
| 1174 | 1224 |
| 1175 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags) | 1225 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags) |
| 1176 { | 1226 { |
| 1177 spinLockLock(&root->lock); | 1227 spinLockLock(&root->lock); |
| 1178 if (flags & PartitionPurgeDecommitEmptyPages) | 1228 if (flags & PartitionPurgeDecommitEmptyPages) |
| 1179 partitionDecommitEmptyPages(root); | 1229 partitionDecommitEmptyPages(root); |
| 1180 if (flags & PartitionPurgeDiscardUnusedSystemPages) { | 1230 if (flags & PartitionPurgeDiscardUnusedSystemPages) { |
| 1181 for (size_t i = 0; i < kGenericNumBuckets; ++i) { | 1231 for (size_t i = 0; i < kGenericNumBuckets; ++i) { |
| 1182 const PartitionBucket* bucket = &root->buckets[i]; | 1232 PartitionBucket* bucket = &root->buckets[i]; |
| 1183 if (bucket->slotSize >= kSystemPageSize) | 1233 if (bucket->slotSize >= kSystemPageSize) |
| 1184 partitionPurgeBucket(bucket); | 1234 partitionPurgeBucket(bucket); |
| 1185 } | 1235 } |
| 1186 } | 1236 } |
| 1187 spinLockUnlock(&root->lock); | 1237 spinLockUnlock(&root->lock); |
| 1188 } | 1238 } |
| 1189 | 1239 |
| 1190 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P
artitionPage* page) | 1240 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P
artitionPage* page) |
| 1191 { | 1241 { |
| 1192 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket); | 1242 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket); |
| 1193 | 1243 |
| 1194 if (partitionPageStateIsDecommitted(page)) { | 1244 if (partitionPageStateIsDecommitted(page)) { |
| 1195 ++statsOut->numDecommittedPages; | 1245 ++statsOut->numDecommittedPages; |
| 1196 return; | 1246 return; |
| 1197 } | 1247 } |
| 1198 | 1248 |
| 1199 statsOut->discardableBytes += partitionPurgePage(page, false); | 1249 statsOut->discardableBytes += partitionPurgePage(const_cast<PartitionPage*>(
page), false); |
| 1200 | 1250 |
| 1201 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); | 1251 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); |
| 1202 if (rawSize) | 1252 if (rawSize) |
| 1203 statsOut->activeBytes += static_cast<uint32_t>(rawSize); | 1253 statsOut->activeBytes += static_cast<uint32_t>(rawSize); |
| 1204 else | 1254 else |
| 1205 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucketSlot
Size); | 1255 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucketSlot
Size); |
| 1206 | 1256 |
| 1207 size_t pageBytesResident = partitionRoundUpToSystemPage((bucketNumSlots - pa
ge->numUnprovisionedSlots) * statsOut->bucketSlotSize); | 1257 size_t pageBytesResident = partitionRoundUpToSystemPage((bucketNumSlots - pa
ge->numUnprovisionedSlots) * statsOut->bucketSlotSize); |
| 1208 statsOut->residentBytes += pageBytesResident; | 1258 statsOut->residentBytes += pageBytesResident; |
| 1209 if (partitionPageStateIsEmpty(page)) { | 1259 if (partitionPageStateIsEmpty(page)) { |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1320 // partitionsDumpBucketStats is called after collecting stats because it | 1370 // partitionsDumpBucketStats is called after collecting stats because it |
| 1321 // can use PartitionAlloc to allocate and this can affect the statistics. | 1371 // can use PartitionAlloc to allocate and this can affect the statistics. |
| 1322 for (size_t i = 0; i < partitionNumBuckets; ++i) { | 1372 for (size_t i = 0; i < partitionNumBuckets; ++i) { |
| 1323 if (memoryStats[i].isValid) | 1373 if (memoryStats[i].isValid) |
| 1324 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo
ryStats[i]); | 1374 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo
ryStats[i]); |
| 1325 } | 1375 } |
| 1326 } | 1376 } |
| 1327 | 1377 |
| 1328 } // namespace WTF | 1378 } // namespace WTF |
| 1329 | 1379 |
| OLD | NEW |