diff --git a/drivers/filesystems/udfs/struct.h b/drivers/filesystems/udfs/struct.h index 74c032224266d..dad711e556bdb 100644 --- a/drivers/filesystems/udfs/struct.h +++ b/drivers/filesystems/udfs/struct.h @@ -684,6 +684,8 @@ struct VCB { PCHAR FSBM_OldBitmap; // 0 - free, 1 - used ULONG BitmapModified; + ULONG BitmapSearchHint; // LBA hint for next free space search (roving pointer) + PCHAR BSBM_Bitmap; // 0 - normal, 1 - bad-block // pointers to Volume Descriptor Sequences diff --git a/drivers/filesystems/udfs/udf_info/alloc.cpp b/drivers/filesystems/udfs/udf_info/alloc.cpp index 530d6b168767e..4ea2cb3f37988 100644 --- a/drivers/filesystems/udfs/udf_info/alloc.cpp +++ b/drivers/filesystems/udfs/udf_info/alloc.cpp @@ -725,6 +725,7 @@ UDFAllocFreeExtent_( EXTENT_AD Ext; PEXTENT_MAP Map = NULL; uint32 len, LBS, BSh, blen; + uint32 EffectiveSearchStart; LBS = Vcb->SectorSize; BSh = Vcb->SectorShift; @@ -740,11 +741,31 @@ UDFAllocFreeExtent_( if (blen > (SearchLim - SearchStart)) { goto no_free_space_err; } + + // Use the bitmap search hint for allocation locality: start near the last + // allocation to avoid scanning already-used space at the start of the partition. + // The hint is only valid when it falls within [SearchStart, SearchLim); any + // out-of-range value is silently ignored and the full range is scanned instead. + EffectiveSearchStart = SearchStart; + if (Vcb->BitmapSearchHint >= SearchStart && Vcb->BitmapSearchHint < SearchLim) { + EffectiveSearchStart = Vcb->BitmapSearchHint; + AdPrint((" BitmapSearchHint applied: hint @%x, effective start @%x\n", + Vcb->BitmapSearchHint, EffectiveSearchStart)); + } + // walk through the free space bitmap & find a single extent or a set of // frags giving in sum the Length specified while(blen) { - Ext.extLocation = UDFFindMinSuitableExtent(Vcb, blen, SearchStart, - SearchLim, &len, AllocFlags); + Ext.extLocation = UDFFindMinSuitableExtent(Vcb, blen, EffectiveSearchStart, + SearchLim, &len, AllocFlags); + if (!len && EffectiveSearchStart > SearchStart) { + // Nothing found from the hint onward; fall back to a full-range search + AdPrint((" BitmapSearchHint: no free space from @%x, falling back to full scan from @%x\n", + EffectiveSearchStart, SearchStart)); + EffectiveSearchStart = SearchStart; + Ext.extLocation = UDFFindMinSuitableExtent(Vcb, blen, SearchStart, + SearchLim, &len, AllocFlags); + } if (len >= blen) { // complete search @@ -777,12 +798,16 @@ UDFAllocFreeExtent_( AdPrint(("newly allocated extent contains BB\n")); UDFMarkSpaceAsXXXNoProtect(Vcb, 0, ExtInfo->Mapping, AS_DISCARDED); // free UDFMarkBadSpaceAsUsed(Vcb, Ext.extLocation, Ext.extLength >> BSh); // bad -> bad+used - // roll back + // Advance past the bad blocks and roll back the length + EffectiveSearchStart = Ext.extLocation + (Ext.extLength >> BSh); blen += Ext.extLength>>BSh; continue; } } + // Advance the search position past the just-allocated extent + EffectiveSearchStart = Ext.extLocation + (Ext.extLength >> BSh); + Ext.extLength |= EXTENT_NOT_RECORDED_ALLOCATED << 30; if (!(ExtInfo->Mapping)) { // create new @@ -818,6 +843,10 @@ UDFAllocFreeExtent_( return STATUS_INSUFFICIENT_RESOURCES; } } + // Persist the search position for future allocations (success path only; + // failure paths all return before reaching here via no_free_space_err). + AdPrint((" BitmapSearchHint updated: @%x -> @%x\n", Vcb->BitmapSearchHint, EffectiveSearchStart)); + Vcb->BitmapSearchHint = EffectiveSearchStart; UDFReleaseResource(&(Vcb->BitMapResource1)); ExtInfo->Length = Length; return STATUS_SUCCESS; diff --git a/drivers/filesystems/udfs/udf_info/extent.cpp b/drivers/filesystems/udfs/udf_info/extent.cpp index f76d82b4577c0..de2f914eb6328 100644 --- a/drivers/filesystems/udfs/udf_info/extent.cpp +++ b/drivers/filesystems/udfs/udf_info/extent.cpp @@ -1492,7 +1492,9 @@ UDFFlushAllCachedAllocations( /* This routine allocates space for FE of the file being created. - Allocates a single block from the partition bitmap. + The BitmapSearchHint roving pointer inside UDFAllocFreeExtent_ automatically + starts the scan near the last successful allocation, providing locality + without a separate window-probe mechanism. */ NTSTATUS UDFAllocateFESpace( @@ -1505,8 +1507,12 @@ UDFAllocateFESpace( ) { UNREFERENCED_PARAMETER(DirInfo); + uint32 p_start = UDFPartStart(Vcb, PartNum); + uint32 p_end = UDFPartEnd(Vcb, PartNum); + AdPrint(("UDFAllocateFESpace: Part %x, [%x..%x], Len %x\n", + PartNum, p_start, p_end, Len)); return UDFAllocFreeExtent(IrpContext, Vcb, Len, - UDFPartStart(Vcb, PartNum), UDFPartEnd(Vcb, PartNum), FEExtInfo, EXTENT_FLAG_VERIFY); + p_start, p_end, FEExtInfo, EXTENT_FLAG_VERIFY); } // end UDFAllocateFESpace() /* @@ -2228,6 +2234,9 @@ UDFResizeExtent( (ExtInfo->Flags & EXTENT_FLAG_ALLOC_MASK) == EXTENT_FLAG_ALLOC_SEQUENTIAL) { AdPrint(("Resize tune for SEQUENTIAL i/o\n")); } + // BitmapSearchHint inside UDFAllocFreeExtent_ provides locality + // automatically by starting the scan near the last successful + // allocation, so a full-partition call is all that is needed. status = UDFAllocFreeExtent(IrpContext, Vcb, Length - l, UDFPartStart(Vcb, PartNum), UDFPartEnd(Vcb, PartNum), diff --git a/drivers/filesystems/udfs/udf_info/udf_info.cpp b/drivers/filesystems/udfs/udf_info/udf_info.cpp index e9556aa93de31..cf6380d8b469b 100644 --- a/drivers/filesystems/udfs/udf_info/udf_info.cpp +++ b/drivers/filesystems/udfs/udf_info/udf_info.cpp @@ -43,6 +43,13 @@ static const char valid_char_arr[] = {"*/:?\"<>|\\"}; #define DOS_CRC_MODULUS 41 #define hexChar crcChar + +// Minimum number of sectors to pre-allocate for a new directory data extent. +// Amortises per-sector cold-write spikes across many file additions. +// The effective pre-allocation is max(2*t, UDF_DIR_PREALLOC_MIN_SECTORS * +// SectorSize) rounded up to a WriteBlockSize boundary. +#define UDF_DIR_PREALLOC_MIN_SECTORS 8 + static const char crcChar[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ#_~-@"; /* Used to convert hex digits to ASCII for readability. */ @@ -1623,8 +1630,16 @@ UDFWriteFile__( WasInIcb ? "In-Icb" : "", Vcb->LowFreeSpace ? "LowSpace" : "")); if (UDFIsADirectory(FileInfo) && !WasInIcb && !Vcb->LowFreeSpace) { + // Pre-allocate directory data extents in larger chunks to amortise + // per-sector cold-write spikes across many file additions. + // The pre-allocation size is at least UDF_DIR_PREALLOC_MIN_SECTORS + // sectors (safety net for small WriteBlockSize configurations) and + // is always rounded up to a full WriteBlockSize boundary. + uint64 prealloc = max((uint64)t * 2, + (uint64)Vcb->SectorSize * UDF_DIR_PREALLOC_MIN_SECTORS); + prealloc = (prealloc + Vcb->WriteBlockSize - 1) & ~(ULONGLONG)(Vcb->WriteBlockSize - 1); FileInfo->Dloc->DataLoc.Flags |= EXTENT_FLAG_ALLOC_SEQUENTIAL; - status = UDFResizeExtent(IrpContext, Vcb, PartNum, (t*2+Vcb->WriteBlockSize-1) & ~(ULONGLONG)(Vcb->WriteBlockSize-1), FALSE, &(Dloc->DataLoc)); + status = UDFResizeExtent(IrpContext, Vcb, PartNum, prealloc, FALSE, &(Dloc->DataLoc)); if (NT_SUCCESS(status)) { AdPrint((" preallocated space for Dir\n")); FileInfo->Dloc->DataLoc.Flags |= EXTENT_FLAG_PREALLOCATED; @@ -3000,8 +3015,15 @@ UDFCreateFile__( FileInfo->Dloc->DataLoc.Mapping[0].extLength &= UDF_EXTENT_LENGTH_MASK; FileInfo->Dloc->DataLoc.Modified = TRUE; FileInfo->Dloc->FELoc.Mapping[0].extLength &= UDF_EXTENT_LENGTH_MASK; - // zero sector for FileEntry + // Mark the FE sector as newly allocated so that UDFFlushFE writes the + // full sector (FE content + zero padding) in a single call instead of + // the previous two-step sequence (explicit zero-write here followed by + // a read-modify-write inside UDFWriteInSector). CDR media still needs + // the explicit zero-write because UDFFlushFE skips the padded path in + // that mode. if (!Vcb->CDR_Mode) { + FileInfo->Dloc->FE_Flags |= UDF_FE_FLAG_NEW_SECTOR; + } else { status = UDFWriteData(IrpContext, Vcb, TRUE, ((int64)(FileInfo->Dloc->FELoc.Mapping[0].extLocation)) << Vcb->SectorShift, LBS, FALSE, Vcb->ZBuffer, &WrittenBytes); if (!NT_SUCCESS(status)) { UDFFlushFI(IrpContext, Vcb, FileInfo, PartNum); @@ -3637,7 +3659,15 @@ UDFRecordDirectory__( if (!NT_SUCCESS(status)) return status; if (CurDirNdx) CurDirNdx->FileCharacteristics = DirInfo->FileIdent->fileCharacteristics; - return UDFIndexDirectory(IrpContext, Vcb, DirInfo); + status = UDFIndexDirectory(IrpContext, Vcb, DirInfo); + if (!NT_SUCCESS(status)) return status; + // The FE was already written by UDFCreateFile__'s UDFFlushFE as a regular + // empty file. UDFWriteFile__ above modified DataLoc (new allocation, + // non-zero informationLength) and the in-memory FE now has + // fileType = UDF_FILE_TYPE_DIRECTORY. Flush the FE now so that the + // on-disk copy is consistent; without this a crash before close would + // leave a corrupt regular-empty-file FE on disk. + return UDFFlushFE(IrpContext, Vcb, DirInfo, PartNum); } // end UDFRecordDirectory__() /* @@ -4180,10 +4210,46 @@ UDFFlushFE( UDFPhysLbaToPart(Vcb, PartNum, lba), 0); } } - status = UDFWriteExtent( - IrpContext, - Vcb, &FileInfo->Dloc->FELoc, 0, (uint32)(FileInfo->Dloc->FELoc.Length), FALSE, - (int8 *)(FileInfo->Dloc->FileEntry), &WrittenBytes); + // Write the FE to disk. When the FE sector was freshly allocated in + // this file-creation call (UDF_FE_FLAG_NEW_SECTOR is set), write the + // full sector as a single zero-padded buffer. This replaces the + // previous three-step sequence (zero-write the sector in + // UDFCreateFile__, then read-before-write inside UDFWriteInSector when + // the FE is smaller than a full sector) with a single write I/O. + { + BOOLEAN newSector = !!(FileInfo->Dloc->FE_Flags & UDF_FE_FLAG_NEW_SECTOR); + uint32 feLen = (uint32)(FileInfo->Dloc->FELoc.Length); + uint32 LBS_fe = Vcb->SectorSize; + FileInfo->Dloc->FE_Flags &= ~UDF_FE_FLAG_NEW_SECTOR; + + if (newSector && feLen < LBS_fe) { + // Allocate a sector-sized staging buffer, copy the FE into the + // beginning, and zero-fill the rest so the trailing bytes are + // clean without a separate read-modify-write. + int8* padBuf = (int8*)MyAllocatePool__(NonPagedPool, LBS_fe); + if (padBuf) { + RtlCopyMemory(padBuf, FileInfo->Dloc->FileEntry, feLen); + RtlZeroMemory(padBuf + feLen, LBS_fe - feLen); + SIZE_T _wb = 0; + status = UDFWriteData(IrpContext, Vcb, TRUE, + ((int64)lba) << Vcb->SectorShift, LBS_fe, FALSE, padBuf, &_wb); + WrittenBytes = _wb; + MyFreePool__(padBuf); + } else { + // Pool exhausted; fall through to the regular path which + // may do a read-modify-write but is always correct. + status = UDFWriteExtent( + IrpContext, + Vcb, &FileInfo->Dloc->FELoc, 0, feLen, FALSE, + (int8 *)(FileInfo->Dloc->FileEntry), &WrittenBytes); + } + } else { + status = UDFWriteExtent( + IrpContext, + Vcb, &FileInfo->Dloc->FELoc, 0, feLen, FALSE, + (int8 *)(FileInfo->Dloc->FileEntry), &WrittenBytes); + } + } if (!NT_SUCCESS(status)) { UDFPrint((" FlushFE: UDFWriteExtent(2) failed (%x)\n", status)); if (status == STATUS_DEVICE_DATA_ERROR) { diff --git a/drivers/filesystems/udfs/udf_info/udf_rel.h b/drivers/filesystems/udfs/udf_info/udf_rel.h index cefcb3b8c2d0c..5ee2ef1c3e34f 100644 --- a/drivers/filesystems/udfs/udf_info/udf_rel.h +++ b/drivers/filesystems/udfs/udf_info/udf_rel.h @@ -325,6 +325,10 @@ typedef struct _UDF_DATALOC_INFO { #define UDF_FE_FLAG_IS_DEL_SDIR (0x20) /// Dloc is being initialized, don't touch it now #define UDF_FE_FLAG_UNDER_INIT (0x40) +/// FE sector is freshly allocated; UDFFlushFE must write it zero-padded +/// to the full sector size to avoid a separate zero-init write followed by +/// a read-modify-write. Cleared by UDFFlushFE after the padded write. +#define UDF_FE_FLAG_NEW_SECTOR (0x80) #define UDF_FILE_INFO_MT PagedPool