Chromium Code Reviews| 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 1043 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1054 size_t copySize = actualOldSize; | 1054 size_t copySize = actualOldSize; |
| 1055 if (newSize < copySize) | 1055 if (newSize < copySize) |
| 1056 copySize = newSize; | 1056 copySize = newSize; |
| 1057 | 1057 |
| 1058 memcpy(ret, ptr, copySize); | 1058 memcpy(ret, ptr, copySize); |
| 1059 partitionFreeGeneric(root, ptr); | 1059 partitionFreeGeneric(root, ptr); |
| 1060 return ret; | 1060 return ret; |
| 1061 #endif | 1061 #endif |
| 1062 } | 1062 } |
| 1063 | 1063 |
| 1064 static size_t partitionPurgePage(const PartitionPage* page, bool discard) | 1064 static size_t partitionPurgePage(PartitionPage* page, bool discard) |
| 1065 { | 1065 { |
| 1066 const PartitionBucket* bucket = page->bucket; | 1066 const PartitionBucket* bucket = page->bucket; |
| 1067 if (bucket->slotSize < kSystemPageSize || !page->numAllocatedSlots) | 1067 size_t slotSize = bucket->slotSize; |
| 1068 if (slotSize < kSystemPageSize || !page->numAllocatedSlots) | |
| 1068 return 0; | 1069 return 0; |
| 1069 | 1070 |
| 1070 size_t bucketNumSlots = partitionBucketSlots(bucket); | 1071 size_t bucketNumSlots = partitionBucketSlots(bucket); |
| 1071 size_t discardableBytes = 0; | 1072 size_t discardableBytes = 0; |
| 1072 | 1073 |
| 1073 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); | 1074 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); |
| 1074 if (rawSize) { | 1075 if (rawSize) { |
| 1075 uint32_t usedBytes = static_cast<uint32_t>(partitionRoundUpToSystemPage( rawSize)); | 1076 uint32_t usedBytes = static_cast<uint32_t>(partitionRoundUpToSystemPage( rawSize)); |
| 1076 discardableBytes = bucket->slotSize - usedBytes; | 1077 discardableBytes = bucket->slotSize - usedBytes; |
| 1077 if (discardableBytes && discard) { | 1078 if (discardableBytes && discard) { |
| 1078 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); | 1079 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); |
| 1079 ptr += usedBytes; | 1080 ptr += usedBytes; |
| 1080 discardSystemPages(ptr, discardableBytes); | 1081 discardSystemPages(ptr, discardableBytes); |
| 1081 } | 1082 } |
| 1082 return discardableBytes; | 1083 return discardableBytes; |
| 1083 } | 1084 } |
| 1084 | 1085 |
| 1085 const size_t maxSlotCount = (kPartitionPageSize * kMaxPartitionPagesPerSlotS pan) / kSystemPageSize; | 1086 const size_t maxSlotCount = (kPartitionPageSize * kMaxPartitionPagesPerSlotS pan) / kSystemPageSize; |
| 1086 ASSERT(bucketNumSlots <= maxSlotCount); | 1087 ASSERT(bucketNumSlots <= maxSlotCount); |
| 1088 ASSERT(page->numUnprovisionedSlots < bucketNumSlots); | |
| 1089 size_t numSlots = bucketNumSlots - page->numUnprovisionedSlots; | |
| 1087 char slotUsage[maxSlotCount]; | 1090 char slotUsage[maxSlotCount]; |
| 1088 size_t lastSlot = -1; | 1091 size_t lastSlot = -1; |
| 1089 memset(slotUsage, 1, sizeof(slotUsage)); | 1092 memset(slotUsage, 1, sizeof(slotUsage)); |
|
haraken
2015/06/24 01:10:16
memset(slotUsage, 1, sizeof(numSlots));
might be
| |
| 1090 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); | 1093 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page)); |
| 1091 PartitionFreelistEntry* entry = page->freelistHead; | 1094 PartitionFreelistEntry* entry = page->freelistHead; |
| 1092 // First, walk the freelist for this page and make a bitmap of which slots | 1095 // First, walk the freelist for this page and make a bitmap of which slots |
| 1093 // are not in use. | 1096 // are not in use. |
| 1094 while (entry) { | 1097 while (entry) { |
| 1095 size_t slotIndex = (reinterpret_cast<char*>(entry) - ptr) / bucket->slot Size; | 1098 size_t slotIndex = (reinterpret_cast<char*>(entry) - ptr) / slotSize; |
| 1096 ASSERT(slotIndex < bucketNumSlots); | 1099 ASSERT(slotIndex < numSlots); |
| 1097 slotUsage[slotIndex] = 0; | 1100 slotUsage[slotIndex] = 0; |
| 1098 entry = partitionFreelistMask(entry->next); | 1101 entry = partitionFreelistMask(entry->next); |
| 1099 // If we have a slot where the masked freelist entry is 0, we can | 1102 // If we have a slot where the masked freelist entry is 0, we can |
| 1100 // actually discard that freelist entry because touching a discarded | 1103 // actually discard that freelist entry because touching a discarded |
| 1101 // page is guaranteed to return original content or 0. | 1104 // page is guaranteed to return original content or 0. |
| 1102 // (Note that this optimization won't fire on big endian machines | 1105 // (Note that this optimization won't fire on big endian machines |
| 1103 // because the masking function is negation.) | 1106 // because the masking function is negation.) |
| 1104 if (!partitionFreelistMask(entry)) | 1107 if (!partitionFreelistMask(entry)) |
| 1105 lastSlot = slotIndex; | 1108 lastSlot = slotIndex; |
| 1106 } | 1109 } |
| 1110 | |
| 1111 // If the slot(s) at the end of the slot span are not in used, we can | |
| 1112 // truncate them entirely and rewrite the freelist. | |
| 1113 size_t truncatedSlots = 0; | |
| 1114 while (!slotUsage[numSlots - 1]) { | |
| 1115 truncatedSlots++; | |
| 1116 numSlots--; | |
| 1117 ASSERT(numSlots); | |
| 1118 } | |
| 1119 // First, do the work of calculating the discardable bytes. Don't actually | |
| 1120 // discard anything unless the discard flag was passed in. | |
| 1121 char* beginPtr; | |
| 1122 char* endPtr; | |
| 1123 size_t unprovisionedBytes = 0; | |
| 1124 if (truncatedSlots) { | |
| 1125 beginPtr = ptr + (numSlots * slotSize); | |
| 1126 endPtr = beginPtr + slotSize; | |
|
haraken
2015/06/24 01:10:16
Shouldn't this be:
endPtr = beginPtr + slotSize
| |
| 1127 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp ret_cast<size_t>(beginPtr))); | |
| 1128 endPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterpre t_cast<size_t>(endPtr))); | |
|
haraken
2015/06/24 01:10:16
Maybe worth having a comment why this is partition
| |
| 1129 if (beginPtr < endPtr) { | |
| 1130 unprovisionedBytes = endPtr - beginPtr; | |
| 1131 discardableBytes += unprovisionedBytes; | |
| 1132 } | |
| 1133 } | |
| 1134 if (truncatedSlots && discard) { | |
| 1135 size_t numNewEntries = 0; | |
| 1136 page->numUnprovisionedSlots += truncatedSlots; | |
|
haraken
2015/06/24 01:10:16
Shall we add:
ASSERT(page->numUnprovisionedSlot
| |
| 1137 // Rewrite the freelist. | |
| 1138 PartitionFreelistEntry** entryPtr = &page->freelistHead; | |
| 1139 bool wroteHead = false; | |
| 1140 entry = page->freelistHead; | |
| 1141 while (entry) { | |
|
haraken
2015/06/24 01:10:16
A more straightforward way would be just to scan a
| |
| 1142 size_t slotIndex = (reinterpret_cast<char*>(entry) - ptr) / slotSize ; | |
| 1143 if (slotIndex < numSlots) { | |
| 1144 if (!wroteHead) { | |
| 1145 *entryPtr = entry; | |
| 1146 wroteHead = true; | |
| 1147 } else { | |
| 1148 *entryPtr = partitionFreelistMask(entry); | |
| 1149 } | |
| 1150 entryPtr = reinterpret_cast<PartitionFreelistEntry**>(entry); | |
| 1151 numNewEntries++; | |
| 1152 } | |
| 1153 entry = partitionFreelistMask(entry->next); | |
| 1154 } | |
| 1155 *entryPtr = nullptr; | |
| 1156 ASSERT(numNewEntries == numSlots - page->numAllocatedSlots); | |
| 1157 | |
| 1158 // Discard the memory. | |
| 1159 discardSystemPages(beginPtr, unprovisionedBytes); | |
| 1160 } | |
| 1161 | |
| 1107 // Next, walk the slots and for any not in use, consider where the system | 1162 // Next, walk the slots and for any not in use, consider where the system |
| 1108 // page boundaries occur. We can release any system pages back to the | 1163 // page boundaries occur. We can release any system pages back to the |
| 1109 // system as long as we don't interfere with a freelist pointer or an | 1164 // system as long as we don't interfere with a freelist pointer or an |
| 1110 // adjacent slot. | 1165 // adjacent slot. |
| 1111 // TODO(cevans): I think we can "truncate" the page, i.e. increase the | 1166 for (size_t i = 0; i < numSlots; ++i) { |
| 1112 // value of page->numUnprovisionedSlots and rewrite(!) the freelist, if | |
| 1113 // we find that to be a win too. | |
| 1114 for (size_t i = 0; i < bucketNumSlots; ++i) { | |
| 1115 if (slotUsage[i]) | 1167 if (slotUsage[i]) |
| 1116 continue; | 1168 continue; |
| 1117 // The first address we can safely discard is just after the freelist | 1169 // The first address we can safely discard is just after the freelist |
| 1118 // pointer. There's one quirk: if the freelist pointer is actually a | 1170 // pointer. There's one quirk: if the freelist pointer is actually a |
| 1119 // null, we can discard that pointer value too. | 1171 // null, we can discard that pointer value too. |
| 1120 char* beginPtr = ptr + (i * bucket->slotSize); | 1172 char* beginPtr = ptr + (i * slotSize); |
| 1121 char* endPtr = beginPtr + bucket->slotSize; | 1173 char* endPtr = beginPtr + slotSize; |
| 1122 if (i != lastSlot) | 1174 if (i != lastSlot) |
| 1123 beginPtr += sizeof(PartitionFreelistEntry); | 1175 beginPtr += sizeof(PartitionFreelistEntry); |
| 1124 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp ret_cast<size_t>(beginPtr))); | 1176 beginPtr = reinterpret_cast<char*>(partitionRoundUpToSystemPage(reinterp ret_cast<size_t>(beginPtr))); |
| 1125 endPtr = reinterpret_cast<char*>(partitionRoundDownToSystemPage(reinterp ret_cast<size_t>(endPtr))); | 1177 endPtr = reinterpret_cast<char*>(partitionRoundDownToSystemPage(reinterp ret_cast<size_t>(endPtr))); |
| 1126 if (beginPtr < endPtr) { | 1178 if (beginPtr < endPtr) { |
| 1127 size_t partialSlotBytes = endPtr - beginPtr; | 1179 size_t partialSlotBytes = endPtr - beginPtr; |
| 1128 discardableBytes += partialSlotBytes; | 1180 discardableBytes += partialSlotBytes; |
| 1129 if (discard) | 1181 if (discard) |
| 1130 discardSystemPages(beginPtr, partialSlotBytes); | 1182 discardSystemPages(beginPtr, partialSlotBytes); |
| 1131 } | 1183 } |
| 1132 } | 1184 } |
| 1133 return discardableBytes; | 1185 return discardableBytes; |
| 1134 } | 1186 } |
| 1135 | 1187 |
| 1136 static void partitionPurgeBucket(const PartitionBucket* bucket) | 1188 static void partitionPurgeBucket(PartitionBucket* bucket) |
| 1137 { | 1189 { |
| 1138 if (bucket->activePagesHead != &PartitionRootGeneric::gSeedPage) { | 1190 if (bucket->activePagesHead != &PartitionRootGeneric::gSeedPage) { |
| 1139 for (const PartitionPage* page = bucket->activePagesHead; page; page = p age->nextPage) { | 1191 for (PartitionPage* page = bucket->activePagesHead; page; page = page->n extPage) { |
| 1140 ASSERT(page != &PartitionRootGeneric::gSeedPage); | 1192 ASSERT(page != &PartitionRootGeneric::gSeedPage); |
| 1141 (void) partitionPurgePage(page, true); | 1193 (void) partitionPurgePage(page, true); |
| 1142 } | 1194 } |
| 1143 } | 1195 } |
| 1144 } | 1196 } |
| 1145 | 1197 |
| 1146 void partitionPurgeMemory(PartitionRoot* root, int flags) | 1198 void partitionPurgeMemory(PartitionRoot* root, int flags) |
| 1147 { | 1199 { |
| 1148 if (flags & PartitionPurgeDecommitEmptyPages) | 1200 if (flags & PartitionPurgeDecommitEmptyPages) |
| 1149 partitionDecommitEmptyPages(root); | 1201 partitionDecommitEmptyPages(root); |
| 1150 // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages | 1202 // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages |
| 1151 // here because that flag is only useful for allocations >= system page | 1203 // here because that flag is only useful for allocations >= system page |
| 1152 // size. We only have allocations that large inside generic partitions | 1204 // size. We only have allocations that large inside generic partitions |
| 1153 // at the moment. | 1205 // at the moment. |
| 1154 } | 1206 } |
| 1155 | 1207 |
| 1156 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags) | 1208 void partitionPurgeMemoryGeneric(PartitionRootGeneric* root, int flags) |
| 1157 { | 1209 { |
| 1158 spinLockLock(&root->lock); | 1210 spinLockLock(&root->lock); |
| 1159 if (flags & PartitionPurgeDecommitEmptyPages) | 1211 if (flags & PartitionPurgeDecommitEmptyPages) |
| 1160 partitionDecommitEmptyPages(root); | 1212 partitionDecommitEmptyPages(root); |
| 1161 if (flags & PartitionPurgeDiscardUnusedSystemPages) { | 1213 if (flags & PartitionPurgeDiscardUnusedSystemPages) { |
| 1162 for (size_t i = 0; i < kGenericNumBuckets; ++i) { | 1214 for (size_t i = 0; i < kGenericNumBuckets; ++i) { |
| 1163 const PartitionBucket* bucket = &root->buckets[i]; | 1215 PartitionBucket* bucket = &root->buckets[i]; |
| 1164 if (bucket->slotSize >= kSystemPageSize) | 1216 if (bucket->slotSize >= kSystemPageSize) |
| 1165 partitionPurgeBucket(bucket); | 1217 partitionPurgeBucket(bucket); |
| 1166 } | 1218 } |
| 1167 } | 1219 } |
| 1168 spinLockUnlock(&root->lock); | 1220 spinLockUnlock(&root->lock); |
| 1169 } | 1221 } |
| 1170 | 1222 |
| 1171 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P artitionPage* page) | 1223 static void partitionDumpPageStats(PartitionBucketMemoryStats* statsOut, const P artitionPage* page) |
| 1172 { | 1224 { |
| 1173 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket); | 1225 uint16_t bucketNumSlots = partitionBucketSlots(page->bucket); |
| 1174 | 1226 |
| 1175 if (partitionPageStateIsDecommitted(page)) { | 1227 if (partitionPageStateIsDecommitted(page)) { |
| 1176 ++statsOut->numDecommittedPages; | 1228 ++statsOut->numDecommittedPages; |
| 1177 return; | 1229 return; |
| 1178 } | 1230 } |
| 1179 | 1231 |
| 1180 statsOut->discardableBytes += partitionPurgePage(page, false); | 1232 statsOut->discardableBytes += partitionPurgePage(const_cast<PartitionPage*>( page), false); |
| 1181 | 1233 |
| 1182 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); | 1234 size_t rawSize = partitionPageGetRawSize(const_cast<PartitionPage*>(page)); |
| 1183 if (rawSize) | 1235 if (rawSize) |
| 1184 statsOut->activeBytes += static_cast<uint32_t>(rawSize); | 1236 statsOut->activeBytes += static_cast<uint32_t>(rawSize); |
| 1185 else | 1237 else |
| 1186 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucketSlot Size); | 1238 statsOut->activeBytes += (page->numAllocatedSlots * statsOut->bucketSlot Size); |
| 1187 | 1239 |
| 1188 size_t pageBytesResident = partitionRoundUpToSystemPage((bucketNumSlots - pa ge->numUnprovisionedSlots) * statsOut->bucketSlotSize); | 1240 size_t pageBytesResident = partitionRoundUpToSystemPage((bucketNumSlots - pa ge->numUnprovisionedSlots) * statsOut->bucketSlotSize); |
| 1189 statsOut->residentBytes += pageBytesResident; | 1241 statsOut->residentBytes += pageBytesResident; |
| 1190 if (partitionPageStateIsEmpty(page)) { | 1242 if (partitionPageStateIsEmpty(page)) { |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1301 // partitionsDumpBucketStats is called after collecting stats because it | 1353 // partitionsDumpBucketStats is called after collecting stats because it |
| 1302 // can use PartitionAlloc to allocate and this can affect the statistics. | 1354 // can use PartitionAlloc to allocate and this can affect the statistics. |
| 1303 for (size_t i = 0; i < partitionNumBuckets; ++i) { | 1355 for (size_t i = 0; i < partitionNumBuckets; ++i) { |
| 1304 if (memoryStats[i].isValid) | 1356 if (memoryStats[i].isValid) |
| 1305 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo ryStats[i]); | 1357 partitionStatsDumper->partitionsDumpBucketStats(partitionName, &memo ryStats[i]); |
| 1306 } | 1358 } |
| 1307 } | 1359 } |
| 1308 | 1360 |
| 1309 } // namespace WTF | 1361 } // namespace WTF |
| 1310 | 1362 |
| OLD | NEW |