Skip to content
Draft
2 changes: 2 additions & 0 deletions drivers/filesystems/udfs/struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 32 additions & 3 deletions drivers/filesystems/udfs/udf_info/alloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
13 changes: 11 additions & 2 deletions drivers/filesystems/udfs/udf_info/extent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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()

/*
Expand Down Expand Up @@ -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),
Expand Down
80 changes: 73 additions & 7 deletions drivers/filesystems/udfs/udf_info/udf_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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__()

/*
Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions drivers/filesystems/udfs/udf_info/udf_rel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down