]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfscommon/Misc/VolumeAllocation.c
xnu-1504.9.17.tar.gz
[apple/xnu.git] / bsd / hfs / hfscommon / Misc / VolumeAllocation.c
index 9ea66862ccf64514eaa412903ebdfb9fe6c4ab59..3d8255a9e18f95b44fa2f329e75f4a2917388760 100644 (file)
@@ -46,6 +46,7 @@ Public routines:
        BlockDeallocate
                                        Deallocate a contiguous run of allocation blocks.
 
+       invalidate_free_extent_cache    Invalidate free extent cache for a given volume.
 
 Internal routines:
        BlockMarkFree
@@ -154,6 +155,8 @@ static OSErr BlockAllocateKnown(
        u_int32_t               *actualStartBlock,
        u_int32_t               *actualNumBlocks);
 
+static int free_extent_cache_active(
+       ExtendedVCB             *vcb);
 
 /*
 ;________________________________________________________________________________
@@ -235,10 +238,7 @@ OSErr BlockAllocate (
        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 */
@@ -246,6 +246,20 @@ OSErr BlockAllocate (
        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
@@ -254,25 +268,38 @@ OSErr BlockAllocate (
        *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;
+               }
        }
 
        //
@@ -384,11 +411,16 @@ Exit:
                                // 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);
 
@@ -425,7 +457,8 @@ __private_extern__
 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;
@@ -449,7 +482,15 @@ OSErr BlockDeallocate (
        //      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;
@@ -459,6 +500,10 @@ OSErr BlockDeallocate (
                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) {
@@ -507,6 +552,7 @@ OSErr BlockDeallocate (
                }
        }
 
+skip_cache:
        MarkVCBDirty(vcb);
        HFS_MOUNT_UNLOCK(vcb, TRUE); 
 
@@ -1040,8 +1086,14 @@ static OSErr BlockAllocateKnown(
        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;
@@ -1774,6 +1826,13 @@ FoundUsed:
                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;
@@ -1838,7 +1897,7 @@ FoundUsed:
                                updated_free_extents = 1;
                        }
                }
-
+skip_cache:
                sanity_check_free_ext(vcb, 0);
 
        } while (currentBlock < stopBlock);
@@ -2018,4 +2077,43 @@ Exit:
        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;
+}