Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions Ext4Fsd/ext3/generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -273,17 +273,36 @@ Ext2DropBH(IN PEXT2_VCB Vcb)
SetFlag(Vcb->Flags, VCB_BEING_DROPPED);
Ext2DropGroupBH(Vcb);

while (!IsListEmpty(&Vcb->bd.bd_bh_free)) {
struct buffer_head *bh;
PLIST_ENTRY l;
l = RemoveHeadList(&Vcb->bd.bd_bh_free);
bh = CONTAINING_RECORD(l, struct buffer_head, b_link);
InitializeListHead(&bh->b_link);
if (0 == atomic_read(&bh->b_count)) {
buffer_head_remove(&Vcb->bd, bh);
free_buffer_head(bh);
}
}
while (!IsListEmpty(&Vcb->bd.bd_bh_free) ||
!IsListEmpty(&Vcb->bd.bd_bh_deferred)) {
struct buffer_head *bh;
PLIST_ENTRY l;
BOOLEAN busy = FALSE;

if (!IsListEmpty(&Vcb->bd.bd_bh_free)) {
l = RemoveHeadList(&Vcb->bd.bd_bh_free);
} else {
l = RemoveHeadList(&Vcb->bd.bd_bh_deferred);
}

bh = CONTAINING_RECORD(l, struct buffer_head, b_link);
InitializeListHead(&bh->b_link);

if (0 == atomic_read(&bh->b_count)) {
buffer_head_remove(&Vcb->bd, bh);
free_buffer_head(bh);
continue;
} else {
/* put it back to deferred queue for late release */
busy = TRUE;
}

InsertTailList(&Vcb->bd.bd_bh_deferred, &bh->b_link);

if (busy) {
break;
Comment on lines +299 to +303

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Detaching busy buffer heads while dropping VCB

When Ext2DropBH sees a buffer head with a non‑zero b_count, it now re‑queues the node onto bd_bh_deferred and breaks out of the loop. Ext2DestroyVcb frees bd_bh_lock, the buffer‑head slab cache, and eventually the VCB itself immediately after calling Ext2DropBH, so any busy buffer head left on bd_bh_deferred keeps list links and lock pointers that refer to freed memory. If the last reference to that buffer is released later (e.g. via __brelse), the code will acquire a deleted resource and run RemoveEntryList against the stale list head, risking a crash. The previous implementation detached busy entries by reinitializing the list head so delayed release was harmless. The drop routine should either keep busy entries detached or delay VCB teardown until the deferred list drains.

Useful? React with 👍 / 👎.

}
}

} __finally {
ExReleaseResourceLite(&Vcb->bd.bd_bh_lock);
Expand Down
13 changes: 7 additions & 6 deletions Ext4Fsd/include/linux/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -555,12 +555,13 @@ struct block_device {
PFILE_OBJECT bd_volume; /* streaming object file */
LARGE_MCB bd_extents; /* dirty extents */

kmem_cache_t * bd_bh_cache;/* memory cache for buffer_head */
ERESOURCE bd_bh_lock; /* lock for bh tree and reaper list */
struct rb_root bd_bh_root; /* buffer_head red-black tree root */
LIST_ENTRY bd_bh_free; /* reaper list */
KEVENT bd_bh_notify; /* notification event for cleanup */
};
kmem_cache_t * bd_bh_cache;/* memory cache for buffer_head */
ERESOURCE bd_bh_lock; /* lock for bh tree and reaper list */
struct rb_root bd_bh_root; /* buffer_head red-black tree root */
LIST_ENTRY bd_bh_free; /* reaper list */
LIST_ENTRY bd_bh_deferred; /* buffer heads deferred from reclaim */
KEVENT bd_bh_notify; /* notification event for cleanup */
};

//
// page information
Expand Down
219 changes: 152 additions & 67 deletions Ext4Fsd/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -2509,7 +2509,8 @@ Ext2InitializeVcb( IN PEXT2_IRP_CONTEXT IrpContext,
Vcb->bd.bd_volume = Vcb->Volume;
Vcb->bd.bd_priv = (void *) Vcb;
memset(&Vcb->bd.bd_bh_root, 0, sizeof(struct rb_root));
InitializeListHead(&Vcb->bd.bd_bh_free);
InitializeListHead(&Vcb->bd.bd_bh_free);
InitializeListHead(&Vcb->bd.bd_bh_deferred);
ExInitializeResourceLite(&Vcb->bd.bd_bh_lock);
KeInitializeEvent(&Vcb->bd.bd_bh_notify,
NotificationEvent, TRUE);
Expand Down Expand Up @@ -3142,51 +3143,112 @@ Ext2McbReaperThread(
/* get buffer heads from global Vcb BH list */

BOOLEAN
Ext2QueryUnusedBH(PEXT2_VCB Vcb, PLIST_ENTRY head)
{
struct buffer_head *bh = NULL;
PLIST_ENTRY next = NULL;
LARGE_INTEGER start, now;
BOOLEAN wake = FALSE;

KeQuerySystemTime(&start);

ExAcquireResourceExclusiveLite(&Vcb->bd.bd_bh_lock, TRUE);

while (!IsListEmpty(&Vcb->bd.bd_bh_free)) {

KeQuerySystemTime(&now);
if (now.QuadPart > start.QuadPart + (LONGLONG)10*1000*1000) {
break;
}
Ext2QueryUnusedBH(PEXT2_VCB Vcb, PLIST_ENTRY head, PBOOLEAN BusyDeferred)
{
struct buffer_head *bh = NULL;
PLIST_ENTRY next = NULL;
LARGE_INTEGER start, now;
BOOLEAN wake = FALSE;
BOOLEAN busy = FALSE;

KeQuerySystemTime(&start);

ExAcquireResourceExclusiveLite(&Vcb->bd.bd_bh_lock, TRUE);

while (!IsListEmpty(&Vcb->bd.bd_bh_deferred)) {

next = RemoveHeadList(&Vcb->bd.bd_bh_deferred);
bh = CONTAINING_RECORD(next, struct buffer_head, b_link);

{
LONG refcount = atomic_read(&bh->b_count);

if (refcount) {
ULONG deferred = 1; /* include current entry */
PLIST_ENTRY cursor;

for (cursor = Vcb->bd.bd_bh_deferred.Flink;
cursor != &Vcb->bd.bd_bh_deferred;
cursor = cursor->Flink) {
deferred++;
}

DEBUG(DL_ERR|DL_BH|DL_WRN, (
"bhReaper: deferred list busy bh=%p blk=%I64u count=%ld outstanding=%lu thread=%p\n",
bh,
(unsigned __int64)bh->b_blocknr,
refcount,
deferred,
PsGetCurrentThread()));
InsertTailList(&Vcb->bd.bd_bh_deferred, &bh->b_link);
busy = TRUE;
break;
}

InsertTailList(&Vcb->bd.bd_bh_free, &bh->b_link);
}
}

while (!IsListEmpty(&Vcb->bd.bd_bh_free)) {

KeQuerySystemTime(&now);
if (now.QuadPart > start.QuadPart + (LONGLONG)10*1000*1000) {
break;
}

next = RemoveHeadList(&Vcb->bd.bd_bh_free);
bh = CONTAINING_RECORD(next, struct buffer_head, b_link);
if (atomic_read(&bh->b_count)) {
InitializeListHead(&bh->b_link);
/* to be inserted by brelse */
continue;
}

if ( IsFlagOn(Vcb->Flags, VCB_BEING_DROPPED) ||
(bh->b_ts_drop.QuadPart + (LONGLONG)10*1000*1000*15) > now.QuadPart ||
(bh->b_ts_creat.QuadPart + (LONGLONG)10*1000*1000*180) > now.QuadPart) {
InsertTailList(head, &bh->b_link);
buffer_head_remove(&Vcb->bd, bh);
} else {
InsertHeadList(&Vcb->bd.bd_bh_free, &bh->b_link);
break;
}
}

wake = IsListEmpty(&Vcb->bd.bd_bh_free);
ExReleaseResourceLite(&Vcb->bd.bd_bh_lock);

if (wake)
KeSetEvent(&Vcb->bd.bd_bh_notify, 0, FALSE);

return IsFlagOn(Vcb->Flags, VCB_BEING_DROPPED);
}
{
LONG refcount = atomic_read(&bh->b_count);

if (refcount) {
ULONG deferred = 1; /* include the bh being deferred */
PLIST_ENTRY cursor;

for (cursor = Vcb->bd.bd_bh_deferred.Flink;
cursor != &Vcb->bd.bd_bh_deferred;
cursor = cursor->Flink) {
deferred++;
}

DEBUG(DL_ERR|DL_BH|DL_WRN, (
"bhReaper: deferred busy bh=%p blk=%I64u count=%ld flags=%lx outstanding=%lu thread=%p\n",
bh,
(unsigned __int64)bh->b_blocknr,
refcount,
bh->b_state,
deferred,
PsGetCurrentThread()));
InsertTailList(&Vcb->bd.bd_bh_deferred, &bh->b_link);
busy = TRUE;
continue;
}
}

if ( IsFlagOn(Vcb->Flags, VCB_BEING_DROPPED) ||
(bh->b_ts_drop.QuadPart + (LONGLONG)10*1000*1000*15) > now.QuadPart ||
(bh->b_ts_creat.QuadPart + (LONGLONG)10*1000*1000*180) > now.QuadPart) {
InsertTailList(head, &bh->b_link);
buffer_head_remove(&Vcb->bd, bh);
} else {
InsertHeadList(&Vcb->bd.bd_bh_free, &bh->b_link);
break;
}
}

wake = IsListEmpty(&Vcb->bd.bd_bh_free) &&
IsListEmpty(&Vcb->bd.bd_bh_deferred);
ExReleaseResourceLite(&Vcb->bd.bd_bh_lock);

if (wake)
KeSetEvent(&Vcb->bd.bd_bh_notify, 0, FALSE);

if (BusyDeferred) {
*BusyDeferred = busy;
}

return IsFlagOn(Vcb->Flags, VCB_BEING_DROPPED);
}

/* Reaper thread to release unused buffer heads */
VOID
Expand Down Expand Up @@ -3235,34 +3297,57 @@ Ext2bhReaperThread(

InitializeListHead(&List);

/* acquire global exclusive lock */
ExAcquireResourceSharedLite(&Ext2Global->Resource, TRUE);
GlobalAcquired = TRUE;
/* search all Vcb to get unused resources freed to system */
for (Link = Ext2Global->VcbList.Flink;
Link != &(Ext2Global->VcbList);
Link = Link->Flink ) {

Vcb = CONTAINING_RECORD(Link, EXT2_VCB, Next);
NonWait = Ext2QueryUnusedBH(Vcb, &List);
}
DidNothing = IsListEmpty(&List);
if (DidNothing) {
KeClearEvent(&Reaper->Wait);
}
if (GlobalAcquired) {
/* acquire global exclusive lock */
ExAcquireResourceSharedLite(&Ext2Global->Resource, TRUE);
GlobalAcquired = TRUE;
/* search all Vcb to get unused resources freed to system */
{
BOOLEAN NextNonWait = FALSE;

for (Link = Ext2Global->VcbList.Flink;
Link != &(Ext2Global->VcbList);
Link = Link->Flink ) {

BOOLEAN BusyDeferred = FALSE;

Vcb = CONTAINING_RECORD(Link, EXT2_VCB, Next);
if (Ext2QueryUnusedBH(Vcb, &List, &BusyDeferred)) {
NextNonWait = TRUE;
}

if (BusyDeferred) {
DEBUG(DL_ERR|DL_BH|DL_WRN, (
"bhReaper: Vcb=%p deferred busy bh detected thread=%p\n",
Vcb,
PsGetCurrentThread()));
NextNonWait = TRUE;
}
}

NonWait = NextNonWait;
}
DidNothing = IsListEmpty(&List);
if (DidNothing) {
KeClearEvent(&Reaper->Wait);
}
if (GlobalAcquired) {
ExReleaseResourceLite(&Ext2Global->Resource);
GlobalAcquired = FALSE;
}

while (!IsListEmpty(&List)) {
struct buffer_head *bh;
Link = RemoveHeadList(&List);
bh = CONTAINING_RECORD(Link, struct buffer_head, b_link);
ASSERT(0 == atomic_read(&bh->b_count));
free_buffer_head(bh);
}
}
while (!IsListEmpty(&List)) {
struct buffer_head *bh;
Link = RemoveHeadList(&List);
bh = CONTAINING_RECORD(Link, struct buffer_head, b_link);
ASSERT(0 == atomic_read(&bh->b_count));
DEBUG(DL_BH, (
"bhReaper: reclaiming bh=%p blk=%I64u thread=%p\n",
bh,
(unsigned __int64)bh->b_blocknr,
PsGetCurrentThread()));
free_buffer_head(bh);
}
}

} __finally {

Expand Down