BlockDeallocate
Deallocate a contiguous run of allocation blocks.
+ invalidate_free_extent_cache Invalidate free extent cache for a given volume.
Internal routines:
BlockMarkFree
u_int32_t *actualStartBlock,
u_int32_t *actualNumBlocks);
+static int free_extent_cache_active(
+ ExtendedVCB *vcb);
/*
;________________________________________________________________________________
u_int32_t startingBlock, /* preferred starting block, or 0 for no preference */
u_int32_t minBlocks, /* desired number of blocks to allocate */
u_int32_t maxBlocks, /* maximum number of blocks to allocate */
- Boolean forceContiguous, /* non-zero to force contiguous allocation and to force */
- /* minBlocks bytes to actually be allocated */
-
- Boolean useMetaZone,
+ u_int32_t flags, /* option flags */
u_int32_t *actualStartBlock, /* actual first block of allocation */
u_int32_t *actualNumBlocks) /* number of blocks actually allocated; if forceContiguous */
/* was zero, then this may represent fewer than minBlocks */
u_int32_t freeBlocks;
OSErr err;
Boolean updateAllocPtr = false; // true if nextAllocation needs to be updated
+ Boolean useMetaZone;
+ Boolean forceContiguous;
+
+ if (flags & HFS_ALLOC_FORCECONTIG) {
+ forceContiguous = true;
+ } else {
+ forceContiguous = false;
+ }
+
+ if (flags & HFS_ALLOC_METAZONE) {
+ useMetaZone = true;
+ } else {
+ useMetaZone = false;
+ }
//
// Initialize outputs in case we get an error
*actualNumBlocks = 0;
freeBlocks = hfs_freeblks(VCBTOHFS(vcb), 0);
- //
- // If the disk is already full, don't bother.
- //
- if (freeBlocks == 0) {
- err = dskFulErr;
- goto Exit;
- }
- if (forceContiguous && freeBlocks < minBlocks) {
- err = dskFulErr;
- goto Exit;
- }
- /*
- * Clip if necessary so we don't over-subscribe the free blocks.
+ /* Skip free block check if blocks are being allocated for relocating
+ * data during truncating a volume.
+ *
+ * During hfs_truncatefs(), the volume free block count is updated
+ * before relocating data to reflect the total number of free blocks
+ * that will exist on the volume after resize is successful. This
+ * means that we have reserved allocation blocks required for relocating
+ * the data and hence there is no need to check the free blocks.
+ * It will also prevent resize failure when the number of blocks in
+ * an extent being relocated is more than the free blocks that will
+ * exist after the volume is resized.
*/
- if (minBlocks > freeBlocks) {
- minBlocks = freeBlocks;
- }
- if (maxBlocks > freeBlocks) {
- maxBlocks = freeBlocks;
+ if ((flags & HFS_ALLOC_SKIPFREEBLKS) == 0) {
+ // If the disk is already full, don't bother.
+ if (freeBlocks == 0) {
+ err = dskFulErr;
+ goto Exit;
+ }
+ if (forceContiguous && freeBlocks < minBlocks) {
+ err = dskFulErr;
+ goto Exit;
+ }
+
+ /*
+ * Clip if necessary so we don't over-subscribe the free blocks.
+ */
+ if (minBlocks > freeBlocks) {
+ minBlocks = freeBlocks;
+ }
+ if (maxBlocks > freeBlocks) {
+ maxBlocks = freeBlocks;
+ }
}
//
// than one entry in the array
}
}
-
- //
- // Update the number of free blocks on the volume
- //
- vcb->freeBlocks -= *actualNumBlocks;
+
+ /*
+ * Update the number of free blocks on the volume
+ *
+ * Skip updating the free blocks count if the block are
+ * being allocated to relocate data as part of hfs_truncatefs()
+ */
+ if ((flags & HFS_ALLOC_SKIPFREEBLKS) == 0) {
+ vcb->freeBlocks -= *actualNumBlocks;
+ }
MarkVCBDirty(vcb);
HFS_MOUNT_UNLOCK(vcb, TRUE);
OSErr BlockDeallocate (
ExtendedVCB *vcb, // Which volume to deallocate space on
u_int32_t firstBlock, // First block in range to deallocate
- u_int32_t numBlocks) // Number of contiguous blocks to deallocate
+ u_int32_t numBlocks, // Number of contiguous blocks to deallocate
+ u_int32_t flags)
{
OSErr err;
u_int32_t tempWord;
// Update the volume's free block count, and mark the VCB as dirty.
//
HFS_MOUNT_LOCK(vcb, TRUE);
- vcb->freeBlocks += numBlocks;
+
+ /*
+ * Do not update the free block count. This flags is specified
+ * when a volume is being truncated.
+ */
+ if ((flags & HFS_ALLOC_SKIPFREEBLKS) == 0) {
+ vcb->freeBlocks += numBlocks;
+ }
+
vcb->hfs_freed_block_count += numBlocks;
if (firstBlock < vcb->sparseAllocation) {
vcb->sparseAllocation = firstBlock;
HFS_UPDATE_NEXT_ALLOCATION(vcb, (vcb->nextAllocation - numBlocks));
}
+ if (free_extent_cache_active(vcb) == 0) {
+ goto skip_cache;
+ }
+
tempWord = vcb->vcbFreeExtCnt;
// Add this free chunk to the free extent list
if (vcb->hfs_flags & HFS_HAS_SPARSE_DEVICE) {
}
}
+skip_cache:
MarkVCBDirty(vcb);
HFS_MOUNT_UNLOCK(vcb, TRUE);
u_int32_t foundBlocks;
u_int32_t newStartBlock, newBlockCount;
- if (vcb->vcbFreeExtCnt == 0 || vcb->vcbFreeExt[0].blockCount == 0)
+ HFS_MOUNT_LOCK(vcb, TRUE);
+ if (free_extent_cache_active(vcb) == 0 ||
+ vcb->vcbFreeExtCnt == 0 ||
+ vcb->vcbFreeExt[0].blockCount == 0) {
+ HFS_MOUNT_UNLOCK(vcb, TRUE);
return dskFulErr;
+ }
+ HFS_MOUNT_UNLOCK(vcb, TRUE);
// Just grab up to maxBlocks of the first (largest) free exent.
*actualStartBlock = vcb->vcbFreeExt[0].startBlock;
if (foundBlocks >= minBlocks)
break; // Found what we needed!
+ HFS_MOUNT_LOCK(vcb, TRUE);
+ if (free_extent_cache_active(vcb) == 0) {
+ HFS_MOUNT_UNLOCK(vcb, TRUE);
+ goto skip_cache;
+ }
+ HFS_MOUNT_UNLOCK(vcb, TRUE);
+
// This free chunk wasn't big enough. Try inserting it into the free extent cache in case
// the allocation wasn't forced contiguous.
really_add = 0;
updated_free_extents = 1;
}
}
-
+skip_cache:
sanity_check_free_ext(vcb, 0);
} while (currentBlock < stopBlock);
return (inuse);
}
+/* Invalidate free extent cache for a given volume.
+ * This cache is invalidated and disabled when a volume is being resized
+ * (via hfs_trucatefs() or hfs_extendefs()).
+ *
+ * Returns: Nothing
+ */
+void invalidate_free_extent_cache(ExtendedVCB *vcb)
+{
+ u_int32_t i;
+
+ HFS_MOUNT_LOCK(vcb, TRUE);
+ for (i = 0; i < vcb->vcbFreeExtCnt; i++) {
+ vcb->vcbFreeExt[i].startBlock = 0;
+ vcb->vcbFreeExt[i].blockCount = 0;
+ }
+ vcb->vcbFreeExtCnt = 0;
+ HFS_MOUNT_UNLOCK(vcb, TRUE);
+
+ return;
+}
+/* Check whether free extent cache is active or not.
+ * This cache is invalidated and disabled when a volume is being resized
+ * (via hfs_trucatefs() or hfs_extendefs()).
+ *
+ * This function assumes that the caller is holding the lock on
+ * the mount point.
+ *
+ * Returns: 0 if the cache is not active,
+ * 1 if the cache is active.
+ */
+static int free_extent_cache_active(ExtendedVCB *vcb)
+{
+ int retval = 1;
+
+ if (vcb->hfs_flags & HFS_RESIZE_IN_PROGRESS) {
+ retval = 0;
+ }
+ return retval;
+}