Last hacks before move to TraceFS analysis SharedCacheMap based Cc.
authorshort <>
Wed, 6 Aug 2003 11:34:17 +0000 (11:34 +0000)
committershort <>
Wed, 6 Aug 2003 11:34:17 +0000 (11:34 +0000)
src/libcaptive/cc/map.c

index df0dacc..7365564 100644 (file)
@@ -36,6 +36,7 @@
 #include <glib/gmain.h>
 #include "captive/leave.h"
 #include <glib/gtree.h>
+#include "reactos/ddk/obfuncs.h"       /* for ObReferenceObject() */
 
 
 /* CONFIG: */
 /* #define CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR 1 */
 
 
+/* Does each privbcb put one reference count on its FileObject?
+ * Causes ntfs.sys of NT-5.1sp1:
+ *     Assertion 'Header->ValidDataLength.QuadPart == Li0.QuadPart' failed at d:\xpsp1\base\fs\ntfs\write.c line 1749
+ */
+/* #define CAPTIVE_PRIVBCB_REFERENCES_FILEOBJECT 1 */
+
+
 /* Flush all the buffers directly on the fly.
  * For details see the comment in libcaptive/client/init.c/captive_shutdown().
  */
@@ -106,6 +114,8 @@ static void fileobject_cached_hash_init(void)
  * Provides support of readahead/writebehind in W32. Function should be called
  * by W32 filesystem to offer its functions to W32 kernel. These functions
  * are never called by libcaptive, this call is a NOP in libcaptive.
+ *
+ * VERIFIED: Double CcInitializeCacheMap() without CcUninitializeCacheMap().
  */
 VOID CcInitializeCacheMap(IN PFILE_OBJECT FileObject,
                IN PCC_FILE_SIZES FileSizes,IN BOOLEAN PinAccess,IN PCACHE_MANAGER_CALLBACKS CallBacks,IN PVOID LazyWriterContext)
@@ -160,6 +170,12 @@ BOOLEAN errboolean;
 
 
 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject);
+struct private_bcb;
+static void CcUninitializeCacheMap_private_bcb_hash_foreach(
+               PUBLIC_BCB *PublicBcb,  /* key */
+               struct private_bcb *privbcb,    /* value */
+               FILE_OBJECT *FileObject);       /* user_data */
+extern GHashTable *private_bcb_hash;
 
 /**
  * CcUninitializeCacheMap:
@@ -195,7 +211,15 @@ BOOLEAN r;
         * should be silently discarded if CcUninitializeCacheMap() but currently
         * the assertion is left here as some debugging aid.
         */
+#if 0
        r=captive_cc_FileObject_delete(FileObject);
+#else
+       r=g_hash_table_remove(fileobject_cached_hash,FileObject);
+       g_hash_table_foreach(
+                       private_bcb_hash,       /* hash_table */
+                       (GHFunc)CcUninitializeCacheMap_private_bcb_hash_foreach,        /* func */
+                       FileObject);    /* user_data */
+#endif
 
        /* FIXME: should we do KePulseEvent? Are we allowed to signal from inside CcUninitializeCacheMap() ? */
        if (UninitializeCompleteEvent)
@@ -317,6 +341,7 @@ struct private_bcb {
        gpointer base;  /* It is the pointer corresponding to MappedFileOffset; it is not PAGE_SIZE aligned. */
        gboolean dirty;
        LARGE_INTEGER lsn; gboolean lsn_valid;
+       gboolean forbid_CcUnpinData_leave_func_finalize_remove;
        };
 
 
@@ -386,6 +411,26 @@ static void private_bcb_lsn_tree_init(void)
                        (GCompareFunc)private_bcb_lsn_tree_key_compare_func);   /* key_compare_func */
 }
 
+
+static gboolean captive_privbcb_flush_ordered(struct private_bcb *privbcb_req);
+
+static void CcUninitializeCacheMap_private_bcb_hash_foreach(
+               PUBLIC_BCB *PublicBcb,  /* key */
+               struct private_bcb *privbcb,    /* value */
+               FILE_OBJECT *FileObject)        /* user_data */
+{
+       g_return_if_fail(validate_Bcb(PublicBcb));
+       g_return_if_fail(privbcb!=NULL);
+       g_return_if_fail(PublicBcb==privbcb->PublicBcb);
+       g_return_if_fail(FileObject!=NULL);
+
+       if (privbcb->FileObject!=FileObject)
+               return;
+
+       captive_privbcb_flush_ordered(privbcb);
+}
+
+
 enum privbcb_item {
        PRIVBCB_ITEM_NOP,       /* for sanity checks */
        PRIVBCB_ITEM_DIRTY,
@@ -557,6 +602,10 @@ gpointer mapped_other,errptr;
                        g_assert(validate_page_position(pagepos));
                }
 
+#ifdef CAPTIVE_PRIVBCB_REFERENCES_FILEOBJECT
+       ObDereferenceObject(value->FileObject);
+#endif
+
        g_free(value);
 }
 
@@ -572,6 +621,9 @@ static void private_bcb_hash_init(void)
 }
 
 
+static void CcUnpinData_leave_unregister(struct private_bcb *privbcb);
+static gboolean captive_privbcb_flush_ordered(struct private_bcb *privbcb_req);
+
 static gboolean captive_cc_FileObject_delete_private_bcb_hash_foreach(
                PUBLIC_BCB *PublicBcb,  /* key */
                struct private_bcb *privbcb,  /* value */
@@ -582,20 +634,27 @@ static gboolean captive_cc_FileObject_delete_private_bcb_hash_foreach(
        g_return_val_if_fail(PublicBcb==privbcb->PublicBcb,FALSE);      /* meaning: do not remove this 'privbcb' */
        g_return_val_if_fail(FileObject!=NULL,FALSE);   /* meaning: do not remove this 'privbcb' */
 
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: PublicBcb=%p,privbcb=%p,privbcb->ref_count=%d,privbcb->leave_func_pending=%d"
-                       ",FileObject=%p",G_STRLOC,
-                       PublicBcb,privbcb,privbcb->ref_count,privbcb->leave_func_pending,FileObject);
+       if (FileObject && privbcb->FileObject!=FileObject)
+               return FALSE;   /* do not remove this 'privbcb' */
 
-       if (FileObject) {
-               if (privbcb->FileObject!=FileObject)
-                       return FALSE;   /* do not remove this 'privbcb' */
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,PublicBcb=%p,privbcb=%p,privbcb->ref_count=%d"
+                                       ",privbcb->leave_func_pending=%d,privbcb->dirty=%d,privbcb->lsn=%" G_GINT64_FORMAT,G_STRLOC,
+                       FileObject,PublicBcb,privbcb,privbcb->ref_count,
+                                       privbcb->leave_func_pending,privbcb->dirty,(gint64)(!privbcb->lsn_valid ? -1 : privbcb->lsn.QuadPart));
 
-               /* It is OK to leave it in 'private_bcb_hash' as it is protected
-                * against reincarnation.
-                * Such check is here to allow it to keep 'dirty' data.
-                */
-               if (privbcb->leave_func_pending)
-                       return FALSE;   /* do not remove this 'privbcb' */
+       /* It is not OK to leave it in 'private_bcb_hash' as although it is protected
+        * against privbcb reincarnation its pagepos items could be reincarnated.
+        */
+       if (privbcb->leave_func_pending) {
+#if 0
+               g_assert_not_reached();
+               captive_privbcb_flush_ordered(privbcb);
+               return FALSE;   /* do not remove this 'privbcb' */
+               g_assert_not_reached();
+               g_assert(privbcb->dirty==FALSE);
+#endif
+               CcUnpinData_leave_unregister(privbcb);
+               g_assert(privbcb->leave_func_pending==FALSE);
                }
 
        /* Do not reuse captive_privbcb_flush_ordered() destructor here as we cannot
@@ -675,8 +734,6 @@ gboolean errbool;
        g_assert(errbool==TRUE);
 }
 
-static gboolean captive_privbcb_flush_ordered(struct private_bcb *privbcb_req);
-
 void captive_cc_flush(void)
 {
 gboolean flushed;
@@ -934,12 +991,17 @@ gboolean after_eof=FALSE; /* Did we reached the end of file already? */
        privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
        privbcb->dirty=FALSE;
        privbcb->lsn_valid=FALSE;
+       privbcb->forbid_CcUnpinData_leave_func_finalize_remove=FALSE;
        g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_hash_table_insert: PublicBcb=%p,privbcb=%p",G_STRLOC,
                        PublicBcb,privbcb);
        g_hash_table_insert(private_bcb_hash,
                        PublicBcb,      /* key */
                        privbcb);       /* value */
 
+#ifdef CAPTIVE_PRIVBCB_REFERENCES_FILEOBJECT
+       ObReferenceObject(FileObject);
+#endif
+
        privbcb_set(privbcb,PRIVBCB_ITEM_NOP,0);        /* just for the assertions */
 
        /* We MUST NOT call captive_Cc_IoPageRead() inside our pagepos filling loop
@@ -1401,9 +1463,14 @@ REACTOS_COMMON_FCB_HEADER *CommonFcb=(REACTOS_COMMON_FCB_HEADER *)privbcb->FileO
         * do not need to successfuly write the whole aligned amount.
         * FIXME: Also we can get just value 0 if the write is considered 'not dirty'
         * during FAT write by fastfat.sys.
+        * We can also get just value 8 during write of PAGE_SIZE aligned block
+        * of privbcb->MappedLength 512 during flush of LSNed buffer on captive_leave(),
+        * probably no assumptions can be made about the returned value at all.
         */
+#if 0
        g_assert(IoStatus.Information==0 || IoStatus.Information>=CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
                        +privbcb->MappedLength);
+#endif
        g_assert(IoStatus.Information<=length_sectoraligned);
 
        if (fileobject_cached)
@@ -1524,11 +1591,14 @@ gboolean errbool;
        g_assert(privbcb->ref_count==1);
        privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,FALSE);
        privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
-       errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
-       g_assert(errbool==TRUE);
+       if (!privbcb->forbid_CcUnpinData_leave_func_finalize_remove) {
+               errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
+               g_assert(errbool==TRUE);
+               }
 }
 
-static void CcUnpinData_leave_func(struct private_bcb *privbcb)        /* NULL to traverse the whole tree */
+
+static void CcUnpinData_leave_func_privbcb_destroy_func(struct private_bcb *privbcb)   /* NULL to traverse the whole tree */
 {
        g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->ref_count=%d,privbcb->leave_func_pending=%d"
                        ",privbcb->lsn_valid=%d",G_STRLOC,
@@ -1543,6 +1613,53 @@ static void CcUnpinData_leave_func(struct private_bcb *privbcb)  /* NULL to trave
        g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return",G_STRLOC);
 }
 
+
+/* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
+static GHashTable *private_bcb_unpin_leave_hash;
+
+static void private_bcb_unpin_leave_hash_init(void)
+{
+       if (private_bcb_unpin_leave_hash)
+               return;
+       private_bcb_unpin_leave_hash=g_hash_table_new_full(
+                       g_direct_hash,  /* hash_func */
+                       g_direct_equal, /* key_equal_func */
+                       NULL,   /* key_destroy_func */
+                       (GDestroyNotify)CcUnpinData_leave_func_privbcb_destroy_func);   /* value_destroy_func */
+}
+
+
+static void CcUnpinData_leave_unregister(struct private_bcb *privbcb)
+{
+gboolean errbool;
+
+       g_return_if_fail(privbcb!=NULL);
+
+       g_return_if_fail(private_bcb_unpin_leave_hash!=NULL);
+
+       g_assert(privbcb->forbid_CcUnpinData_leave_func_finalize_remove==FALSE);
+       privbcb->forbid_CcUnpinData_leave_func_finalize_remove=TRUE;
+       g_assert(privbcb==g_hash_table_lookup(private_bcb_unpin_leave_hash,privbcb->PublicBcb));
+       errbool=g_hash_table_remove(private_bcb_unpin_leave_hash,privbcb->PublicBcb);
+       g_assert(errbool==TRUE);
+       g_assert(NULL==g_hash_table_lookup(private_bcb_unpin_leave_hash,privbcb->PublicBcb));
+}
+
+
+static void CcUnpinData_leave_func(struct private_bcb *privbcb /* user_data; unused */)
+{
+GHashTable *private_bcb_unpin_leave_hash_local;
+
+       if (!private_bcb_unpin_leave_hash)
+               return;
+
+       private_bcb_unpin_leave_hash_local=private_bcb_unpin_leave_hash;
+       private_bcb_unpin_leave_hash=NULL;
+
+       g_hash_table_destroy(private_bcb_unpin_leave_hash_local);       /* utilize value_destroy_func */
+}
+
+
 /**
  * CcUnpinData:
  * @Bcb: Initialized #PUBLIC_BCB structure.
@@ -1597,9 +1714,14 @@ struct private_bcb *privbcb;
        if (!privbcb->leave_func_pending) {
                privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1);      /* '+1' gets decreased by 'leave_func_pending'. */
                privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,TRUE);
-               captive_leave_register(
-                               (captive_leave_func)CcUnpinData_leave_func,     /* func */
-                               privbcb);       /* data; privbcb */
+               if (!private_bcb_unpin_leave_hash) {
+                       private_bcb_unpin_leave_hash_init();
+                       g_assert(private_bcb_unpin_leave_hash!=NULL);
+                       captive_leave_register(
+                                       (captive_leave_func)CcUnpinData_leave_func,     /* func */
+                                       NULL);  /* data; unused */
+                       }
+               g_hash_table_insert(private_bcb_unpin_leave_hash,PublicBcb,privbcb);
                }
 
        if (captive_cc_unmounting)
@@ -2028,6 +2150,9 @@ VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
  *
  * Flushes out any pending dirty data in cache manager BCB mapping.
  * FIXME: libcaptive currently always flushes the full file ignoring any @FileOffset or @Length.
+ *
+ * VERIFIED: Ranged flushes.
+ * VERIFIED: Synchronous write.
  */
 VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
                IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
@@ -2165,6 +2290,12 @@ struct FileObject_logging *FileObject_logging;
                        privbcb,privbcb->FileObject,(guint64)privbcb->lsn.QuadPart);
 }
 
+/**
+ * CcSetLogHandleForFile:
+ *
+ * VERIFIED: LogHandle may be NULL, FlushToLsnRoutine is never called with LogHandle==NULL.
+ * VERIFIED: Only one !=NULL LogHandle and one FlushToLsnRoutine used in one session.
+ */
 VOID CcSetLogHandleForFile(IN PFILE_OBJECT FileObject,IN PVOID LogHandle,IN PFLUSH_TO_LSN FlushToLsnRoutine)
 {
 GList *LogHandle_list;
@@ -2403,6 +2534,8 @@ LARGE_INTEGER OldestLsn_check,NewestLsn_check;
  * Function returns value %0 if no appropriate LSN was found.
  * libcaptive must return #gint64 instead of the official #LARGE_INTEGER
  * as W32 expects it as value in EAX:EDX but GCC returns the structure address in EAX.
+ *
+ * VERIFIED: func called in runs for each FileObject, FileOffset sorted desc.
  */
 gint64 /* instead of LARGE_INTEGER */ CcGetDirtyPages(IN PVOID LogHandle,
                IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,IN PVOID Context1,IN PVOID Context2)