mirror of
https://github.com/tianocore/edk2
synced 2026-08-27 00:23:19 -04:00
FatPkg: Clean any volume caches during exit boot services
The current implementation assumes the the caller will perform the necessary cleanup before exiting boot services. This has been observed to drop some cached file writes that occur even before the application calling exit boot services is launched. This commit adds a per-volume pre-ExitBootServices event to flush any dirty caches and perform other cleanup the volume to ensure all write data is persisted and consistent. After the flush, caching will be disabled for the volume in the future to ensure that all subsequent access persists. Signed-off-by: Chris Fernald <chfernal@microsoft.com>
This commit is contained in:
parent
c3ba0416ce
commit
12c5ded287
5 changed files with 204 additions and 29 deletions
|
|
@ -13,6 +13,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
|
|||
#include <Guid/FileInfo.h>
|
||||
#include <Guid/FileSystemInfo.h>
|
||||
#include <Guid/FileSystemVolumeLabelInfo.h>
|
||||
#include <Guid/EventGroup.h>
|
||||
#include <Protocol/BlockIo.h>
|
||||
#include <Protocol/DiskIo.h>
|
||||
#include <Protocol/DiskIo2.h>
|
||||
|
|
@ -388,6 +389,16 @@ struct _FAT_VOLUME {
|
|||
//
|
||||
VOID *CacheBuffer;
|
||||
DISK_CACHE DiskCache[CacheMaxType];
|
||||
|
||||
//
|
||||
// Event signaled before ExitBootServices that flushes any dirty caches.
|
||||
//
|
||||
EFI_EVENT FlushEvent;
|
||||
|
||||
//
|
||||
// A flag that disables caching on this volume.
|
||||
//
|
||||
BOOLEAN CachingDisabled;
|
||||
};
|
||||
|
||||
//
|
||||
|
|
@ -847,6 +858,24 @@ FatIFileClose (
|
|||
FAT_IFILE *IFile
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
Write back any dirty FAT metadata and disk-cache pages for the volume to
|
||||
the underlying media.
|
||||
|
||||
@param Volume - The volume whose dirty cache should be flushed.
|
||||
@param Task - Point to task instance, may be NULL.
|
||||
|
||||
@retval EFI_SUCCESS - Any dirty caches were flushed.
|
||||
@return Others - An I/O error occurred while writing back.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
FatFlushDirtyCache (
|
||||
IN FAT_VOLUME *Volume,
|
||||
IN FAT_TASK *Task
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
Set error status for a specific OFile, reference checking the volume.
|
||||
|
|
@ -2029,3 +2058,17 @@ extern EFI_COMPONENT_NAME2_PROTOCOL gFatComponentName2;
|
|||
extern EFI_LOCK FatFsLock;
|
||||
extern EFI_LOCK FatTaskLock;
|
||||
extern EFI_FILE_PROTOCOL FatFileInterface;
|
||||
|
||||
/**
|
||||
Notification callback for the pre-ExitBootServices event to flush any dirty caches.
|
||||
|
||||
@param Event - The event that was signaled.
|
||||
@param Context - The context of the event, which is the FAT_VOLUME for which to flush caches.
|
||||
|
||||
**/
|
||||
VOID
|
||||
EFIAPI
|
||||
FatOnBeforeExitBootServices (
|
||||
IN EFI_EVENT Event,
|
||||
IN VOID *Context
|
||||
);
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@
|
|||
gEfiFileInfoGuid ## SOMETIMES_CONSUMES ## UNDEFINED
|
||||
gEfiFileSystemInfoGuid ## SOMETIMES_CONSUMES ## UNDEFINED
|
||||
gEfiFileSystemVolumeLabelInfoIdGuid ## SOMETIMES_CONSUMES ## UNDEFINED
|
||||
gEfiEventBeforeExitBootServicesGuid ## CONSUMES ## Event
|
||||
|
||||
[Protocols]
|
||||
gEfiDiskIoProtocolGuid ## TO_START
|
||||
|
|
|
|||
|
|
@ -364,6 +364,57 @@ FatCheckVolumeRef (
|
|||
|
||||
/**
|
||||
|
||||
Write back any dirty FAT metadata and disk-cache pages for the volume to
|
||||
the underlying media.
|
||||
|
||||
@param Volume - The volume whose dirty cache should be flushed.
|
||||
@param Task - Point to task instance, may be NULL.
|
||||
|
||||
@retval EFI_SUCCESS - Any dirty caches were flushed.
|
||||
@return Others - An I/O error occurred while writing back.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
FatFlushDirtyCache (
|
||||
IN FAT_VOLUME *Volume,
|
||||
IN FAT_TASK *Task
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
|
||||
if (!Volume->Valid) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Update the free hint info. Volume->FreeInfoPos != 0
|
||||
// indicates this a FAT32 volume
|
||||
//
|
||||
if (Volume->FreeInfoValid && Volume->FatDirty && Volume->FreeInfoPos) {
|
||||
Status = FatDiskIo (Volume, WriteDisk, Volume->FreeInfoPos, sizeof (FAT_INFO_SECTOR), &Volume->FatInfoSector, Task);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Update that the volume is not dirty
|
||||
//
|
||||
if (Volume->FatDirty && (Volume->FatType != Fat12)) {
|
||||
Volume->FatDirty = FALSE;
|
||||
Status = FatAccessVolumeDirty (Volume, WriteFat, &Volume->NotDirtyValue);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Flush all dirty cache entries to disk
|
||||
//
|
||||
return FatVolumeFlushCache (Volume, Task);
|
||||
}
|
||||
|
||||
/**
|
||||
Set error status for a specific OFile, reference checking the volume.
|
||||
If volume is already marked as invalid, and all resources are freed
|
||||
after reference checking, the file system protocol is uninstalled and
|
||||
|
|
@ -401,36 +452,14 @@ FatCleanupVolume (
|
|||
// volume be cleaned up even the volume is invalid.
|
||||
//
|
||||
FatCheckVolumeRef (Volume);
|
||||
if (Volume->Valid) {
|
||||
//
|
||||
// Update the free hint info. Volume->FreeInfoPos != 0
|
||||
// indicates this a FAT32 volume
|
||||
//
|
||||
if (Volume->FreeInfoValid && Volume->FatDirty && Volume->FreeInfoPos) {
|
||||
Status = FatDiskIo (Volume, WriteDisk, Volume->FreeInfoPos, sizeof (FAT_INFO_SECTOR), &Volume->FatInfoSector, Task);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Update that the volume is not dirty
|
||||
//
|
||||
if (Volume->FatDirty && (Volume->FatType != Fat12)) {
|
||||
Volume->FatDirty = FALSE;
|
||||
Status = FatAccessVolumeDirty (Volume, WriteFat, &Volume->NotDirtyValue);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Flush all dirty cache entries to disk
|
||||
//
|
||||
Status = FatVolumeFlushCache (Volume, Task);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
//
|
||||
// Write back any dirty FAT metadata and disk-cache pages. No-op when the
|
||||
// volume is no longer Valid.
|
||||
//
|
||||
Status = FatFlushDirtyCache (Volume, Task);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -122,6 +122,30 @@ FatAllocateVolume (
|
|||
DEBUG ((DEBUG_INIT, "Installed Fat filesystem on %p\n", Handle));
|
||||
Volume->Valid = TRUE;
|
||||
|
||||
//
|
||||
// Create a pre-ExitBootServices event for this volume to flush any dirty caches so
|
||||
// they are not lost. before ExitBootServices is required because the underlying
|
||||
// Block-io and device protocols may not be available later than this.
|
||||
//
|
||||
Status = gBS->CreateEventEx (
|
||||
EVT_NOTIFY_SIGNAL,
|
||||
TPL_CALLBACK,
|
||||
FatOnBeforeExitBootServices,
|
||||
Volume,
|
||||
&gEfiEventBeforeExitBootServicesGuid,
|
||||
&Volume->FlushEvent
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((
|
||||
DEBUG_WARN,
|
||||
"%a: CreateEventEx for pre-ExitBootServices failed (%r), dirty caches will not be flushed on exit!\n",
|
||||
__func__,
|
||||
Status
|
||||
));
|
||||
Volume->FlushEvent = NULL;
|
||||
Status = EFI_SUCCESS;
|
||||
}
|
||||
|
||||
Done:
|
||||
if (EFI_ERROR (Status)) {
|
||||
FatFreeVolume (Volume);
|
||||
|
|
|
|||
|
|
@ -331,6 +331,14 @@ FatDiskIo (
|
|||
//
|
||||
Status = EFI_VOLUME_CORRUPTED;
|
||||
if (Offset + BufferSize <= Volume->VolumeSize) {
|
||||
if (Volume->CachingDisabled) {
|
||||
//
|
||||
// Caching has been turned off for this volume. Convert
|
||||
// IO mode to raw disk access equivalent.
|
||||
//
|
||||
IoMode = (IO_MODE)RAW_ACCESS (IoMode);
|
||||
}
|
||||
|
||||
if (CACHE_ENABLED (IoMode)) {
|
||||
//
|
||||
// Access cache
|
||||
|
|
@ -449,6 +457,68 @@ FatFreeDirEnt (
|
|||
FreePool (DirEnt);
|
||||
}
|
||||
|
||||
/**
|
||||
Pre-ExitBootServices notification, signaled once per FAT volume. Context
|
||||
is the FAT_VOLUME this event was created for. This routine will flush any
|
||||
dirty caches for the volume.
|
||||
|
||||
@param Event - The event that was signaled.
|
||||
@param Context - The context of the event, which is the FAT_VOLUME for which to flush caches.
|
||||
|
||||
**/
|
||||
VOID
|
||||
EFIAPI
|
||||
|
||||
FatOnBeforeExitBootServices (
|
||||
IN EFI_EVENT Event,
|
||||
IN VOID *Context
|
||||
)
|
||||
{
|
||||
FAT_VOLUME *Volume;
|
||||
EFI_STATUS Status;
|
||||
|
||||
Volume = (FAT_VOLUME *)Context;
|
||||
if ((Volume == NULL) || (Volume->Signature != FAT_VOLUME_SIGNATURE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Volume->Valid || Volume->ReadOnly || Volume->DiskError) {
|
||||
return;
|
||||
}
|
||||
|
||||
Status = FatAcquireLockOrFail ();
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_WARN, "%a: FAT lock busy, skipping flush of %p\n", __func__, Volume->Handle));
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Flush any dirty caches. This will still leave all handles valid in case
|
||||
// other callback intend on using the file system protocol to flush high
|
||||
// level data in pre-ExitBootServices. Those callers will just have to
|
||||
// explicitly flush/close the handles.
|
||||
//
|
||||
Status = FatFlushDirtyCache (Volume, NULL);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((
|
||||
DEBUG_ERROR,
|
||||
"%a: FatFlushDirtyCache on %p returned %r\n",
|
||||
__func__,
|
||||
Volume->Handle,
|
||||
Status
|
||||
));
|
||||
}
|
||||
|
||||
//
|
||||
// Disable caching from this point forward to ensure that any further writes
|
||||
// don't get dropped.
|
||||
//
|
||||
|
||||
Volume->CachingDisabled = TRUE;
|
||||
|
||||
FatReleaseLock ();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Free volume structure (including the contents of directory cache and disk cache).
|
||||
|
|
@ -461,6 +531,14 @@ FatFreeVolume (
|
|||
IN FAT_VOLUME *Volume
|
||||
)
|
||||
{
|
||||
//
|
||||
// Close the per-volume pre-ExitBootServices event.
|
||||
//
|
||||
if (Volume->FlushEvent != NULL) {
|
||||
gBS->CloseEvent (Volume->FlushEvent);
|
||||
Volume->FlushEvent = NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// Free disk cache
|
||||
//
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue