diff --git a/Ext4Fsd/ext3/generic.c b/Ext4Fsd/ext3/generic.c index 5056c6d..13896c8 100644 --- a/Ext4Fsd/ext3/generic.c +++ b/Ext4Fsd/ext3/generic.c @@ -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; + } + } } __finally { ExReleaseResourceLite(&Vcb->bd.bd_bh_lock); diff --git a/Ext4Fsd/include/linux/module.h b/Ext4Fsd/include/linux/module.h index 1c3ff8b..a1d7f13 100644 --- a/Ext4Fsd/include/linux/module.h +++ b/Ext4Fsd/include/linux/module.h @@ -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 diff --git a/Ext4Fsd/memory.c b/Ext4Fsd/memory.c index f5debc2..46d2882 100644 --- a/Ext4Fsd/memory.c +++ b/Ext4Fsd/memory.c @@ -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); @@ -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 @@ -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 {