Changeset 38621 in vbox
- Timestamp:
- Sep 4, 2011 4:56:56 PM (14 years ago)
- svn:sync-xref-src-repo-rev:
- 73828
- Location:
- trunk
- Files:
-
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/VBox/err.h
r37794 r38621 1334 1334 /** The cache is not up to date with the image. */ 1335 1335 #define VERR_VD_CACHE_NOT_UP_TO_DATE (-3276) 1336 /** The given range does not meet the required alignment. */ 1337 #define VERR_VD_DISCARD_ALIGNMENT_NOT_MET (-3277) 1338 /** The discard operation is not supported for this image. */ 1339 #define VERR_VD_DISCARD_NOT_SUPPORTED (-3278) 1336 1340 /** @} */ 1337 1341 -
trunk/include/VBox/vd-plugin.h
r38469 r38621 4 4 5 5 /* 6 * Copyright (C) 2006-201 0Oracle Corporation6 * Copyright (C) 2006-2011 Oracle Corporation 7 7 * 8 8 * This file is part of VirtualBox Open Source Edition (OSE), as … … 40 40 /** @}*/ 41 41 42 /** @name VBox HDD backend discard flags 43 * @{ 44 */ 45 /** Don't discard block but mark the given range as unused 46 * (usually by writing 0's to it). 47 * This doesn't require the range to be aligned on a block boundary but 48 * the image size might not be decreased. */ 49 #define VD_DISCARD_MARK_UNUSED RT_BIT(0) 50 /** @}*/ 51 42 52 43 53 /** … … 540 550 * 541 551 * @returns VBox status code. 542 * @returns VERR_NOT_SUPPORTED if this image cannot be compacted yet.552 * @returns VERR_NOT_SUPPORTED if this image cannot be resized yet. 543 553 * @param pBackendData Opaque state data for this image. 544 554 * @param cbSize New size of the image. … … 559 569 PVDINTERFACE pVDIfsImage, 560 570 PVDINTERFACE pVDIfsOperation)); 571 572 /** 573 * Discards the given amount of bytes decreasing the size of the image if possible. 574 * 575 * @returns VBox status code. 576 * @retval VERR_VD_DISCARD_ALIGNMENT_NOT_MET if the range doesn't meet the required alignment 577 * for the discard. 578 * @param pBackendData Opaque state data for this image. 579 * @param uOffset The offset of the first byte to discard. 580 * @param cbDiscard How many bytes to discard. 581 * @param pcbPreAllocated Pointer to the returned amount of bytes that must 582 * be discarded before the range to perform a full 583 * block discard. 584 * @param pcbPostAllocated Pointer to the returned amount of bytes that must 585 * be discarded after the range to perform a full 586 * block discard. 587 * @param pcbActuallyDiscarded Pointer to the returned amount of bytes which 588 * could be actually discarded. 589 * @param ppbmAllocationBitmap Where to store the pointer to the allocation bitmap 590 * if VERR_VD_DISCARD_ALIGNMENT_NOT_MET is returned or NULL 591 * if the allocation bitmap should be returned. 592 * @param fDiscard Flags which affect discard behavior. Combination 593 * of the VD_DISCARD_* flags. 594 */ 595 DECLR3CALLBACKMEMBER(int, pfnDiscard, (void *pBackendData, 596 uint64_t uOffset, size_t cbDiscard, 597 size_t *pcbPreAllocated, 598 size_t *pcbPostAllocated, 599 size_t *pcbActuallyDiscarded, 600 void **ppbmAllocationBitmap, 601 unsigned fDiscard)); 561 602 562 603 } VBOXHDDBACKEND; -
trunk/include/VBox/vd.h
r38469 r38621 4 4 5 5 /* 6 * Copyright (C) 2006-201 0Oracle Corporation6 * Copyright (C) 2006-2011 Oracle Corporation 7 7 * 8 8 * This file is part of VirtualBox Open Source Edition (OSE), as … … 180 180 * will not fail if it cannot do this, the flag will be simply ignored. */ 181 181 #define VD_OPEN_FLAGS_SEQUENTIAL RT_BIT(6) 182 /** Allow the discard operation if supported. Only available if VD_CAP_DISCARD 183 * is set. VDOpen fails with VERR_VD_DISCARD_NOT_SUPPORTED if discarding is not 184 * supported. */ 185 #define VD_OPEN_FLAGS_DISCARD RT_BIT(7) 182 186 /** Mask of valid flags. */ 183 #define VD_OPEN_FLAGS_MASK (VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_HONOR_ZEROES | VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL )187 #define VD_OPEN_FLAGS_MASK (VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_HONOR_ZEROES | VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_DISCARD) 184 188 /** @}*/ 185 189 … … 249 253 * VDINTERFACEIO exclusively for all file operations. */ 250 254 #define VD_CAP_VFS RT_BIT(9) 255 /** The backend supports the discard operation. */ 256 #define VD_CAP_DISCARD RT_BIT(10) 251 257 /** @}*/ 252 258 … … 392 398 393 399 /** 400 * VD range descriptor. 401 */ 402 typedef struct VDRANGE 403 { 404 /** Start offset in bytes, multiple of 512. */ 405 uint64_t offStart; 406 /** Amount of bytes described by this range, multiple of 512. */ 407 size_t cbRange; 408 } VDRANGE; 409 410 /** Pointer to a range descriptor. */ 411 typedef VDRANGE *PVDRANGE; 412 /** Pointer to a constant range descriptor. */ 413 typedef const VDRANGE *PCVDRANGE; 414 415 /** 394 416 * VBox HDD Container main structure. 395 417 */ … … 1090 1112 1091 1113 /** 1114 * Discards unused ranges given as a list. 1115 * 1116 * @return VBox status code. 1117 * @param pDisk Pointer to HDD container. 1118 * @param paRanges The array of ranges to discard. 1119 * @param cRanges Number of entries in the array. 1120 * 1121 * @note In contrast to VDCompact() the ranges are always discarded even if they 1122 * appear to contain data. This method is mainly used to implement TRIM support. 1123 */ 1124 VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCVDRANGE paRanges, unsigned cRanges); 1125 1126 1127 /** 1092 1128 * Start an asynchronous read request. 1093 1129 * -
trunk/src/VBox/Storage/DMG.cpp
r38469 r38621 2332 2332 NULL, 2333 2333 /* pfnResize */ 2334 NULL, 2335 /* pfnDiscard */ 2334 2336 NULL 2335 2337 }; -
trunk/src/VBox/Storage/ISCSI.cpp
r38469 r38621 5570 5570 NULL, 5571 5571 /* pfnResize */ 5572 NULL, 5573 /* pfnDiscard */ 5572 5574 NULL 5573 5575 }; -
trunk/src/VBox/Storage/QCOW.cpp
r38563 r38621 2745 2745 NULL, 2746 2746 /* pfnResize */ 2747 NULL, 2748 /* pfnDiscard */ 2747 2749 NULL 2748 2750 }; -
trunk/src/VBox/Storage/QED.cpp
r38562 r38621 2919 2919 NULL, 2920 2920 /* pfnResize */ 2921 qedResize 2921 qedResize, 2922 /* pfnDiscard */ 2923 NULL 2922 2924 }; -
trunk/src/VBox/Storage/RAW.cpp
r38469 r38621 1290 1290 NULL, 1291 1291 /* pfnResize */ 1292 NULL, 1293 /* pfnDiscard */ 1292 1294 NULL 1293 1295 }; -
trunk/src/VBox/Storage/VD.cpp
r38563 r38621 5 5 6 6 /* 7 * Copyright (C) 2006-201 0Oracle Corporation7 * Copyright (C) 2006-2011 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 52 52 #define VD_IO_TASK_SEGMENTS_MAX 64 53 53 54 /** Threshold after not recently used blocks are removed from the list. */ 55 #define VD_DISCARD_REMOVE_THRESHOLD (10 * _1M) /** @todo: experiment */ 56 54 57 /** 55 58 * VD async I/O interface storage descriptor. … … 146 149 147 150 /** 151 * A block waiting for a discard. 152 */ 153 typedef struct VDDISCARDBLOCK 154 { 155 /** AVL core. */ 156 AVLRU64NODECORE Core; 157 /** LRU list node. */ 158 RTLISTNODE NodeLru; 159 /** Number of bytes to discard. */ 160 size_t cbDiscard; 161 /** Bitmap of allocated sectors. */ 162 void *pbmAllocated; 163 } VDDISCARDBLOCK, *PVDDISCARDBLOCK; 164 165 /** 166 * VD discard state. 167 */ 168 typedef struct VDDISCARDSTATE 169 { 170 /** Number of bytes waiting for a discard. */ 171 size_t cbDiscarding; 172 /** AVL tree with blocks waiting for a discard. 173 * The uOffset + cbDiscard range is the search key. */ 174 PAVLRU64TREE pTreeBlocks; 175 /** LRU list of the least frequently discarded blocks. 176 * If there are to many blocks waiting the least frequently used 177 * will be removed and the range will be set to 0. 178 */ 179 RTLISTNODE ListLru; 180 } VDDISCARDSTATE, *PVDDISCARDSTATE; 181 182 /** 148 183 * VBox HDD Container main structure, private part. 149 184 */ … … 151 186 { 152 187 /** Structure signature (VBOXHDDDISK_SIGNATURE). */ 153 uint32_t u32Signature;188 uint32_t u32Signature; 154 189 155 190 /** Image type. */ … … 205 240 /** Pointer to the L2 disk cache if any. */ 206 241 PVDCACHE pCache; 242 /** Pointer to the discard state if any. */ 243 PVDDISCARDSTATE pDiscard; 207 244 }; 208 245 … … 936 973 } 937 974 975 /** 976 * Creates a new empty discard state. 977 * 978 * @returns Pointer to the new discard state or NULL if out of memory. 979 */ 980 static PVDDISCARDSTATE vdDiscardStateCreate(void) 981 { 982 PVDDISCARDSTATE pDiscard = (PVDDISCARDSTATE)RTMemAllocZ(sizeof(VDDISCARDSTATE)); 983 984 if (pDiscard) 985 { 986 RTListInit(&pDiscard->ListLru); 987 pDiscard->pTreeBlocks = (PAVLRU64TREE)RTMemAllocZ(sizeof(AVLRU64TREE)); 988 if (!pDiscard->pTreeBlocks) 989 { 990 RTMemFree(pDiscard); 991 pDiscard = NULL; 992 } 993 } 994 995 return pDiscard; 996 } 997 998 /** 999 * Removes the least recently used blocks from the waiting list until 1000 * the new value is reached. 1001 * 1002 * @returns VBox status code. 1003 * @param pDisk VD disk container. 1004 * @param pDiscard The discard state. 1005 * @param cbDiscardingNew How many bytes should be waiting on success. 1006 * The number of bytes waiting can be less. 1007 */ 1008 static int vdDiscardRemoveBlocks(PVBOXHDD pDisk, PVDDISCARDSTATE pDiscard, size_t cbDiscardingNew) 1009 { 1010 int rc = VINF_SUCCESS; 1011 1012 while (pDiscard->cbDiscarding > cbDiscardingNew) 1013 { 1014 PVDDISCARDBLOCK pBlock = RTListGetLast(&pDiscard->ListLru, VDDISCARDBLOCK, NodeLru); 1015 1016 Assert(!RTListIsEmpty(&pDiscard->ListLru)); 1017 1018 /* Go over the allocation bitmap and mark all discarded sectors as unused. */ 1019 uint64_t offStart = pBlock->Core.Key; 1020 uint32_t idxStart = 0; 1021 size_t cbLeft = pBlock->cbDiscard; 1022 bool fAllocated = ASMBitTest(pBlock->pbmAllocated, idxStart); 1023 uint32_t cSectors = pBlock->cbDiscard * 8; 1024 1025 while (cbLeft > 0) 1026 { 1027 int32_t idxEnd; 1028 size_t cbThis = cbLeft; 1029 1030 if (fAllocated) 1031 { 1032 /* Check for the first unallocated bit. */ 1033 idxEnd = ASMBitNextClear(pBlock->pbmAllocated, cSectors, idxStart); 1034 if (idxEnd != -1) 1035 { 1036 cbThis = (idxEnd - idxStart) * 512; 1037 fAllocated = false; 1038 } 1039 } 1040 else 1041 { 1042 /* Mark as unused and check for the first set bit. */ 1043 idxEnd = ASMBitNextSet(pBlock->pbmAllocated, cSectors, idxStart); 1044 if (idxEnd != -1) 1045 cbThis = (idxEnd - idxStart) * 512; 1046 1047 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, offStart, 1048 cbThis, NULL, NULL, &cbThis, 1049 NULL, VD_DISCARD_MARK_UNUSED); 1050 if (RT_FAILURE(rc)) 1051 break; 1052 1053 fAllocated = true; 1054 } 1055 1056 idxStart = idxEnd; 1057 offStart += cbThis; 1058 cbLeft -= cbThis; 1059 } 1060 1061 if (RT_FAILURE(rc)) 1062 break; 1063 1064 PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key); 1065 Assert(pBlockRemove == pBlock); 1066 RTListNodeRemove(&pBlock->NodeLru); 1067 1068 pDiscard->cbDiscarding -= pBlock->cbDiscard; 1069 RTMemFree(pBlock->pbmAllocated); 1070 RTMemFree(pBlock); 1071 } 1072 1073 Assert(RT_FAILURE(rc) || pDiscard->cbDiscarding <= cbDiscardingNew); 1074 1075 return rc; 1076 } 1077 1078 /** 1079 * Destroys the current discard state, writing any waiting blocks to the image. 1080 * 1081 * @returns VBox status code. 1082 * @param pDisk VD disk container. 1083 */ 1084 static int vdDiscardStateDestroy(PVBOXHDD pDisk) 1085 { 1086 int rc = VINF_SUCCESS; 1087 1088 if (pDisk->pDiscard) 1089 { 1090 rc = vdDiscardRemoveBlocks(pDisk, pDisk->pDiscard, 0 /* Remove all blocks. */); 1091 AssertRC(rc); 1092 RTMemFree(pDisk->pDiscard->pTreeBlocks); 1093 RTMemFree(pDisk->pDiscard); 1094 pDisk->pDiscard = NULL; 1095 } 1096 1097 return rc; 1098 } 1099 1100 /** 1101 * Discards the given range from the underlying block. 1102 * 1103 * @returns VBox status code. 1104 * @param pDisk VD container data. 1105 * @param offStart Where to start discarding. 1106 * @param cbDiscard How many bytes to discard. 1107 */ 1108 static int vdDiscardRange(PVBOXHDD pDisk, PVDDISCARDSTATE pDiscard, uint64_t offStart, size_t cbDiscard) 1109 { 1110 int rc = VINF_SUCCESS; 1111 1112 LogFlowFunc(("pDisk=%#p pDiscard=%#p offStart=%llu cbDiscard=%zu\n", 1113 pDisk, pDiscard, offStart, cbDiscard)); 1114 1115 do 1116 { 1117 size_t cbThisDiscard; 1118 1119 /* Look for a matching block in the AVL tree first. */ 1120 PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, false); 1121 if (!pBlock || pBlock->Core.KeyLast < offStart) 1122 { 1123 void *pbmAllocated = NULL; 1124 size_t cbPreAllocated, cbPostAllocated; 1125 PVDDISCARDBLOCK pBlockAbove = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, offStart, true); 1126 1127 /* Clip range to remain in the current block. */ 1128 if (pBlockAbove) 1129 cbThisDiscard = RT_MIN(cbDiscard, pBlockAbove->Core.Key - (offStart + cbDiscard - 1)); 1130 else 1131 cbThisDiscard = cbDiscard; 1132 1133 Assert(cbThisDiscard % 512 == 0); 1134 1135 /* No block found, try to discard using the backend first. */ 1136 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, offStart, 1137 cbThisDiscard, &cbPreAllocated, 1138 &cbPostAllocated, &cbThisDiscard, 1139 &pbmAllocated, 0); 1140 if (rc == VERR_VD_DISCARD_ALIGNMENT_NOT_MET) 1141 { 1142 /* Create new discard block. */ 1143 pBlock = (PVDDISCARDBLOCK)RTMemAllocZ(sizeof(VDDISCARDBLOCK)); 1144 if (pBlock) 1145 { 1146 pBlock->Core.Key = offStart + cbPreAllocated; 1147 pBlock->Core.KeyLast = offStart + cbThisDiscard + cbPreAllocated - 1; 1148 pBlock->cbDiscard = cbThisDiscard; 1149 pBlock->pbmAllocated = pbmAllocated; 1150 bool fInserted = RTAvlrU64Insert(pDiscard->pTreeBlocks, &pBlock->Core); 1151 Assert(fInserted); 1152 1153 RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru); 1154 pDiscard->cbDiscarding += cbThisDiscard; 1155 if (pDiscard->cbDiscarding > VD_DISCARD_REMOVE_THRESHOLD) 1156 rc = vdDiscardRemoveBlocks(pDisk, pDiscard, VD_DISCARD_REMOVE_THRESHOLD); 1157 else 1158 rc = VINF_SUCCESS; 1159 } 1160 else 1161 rc = VERR_NO_MEMORY; 1162 } 1163 } 1164 else 1165 { 1166 /* Range lies partly in the block, update allocation bitmap. */ 1167 cbThisDiscard = pBlock->Core.KeyLast - offStart + 1; 1168 rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET; 1169 } 1170 1171 if (rc == VERR_VD_DISCARD_ALIGNMENT_NOT_MET) 1172 { 1173 int32_t idxStart, idxEnd; 1174 1175 AssertPtr(pBlock); 1176 1177 Assert(!(cbThisDiscard % 512)); 1178 Assert(!((offStart - pBlock->Core.Key) % 512)); 1179 1180 idxStart = (offStart - pBlock->Core.Key) / 512; 1181 idxEnd = idxStart + (cbThisDiscard / 512); 1182 1183 ASMBitClearRange(pBlock->pbmAllocated, idxStart, idxEnd); 1184 1185 /* Call the backend to discard the block if it is completely unallocated now. */ 1186 if (ASMBitFirstSet((volatile void *)pBlock->pbmAllocated, pBlock->cbDiscard / 512) == -1) 1187 { 1188 size_t cbPreAllocated, cbPostAllocated, cbActuallyDiscarded; 1189 1190 rc = pDisk->pLast->Backend->pfnDiscard(pDisk->pLast->pBackendData, pBlock->Core.Key, 1191 pBlock->cbDiscard, &cbPreAllocated, 1192 &cbPostAllocated, &cbActuallyDiscarded, 1193 NULL, 0); 1194 Assert(rc != VERR_VD_DISCARD_ALIGNMENT_NOT_MET); 1195 Assert(!cbPreAllocated); 1196 Assert(!cbPostAllocated); 1197 Assert(cbActuallyDiscarded == pBlock->cbDiscard || RT_FAILURE(rc)); 1198 1199 /* Remove the block on success. */ 1200 if (RT_SUCCESS(rc)) 1201 { 1202 PVDDISCARDBLOCK pBlockRemove = (PVDDISCARDBLOCK)RTAvlrU64RangeRemove(pDiscard->pTreeBlocks, pBlock->Core.Key); 1203 Assert(pBlockRemove == pBlock); 1204 1205 RTListNodeRemove(&pBlock->NodeLru); 1206 RTMemFree(pBlock->pbmAllocated); 1207 RTMemFree(pBlock); 1208 } 1209 } 1210 else 1211 { 1212 RTListNodeRemove(&pBlock->NodeLru); 1213 RTListPrepend(&pDiscard->ListLru, &pBlock->NodeLru); 1214 } 1215 } 1216 1217 cbDiscard -= cbThisDiscard; 1218 offStart += cbThisDiscard; 1219 } while (cbDiscard != 0 && RT_SUCCESS(rc)); 1220 1221 LogFlowFunc(("returns rc=%Rrc\n", rc)); 1222 return rc; 1223 } 1224 1225 /** 1226 * Discard helper. 1227 * 1228 * @returns VBox status code. 1229 * @param pDisk VD container data. 1230 * @param paRanges The array of ranges to discard. 1231 * @param cRanges The number of ranges in the array. 1232 */ 1233 static int vdDiscardHelper(PVBOXHDD pDisk, PCVDRANGE paRanges, unsigned cRanges) 1234 { 1235 int rc = VINF_SUCCESS; 1236 PVDDISCARDSTATE pDiscard = pDisk->pDiscard; 1237 1238 if (RT_UNLIKELY(!pDiscard)) 1239 { 1240 pDiscard = vdDiscardStateCreate(); 1241 if (!pDiscard) 1242 return VERR_NO_MEMORY; 1243 1244 pDisk->pDiscard = pDiscard; 1245 } 1246 1247 /* Go over the range array and discard individual blocks. */ 1248 for (unsigned i = 0; i < cRanges; i++) 1249 { 1250 rc = vdDiscardRange(pDisk, pDiscard, paRanges[i].offStart, paRanges[i].cbRange); 1251 if (RT_FAILURE(rc)) 1252 break; 1253 } 1254 1255 return rc; 1256 } 1257 1258 /** 1259 * Marks the given range as allocated in the image. 1260 * Required if there are discards in progress and a write to a block which can get discarded 1261 * is written to. 1262 * 1263 * @returns VBox status code. 1264 * @param pDisk VD container data. 1265 * @param uOffset First byte to mark as allocated. 1266 * @param cbRange Number of bytes to mark as allocated. 1267 */ 1268 static int vdDiscardSetRangeAllocated(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRange) 1269 { 1270 PVDDISCARDSTATE pDiscard = pDisk->pDiscard; 1271 int rc = VINF_SUCCESS; 1272 1273 if (pDiscard) 1274 { 1275 do 1276 { 1277 size_t cbThisRange = cbRange; 1278 PVDDISCARDBLOCK pBlock = (PVDDISCARDBLOCK)RTAvlrU64RangeGet(pDiscard->pTreeBlocks, uOffset); 1279 1280 if (pBlock) 1281 { 1282 int32_t idxStart, idxEnd; 1283 1284 Assert(!(cbThisRange % 512)); 1285 Assert(!((uOffset - pBlock->Core.Key) % 512)); 1286 1287 idxStart = (uOffset - pBlock->Core.Key) / 512; 1288 idxEnd = idxStart + (cbThisRange / 512); 1289 ASMBitSetRange(pBlock->pbmAllocated, idxStart, idxEnd); 1290 } 1291 else 1292 { 1293 pBlock = (PVDDISCARDBLOCK)RTAvlrU64GetBestFit(pDiscard->pTreeBlocks, uOffset, true); 1294 if (pBlock) 1295 cbThisRange = RT_MIN(cbThisRange, pBlock->Core.Key - uOffset); 1296 } 1297 1298 uOffset += cbThisRange; 1299 cbRange -= cbThisRange; 1300 } while (cbRange != 0); 1301 } 1302 1303 return rc; 1304 } 1305 938 1306 DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir, 939 1307 uint64_t uOffset, size_t cbTransfer, … … 1693 2061 && fUpdateCache) 1694 2062 rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf, cbWrite, NULL); 2063 2064 if (RT_SUCCESS(rc)) 2065 rc = vdDiscardSetRangeAllocated(pDisk, uOffset, cbWrite); 1695 2066 1696 2067 return rc; … … 4347 4718 rc = VERR_INVALID_PARAMETER); 4348 4719 4720 /* 4721 * Destroy the current discard state first which might still have pending blocks 4722 * for the currently opened image which will be switched to readonly mode. 4723 */ 4724 /* Lock disk for writing, as we modify pDisk information below. */ 4725 rc2 = vdThreadStartWrite(pDisk); 4726 AssertRC(rc2); 4727 fLockWrite = true; 4728 rc = vdDiscardStateDestroy(pDisk); 4729 if (RT_FAILURE(rc)) 4730 break; 4731 rc2 = vdThreadFinishWrite(pDisk); 4732 AssertRC(rc2); 4733 fLockWrite = false; 4734 4349 4735 /* Set up image descriptor. */ 4350 4736 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE)); … … 4386 4772 } 4387 4773 4774 /* 4775 * Fail if the the backend doesn't support the discard operation but the 4776 * flag is set. 4777 */ 4778 if ( !(pImage->Backend->uBackendCaps & VD_CAP_DISCARD) 4779 && (uOpenFlags & VD_OPEN_FLAGS_DISCARD)) 4780 { 4781 rc = vdError(pDisk, VERR_VD_DISCARD_NOT_SUPPORTED, RT_SRC_POS, 4782 N_("VD: Backend '%s' does not support discard"), pszBackend); 4783 break; 4784 } 4785 4388 4786 /* Set up the I/O interface. */ 4389 4787 pImage->VDIo.pInterfaceIo = VDIfIoGet(pVDIfsImage); … … 4403 4801 AssertRC(rc); 4404 4802 4405 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;4803 pImage->uOpenFlags = uOpenFlags & (VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_DISCARD); 4406 4804 rc = pImage->Backend->pfnOpen(pImage->pszFilename, 4407 4805 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME, … … 5110 5508 fLockRead = false; 5111 5509 5510 /* 5511 * Destroy the current discard state first which might still have pending blocks 5512 * for the currently opened image which will be switched to readonly mode. 5513 */ 5514 /* Lock disk for writing, as we modify pDisk information below. */ 5515 rc2 = vdThreadStartWrite(pDisk); 5516 AssertRC(rc2); 5517 fLockWrite = true; 5518 rc = vdDiscardStateDestroy(pDisk); 5519 if (RT_FAILURE(rc)) 5520 break; 5521 rc2 = vdThreadFinishWrite(pDisk); 5522 AssertRC(rc2); 5523 fLockWrite = false; 5524 5112 5525 /* Set up image descriptor. */ 5113 5526 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE)); … … 6556 6969 break; 6557 6970 } 6971 6972 /* Destroy the current discard state first which might still have pending blocks. */ 6973 rc = vdDiscardStateDestroy(pDisk); 6974 if (RT_FAILURE(rc)) 6975 break; 6976 6558 6977 unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData); 6559 6978 /* Remove image from list of opened images. */ … … 8179 8598 8180 8599 8600 VBOXDDU_DECL(int) VDDiscardRanges(PVBOXHDD pDisk, PCVDRANGE paRanges, unsigned cRanges) 8601 { 8602 int rc; 8603 int rc2; 8604 bool fLockWrite = false; 8605 8606 LogFlowFunc(("pDisk=%#p paRanges=%#p cRanges=%u\n", 8607 pDisk, paRanges, cRanges)); 8608 do 8609 { 8610 /* sanity check */ 8611 AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER); 8612 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature)); 8613 8614 /* Check arguments. */ 8615 AssertMsgBreakStmt(cRanges, 8616 ("cRanges=%u\n", cRanges), 8617 rc = VERR_INVALID_PARAMETER); 8618 AssertMsgBreakStmt(VALID_PTR(paRanges), 8619 ("paRanges=%#p\n", paRanges), 8620 rc = VERR_INVALID_PARAMETER); 8621 8622 rc2 = vdThreadStartWrite(pDisk); 8623 AssertRC(rc2); 8624 fLockWrite = true; 8625 8626 AssertPtrBreakStmt(pDisk->pLast, rc = VERR_VD_NOT_OPENED); 8627 8628 AssertMsgBreakStmt(pDisk->pLast->uOpenFlags & VD_OPEN_FLAGS_DISCARD, 8629 ("Discarding not supported\n"), 8630 rc = VERR_NOT_SUPPORTED); 8631 8632 vdSetModifiedFlag(pDisk); 8633 rc = vdDiscardHelper(pDisk, paRanges, cRanges); 8634 } while (0); 8635 8636 if (RT_UNLIKELY(fLockWrite)) 8637 { 8638 rc2 = vdThreadFinishWrite(pDisk); 8639 AssertRC(rc2); 8640 } 8641 8642 LogFlowFunc(("returns %Rrc\n", rc)); 8643 return rc; 8644 } 8645 8646 8181 8647 VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead, 8182 8648 PCRTSGBUF pcSgBuf, -
trunk/src/VBox/Storage/VDI.cpp
r38469 r38621 102 102 } 103 103 104 if (pImage->paBlocksRev) 105 { 106 RTMemFree(pImage->paBlocksRev); 107 pImage->paBlocksRev = NULL; 108 } 109 104 110 if (fDelete && pImage->pszFilename) 105 111 vdIfIoIntFileDelete(pImage->pIfIo, pImage->pszFilename); … … 703 709 getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER), 704 710 NULL); 711 712 if (uOpenFlags & VD_OPEN_FLAGS_DISCARD) 713 { 714 /* 715 * Create the back resolving table for discards. 716 * any error or inconsistency results in a fail because this might 717 * get us into trouble later on. 718 */ 719 pImage->paBlocksRev = (unsigned *)RTMemAllocZ(sizeof(unsigned) * getImageBlocks(&pImage->Header)); 720 if (pImage->paBlocksRev) 721 { 722 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header); 723 unsigned cBlocks = getImageBlocks(&pImage->Header); 724 725 for (unsigned i = 0; i < cBlocksAllocated; i++) 726 pImage->paBlocksRev[i] = VDI_IMAGE_BLOCK_FREE; 727 728 for (unsigned i = 0; i < cBlocks; i++) 729 { 730 VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i]; 731 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock)) 732 { 733 if (ptrBlock < cBlocksAllocated) 734 { 735 if (pImage->paBlocksRev[ptrBlock] == VDI_IMAGE_BLOCK_FREE) 736 pImage->paBlocksRev[ptrBlock] = i; 737 else 738 { 739 rc = VERR_VD_VDI_INVALID_HEADER; 740 break; 741 } 742 } 743 else 744 { 745 rc = VERR_VD_VDI_INVALID_HEADER; 746 break; 747 } 748 } 749 } 750 } 751 else 752 rc = VERR_NO_MEMORY; 753 } 705 754 706 755 out: … … 1205 1254 goto out; 1206 1255 pImage->paBlocks[uBlock] = cBlocksAllocated; 1256 1257 if (pImage->paBlocksRev) 1258 pImage->paBlocksRev[cBlocksAllocated] = uBlock; 1259 1207 1260 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1); 1208 1261 … … 1488 1541 1489 1542 /* Image must be opened and the new flags must be valid. */ 1490 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL )))1543 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_DISCARD))) 1491 1544 { 1492 1545 rc = VERR_INVALID_PARAMETER; … … 2004 2057 goto out; 2005 2058 pImage->paBlocks[uBlock] = cBlocksAllocated; 2059 2060 if (pImage->paBlocksRev) 2061 pImage->paBlocksRev[cBlocksAllocated] = uBlock; 2062 2006 2063 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1); 2007 2064 … … 2481 2538 2482 2539 2540 /** @copydoc VBOXHDDBACKEND::pfnDiscard */ 2541 static DECLCALLBACK(int) vdiDiscard(void *pBackendData, 2542 uint64_t uOffset, size_t cbDiscard, 2543 size_t *pcbPreAllocated, 2544 size_t *pcbPostAllocated, 2545 size_t *pcbActuallyDiscarded, 2546 void **ppbmAllocationBitmap, 2547 unsigned fDiscard) 2548 { 2549 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData; 2550 unsigned uBlock; 2551 unsigned offDiscard; 2552 int rc = VINF_SUCCESS; 2553 void *pvBlock = NULL; 2554 2555 LogFlowFunc(("pBackendData=%#p uOffset=%llu cbDiscard=%zu pcbPreAllocated=%#p pcbPostAllocated=%#p pcbActuallyDiscarded=%#p ppbmAllocationBitmap=%#p fDiscard=%#x\n", 2556 pBackendData, uOffset, cbDiscard, pcbPreAllocated, pcbPostAllocated, pcbActuallyDiscarded, ppbmAllocationBitmap, fDiscard)); 2557 2558 AssertPtr(pImage); 2559 Assert(!(uOffset % 512)); 2560 Assert(!(cbDiscard % 512)); 2561 2562 do 2563 { 2564 AssertMsgBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY), 2565 ("Image is opened readonly\n"), 2566 rc = VERR_VD_IMAGE_READ_ONLY); 2567 2568 AssertMsgBreakStmt(cbDiscard, 2569 ("cbDiscard=%u\n", cbDiscard), 2570 rc = VERR_INVALID_PARAMETER); 2571 2572 /* Calculate starting block number and offset inside it. */ 2573 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index); 2574 offDiscard = (unsigned)uOffset & pImage->uBlockMask; 2575 2576 /* Clip range to at most the rest of the block. */ 2577 cbDiscard = RT_MIN(cbDiscard, getImageBlockSize(&pImage->Header) - offDiscard); 2578 Assert(!(cbDiscard % 512)); 2579 2580 if (pcbPreAllocated) 2581 *pcbPreAllocated = 0; 2582 2583 if (pcbPostAllocated) 2584 *pcbPostAllocated = 0; 2585 2586 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock])) 2587 { 2588 uint8_t *pbBlockData; 2589 size_t cbPreAllocated, cbPostAllocated; 2590 2591 cbPreAllocated = offDiscard % getImageBlockSize(&pImage->Header); 2592 cbPostAllocated = getImageBlockSize(&pImage->Header) - cbDiscard - cbPreAllocated; 2593 2594 /* Read the block data. */ 2595 pvBlock = RTMemAlloc(pImage->cbTotalBlockData); 2596 if (!pvBlock) 2597 { 2598 rc = VERR_NO_MEMORY; 2599 break; 2600 } 2601 pbBlockData = (uint8_t *)pvBlock + pImage->offStartBlockData; 2602 2603 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData; 2604 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset, 2605 pvBlock, pImage->cbTotalBlockData, NULL); 2606 if (RT_FAILURE(rc)) 2607 break; 2608 2609 /* Clear data. */ 2610 memset(pbBlockData + offDiscard , 0, cbDiscard); 2611 2612 Assert(!(cbDiscard % 4)); 2613 Assert(cbDiscard * 8 <= UINT32_MAX); 2614 if (ASMBitFirstSet((volatile void *)pbBlockData, getImageBlockSize(&pImage->Header) * 8) == -1) 2615 { 2616 uint64_t cbImage; 2617 unsigned idxLastBlock = getImageBlocksAllocated(&pImage->Header) - 1; 2618 unsigned uBlockLast = pImage->paBlocksRev[idxLastBlock]; 2619 2620 pImage->paBlocksRev[idxLastBlock] = VDI_IMAGE_BLOCK_FREE; 2621 2622 /* 2623 * The block is empty, remove it. 2624 * Read the last block of the image first. 2625 */ 2626 if (idxLastBlock != pImage->paBlocks[uBlock]) 2627 { 2628 u64Offset = (uint64_t)idxLastBlock * pImage->cbTotalBlockData + pImage->offStartData; 2629 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset, 2630 pvBlock, pImage->cbTotalBlockData, NULL); 2631 if (RT_FAILURE(rc)) 2632 break; 2633 2634 /* Write to the now unallocated block. */ 2635 u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData; 2636 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset, 2637 pvBlock, pImage->cbTotalBlockData, NULL); 2638 if (RT_FAILURE(rc)) 2639 break; 2640 2641 /* Update block and reverse block tables. */ 2642 pImage->paBlocks[uBlockLast] = pImage->paBlocks[uBlock]; 2643 rc = vdiUpdateBlockInfo(pImage, uBlockLast); 2644 if (RT_FAILURE(rc)) 2645 break; 2646 } 2647 2648 pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO; 2649 2650 /* Update the block pointers. */ 2651 setImageBlocksAllocated(&pImage->Header, idxLastBlock); 2652 rc = vdiUpdateBlockInfo(pImage, uBlock); 2653 if (RT_FAILURE(rc)) 2654 break; 2655 2656 /* Set new file size. */ 2657 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbImage); 2658 if (RT_FAILURE(rc)) 2659 break; 2660 2661 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, cbImage - pImage->cbTotalBlockData); 2662 } 2663 else /* if (fDiscard & VD_DISCARD_MARK_UNUSED) */ 2664 { 2665 /* Write changed data to the image. */ 2666 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset + offDiscard, 2667 pbBlockData + offDiscard, cbDiscard, NULL); 2668 } 2669 #if 0 2670 else 2671 { 2672 /* Block has data, create allocation bitmap. */ 2673 *pcbPreAllocated = cbPreAllocated; 2674 *pcbPostAllocated = cbPostAllocated; 2675 *ppbmAllocationBitmap = vdAllocationBitmapCreate(pvBlock, getImageBlockSize(&pImage->Header)); 2676 if (RT_UNLIKELY(!*ppbmAllocationBitmap)) 2677 rc = VERR_NO_MEMORY; 2678 else 2679 rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET; 2680 } 2681 #endif 2682 } 2683 /* else: nothing to do. */ 2684 } while (0); 2685 2686 if (pcbActuallyDiscarded) 2687 *pcbActuallyDiscarded = cbDiscard; 2688 2689 if (pvBlock) 2690 RTMemFree(pvBlock); 2691 2692 LogFlowFunc(("returns %Rrc\n", rc)); 2693 return rc; 2694 } 2695 2483 2696 VBOXHDDBACKEND g_VDIBackend = 2484 2697 { … … 2489 2702 /* uBackendCaps */ 2490 2703 VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC 2491 | VD_CAP_DIFF | VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS ,2704 | VD_CAP_DIFF | VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS | VD_CAP_DISCARD, 2492 2705 /* paFileExtensions */ 2493 2706 s_aVdiFileExtensions, … … 2577 2790 vdiCompact, 2578 2791 /* pfnResize */ 2579 vdiResize 2792 vdiResize, 2793 /* pfnDiscard */ 2794 vdiDiscard 2580 2795 }; -
trunk/src/VBox/Storage/VDICore.h
r38469 r38621 5 5 6 6 /* 7 * Copyright (C) 2006-201 0Oracle Corporation7 * Copyright (C) 2006-2011 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 526 526 /** Pointer to a block array. */ 527 527 PVDIIMAGEBLOCKPOINTER paBlocks; 528 /** Pointer to the block array for back resolving (used if discarding is enabled). */ 529 unsigned *paBlocksRev; 528 530 /** fFlags copy from image header, for speed optimization. */ 529 531 unsigned uImageFlags; -
trunk/src/VBox/Storage/VHD.cpp
r38521 r38621 3139 3139 vhdCompact, 3140 3140 /* pfnResize */ 3141 vhdResize 3141 vhdResize, 3142 /* pfnDiscard */ 3143 NULL 3142 3144 }; -
trunk/src/VBox/Storage/VMDK.cpp
r38469 r38621 7178 7178 NULL, 7179 7179 /* pfnResize */ 7180 NULL, 7181 /* pfnDiscard */ 7180 7182 NULL 7181 7183 }; -
trunk/src/VBox/Storage/testcase/tstVDIo.cpp
r38469 r38621 296 296 static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); 297 297 static DECLCALLBACK(int) vdScriptHandlerCompact(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); 298 static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); 298 299 static DECLCALLBACK(int) vdScriptHandlerCopy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); 299 300 static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); … … 333 334 {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0}, 334 335 {"shareable", 's', VDSCRIPTARGTYPE_BOOL, 0}, 335 {"readonly", 'r', VDSCRIPTARGTYPE_BOOL, 0} 336 {"readonly", 'r', VDSCRIPTARGTYPE_BOOL, 0}, 337 {"discard", 'i', VDSCRIPTARGTYPE_BOOL, 0}, 336 338 }; 337 339 … … 376 378 }; 377 379 380 /* Discard a part of a disk */ 381 const VDSCRIPTARGDESC g_aArgDiscard[] = 382 { 383 /* pcszName chId enmType fFlags */ 384 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, 385 {"off", 'o', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}, 386 {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}, 387 }; 388 378 389 /* Compact a disk */ 379 390 const VDSCRIPTARGDESC g_aArgCopy[] = … … 504 515 {"merge", g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge}, 505 516 {"compact", g_aArgCompact, RT_ELEMENTS(g_aArgCompact), vdScriptHandlerCompact}, 517 {"discard", g_aArgDiscard, RT_ELEMENTS(g_aArgDiscard), vdScriptHandlerDiscard}, 506 518 {"copy", g_aArgCopy, RT_ELEMENTS(g_aArgCopy), vdScriptHandlerCopy}, 507 519 {"iorngcreate", g_aArgIoRngCreate, RT_ELEMENTS(g_aArgIoRngCreate), vdScriptHandlerIoRngCreate}, … … 654 666 bool fReadonly = false; 655 667 bool fAsyncIo = true; 668 bool fDiscard = false; 656 669 657 670 for (unsigned i = 0; i < cScriptArgs; i++) … … 687 700 { 688 701 fAsyncIo = paScriptArgs[i].u.fFlag; 702 break; 703 } 704 case 'i': 705 { 706 fDiscard = paScriptArgs[i].u.fFlag; 689 707 break; 690 708 } … … 710 728 if (fReadonly) 711 729 fOpenFlags |= VD_OPEN_FLAGS_READONLY; 730 if (fDiscard) 731 fOpenFlags |= VD_OPEN_FLAGS_DISCARD; 712 732 713 733 rc = VDOpen(pDisk->pVD, pcszBackend, pcszImage, fOpenFlags, pGlob->pInterfacesImages); … … 1222 1242 */ 1223 1243 rc = VDCompact(pDisk->pVD, nImage, NULL); 1244 } 1245 } 1246 1247 return rc; 1248 } 1249 1250 static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) 1251 { 1252 int rc = VINF_SUCCESS; 1253 const char *pcszDisk = NULL; 1254 PVDDISK pDisk = NULL; 1255 uint64_t off; 1256 size_t cbDiscard; 1257 1258 for (unsigned i = 0; i < cScriptArgs; i++) 1259 { 1260 switch (paScriptArgs[i].chId) 1261 { 1262 case 'd': 1263 { 1264 pcszDisk = paScriptArgs[i].u.pcszString; 1265 break; 1266 } 1267 case 'o': 1268 { 1269 off = paScriptArgs[i].u.u64; 1270 break; 1271 } 1272 case 's': 1273 { 1274 cbDiscard = paScriptArgs[i].u.u64; 1275 break; 1276 } 1277 1278 default: 1279 AssertMsgFailed(("Invalid argument given!\n")); 1280 } 1281 1282 if (RT_FAILURE(rc)) 1283 break; 1284 } 1285 1286 if (RT_SUCCESS(rc)) 1287 { 1288 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk); 1289 if (!pDisk) 1290 rc = VERR_NOT_FOUND; 1291 else 1292 { 1293 VDRANGE Range; 1294 1295 Range.offStart = off; 1296 Range.cbRange = cbDiscard; 1297 1298 rc = VDDiscardRanges(pDisk->pVD, &Range, 1); 1299 if ( RT_SUCCESS(rc) 1300 && pDisk->pMemDiskVerify) 1301 { 1302 void *pv = RTMemAllocZ(cbDiscard); 1303 if (pv) 1304 { 1305 RTSGSEG SgSeg; 1306 RTSGBUF SgBuf; 1307 1308 SgSeg.pvSeg = pv; 1309 SgSeg.cbSeg = cbDiscard; 1310 RTSgBufInit(&SgBuf, &SgSeg, 1); 1311 rc = VDMemDiskWrite(pDisk->pMemDiskVerify, off, cbDiscard, &SgBuf); 1312 RTMemFree(pv); 1313 } 1314 else 1315 rc = VERR_NO_MEMORY; 1316 } 1224 1317 } 1225 1318 }
Note:
See TracChangeset
for help on using the changeset viewer.