883
883
* Fewer users in the saved state than in the current VM are allowed
884
884
* because that means that there are only new ones which don't have any saved state
885
885
* which can get lost.
886
* More saved entries that current ones are not allowed because this could result in
886
* More saved state entries than registered cache users are only allowed if the
887
* missing users don't have any data saved in the cache.
889
if (cRefs <= pBlkCacheGlobal->cRefs)
889
int rc = VINF_SUCCESS;
896
PPDMBLKCACHE pBlkCache = NULL;
899
SSMR3GetU32(pSSM, &cbId);
902
cbId++; /* Include terminator */
903
pszId = (char *)RTMemAllocZ(cbId * sizeof(char));
895
PPDMBLKCACHE pBlkCache = NULL;
898
SSMR3GetU32(pSSM, &cbId);
901
cbId++; /* Include terminator */
902
pszId = (char *)RTMemAllocZ(cbId * sizeof(char));
909
rc = SSMR3GetStrZ(pSSM, pszId, cbId);
912
/* Search for the block cache with the provided id. */
913
pBlkCache = pdmR3BlkCacheFindById(pBlkCacheGlobal, pszId);
915
/* Get the entries */
917
SSMR3GetU32(pSSM, &cEntries);
919
if (!pBlkCache && (cEntries > 0))
921
rc = SSMR3SetCfgError(pSSM, RT_SRC_POS,
922
N_("The VM is missing a block device and there is data in the cache. Please make sure the source and target VMs have compatible storage configurations"));
931
PPDMBLKCACHEENTRY pEntry;
935
SSMR3GetU64(pSSM, &off);
936
SSMR3GetU32(pSSM, &cbEntry);
938
pEntry = pdmBlkCacheEntryAlloc(pBlkCache, off, cbEntry, NULL);
906
941
rc = VERR_NO_MEMORY;
910
rc = SSMR3GetStrZ(pSSM, pszId, cbId);
913
/* Search for the block cache with the provided id. */
914
pBlkCache = pdmR3BlkCacheFindById(pBlkCacheGlobal, pszId);
945
rc = SSMR3GetMem(pSSM, pEntry->pbData, cbEntry);
917
rc = SSMR3SetCfgError(pSSM, RT_SRC_POS,
918
N_("The VM is missing a block device. Please make sure the source and target VMs have compatible storage configurations"));
948
RTMemFree(pEntry->pbData);
925
/* Get the entries */
927
SSMR3GetU32(pSSM, &cEntries);
931
PPDMBLKCACHEENTRY pEntry;
935
SSMR3GetU64(pSSM, &off);
936
SSMR3GetU32(pSSM, &cbEntry);
938
pEntry = pdmBlkCacheEntryAlloc(pBlkCache, off, cbEntry, NULL);
945
rc = SSMR3GetMem(pSSM, pEntry->pbData, cbEntry);
948
RTMemFree(pEntry->pbData);
953
/* Insert into the tree. */
954
bool fInserted = RTAvlrU64Insert(pBlkCache->pTree, &pEntry->Core);
957
/* Add to the dirty list. */
958
pdmBlkCacheAddDirtyEntry(pBlkCache, pEntry);
959
pdmBlkCacheEntryAddToList(&pBlkCacheGlobal->LruRecentlyUsedIn, pEntry);
960
pdmBlkCacheAdd(pBlkCacheGlobal, cbEntry);
961
pdmBlkCacheEntryRelease(pEntry);
953
/* Insert into the tree. */
954
bool fInserted = RTAvlrU64Insert(pBlkCache->pTree, &pEntry->Core);
955
Assert(fInserted); NOREF(fInserted);
957
/* Add to the dirty list. */
958
pdmBlkCacheAddDirtyEntry(pBlkCache, pEntry);
959
pdmBlkCacheEntryAddToList(&pBlkCacheGlobal->LruRecentlyUsedIn, pEntry);
960
pdmBlkCacheAdd(pBlkCacheGlobal, cbEntry);
961
pdmBlkCacheEntryRelease(pEntry);
971
if (cRefs && RT_SUCCESS(rc))
972
972
rc = SSMR3SetCfgError(pSSM, RT_SRC_POS,
973
N_("The VM is missing a block device. Please make sure the source and target VMs have compatible storage configurations"));
973
N_("Unexpected error while restoring state. Please make sure the source and target VMs have compatible storage configurations"));
975
975
pdmBlkCacheLockLeave(pBlkCacheGlobal);
1579
1582
RTSemRWReleaseRead(pBlkCache->SemRWEntries);
1581
STAM_PROFILE_ADV_STOP(&pCache->StatTreeGet, Cache);
1584
STAM_PROFILE_ADV_STOP(&pBlkCache->pCache->StatTreeGet, Cache);
1584
1587
static void pdmBlkCacheInsertEntry(PPDMBLKCACHE pBlkCache, PPDMBLKCACHEENTRY pEntry)
1586
PPDMBLKCACHEGLOBAL pCache = pBlkCache->pCache;
1588
STAM_PROFILE_ADV_START(&pCache->StatTreeInsert, Cache);
1589
STAM_PROFILE_ADV_START(&pBlkCache->pCache->StatTreeInsert, Cache);
1589
1590
RTSemRWRequestWrite(pBlkCache->SemRWEntries, RT_INDEFINITE_WAIT);
1590
1591
bool fInserted = RTAvlrU64Insert(pBlkCache->pTree, &pEntry->Core);
1591
AssertMsg(fInserted, ("Node was not inserted into tree\n"));
1592
STAM_PROFILE_ADV_STOP(&pCache->StatTreeInsert, Cache);
1592
AssertMsg(fInserted, ("Node was not inserted into tree\n")); NOREF(fInserted);
1593
STAM_PROFILE_ADV_STOP(&pBlkCache->pCache->StatTreeInsert, Cache);
1593
1594
RTSemRWReleaseWrite(pBlkCache->SemRWEntries);
1741
* Calculate aligned offset and size for a new cache entry
1742
* which do not intersect with an already existing entry and the
1743
* Calculate aligned offset and size for a new cache entry which do not
1744
* intersect with an already existing entry and the file end.
1745
1746
* @returns The number of bytes the entry can hold of the requested amount
1747
* @param pEndpoint The endpoint.
1748
* @param pBlkCache The endpoint cache.
1749
* @param off The start offset.
1750
* @param cb The number of bytes the entry needs to hold at least.
1751
* @param uAlignment Alignment of the boundary sizes.
1752
* @param poffAligned Where to store the aligned offset.
1753
* @param pcbAligned Where to store the aligned size of the entry.
1748
* @param pEndpoint The endpoint.
1749
* @param pBlkCache The endpoint cache.
1750
* @param off The start offset.
1751
* @param cb The number of bytes the entry needs to hold at
1753
* @param pcbEntry Where to store the number of bytes the entry can hold.
1754
* Can be less than given because of other entries.
1755
static size_t pdmBlkCacheEntryBoundariesCalc(PPDMBLKCACHE pBlkCache,
1756
uint64_t off, size_t cb,
1757
unsigned uAlignment,
1758
uint64_t *poffAligned, size_t *pcbAligned)
1756
static uint32_t pdmBlkCacheEntryBoundariesCalc(PPDMBLKCACHE pBlkCache,
1757
uint64_t off, uint32_t cb,
1761
size_t cbInEntry = 0;
1762
uint64_t offAligned;
1760
/* Get the best fit entries around the offset */
1763
1761
PPDMBLKCACHEENTRY pEntryAbove = NULL;
1765
/* Get the best fit entries around the offset */
1766
1762
pdmBlkCacheGetCacheBestFitEntryByOffset(pBlkCache, off, &pEntryAbove);
1768
1764
/* Log the info */
1773
1769
pEntryAbove ? pEntryAbove->Core.KeyLast : 0,
1774
1770
pEntryAbove ? pEntryAbove->cbData : 0));
1778
1774
if ( pEntryAbove
1779
1775
&& off + cb > pEntryAbove->Core.Key)
1781
cbInEntry = pEntryAbove->Core.Key - off;
1782
cbAligned = pEntryAbove->Core.Key - offAligned;
1777
cbInEntry = (uint32_t)(pEntryAbove->Core.Key - off);
1778
cbNext = (uint32_t)(pEntryAbove->Core.Key - off);
1787
1782
cbInEntry = cb;
1790
1786
/* A few sanity checks */
1791
AssertMsg(!pEntryAbove || (offAligned + cbAligned) <= pEntryAbove->Core.Key,
1787
AssertMsg(!pEntryAbove || off + cbNext <= pEntryAbove->Core.Key,
1792
1788
("Aligned size intersects with another cache entry\n"));
1793
Assert(cbInEntry <= cbAligned);
1789
Assert(cbInEntry <= cbNext);
1795
1791
if (pEntryAbove)
1796
1792
pdmBlkCacheEntryRelease(pEntryAbove);
1798
LogFlow(("offAligned=%llu cbAligned=%u\n", offAligned, cbAligned));
1794
LogFlow(("off=%llu cbNext=%u\n", off, cbNext));
1800
*poffAligned = offAligned;
1801
*pcbAligned = cbAligned;
1803
1798
return cbInEntry;
1812
1807
* @param pBlkCache The endpoint cache.
1813
1808
* @param off The offset.
1814
1809
* @param cb Number of bytes the cache entry should have.
1815
* @param uAlignment Alignment the size of the entry should have.
1816
1810
* @param pcbData Where to store the number of bytes the new
1817
1811
* entry can hold. May be lower than actually requested
1818
1812
* due to another entry intersecting the access range.
1820
1814
static PPDMBLKCACHEENTRY pdmBlkCacheEntryCreate(PPDMBLKCACHE pBlkCache,
1821
1815
uint64_t off, size_t cb,
1822
unsigned uAlignment,
1823
1816
size_t *pcbData)
1825
uint64_t offStart = 0;
1818
uint32_t cbEntry = 0;
1820
*pcbData = pdmBlkCacheEntryBoundariesCalc(pBlkCache, off, (uint32_t)cb, &cbEntry);
1821
AssertReturn(cb <= UINT32_MAX, NULL);
1823
PPDMBLKCACHEGLOBAL pCache = pBlkCache->pCache;
1824
pdmBlkCacheLockEnter(pCache);
1827
1826
PPDMBLKCACHEENTRY pEntryNew = NULL;
1828
PPDMBLKCACHEGLOBAL pCache = pBlkCache->pCache;
1829
uint8_t *pbBuffer = NULL;
1831
*pcbData = pdmBlkCacheEntryBoundariesCalc(pBlkCache, off, cb, uAlignment,
1832
&offStart, &cbEntry);
1834
pdmBlkCacheLockEnter(pCache);
1827
uint8_t *pbBuffer = NULL;
1835
1828
bool fEnough = pdmBlkCacheReclaim(pCache, cbEntry, true, &pbBuffer);
1839
1831
LogFlow(("Evicted enough bytes (%u requested). Creating new cache entry\n", cbEntry));
1841
pEntryNew = pdmBlkCacheEntryAlloc(pBlkCache, offStart, cbEntry, pbBuffer);
1833
pEntryNew = pdmBlkCacheEntryAlloc(pBlkCache, off, cbEntry, pbBuffer);
1842
1834
if (RT_LIKELY(pEntryNew))
1844
1836
pdmBlkCacheEntryAddToList(&pCache->LruRecentlyUsedIn, pEntryNew);
2407
2393
return VINF_AIO_TASK_PENDING;
2396
VMMR3DECL(int) PDMR3BlkCacheDiscard(PPDMBLKCACHE pBlkCache, PCRTRANGE paRanges,
2397
unsigned cRanges, void *pvUser)
2399
int rc = VINF_SUCCESS;
2400
PPDMBLKCACHEGLOBAL pCache = pBlkCache->pCache;
2401
PPDMBLKCACHEENTRY pEntry;
2402
PPDMBLKCACHEREQ pReq;
2404
LogFlowFunc((": pBlkCache=%#p{%s} paRanges=%#p cRanges=%u pvUser=%#p\n",
2405
pBlkCache, pBlkCache->pszId, paRanges, cRanges, pvUser));
2407
AssertPtrReturn(pBlkCache, VERR_INVALID_POINTER);
2408
AssertReturn(!pBlkCache->fSuspended, VERR_INVALID_STATE);
2410
/* Allocate new request structure. */
2411
pReq = pdmBlkCacheReqAlloc(pvUser);
2412
if (RT_UNLIKELY(!pReq))
2413
return VERR_NO_MEMORY;
2415
/* Increment data transfer counter to keep the request valid while we access it. */
2416
ASMAtomicIncU32(&pReq->cXfersPending);
2418
for (unsigned i = 0; i < cRanges; i++)
2420
uint64_t offCur = paRanges[i].offStart;
2421
size_t cbLeft = paRanges[i].cbRange;
2425
size_t cbThisDiscard = 0;
2427
pEntry = pdmBlkCacheGetCacheEntryByOffset(pBlkCache, offCur);
2431
/* Write the data into the entry and mark it as dirty */
2432
AssertPtr(pEntry->pList);
2434
uint64_t offDiff = offCur - pEntry->Core.Key;
2436
AssertMsg(offCur >= pEntry->Core.Key,
2437
("Overflow in calculation offCur=%llu OffsetAligned=%llu\n",
2438
offCur, pEntry->Core.Key));
2440
cbThisDiscard = RT_MIN(pEntry->cbData - offDiff, cbLeft);
2442
/* Ghost lists contain no data. */
2443
if ( (pEntry->pList == &pCache->LruRecentlyUsedIn)
2444
|| (pEntry->pList == &pCache->LruFrequentlyUsed))
2446
/* Check if the entry is dirty. */
2447
if (pdmBlkCacheEntryFlagIsSetClearAcquireLock(pBlkCache, pEntry,
2448
PDMBLKCACHE_ENTRY_IS_DIRTY,
2451
/* If it is dirty but not yet in progress remove it. */
2452
if (!(pEntry->fFlags & PDMBLKCACHE_ENTRY_IO_IN_PROGRESS))
2454
pdmBlkCacheLockEnter(pCache);
2455
pdmBlkCacheEntryRemoveFromList(pEntry);
2457
STAM_PROFILE_ADV_START(&pCache->StatTreeRemove, Cache);
2458
RTAvlrU64Remove(pBlkCache->pTree, pEntry->Core.Key);
2459
STAM_PROFILE_ADV_STOP(&pCache->StatTreeRemove, Cache);
2461
pdmBlkCacheLockLeave(pCache);
2468
/* The data isn't written to the file yet */
2469
pdmBlkCacheEntryWaitersAdd(pEntry, pReq,
2470
&SgBuf, offDiff, cbToWrite,
2472
STAM_COUNTER_INC(&pBlkCache->StatWriteDeferred);
2476
RTSemRWReleaseWrite(pBlkCache->SemRWEntries);
2477
pdmBlkCacheEntryRelease(pEntry);
2479
else /* Dirty bit not set */
2482
* Check if a read is in progress for this entry.
2483
* We have to defer processing in that case.
2485
if(pdmBlkCacheEntryFlagIsSetClearAcquireLock(pBlkCache, pEntry,
2486
PDMBLKCACHE_ENTRY_IO_IN_PROGRESS,
2490
pdmBlkCacheEntryWaitersAdd(pEntry, pReq,
2491
&SgBuf, offDiff, cbToWrite,
2494
STAM_COUNTER_INC(&pBlkCache->StatWriteDeferred);
2495
RTSemRWReleaseWrite(pBlkCache->SemRWEntries);
2496
pdmBlkCacheEntryRelease(pEntry);
2498
else /* I/O in progress flag not set */
2500
pdmBlkCacheLockEnter(pCache);
2501
pdmBlkCacheEntryRemoveFromList(pEntry);
2503
RTSemRWRequestWrite(pBlkCache->SemRWEntries, RT_INDEFINITE_WAIT);
2504
STAM_PROFILE_ADV_START(&pCache->StatTreeRemove, Cache);
2505
RTAvlrU64Remove(pBlkCache->pTree, pEntry->Core.Key);
2506
STAM_PROFILE_ADV_STOP(&pCache->StatTreeRemove, Cache);
2507
RTSemRWReleaseWrite(pBlkCache->SemRWEntries);
2509
pdmBlkCacheLockLeave(pCache);
2513
} /* Dirty bit not set */
2515
else /* Entry is on the ghost list just remove cache entry. */
2517
pdmBlkCacheLockEnter(pCache);
2518
pdmBlkCacheEntryRemoveFromList(pEntry);
2520
RTSemRWRequestWrite(pBlkCache->SemRWEntries, RT_INDEFINITE_WAIT);
2521
STAM_PROFILE_ADV_START(&pCache->StatTreeRemove, Cache);
2522
RTAvlrU64Remove(pBlkCache->pTree, pEntry->Core.Key);
2523
STAM_PROFILE_ADV_STOP(&pCache->StatTreeRemove, Cache);
2524
RTSemRWReleaseWrite(pBlkCache->SemRWEntries);
2526
pdmBlkCacheLockLeave(pCache);
2531
/* else: no entry found. */
2533
offCur += cbThisDiscard;
2534
cbLeft -= cbThisDiscard;
2538
if (!pdmBlkCacheReqUpdate(pBlkCache, pReq, rc, false))
2539
rc = VINF_AIO_TASK_PENDING;
2541
LogFlowFunc((": Leave rc=%Rrc\n", rc));
2411
2547
* Completes a task segment freeing all resources and completes the task handle
2412
2548
* if everything was transferred.