diff options
author | John Mainzer <mainzer@hdfgroup.org> | 2006-04-28 13:27:54 (GMT) |
---|---|---|
committer | John Mainzer <mainzer@hdfgroup.org> | 2006-04-28 13:27:54 (GMT) |
commit | 8a7b9b3221f192ed0e64b00c3a90d5e6a1fb9e19 (patch) | |
tree | 2a4a38cc7b359149409ba55d919031ce9d25e932 /src | |
parent | 6a77572c26b354541cafed74bf583e0de0ddfe9a (diff) | |
download | hdf5-8a7b9b3221f192ed0e64b00c3a90d5e6a1fb9e19.zip hdf5-8a7b9b3221f192ed0e64b00c3a90d5e6a1fb9e19.tar.gz hdf5-8a7b9b3221f192ed0e64b00c3a90d5e6a1fb9e19.tar.bz2 |
[svn-r12311] Purpose:
Add pinned entry capability to cache.
Description:
For frequently accessed cache entries, the protect/unprotect overhead
is sometimes a bottleneck.
Solution:
Allow entries to be pinned in the cache. Pinned entries can't be
evicted, but can be flushed or modified.
Platforms tested:
h5committested -- minus one small typo in test/cache.c whose fix was
tested on copper and heping only.
Misc. update:
Diffstat (limited to 'src')
-rw-r--r-- | src/H5AC.c | 520 | ||||
-rw-r--r-- | src/H5ACpkg.h | 315 | ||||
-rw-r--r-- | src/H5ACprivate.h | 28 | ||||
-rw-r--r-- | src/H5C.c | 1649 | ||||
-rw-r--r-- | src/H5Cpkg.h | 101 | ||||
-rw-r--r-- | src/H5Cprivate.h | 59 | ||||
-rw-r--r-- | src/H5Edefin.h | 3 | ||||
-rw-r--r-- | src/H5Einit.h | 15 | ||||
-rw-r--r-- | src/H5Epubgen.h | 6 | ||||
-rw-r--r-- | src/H5Eterm.h | 3 | ||||
-rw-r--r-- | src/H5err.txt | 3 |
11 files changed, 2060 insertions, 642 deletions
@@ -43,6 +43,7 @@ */ #define H5C_PACKAGE /*suppress error about including H5Cpkg */ +#define H5AC_PACKAGE /*suppress error about including H5ACpkg */ #define H5F_PACKAGE /*suppress error about including H5Fpkg */ /* Interface initialization */ @@ -53,7 +54,7 @@ #endif /* H5_HAVE_PARALLEL */ #include "H5private.h" /* Generic Functions */ -#include "H5ACprivate.h" /* Metadata cache */ +#include "H5ACpkg.h" /* Metadata cache */ #include "H5Cpkg.h" /* Cache */ #include "H5Dprivate.h" /* Dataset functions */ #include "H5Eprivate.h" /* Error handling */ @@ -64,268 +65,9 @@ #include "H5MMprivate.h" /* Memory management */ #include "H5Pprivate.h" /* Property lists */ -#define H5AC_DEBUG_DIRTY_BYTES_CREATION 0 - -/*------------------------------------------------------------------------- - * It is a bit difficult to set ranges of allowable values on the - * dirty_bytes_threshold field of H5AC_aux_t. The following are - * probably broader than they should be. - *------------------------------------------------------------------------- - */ - -#define H5AC__MIN_DIRTY_BYTES_THRESHOLD (int32_t) \ - (H5C__MIN_MAX_CACHE_SIZE / 2) -#define H5AC__DEFAULT_DIRTY_BYTES_THRESHOLD (256 * 1024) -#define H5AC__MAX_DIRTY_BYTES_THRESHOLD (int32_t) \ - (H5C__MAX_MAX_CACHE_SIZE / 4) - -/**************************************************************************** - * - * structure H5AC_aux_t - * - * While H5AC has become a wrapper for the cache implemented in H5C.c, there - * are some features of the metadata cache that are specific to it, and which - * therefore do not belong in the more generic H5C cache code. - * - * In particular, there is the matter of synchronizing writes from the - * metadata cache to disk in the PHDF5 case. - * - * Prior to this update, the presumption was that all metadata caches would - * write the same data at the same time since all operations modifying - * metadata must be performed collectively. Given this assumption, it was - * safe to allow only the writes from process 0 to actually make it to disk, - * while metadata writes from all other processes were discarded. - * - * Unfortunately, this presumption is in error as operations that read - * metadata need not be collective, but can change the location of dirty - * entries in the metadata cache LRU lists. This can result in the same - * metadata write operation triggering writes from the metadata caches on - * some processes, but not all (causing a hang), or in different sets of - * entries being written from different caches (potentially resulting in - * metadata corruption in the file). - * - * To deal with this issue, I decided to apply a paradigm shift to the way - * metadata is written to disk. - * - * With this set of changes, only the metadata cache on process 0 is able - * to write metadata to disk, although metadata caches on all other - * processes can read metadata from disk as before. - * - * To keep all the other caches from getting plugged up with dirty metadata, - * process 0 periodically broadcasts a list of entries that it has flushed - * since that last notice, and which are currently clean. The other caches - * mark these entries as clean as well, which allows them to evict the - * entries as needed. - * - * One obvious problem in this approach is synchronizing the broadcasts - * and receptions, as different caches may see different amounts of - * activity. - * - * The current solution is for the caches to track the number of bytes - * of newly generated dirty metadata, and to broadcast and receive - * whenever this value exceeds some user specified threshold. - * - * Maintaining this count is easy for all processes not on process 0 -- - * all that is necessary is to add the size of the entry to the total - * whenever there is an insertion, a rename of a previously clean entry, - * or whever a previously clean entry is marked dirty in an unprotect. - * - * On process 0, we have to be careful not to count dirty bytes twice. - * If an entry is marked dirty, flushed, and marked dirty again, all - * within a single reporting period, it only th first marking should - * be added to the dirty bytes generated tally, as that is all that - * the other processes will see. - * - * At present, this structure exists to maintain the fields needed to - * implement the above scheme, and thus is only used in the parallel - * case. However, other uses may arise in the future. - * - * Instance of this structure are associated with metadata caches via - * the aux_ptr field of H5C_t (see H5Cpkg.h). The H5AC code is - * responsible for allocating, maintaining, and discarding instances - * of H5AC_aux_t. - * - * The remainder of this header comments documents the individual fields - * of the structure. - * - * JRM - 6/27/05 - * - * magic: Unsigned 32 bit integer always set to - * H5AC__H5AC_AUX_T_MAGIC. This field is used to validate - * pointers to instances of H5AC_aux_t. - * - * mpi_comm: MPI communicator associated with the file for which the - * cache has been created. - * - * mpi_rank: MPI rank of this process within mpi_comm. - * - * mpi_size: Number of processes in mpi_comm. - * - * write_permitted: Boolean flag used to control whether the cache - * is permitted to write to file. - * - * dirty_bytes_threshold: Integer field containing the dirty bytes - * generation threashold. Whenever dirty byte creation - * exceeds this value, the metadata cache on process 0 - * broadcasts a list of the entries it has flushed since - * the last broadcast (or since the beginning of execution) - * and which are currently clean (if they are still in the - * cache) - * - * Similarly, metadata caches on processes other than process - * 0 will attempt to receive a list of clean entries whenever - * the threshold is exceeded. - * - * dirty_bytes: Integer field containing the number of bytes of dirty - * metadata generated since the beginning of the computation, - * or (more typically) since the last clean entries list - * broadcast. This field is reset to zero after each such - * broadcast. - * - * dirty_bytes_propagations: This field only exists when the - * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. - * - * It is used to track the number of times the cleaned list - * has been propagated from process 0 to the other - * processes. - * - * unprotect_dirty_bytes: This field only exists when the - * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. - * - * It is used to track the number of dirty bytes created - * via unprotect operations since the last time the cleaned - * list was propagated. - * - * unprotect_dirty_bytes_updates: This field only exists when the - * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. - * - * It is used to track the number of times dirty bytes have - * been created via unprotect operations since the last time - * the cleaned list was propagated. - * - * insert_dirty_bytes: This field only exists when the - * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. - * - * It is used to track the number of dirty bytes created - * via insert operations since the last time the cleaned - * list was propagated. - * - * insert_dirty_bytes_updates: This field only exists when the - * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. - * - * It is used to track the number of times dirty bytes have - * been created via insert operations since the last time - * the cleaned list was propagated. - * - * rename_dirty_bytes: This field only exists when the - * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. - * - * It is used to track the number of dirty bytes created - * via rename operations since the last time the cleaned - * list was propagated. - * - * rename_dirty_bytes_updates: This field only exists when the - * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. - * - * It is used to track the number of times dirty bytes have - * been created via rename operations since the last time - * the cleaned list was propagated. - * - * d_slist_ptr: Pointer to an instance of H5SL_t used to maintain a list - * of entries that have been dirtied since the last time they - * were listed in a clean entries broadcast. This list is - * only maintained by the metadata cache on process 0 -- it - * it used to maintain a view of the dirty entries as seen - * by the other caches, so as to keep the dirty bytes count - * in synchronization with them. - * - * Thus on process 0, the dirty_bytes count is incremented - * only if either - * - * 1) an entry is inserted in the metadata cache, or - * - * 2) a previously clean entry is renamed, and it does not - * already appear in the dirty entry list, or - * - * 3) a previously clean entry is unprotected with the - * dirtied flag set and the entry does not already appear - * in the dirty entry list. - * - * Entries are added to the dirty entry list whever they cause - * the dirty bytes count to be increased. They are removed - * when they appear in a clean entries broadcast. Note that - * renames must be reflected in the dirty entry list. - * - * To reitterate, this field is only used on process 0 -- it - * should be NULL on all other processes. - * - * d_slist_len: Integer field containing the number of entries in the - * dirty entry list. This field should always contain the - * value 0 on all processes other than process 0. It exists - * primarily for sanity checking. - * - * c_slist_ptr: Pointer to an instance of H5SL_t used to maintain a list - * of entries that were dirty, have been flushed - * to disk since the last clean entries broadcast, and are - * still clean. Since only process 0 can write to disk, this - * list only exists on process 0. - * - * In essence, this slist is used to assemble the contents of - * the next clean entries broadcast. The list emptied after - * each broadcast. - * - * c_slist_len: Integer field containing the number of entries in the clean - * entries list (*c_slist_ptr). This field should always - * contain the value 0 on all processes other than process 0. - * It exists primarily for sanity checking. - * - ****************************************************************************/ #ifdef H5_HAVE_PARALLEL -#define H5AC__H5AC_AUX_T_MAGIC (unsigned)0x00D0A01 - -typedef struct H5AC_aux_t -{ - uint32_t magic; - - MPI_Comm mpi_comm; - - int mpi_rank; - - int mpi_size; - - hbool_t write_permitted; - - int32_t dirty_bytes_threshold; - - int32_t dirty_bytes; - -#if H5AC_DEBUG_DIRTY_BYTES_CREATION - - int32_t dirty_bytes_propagations; - - int32_t unprotect_dirty_bytes; - int32_t unprotect_dirty_bytes_updates; - - int32_t insert_dirty_bytes; - int32_t insert_dirty_bytes_updates; - - int32_t rename_dirty_bytes; - int32_t rename_dirty_bytes_updates; - -#endif /* H5AC_DEBUG_DIRTY_BYTES_CREATION */ - - H5SL_t * d_slist_ptr; - - int32_t d_slist_len; - - H5SL_t * c_slist_ptr; - - int32_t c_slist_len; - -} H5AC_aux_t; /* struct H5AC_aux_t */ - /* Declare a free list to manage the H5AC_aux_t struct */ H5FL_DEFINE_STATIC(H5AC_aux_t); @@ -1221,6 +963,88 @@ done: /*------------------------------------------------------------------------- + * Function: H5AC_get_entry_status + * + * Purpose: Given a file address, determine whether the metadata + * cache contains an entry at that location. If it does, + * also determine whether the entry is dirty, protected, + * pinned, etc. and return that information to the caller + * in *status_ptr. + * + * If the specified entry doesn't exist, set *status_ptr + * to zero. + * + * On error, the value of *status_ptr is undefined. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 4/27/06 + * + * Modifications: + * + * None. + * + *------------------------------------------------------------------------- + */ + +herr_t +H5AC_get_entry_status(H5C_t * cache_ptr, + haddr_t addr, + unsigned * status_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + herr_t result; + hbool_t in_cache; + hbool_t is_dirty; + hbool_t is_protected; + hbool_t is_pinned; + size_t entry_size; + unsigned status = 0; + + FUNC_ENTER_NOAPI(H5AC_get_entry_status, FAIL) + + if ( ( cache_ptr == NULL ) || + ( cache_ptr->magic != H5C__H5C_T_MAGIC ) || + ( ! H5F_addr_defined(addr) ) || + ( status_ptr == NULL ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad param(s) on entry.") + } + + result = H5C_get_entry_status(cache_ptr, addr, &entry_size, &in_cache, + &is_dirty, &is_protected, &is_pinned); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "H5C_get_entry_status() failed.") + } + + if ( in_cache ) { + + status |= H5AC_ES__IN_CACHE; + + if ( is_dirty ) + status |= H5AC_ES__IS_DIRTY; + + if ( is_protected ) + status |= H5AC_ES__IS_PROTECTED; + + if ( is_pinned ) + status |= H5AC_ES__IS_PINNED; + } + + *status_ptr = status; + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5AC_get_entry_status() */ + + +/*------------------------------------------------------------------------- * Function: H5AC_set * * Purpose: Adds the specified thing to the cache. The thing need not @@ -1365,6 +1189,99 @@ done: /*------------------------------------------------------------------------- + * Function: H5AC_mark_pinned_entry_dirty + * + * Purpose: Mark a pinned entry as dirty. The target entry MUST be + * be pinned, and MUST be unprotected. + * + * If the entry has changed size, the function updates + * data structures for the size change. + * + * If the entry is not already dirty, the function places + * the entry on the skip list. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 4/11/06 + * + * Modifications: + * + * None + * + *------------------------------------------------------------------------- + */ +herr_t +H5AC_mark_pinned_entry_dirty(H5C_t * cache_ptr, + void * thing, + hbool_t size_changed, + size_t new_size) +{ + herr_t result; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5AC_mark_pinned_entry_dirty, FAIL) + +#ifdef H5_HAVE_PARALLEL + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( thing ); + + if ( ( ((H5AC_info_t *)thing)->is_dirty == FALSE ) && + ( NULL != cache_ptr->aux_ptr) ) { + + H5AC_info_t * entry_ptr; + + HDassert( ( size_changed == TRUE ) || ( size_changed == FALSE ) ); + + entry_ptr = (H5AC_info_t *)thing; + + if ( ! ( entry_ptr->is_pinned ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \ + "Entry isn't pinned??") + } + + if ( entry_ptr->is_protected ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \ + "Entry is protected??") + } + + result = H5AC_log_dirtied_entry(cache_ptr, + entry_ptr, + entry_ptr->addr, + size_changed, + new_size); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, \ + "H5AC_log_dirtied_entry() failed.") + } + } +#endif /* H5_HAVE_PARALLEL */ + + result = H5C_mark_pinned_entry_dirty(cache_ptr, + thing, + size_changed, + new_size); + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \ + "H5C_mark_pinned_entry_dirty() failed.") + + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5AC_mark_pinned_entry_dirty() */ + + +/*------------------------------------------------------------------------- * Function: H5AC_rename * * Purpose: Use this function to notify the cache that an object's @@ -1474,6 +1391,47 @@ done: /*------------------------------------------------------------------------- + * Function: H5AC_pin_protected_entry() + * + * Purpose: Pin a protected cache entry. The entry must be protected + * at the time of call, and must be unpinned. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 4/27/06 + * + * Modifications: + * + * None. + * + *------------------------------------------------------------------------- + */ +herr_t +H5AC_pin_protected_entry(H5C_t * cache_ptr, + void * thing) +{ + herr_t result; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5AC_pin_protected_entry, FAIL) + + result = H5C_pin_protected_entry(cache_ptr, thing); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, \ + "H5C_pin_protected_entry() failed.") + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5AC_pin_protected_entry() */ + + +/*------------------------------------------------------------------------- * Function: H5AC_protect * * Purpose: If the target entry is not in the cache, load it. If @@ -1579,6 +1537,46 @@ done: /*------------------------------------------------------------------------- + * Function: H5AC_unpin_entry() + * + * Purpose: Unpin a cache entry. The entry must be unprotected at + * the time of call, and must be pinned. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 4/11/06 + * + * Modifications: + * + * None. + * + *------------------------------------------------------------------------- + */ +herr_t +H5AC_unpin_entry(H5C_t * cache_ptr, + void * thing) +{ + herr_t result; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(H5AC_unpin_entry, FAIL) + + result = H5C_unpin_entry(cache_ptr, thing); + + if ( result < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "H5C_unpin_entry() failed.") + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5AC_unpin_entry() */ + + +/*------------------------------------------------------------------------- * Function: H5AC_unprotect * * Purpose: Undo an H5AC_protect() call -- specifically, mark the @@ -3212,7 +3210,7 @@ H5AC_log_renamed_entry(H5AC_t * cache_ptr, /* get entry status, size, etc here */ if ( H5C_get_entry_status(cache_ptr, old_addr, &entry_size, &entry_in_cache, - &entry_dirty, NULL) < 0 ) { + &entry_dirty, NULL, NULL) < 0 ) { HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't get entry status.") diff --git a/src/H5ACpkg.h b/src/H5ACpkg.h new file mode 100644 index 0000000..903eca3 --- /dev/null +++ b/src/H5ACpkg.h @@ -0,0 +1,315 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by the Board of Trustees of the University of Illinois. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Programmer: John Mainzer -- 4/19/06 + * + * Purpose: This file contains declarations which are normally visible + * only within the H5AC package (just H5AC.c at present). + * + * Source files outside the H5AC package should include + * H5ACprivate.h instead. + * + * The one exception to this rule is testpar/t_cache.c. The + * test code is easier to write if it can look at H5AC_aux_t. + * Indeed, this is the main reason why this file was created. + */ + +#ifndef H5AC_PACKAGE +#error "Do not include this file outside the H5AC package!" +#endif + +#ifndef _H5ACpkg_H +#define _H5ACpkg_H + +#define H5C_PACKAGE /*suppress error about including H5Cpkg */ + +/* Get package's private header */ +#include "H5Cprivate.h" + + +/* Get needed headers */ +#include "H5Cpkg.h" /* Cache */ +#include "H5SLprivate.h" /* Skip lists */ + +#ifdef H5_HAVE_PARALLEL +#include <mpi.h> +#endif /* H5_HAVE_PARALLEL */ + + +#define H5AC_DEBUG_DIRTY_BYTES_CREATION 0 + +/*------------------------------------------------------------------------- + * It is a bit difficult to set ranges of allowable values on the + * dirty_bytes_threshold field of H5AC_aux_t. The following are + * probably broader than they should be. + *------------------------------------------------------------------------- + */ + +#define H5AC__MIN_DIRTY_BYTES_THRESHOLD (int32_t) \ + (H5C__MIN_MAX_CACHE_SIZE / 2) +#define H5AC__DEFAULT_DIRTY_BYTES_THRESHOLD (256 * 1024) +#define H5AC__MAX_DIRTY_BYTES_THRESHOLD (int32_t) \ + (H5C__MAX_MAX_CACHE_SIZE / 4) + +/**************************************************************************** + * + * structure H5AC_aux_t + * + * While H5AC has become a wrapper for the cache implemented in H5C.c, there + * are some features of the metadata cache that are specific to it, and which + * therefore do not belong in the more generic H5C cache code. + * + * In particular, there is the matter of synchronizing writes from the + * metadata cache to disk in the PHDF5 case. + * + * Prior to this update, the presumption was that all metadata caches would + * write the same data at the same time since all operations modifying + * metadata must be performed collectively. Given this assumption, it was + * safe to allow only the writes from process 0 to actually make it to disk, + * while metadata writes from all other processes were discarded. + * + * Unfortunately, this presumption is in error as operations that read + * metadata need not be collective, but can change the location of dirty + * entries in the metadata cache LRU lists. This can result in the same + * metadata write operation triggering writes from the metadata caches on + * some processes, but not all (causing a hang), or in different sets of + * entries being written from different caches (potentially resulting in + * metadata corruption in the file). + * + * To deal with this issue, I decided to apply a paradigm shift to the way + * metadata is written to disk. + * + * With this set of changes, only the metadata cache on process 0 is able + * to write metadata to disk, although metadata caches on all other + * processes can read metadata from disk as before. + * + * To keep all the other caches from getting plugged up with dirty metadata, + * process 0 periodically broadcasts a list of entries that it has flushed + * since that last notice, and which are currently clean. The other caches + * mark these entries as clean as well, which allows them to evict the + * entries as needed. + * + * One obvious problem in this approach is synchronizing the broadcasts + * and receptions, as different caches may see different amounts of + * activity. + * + * The current solution is for the caches to track the number of bytes + * of newly generated dirty metadata, and to broadcast and receive + * whenever this value exceeds some user specified threshold. + * + * Maintaining this count is easy for all processes not on process 0 -- + * all that is necessary is to add the size of the entry to the total + * whenever there is an insertion, a rename of a previously clean entry, + * or whever a previously clean entry is marked dirty in an unprotect. + * + * On process 0, we have to be careful not to count dirty bytes twice. + * If an entry is marked dirty, flushed, and marked dirty again, all + * within a single reporting period, it only th first marking should + * be added to the dirty bytes generated tally, as that is all that + * the other processes will see. + * + * At present, this structure exists to maintain the fields needed to + * implement the above scheme, and thus is only used in the parallel + * case. However, other uses may arise in the future. + * + * Instance of this structure are associated with metadata caches via + * the aux_ptr field of H5C_t (see H5Cpkg.h). The H5AC code is + * responsible for allocating, maintaining, and discarding instances + * of H5AC_aux_t. + * + * The remainder of this header comments documents the individual fields + * of the structure. + * + * JRM - 6/27/05 + * + * magic: Unsigned 32 bit integer always set to + * H5AC__H5AC_AUX_T_MAGIC. This field is used to validate + * pointers to instances of H5AC_aux_t. + * + * mpi_comm: MPI communicator associated with the file for which the + * cache has been created. + * + * mpi_rank: MPI rank of this process within mpi_comm. + * + * mpi_size: Number of processes in mpi_comm. + * + * write_permitted: Boolean flag used to control whether the cache + * is permitted to write to file. + * + * dirty_bytes_threshold: Integer field containing the dirty bytes + * generation threashold. Whenever dirty byte creation + * exceeds this value, the metadata cache on process 0 + * broadcasts a list of the entries it has flushed since + * the last broadcast (or since the beginning of execution) + * and which are currently clean (if they are still in the + * cache) + * + * Similarly, metadata caches on processes other than process + * 0 will attempt to receive a list of clean entries whenever + * the threshold is exceeded. + * + * dirty_bytes: Integer field containing the number of bytes of dirty + * metadata generated since the beginning of the computation, + * or (more typically) since the last clean entries list + * broadcast. This field is reset to zero after each such + * broadcast. + * + * dirty_bytes_propagations: This field only exists when the + * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. + * + * It is used to track the number of times the cleaned list + * has been propagated from process 0 to the other + * processes. + * + * unprotect_dirty_bytes: This field only exists when the + * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. + * + * It is used to track the number of dirty bytes created + * via unprotect operations since the last time the cleaned + * list was propagated. + * + * unprotect_dirty_bytes_updates: This field only exists when the + * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. + * + * It is used to track the number of times dirty bytes have + * been created via unprotect operations since the last time + * the cleaned list was propagated. + * + * insert_dirty_bytes: This field only exists when the + * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. + * + * It is used to track the number of dirty bytes created + * via insert operations since the last time the cleaned + * list was propagated. + * + * insert_dirty_bytes_updates: This field only exists when the + * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. + * + * It is used to track the number of times dirty bytes have + * been created via insert operations since the last time + * the cleaned list was propagated. + * + * rename_dirty_bytes: This field only exists when the + * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. + * + * It is used to track the number of dirty bytes created + * via rename operations since the last time the cleaned + * list was propagated. + * + * rename_dirty_bytes_updates: This field only exists when the + * H5AC_DEBUG_DIRTY_BYTES_CREATION #define is TRUE. + * + * It is used to track the number of times dirty bytes have + * been created via rename operations since the last time + * the cleaned list was propagated. + * + * d_slist_ptr: Pointer to an instance of H5SL_t used to maintain a list + * of entries that have been dirtied since the last time they + * were listed in a clean entries broadcast. This list is + * only maintained by the metadata cache on process 0 -- it + * it used to maintain a view of the dirty entries as seen + * by the other caches, so as to keep the dirty bytes count + * in synchronization with them. + * + * Thus on process 0, the dirty_bytes count is incremented + * only if either + * + * 1) an entry is inserted in the metadata cache, or + * + * 2) a previously clean entry is renamed, and it does not + * already appear in the dirty entry list, or + * + * 3) a previously clean entry is unprotected with the + * dirtied flag set and the entry does not already appear + * in the dirty entry list. + * + * Entries are added to the dirty entry list whever they cause + * the dirty bytes count to be increased. They are removed + * when they appear in a clean entries broadcast. Note that + * renames must be reflected in the dirty entry list. + * + * To reitterate, this field is only used on process 0 -- it + * should be NULL on all other processes. + * + * d_slist_len: Integer field containing the number of entries in the + * dirty entry list. This field should always contain the + * value 0 on all processes other than process 0. It exists + * primarily for sanity checking. + * + * c_slist_ptr: Pointer to an instance of H5SL_t used to maintain a list + * of entries that were dirty, have been flushed + * to disk since the last clean entries broadcast, and are + * still clean. Since only process 0 can write to disk, this + * list only exists on process 0. + * + * In essence, this slist is used to assemble the contents of + * the next clean entries broadcast. The list emptied after + * each broadcast. + * + * c_slist_len: Integer field containing the number of entries in the clean + * entries list (*c_slist_ptr). This field should always + * contain the value 0 on all processes other than process 0. + * It exists primarily for sanity checking. + * + ****************************************************************************/ + +#ifdef H5_HAVE_PARALLEL + +#define H5AC__H5AC_AUX_T_MAGIC (unsigned)0x00D0A01 + +typedef struct H5AC_aux_t +{ + uint32_t magic; + + MPI_Comm mpi_comm; + + int mpi_rank; + + int mpi_size; + + hbool_t write_permitted; + + int32_t dirty_bytes_threshold; + + int32_t dirty_bytes; + +#if H5AC_DEBUG_DIRTY_BYTES_CREATION + + int32_t dirty_bytes_propagations; + + int32_t unprotect_dirty_bytes; + int32_t unprotect_dirty_bytes_updates; + + int32_t insert_dirty_bytes; + int32_t insert_dirty_bytes_updates; + + int32_t rename_dirty_bytes; + int32_t rename_dirty_bytes_updates; + +#endif /* H5AC_DEBUG_DIRTY_BYTES_CREATION */ + + H5SL_t * d_slist_ptr; + + int32_t d_slist_len; + + H5SL_t * c_slist_ptr; + + int32_t c_slist_len; + +} H5AC_aux_t; /* struct H5AC_aux_t */ + +#endif /* H5_HAVE_PARALLEL */ + +#endif /* _H5Cpkg_H */ diff --git a/src/H5ACprivate.h b/src/H5ACprivate.h index 9d89621..a6844e4 100644 --- a/src/H5ACprivate.h +++ b/src/H5ACprivate.h @@ -221,23 +221,45 @@ extern hid_t H5AC_ind_dxpl_id; #define H5AC__DELETED_FLAG H5C__DELETED_FLAG #define H5AC__DIRTIED_FLAG H5C__DIRTIED_FLAG #define H5AC__SIZE_CHANGED_FLAG H5C__SIZE_CHANGED_FLAG +#define H5AC__PIN_ENTRY_FLAG H5C__PIN_ENTRY_FLAG +#define H5AC__UNPIN_ENTRY_FLAG H5C__UNPIN_ENTRY_FLAG #define H5AC__FLUSH_INVALIDATE_FLAG H5C__FLUSH_INVALIDATE_FLAG #define H5AC__FLUSH_CLEAR_ONLY_FLAG H5C__FLUSH_CLEAR_ONLY_FLAG #define H5AC__FLUSH_MARKED_ENTRIES_FLAG H5C__FLUSH_MARKED_ENTRIES_FLAG +/* #defines of flags used to report entry status in the + * H5AC_get_entry_status() call. + */ + +#define H5AC_ES__IN_CACHE 0x0001 +#define H5AC_ES__IS_DIRTY 0x0002 +#define H5AC_ES__IS_PROTECTED 0x0004 +#define H5AC_ES__IS_PINNED 0x0008 + + +/* external function declarations: */ H5_DLL herr_t H5AC_init(void); H5_DLL herr_t H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr); +H5_DLL herr_t H5AC_get_entry_status(H5C_t * cache_ptr, haddr_t addr, + unsigned * status_ptr); H5_DLL herr_t H5AC_set(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type, haddr_t addr, void *thing, unsigned int flags); -H5_DLL void *H5AC_protect(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type, - haddr_t addr, const void *udata1, void *udata2, - H5AC_protect_t rw); +H5_DLL herr_t H5AC_pin_protected_entry(H5C_t * cache_ptr, void * thing); +H5_DLL void * H5AC_protect(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type, + haddr_t addr, const void *udata1, void *udata2, + H5AC_protect_t rw); +H5_DLL herr_t H5AC_unpin_entry(H5C_t * cache_ptr, + void * thing); H5_DLL herr_t H5AC_unprotect(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type, haddr_t addr, void *thing, unsigned flags); H5_DLL herr_t H5AC_flush(H5F_t *f, hid_t dxpl_id, unsigned flags); +H5_DLL herr_t H5AC_mark_pinned_entry_dirty(H5C_t * cache_ptr, + void * thing, + hbool_t size_changed, + size_t new_size); H5_DLL herr_t H5AC_rename(H5F_t *f, const H5AC_class_t *type, haddr_t old_addr, haddr_t new_addr); @@ -143,7 +143,7 @@ * * One could argue that I should have given the epoch markers a positive * size, but this would break the index_size = LRU_list_size + pl_size - * invarient. + * + pel_size invarient. * * Alternatively, I could pass the current decr_mode in to the macro, * and just skip the check whenever epoch markers may be in use. @@ -528,6 +528,11 @@ if ( ( (entry_ptr) == NULL ) || \ * H5C__UPDATE_CACHE_HIT_RATE_STATS(), which is always active as * the cache hit rate stats are always collected and available. * + * Changes: + * + * JRM -- 3/21/06 + * Added / updated macros for pinned entry related stats. + * ***********************************************************************/ #define H5C__UPDATE_CACHE_HIT_RATE_STATS(cache_ptr, hit) \ @@ -538,6 +543,9 @@ if ( ( (entry_ptr) == NULL ) || \ #if H5C_COLLECT_CACHE_STATS +#define H5C__UPDATE_STATS_FOR_DIRTY_PIN(cache_ptr, entry_ptr) \ + (((cache_ptr)->dirty_pins)[(entry_ptr)->type->id])++; + #define H5C__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr) \ (((cache_ptr)->insertions)[(entry_ptr)->type->id])++; \ if ( (cache_ptr)->index_len > (cache_ptr)->max_index_len ) \ @@ -558,7 +566,11 @@ if ( ( (entry_ptr) == NULL ) || \ if ( (cache_ptr)->slist_len > (cache_ptr)->max_slist_len ) \ (cache_ptr)->max_slist_len = (cache_ptr)->slist_len; \ if ( (cache_ptr)->slist_size > (cache_ptr)->max_slist_size ) \ - (cache_ptr)->max_slist_size = (cache_ptr)->slist_size; + (cache_ptr)->max_slist_size = (cache_ptr)->slist_size; \ + if ( (cache_ptr)->pel_len > (cache_ptr)->max_pel_len ) \ + (cache_ptr)->max_pel_len = (cache_ptr)->pel_len; \ + if ( (cache_ptr)->pel_size > (cache_ptr)->max_pel_size ) \ + (cache_ptr)->max_pel_size = (cache_ptr)->pel_size; #define H5C__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) \ (((cache_ptr)->renames)[(entry_ptr)->type->id])++; @@ -591,19 +603,29 @@ if ( ( (entry_ptr) == NULL ) || \ (cache_ptr)->total_failed_ht_search_depth += depth; \ } +#define H5C__UPDATE_STATS_FOR_UNPIN(cache_ptr, entry_ptr) \ + ((cache_ptr)->unpins)[(entry_ptr)->type->id]++; + #if H5C_COLLECT_CACHE_ENTRY_STATS #define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) \ (entry_ptr)->accesses = 0; \ (entry_ptr)->clears = 0; \ - (entry_ptr)->flushes = 0; - -#define H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) \ - (((cache_ptr)->clears)[(entry_ptr)->type->id])++; \ + (entry_ptr)->flushes = 0; \ + (entry_ptr)->pins = 0; + +#define H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) \ + (((cache_ptr)->clears)[(entry_ptr)->type->id])++; \ + if ( (entry_ptr)->is_pinned ) { \ + (((cache_ptr)->pinned_clears)[(entry_ptr)->type->id])++; \ + } \ ((entry_ptr)->clears)++; -#define H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) \ - (((cache_ptr)->flushes)[(entry_ptr)->type->id])++; \ +#define H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) \ + (((cache_ptr)->flushes)[(entry_ptr)->type->id])++; \ + if ( (entry_ptr)->is_pinned ) { \ + (((cache_ptr)->pinned_flushes)[(entry_ptr)->type->id])++; \ + } \ ((entry_ptr)->flushes)++; #define H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) \ @@ -633,6 +655,11 @@ if ( ( (entry_ptr) == NULL ) || \ ((cache_ptr)->max_size)[(entry_ptr)->type->id] \ = (entry_ptr)->size; \ } \ + if ( (entry_ptr)->pins > \ + ((cache_ptr)->max_pins)[(entry_ptr)->type->id] ) { \ + ((cache_ptr)->max_pins)[(entry_ptr)->type->id] \ + = (entry_ptr)->pins; \ + } #define H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) \ if ( hit ) \ @@ -654,15 +681,29 @@ if ( ( (entry_ptr) == NULL ) || \ } \ ((entry_ptr)->accesses)++; +#define H5C__UPDATE_STATS_FOR_PIN(cache_ptr, entry_ptr) \ + ((cache_ptr)->pins)[(entry_ptr)->type->id]++; \ + (entry_ptr)->pins++; \ + if ( (cache_ptr)->pel_len > (cache_ptr)->max_pel_len ) \ + (cache_ptr)->max_pel_len = (cache_ptr)->pel_len; \ + if ( (cache_ptr)->pel_size > (cache_ptr)->max_pel_size ) \ + (cache_ptr)->max_pel_size = (cache_ptr)->pel_size; + #else /* H5C_COLLECT_CACHE_ENTRY_STATS */ #define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) -#define H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) \ +#define H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr) \ + if ( (entry_ptr)->is_pinned ) { \ + (((cache_ptr)->pinned_clears)[(entry_ptr)->type->id])++; \ + } \ (((cache_ptr)->clears)[(entry_ptr)->type->id])++; -#define H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) \ - (((cache_ptr)->flushes)[(entry_ptr)->type->id])++; +#define H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) \ + (((cache_ptr)->flushes)[(entry_ptr)->type->id])++; \ + if ( (entry_ptr)->is_pinned ) { \ + (((cache_ptr)->pinned_flushes)[(entry_ptr)->type->id])++; \ + } #define H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) \ (((cache_ptr)->evictions)[(entry_ptr)->type->id])++; @@ -681,11 +722,19 @@ if ( ( (entry_ptr) == NULL ) || \ if ( (cache_ptr)->pl_size > (cache_ptr)->max_pl_size ) \ (cache_ptr)->max_pl_size = (cache_ptr)->pl_size; +#define H5C__UPDATE_STATS_FOR_PIN(cache_ptr, entry_ptr) \ + ((cache_ptr)->pins)[(entry_ptr)->type->id]++; \ + if ( (cache_ptr)->pel_len > (cache_ptr)->max_pel_len ) \ + (cache_ptr)->max_pel_len = (cache_ptr)->pel_len; \ + if ( (cache_ptr)->pel_size > (cache_ptr)->max_pel_size ) \ + (cache_ptr)->max_pel_size = (cache_ptr)->pel_size; + #endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ #else /* H5C_COLLECT_CACHE_STATS */ #define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) +#define H5C__UPDATE_STATS_FOR_DIRTY_PIN(cache_ptr, entry_ptr) #define H5C__UPDATE_STATS_FOR_UNPROTECT(cache_ptr) #define H5C__UPDATE_STATS_FOR_RENAME(cache_ptr, entry_ptr) #define H5C__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, entry_ptr, new_size) @@ -697,6 +746,8 @@ if ( ( (entry_ptr) == NULL ) || \ #define H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr) #define H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr) #define H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit) +#define H5C__UPDATE_STATS_FOR_PIN(cache_ptr, entry_ptr) +#define H5C__UPDATE_STATS_FOR_UNPIN(cache_ptr, entry_ptr) #endif /* H5C_COLLECT_CACHE_STATS */ @@ -1139,7 +1190,11 @@ if ( ( (cache_ptr) == NULL ) || \ * * Modifications: * - * None. + * JRM -- 3/20/06 + * Modified macro to ignore pinned entries. Pinned entries + * do not appear in the data structures maintained by the + * replacement policy code, and thus this macro has nothing + * to do if called for such an entry. * *------------------------------------------------------------------------- */ @@ -1154,50 +1209,55 @@ if ( ( (cache_ptr) == NULL ) || \ HDassert( !((entry_ptr)->is_protected) ); \ HDassert( (entry_ptr)->size > 0 ); \ \ - /* modified LRU specific code */ \ + if ( ! ((entry_ptr)->is_pinned) ) { \ \ - /* remove the entry from the LRU list, and re-insert it at the head. */ \ + /* modified LRU specific code */ \ \ - H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + /* remove the entry from the LRU list, and re-insert it at the head.\ + */ \ \ - H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ - /* Use the dirty flag to infer whether the entry is on the clean or \ - * dirty LRU list, and remove it. Then insert it at the head of the \ - * same LRU list. \ - * \ - * At least initially, all entries should be clean. That may change, \ - * so we may as well deal with both cases now. \ - */ \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ - if ( (entry_ptr)->is_dirty ) { \ - H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ - (cache_ptr)->dLRU_tail_ptr, \ - (cache_ptr)->dLRU_list_len, \ - (cache_ptr)->dLRU_list_size, (fail_val)) \ + /* Use the dirty flag to infer whether the entry is on the clean or \ + * dirty LRU list, and remove it. Then insert it at the head of \ + * the same LRU list. \ + * \ + * At least initially, all entries should be clean. That may \ + * change, so we may as well deal with both cases now. \ + */ \ \ - H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ - (cache_ptr)->dLRU_tail_ptr, \ - (cache_ptr)->dLRU_list_len, \ - (cache_ptr)->dLRU_list_size, (fail_val)) \ - } else { \ - H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ - (cache_ptr)->cLRU_tail_ptr, \ - (cache_ptr)->cLRU_list_len, \ - (cache_ptr)->cLRU_list_size, (fail_val)) \ + if ( (entry_ptr)->is_dirty ) { \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ \ - H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ - (cache_ptr)->cLRU_tail_ptr, \ - (cache_ptr)->cLRU_list_len, \ - (cache_ptr)->cLRU_list_size, (fail_val)) \ - } \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + } else { \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ \ - /* End modified LRU specific code. */ \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ \ + /* End modified LRU specific code. */ \ + } \ } /* H5C__FAKE_RP_FOR_MOST_RECENT_ACCESS */ #else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ @@ -1210,20 +1270,25 @@ if ( ( (cache_ptr) == NULL ) || \ HDassert( !((entry_ptr)->is_protected) ); \ HDassert( (entry_ptr)->size > 0 ); \ \ - /* modified LRU specific code */ \ + if ( ! ((entry_ptr)->is_pinned) ) { \ \ - /* remove the entry from the LRU list, and re-insert it at the head. */ \ + /* modified LRU specific code */ \ \ - H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + /* remove the entry from the LRU list, and re-insert it at the head \ + */ \ \ - H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ - /* End modified LRU specific code. */ \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ + /* End modified LRU specific code. */ \ + } \ } /* H5C__FAKE_RP_FOR_MOST_RECENT_ACCESS */ #endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ @@ -1261,6 +1326,10 @@ if ( ( (cache_ptr) == NULL ) || \ * dirty LRU lists, and the other not. Yet another attempt * at optimization. * + * JRM - 3/20/06 + * Pinned entries can't be evicted, so this entry should never + * be called on a pinned entry. Added assert to verify this. + * *------------------------------------------------------------------------- */ @@ -1272,6 +1341,7 @@ if ( ( (cache_ptr) == NULL ) || \ HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ HDassert( (entry_ptr) ); \ HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( !((entry_ptr)->is_pinned) ); \ HDassert( (entry_ptr)->size > 0 ); \ \ /* modified LRU specific code */ \ @@ -1311,6 +1381,7 @@ if ( ( (cache_ptr) == NULL ) || \ HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ HDassert( (entry_ptr) ); \ HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( !((entry_ptr)->is_pinned) ); \ HDassert( (entry_ptr)->size > 0 ); \ \ /* modified LRU specific code */ \ @@ -1354,10 +1425,16 @@ if ( ( (cache_ptr) == NULL ) || \ * pre-processor, I'll have to remove them. * * JRM - 7/28/04 - * Split macro into two version, one supporting the clean and + * Split macro into two versions, one supporting the clean and * dirty LRU lists, and the other not. Yet another attempt * at optimization. * + * JRM - 3/20/06 + * While pinned entries can be flushed, they don't reside in + * the replacement policy data structures when unprotected. + * Thus I modified this macro to do nothing if the entry is + * pinned. + * *------------------------------------------------------------------------- */ @@ -1371,47 +1448,54 @@ if ( ( (cache_ptr) == NULL ) || \ HDassert( !((entry_ptr)->is_protected) ); \ HDassert( (entry_ptr)->size > 0 ); \ \ - /* modified LRU specific code */ \ + if ( ! ((entry_ptr)->is_pinned) ) { \ \ - /* remove the entry from the LRU list, and re-insert it at the head. */ \ + /* modified LRU specific code */ \ \ - H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + /* remove the entry from the LRU list, and re-insert it at the \ + * head. \ + */ \ \ - H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ - /* since the entry is being flushed or cleared, one would think that it \ - * must be dirty -- but that need not be the case. Use the dirty flag \ - * to infer whether the entry is on the clean or dirty LRU list, and \ - * remove it. Then insert it at the head of the clean LRU list. \ - * \ - * The function presumes that a dirty entry will be either cleared or \ - * flushed shortly, so it is OK if we put a dirty entry on the clean \ - * LRU list. \ - */ \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ - if ( (entry_ptr)->is_dirty ) { \ - H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ - (cache_ptr)->dLRU_tail_ptr, \ - (cache_ptr)->dLRU_list_len, \ - (cache_ptr)->dLRU_list_size, (fail_val)) \ - } else { \ - H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ - (cache_ptr)->cLRU_tail_ptr, \ - (cache_ptr)->cLRU_list_len, \ - (cache_ptr)->cLRU_list_size, (fail_val)) \ - } \ + /* since the entry is being flushed or cleared, one would think \ + * that it must be dirty -- but that need not be the case. Use the \ + * dirty flag to infer whether the entry is on the clean or dirty \ + * LRU list, and remove it. Then insert it at the head of the \ + * clean LRU list. \ + * \ + * The function presumes that a dirty entry will be either cleared \ + * or flushed shortly, so it is OK if we put a dirty entry on the \ + * clean LRU list. \ + */ \ \ - H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ - (cache_ptr)->cLRU_tail_ptr, \ - (cache_ptr)->cLRU_list_len, \ - (cache_ptr)->cLRU_list_size, (fail_val)) \ + if ( (entry_ptr)->is_dirty ) { \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + } else { \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ \ - /* End modified LRU specific code. */ \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ \ + /* End modified LRU specific code. */ \ + } \ } /* H5C__UPDATE_RP_FOR_FLUSH */ #else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ @@ -1424,20 +1508,26 @@ if ( ( (cache_ptr) == NULL ) || \ HDassert( !((entry_ptr)->is_protected) ); \ HDassert( (entry_ptr)->size > 0 ); \ \ - /* modified LRU specific code */ \ + if ( ! ((entry_ptr)->is_pinned) ) { \ \ - /* remove the entry from the LRU list, and re-insert it at the head. */ \ + /* modified LRU specific code */ \ \ - H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + /* remove the entry from the LRU list, and re-insert it at the \ + * head. \ + */ \ \ - H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ - /* End modified LRU specific code. */ \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ + /* End modified LRU specific code. */ \ + } \ } /* H5C__UPDATE_RP_FOR_FLUSH */ #endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ @@ -1475,6 +1565,10 @@ if ( ( (cache_ptr) == NULL ) || \ * dirty LRU lists, and the other not. Yet another attempt * at optimization. * + * JRM - 3/10/06 + * This macro should never be called on a pinned entry. + * Inserted an assert to verify this. + * *------------------------------------------------------------------------- */ @@ -1486,6 +1580,7 @@ if ( ( (cache_ptr) == NULL ) || \ HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ HDassert( (entry_ptr) ); \ HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( !((entry_ptr)->is_pinned) ); \ HDassert( (entry_ptr)->size > 0 ); \ \ /* modified LRU specific code */ \ @@ -1524,6 +1619,7 @@ if ( ( (cache_ptr) == NULL ) || \ HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ HDassert( (entry_ptr) ); \ HDassert( !((entry_ptr)->is_protected) ); \ + HDassert( !((entry_ptr)->is_pinned) ); \ HDassert( (entry_ptr)->size > 0 ); \ \ /* modified LRU specific code */ \ @@ -1577,6 +1673,11 @@ if ( ( (cache_ptr) == NULL ) || \ * dirty LRU lists, and the other not. Yet another attempt * at optimization. * + * JRM - 3/17/06 + * Modified macro to attempt to remove pinned entriese from + * the pinned entry list instead of from the data structures + * maintained by the replacement policy. + * *------------------------------------------------------------------------- */ @@ -1589,38 +1690,49 @@ if ( ( (cache_ptr) == NULL ) || \ HDassert( (entry_ptr) ); \ HDassert( !((entry_ptr)->is_protected) ); \ HDassert( (entry_ptr)->size > 0 ); \ + \ + if ( (entry_ptr)->is_pinned ) { \ \ - /* modified LRU specific code */ \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pel_head_ptr, \ + (cache_ptr)->pel_tail_ptr, \ + (cache_ptr)->pel_len, \ + (cache_ptr)->pel_size, (fail_val)) \ \ - /* remove the entry from the LRU list. */ \ + } else { \ \ - H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + /* modified LRU specific code */ \ \ - /* Similarly, remove the entry from the clean or dirty LRU list \ - * as appropriate. \ - */ \ + /* remove the entry from the LRU list. */ \ \ - if ( (entry_ptr)->is_dirty ) { \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ - H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ - (cache_ptr)->dLRU_tail_ptr, \ - (cache_ptr)->dLRU_list_len, \ - (cache_ptr)->dLRU_list_size, (fail_val)) \ + /* Similarly, remove the entry from the clean or dirty LRU list \ + * as appropriate. \ + */ \ \ - } else { \ + if ( (entry_ptr)->is_dirty ) { \ \ - H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ - (cache_ptr)->cLRU_tail_ptr, \ - (cache_ptr)->cLRU_list_len, \ - (cache_ptr)->cLRU_list_size, (fail_val)) \ - } \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + \ + } else { \ \ - /* End modified LRU specific code. */ \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ \ - /* Regardless of the replacement policy, now add the entry to the \ - * protected list. \ + /* End modified LRU specific code. */ \ + } \ + \ + /* Regardless of the replacement policy, or whether the entry is \ + * pinned, now add the entry to the protected list. \ */ \ \ H5C__DLL_APPEND((entry_ptr), (cache_ptr)->pl_head_ptr, \ @@ -1638,19 +1750,30 @@ if ( ( (cache_ptr) == NULL ) || \ HDassert( (entry_ptr) ); \ HDassert( !((entry_ptr)->is_protected) ); \ HDassert( (entry_ptr)->size > 0 ); \ + \ + if ( (entry_ptr)->is_pinned ) { \ + \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pel_head_ptr, \ + (cache_ptr)->pel_tail_ptr, \ + (cache_ptr)->pel_len, \ + (cache_ptr)->pel_size, (fail_val)) \ \ - /* modified LRU specific code */ \ + } else { \ \ - /* remove the entry from the LRU list. */ \ + /* modified LRU specific code */ \ \ - H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + /* remove the entry from the LRU list. */ \ \ - /* End modified LRU specific code. */ \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + } \ \ - /* Regardless of the replacement policy, now add the entry to the \ - * protected list. \ + /* Regardless of the replacement policy, or whether the entry is \ + * pinned, now add the entry to the protected list. \ */ \ \ H5C__DLL_APPEND((entry_ptr), (cache_ptr)->pl_head_ptr, \ @@ -1705,6 +1828,12 @@ if ( ( (cache_ptr) == NULL ) || \ * to allow it to function correctly should that policy * be relaxed in the future. * + * JRM - 3/17/06 + * Modified macro to do nothing if the entry is pinned. + * In this case, the entry is on the pinned entry list, not + * in the replacement policy data structures, so there is + * nothing to be done. + * *------------------------------------------------------------------------- */ @@ -1718,57 +1847,62 @@ if ( ( (cache_ptr) == NULL ) || \ HDassert( !((entry_ptr)->is_protected) ); \ HDassert( (entry_ptr)->size > 0 ); \ \ - /* modified LRU specific code */ \ + if ( ! ((entry_ptr)->is_pinned) ) { \ \ - /* remove the entry from the LRU list, and re-insert it at the head. */ \ + /* modified LRU specific code */ \ \ - H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + /* remove the entry from the LRU list, and re-insert it at the head. \ + */ \ \ - H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ - /* remove the entry from either the clean or dirty LUR list as \ - * indicated by the was_dirty parameter \ - */ \ - if ( was_dirty ) { \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ - H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ - (cache_ptr)->dLRU_tail_ptr, \ - (cache_ptr)->dLRU_list_len, \ - (cache_ptr)->dLRU_list_size, (fail_val)) \ + /* remove the entry from either the clean or dirty LUR list as \ + * indicated by the was_dirty parameter \ + */ \ + if ( was_dirty ) { \ \ - } else { \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ \ - H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ - (cache_ptr)->cLRU_tail_ptr, \ - (cache_ptr)->cLRU_list_len, \ - (cache_ptr)->cLRU_list_size, (fail_val)) \ - } \ + } else { \ \ - /* insert the entry at the head of either the clean or dirty LRU list \ - * as appropriate. \ - */ \ + H5C__AUX_DLL_REMOVE((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ \ - if ( (entry_ptr)->is_dirty ) { \ + /* insert the entry at the head of either the clean or dirty LRU \ + * list as appropriate. \ + */ \ \ - H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ - (cache_ptr)->dLRU_tail_ptr, \ - (cache_ptr)->dLRU_list_len, \ - (cache_ptr)->dLRU_list_size, (fail_val)) \ + if ( (entry_ptr)->is_dirty ) { \ \ - } else { \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ \ - H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ - (cache_ptr)->cLRU_tail_ptr, \ - (cache_ptr)->cLRU_list_len, \ - (cache_ptr)->cLRU_list_size, (fail_val)) \ - } \ + } else { \ \ - /* End modified LRU specific code. */ \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ \ + /* End modified LRU specific code. */ \ + } \ } /* H5C__UPDATE_RP_FOR_RENAME */ #else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ @@ -1781,20 +1915,25 @@ if ( ( (cache_ptr) == NULL ) || \ HDassert( !((entry_ptr)->is_protected) ); \ HDassert( (entry_ptr)->size > 0 ); \ \ - /* modified LRU specific code */ \ + if ( ! ((entry_ptr)->is_pinned) ) { \ \ - /* remove the entry from the LRU list, and re-insert it at the head. */ \ + /* modified LRU specific code */ \ \ - H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + /* remove the entry from the LRU list, and re-insert it at the head. \ + */ \ \ - H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ - (cache_ptr)->LRU_tail_ptr, (cache_ptr)->LRU_list_len, \ - (cache_ptr)->LRU_list_size, (fail_val)) \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ - /* End modified LRU specific code. */ \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ \ + /* End modified LRU specific code. */ \ + } \ } /* H5C__UPDATE_RP_FOR_RENAME */ #endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ @@ -1802,59 +1941,48 @@ if ( ( (cache_ptr) == NULL ) || \ /*------------------------------------------------------------------------- * - * Macro: H5C__UPDATE_RP_FOR_UNPROTECT + * Macro: H5C__UPDATE_RP_FOR_UNPIN * * Purpose: Update the replacement policy data structures for an - * unprotect of the specified cache entry. + * unpin of the specified cache entry. * * To do this, unlink the specified entry from the protected - * list, and re-insert it in the data structures used by the - * current replacement policy. + * entry list, and re-insert it in the data structures used + * by the current replacement policy. * * At present, we only support the modified LRU policy, so * this function deals with that case unconditionally. If - * we ever support other replacement policies, the function + * we ever support other replacement policies, the macro * should switch on the current policy and act accordingly. * * Return: N/A * - * Programmer: John Mainzer, 5/19/04 + * Programmer: John Mainzer, 3/22/06 * * Modifications: * - * JRM - 7/27/04 - * Converted the function H5C_update_rp_for_unprotect() to - * the macro H5C__UPDATE_RP_FOR_UNPROTECT in an effort to - * squeeze a bit more performance out of the cache. - * - * At least for the first cut, I am leaving the comments and - * white space in the macro. If they cause dificulties with - * pre-processor, I'll have to remove them. - * - * JRM - 7/28/04 - * Split macro into two version, one supporting the clean and - * dirty LRU lists, and the other not. Yet another attempt - * at optimization. + * None. * *------------------------------------------------------------------------- */ #if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS -#define H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, fail_val) \ +#define H5C__UPDATE_RP_FOR_UNPIN(cache_ptr, entry_ptr, fail_val) \ { \ HDassert( (cache_ptr) ); \ HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ HDassert( (entry_ptr) ); \ - HDassert( (entry_ptr)->is_protected); \ + HDassert( ! ((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->is_pinned); \ HDassert( (entry_ptr)->size > 0 ); \ \ /* Regardless of the replacement policy, remove the entry from the \ - * protected list. \ + * pinned entry list. \ */ \ - H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pl_head_ptr, \ - (cache_ptr)->pl_tail_ptr, (cache_ptr)->pl_len, \ - (cache_ptr)->pl_size, (fail_val)) \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pel_head_ptr, \ + (cache_ptr)->pel_tail_ptr, (cache_ptr)->pel_len, \ + (cache_ptr)->pel_size, (fail_val)) \ \ /* modified LRU specific code */ \ \ @@ -1886,24 +2014,25 @@ if ( ( (cache_ptr) == NULL ) || \ \ /* End modified LRU specific code. */ \ \ -} /* H5C__UPDATE_RP_FOR_UNPROTECT */ +} /* H5C__UPDATE_RP_FOR_UNPIN */ #else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ -#define H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, fail_val) \ +#define H5C__UPDATE_RP_FOR_UNPIN(cache_ptr, entry_ptr, fail_val) \ { \ HDassert( (cache_ptr) ); \ HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ HDassert( (entry_ptr) ); \ - HDassert( (entry_ptr)->is_protected); \ + HDassert( ! ((entry_ptr)->is_protected) ); \ + HDassert( (entry_ptr)->is_pinned); \ HDassert( (entry_ptr)->size > 0 ); \ \ /* Regardless of the replacement policy, remove the entry from the \ - * protected list. \ + * pinned entry list. \ */ \ - H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pl_head_ptr, \ - (cache_ptr)->pl_tail_ptr, (cache_ptr)->pl_len, \ - (cache_ptr)->pl_size, (fail_val)) \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pel_head_ptr, \ + (cache_ptr)->pel_tail_ptr, (cache_ptr)->pel_len, \ + (cache_ptr)->pel_size, (fail_val)) \ \ /* modified LRU specific code */ \ \ @@ -1916,6 +2045,151 @@ if ( ( (cache_ptr) == NULL ) || \ \ /* End modified LRU specific code. */ \ \ +} /* H5C__UPDATE_RP_FOR_UNPIN */ + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/*------------------------------------------------------------------------- + * + * Macro: H5C__UPDATE_RP_FOR_UNPROTECT + * + * Purpose: Update the replacement policy data structures for an + * unprotect of the specified cache entry. + * + * To do this, unlink the specified entry from the protected + * list, and re-insert it in the data structures used by the + * current replacement policy. + * + * At present, we only support the modified LRU policy, so + * this function deals with that case unconditionally. If + * we ever support other replacement policies, the function + * should switch on the current policy and act accordingly. + * + * Return: N/A + * + * Programmer: John Mainzer, 5/19/04 + * + * Modifications: + * + * JRM - 7/27/04 + * Converted the function H5C_update_rp_for_unprotect() to + * the macro H5C__UPDATE_RP_FOR_UNPROTECT in an effort to + * squeeze a bit more performance out of the cache. + * + * At least for the first cut, I am leaving the comments and + * white space in the macro. If they cause dificulties with + * pre-processor, I'll have to remove them. + * + * JRM - 7/28/04 + * Split macro into two version, one supporting the clean and + * dirty LRU lists, and the other not. Yet another attempt + * at optimization. + * + * JRM - 3/17/06 + * Modified macro to put pinned entries on the pinned entry + * list instead of inserting them in the data structures + * maintained by the replacement policy. + * + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( (entry_ptr)->is_protected); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* Regardless of the replacement policy, remove the entry from the \ + * protected list. \ + */ \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pl_head_ptr, \ + (cache_ptr)->pl_tail_ptr, (cache_ptr)->pl_len, \ + (cache_ptr)->pl_size, (fail_val)) \ + \ + if ( (entry_ptr)->is_pinned ) { \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->pel_head_ptr, \ + (cache_ptr)->pel_tail_ptr, \ + (cache_ptr)->pel_len, \ + (cache_ptr)->pel_size, (fail_val)) \ + \ + } else { \ + \ + /* modified LRU specific code */ \ + \ + /* insert the entry at the head of the LRU list. */ \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* Similarly, insert the entry at the head of either the clean or \ + * dirty LRU list as appropriate. \ + */ \ + \ + if ( (entry_ptr)->is_dirty ) { \ + \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \ + (cache_ptr)->dLRU_tail_ptr, \ + (cache_ptr)->dLRU_list_len, \ + (cache_ptr)->dLRU_list_size, (fail_val)) \ + \ + } else { \ + \ + H5C__AUX_DLL_PREPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \ + (cache_ptr)->cLRU_tail_ptr, \ + (cache_ptr)->cLRU_list_len, \ + (cache_ptr)->cLRU_list_size, (fail_val)) \ + } \ + \ + /* End modified LRU specific code. */ \ + } \ + \ +} /* H5C__UPDATE_RP_FOR_UNPROTECT */ + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, fail_val) \ +{ \ + HDassert( (cache_ptr) ); \ + HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \ + HDassert( (entry_ptr) ); \ + HDassert( (entry_ptr)->is_protected); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + /* Regardless of the replacement policy, remove the entry from the \ + * protected list. \ + */ \ + H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pl_head_ptr, \ + (cache_ptr)->pl_tail_ptr, (cache_ptr)->pl_len, \ + (cache_ptr)->pl_size, (fail_val)) \ + \ + if ( (entry_ptr)->is_pinned ) { \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->pel_head_ptr, \ + (cache_ptr)->pel_tail_ptr, \ + (cache_ptr)->pel_len, \ + (cache_ptr)->pel_size, (fail_val)) \ + \ + } else { \ + \ + /* modified LRU specific code */ \ + \ + /* insert the entry at the head of the LRU list. */ \ + \ + H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* End modified LRU specific code. */ \ + } \ } /* H5C__UPDATE_RP_FOR_UNPROTECT */ #endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ @@ -1974,6 +2248,12 @@ static herr_t H5C_flush_single_entry(H5F_t * f, hbool_t * first_flush_ptr, hbool_t del_entry_from_slist_on_destroy); +static herr_t H5C_flush_invalidate_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + unsigned flags); + static void * H5C_load_entry(H5F_t * f, hid_t dxpl_id, const H5C_class_t * type, @@ -2176,6 +2456,9 @@ done: * JRM -- 1/20/06 * Added initialization of the new prefix field in H5C_t. * + * JRM -- 3/16/06 + * Added initialization for the pinned entry related fields. + * *------------------------------------------------------------------------- */ @@ -2259,6 +2542,11 @@ H5C_create(size_t max_cache_size, cache_ptr->pl_head_ptr = NULL; cache_ptr->pl_tail_ptr = NULL; + cache_ptr->pel_len = 0; + cache_ptr->pel_size = (size_t)0; + cache_ptr->pel_head_ptr = NULL; + cache_ptr->pel_tail_ptr = NULL; + cache_ptr->LRU_list_len = 0; cache_ptr->LRU_list_size = (size_t)0; cache_ptr->LRU_head_ptr = NULL; @@ -2320,6 +2608,7 @@ H5C_create(size_t max_cache_size, ((cache_ptr->epoch_markers)[i]).type = &epoch_marker_class; ((cache_ptr->epoch_markers)[i]).is_dirty = FALSE; ((cache_ptr->epoch_markers)[i]).is_protected = FALSE; + ((cache_ptr->epoch_markers)[i]).is_pinned = FALSE; ((cache_ptr->epoch_markers)[i]).in_slist = FALSE; ((cache_ptr->epoch_markers)[i]).ht_next = NULL; ((cache_ptr->epoch_markers)[i]).ht_prev = NULL; @@ -2331,6 +2620,7 @@ H5C_create(size_t max_cache_size, ((cache_ptr->epoch_markers)[i]).accesses = 0; ((cache_ptr->epoch_markers)[i]).clears = 0; ((cache_ptr->epoch_markers)[i]).flushes = 0; + ((cache_ptr->epoch_markers)[i]).pins = 0; #endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ } @@ -2721,12 +3011,15 @@ done: * and then restoring LRU order. * * However, it is possible that the cache will contain other, - * unmarked entries, when we make this call. This new flag - * allows us to ignore this. + * unmarked protected entries, when we make this call. This + * new flag allows us to ignore them. * * Note that even with this flag set, it is still an error * to try to flush a protected entry. * + * JRM -- 3/25/065 + * Updated function to handle pinned entries. + * *------------------------------------------------------------------------- */ herr_t @@ -2744,7 +3037,6 @@ H5C_flush_cache(H5F_t * f, hbool_t ignore_protected; hbool_t tried_to_flush_protected_entry = FALSE; int32_t protected_entries = 0; - int32_t i; H5SL_node_t * node_ptr = NULL; H5C_cache_entry_t * entry_ptr = NULL; #if H5C_DO_SANITY_CHECKS @@ -2773,146 +3065,75 @@ H5C_flush_cache(H5F_t * f, HDassert( ! ( destroy && ignore_protected ) ); - if ( ( destroy ) && ( cache_ptr->epoch_markers_active > 0 ) ) { + if ( destroy ) { - status = H5C__autoadjust__ageout__remove_all_markers(cache_ptr); + status = H5C_flush_invalidate_cache(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + flags); - if ( status != SUCCEED ) { + if ( status < 0 ) { - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "error removing all epoch markers.") + /* This shouldn't happen -- if it does, we are toast so + * just scream and die. + */ + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "flush invalidate failed.") } - } - - - if ( cache_ptr->slist_len == 0 ) { - - node_ptr = NULL; - HDassert( cache_ptr->slist_size == 0 ); - } else { - node_ptr = H5SL_first(cache_ptr->slist_ptr); + if ( cache_ptr->slist_len == 0 ) { -#if H5C_DO_SANITY_CHECKS - /* H5C_flush_single_entry() now removes dirty entries from the - * slist as it flushes them. Thus for sanity checks we must - * make note of the initial slist length and size before we - * do any flushes. - */ - initial_slist_len = cache_ptr->slist_len; - initial_slist_size = cache_ptr->slist_size; -#endif /* H5C_DO_SANITY_CHECKS */ + node_ptr = NULL; + HDassert( cache_ptr->slist_size == 0 ); - } - - while ( node_ptr != NULL ) - { - entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr); - - /* increment node pointer now, before we delete its target - * from the slist. - */ - node_ptr = H5SL_next(node_ptr); + } else { - HDassert( entry_ptr != NULL ); - HDassert( entry_ptr->in_slist ); + node_ptr = H5SL_first(cache_ptr->slist_ptr); #if H5C_DO_SANITY_CHECKS - actual_slist_len++; - actual_slist_size += entry_ptr->size; + /* H5C_flush_single_entry() now removes dirty entries from the + * slist as it flushes them. Thus for sanity checks we must + * make note of the initial slist length and size before we + * do any flushes. + */ + initial_slist_len = cache_ptr->slist_len; + initial_slist_size = cache_ptr->slist_size; #endif /* H5C_DO_SANITY_CHECKS */ - if ( ( ! flush_marked_entries ) || ( entry_ptr->flush_marker ) ) { - - if ( entry_ptr->is_protected ) { - - /* we probably have major problems -- but lets flush - * everything we can before we decide whether to flag - * an error. - */ - tried_to_flush_protected_entry = TRUE; - protected_entries++; - - } else { - - status = H5C_flush_single_entry(f, - primary_dxpl_id, - secondary_dxpl_id, - cache_ptr, - NULL, - entry_ptr->addr, - flags, - &first_flush, - FALSE); - if ( status < 0 ) { - - /* This shouldn't happen -- if it does, we are toast so - * just scream and die. - */ - HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ - "Can't flush entry.") - } - } } - } /* while */ - -#if H5C_DO_SANITY_CHECKS - HDassert( actual_slist_len == initial_slist_len ); - HDassert( actual_slist_size == initial_slist_size ); - if ( (flags & H5C__FLUSH_INVALIDATE_FLAG) != 0 ) { + while ( node_ptr != NULL ) + { + entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr); - HDassert( cache_ptr->slist_len == initial_slist_len ); - HDassert( cache_ptr->slist_size == initial_slist_size ); + /* increment node pointer now, before we delete its target + * from the slist. + */ + node_ptr = H5SL_next(node_ptr); - } else if ( ! flush_marked_entries ) { + HDassert( entry_ptr != NULL ); + HDassert( entry_ptr->in_slist ); - HDassert( cache_ptr->slist_len == 0 ); - HDassert( cache_ptr->slist_size == 0 ); - } +#if H5C_DO_SANITY_CHECKS + actual_slist_len++; + actual_slist_size += entry_ptr->size; #endif /* H5C_DO_SANITY_CHECKS */ - if ( destroy ) { - - if(cache_ptr->slist_ptr) { - - /* Release all nodes from skip list, but keep list active */ - H5SL_release(cache_ptr->slist_ptr); - - } - cache_ptr->slist_len = 0; - cache_ptr->slist_size = 0; - - /* Since we are doing a destroy, we must make a pass through - * the hash table and flush all entries that remain. Note that - * all remaining entries entries must be clean, so this will - * not result in any writes to disk. - */ - for ( i = 0; i < H5C__HASH_TABLE_LEN; i++ ) - { - while ( cache_ptr->index[i] ) - { - entry_ptr = cache_ptr->index[i]; + if ( ( ! flush_marked_entries ) || ( entry_ptr->flush_marker ) ) { if ( entry_ptr->is_protected ) { - /* we have major problems -- but lets flush and destroy - * everything we can before we flag an error. + /* we probably have major problems -- but lets flush + * everything we can before we decide whether to flag + * an error. */ + tried_to_flush_protected_entry = TRUE; + protected_entries++; - H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr) - - if ( !entry_ptr->in_slist ) { - - protected_entries++; - HDassert( !(entry_ptr->is_dirty) ); - } } else { - HDassert( !(entry_ptr->is_dirty) ); - HDassert( !(entry_ptr->in_slist) ); - status = H5C_flush_single_entry(f, primary_dxpl_id, secondary_dxpl_id, @@ -2932,42 +3153,28 @@ H5C_flush_cache(H5F_t * f, } } } - } - - HDassert( protected_entries == cache_ptr->pl_len ); + } /* while */ - if ( protected_entries > 0 ) - { - /* the caller asked us to flush and destroy a cache that - * contains one or more protected entries. Since we can't - * flush protected entries, we haven't destroyed them either. - * Since they are all on the protected list, just re-insert - * them into the cache before we flag an error. - */ - entry_ptr = cache_ptr->pl_head_ptr; - - while ( entry_ptr != NULL ) - { - entry_ptr->in_slist = FALSE; +#if H5C_DO_SANITY_CHECKS + HDassert( actual_slist_len == initial_slist_len ); + HDassert( actual_slist_size == initial_slist_size ); - H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL) + if ( ! flush_marked_entries ) { - if ( entry_ptr->is_dirty ) { - - H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr) - } - entry_ptr = entry_ptr->next; - } + HDassert( cache_ptr->slist_len == 0 ); + HDassert( cache_ptr->slist_size == 0 ); } - } +#endif /* H5C_DO_SANITY_CHECKS */ - HDassert( protected_entries <= cache_ptr->pl_len ); + HDassert( protected_entries <= cache_ptr->pl_len ); - if ( ( ( cache_ptr->pl_len > 0 ) && ( !ignore_protected ) ) - || - ( tried_to_flush_protected_entry ) ) { + if ( ( ( cache_ptr->pl_len > 0 ) && ( !ignore_protected ) ) + || + ( tried_to_flush_protected_entry ) ) { - HGOTO_ERROR(H5E_CACHE, H5E_PROTECT, FAIL, "cache has protected items") + HGOTO_ERROR(H5E_CACHE, H5E_PROTECT, FAIL, \ + "cache has protected items") + } } done: @@ -3395,6 +3602,9 @@ done: * * Modifications: * + * JRM -- 4/26/06 + * Added the is_pinned_ptr parameter and supporting code. + * *------------------------------------------------------------------------- */ @@ -3404,7 +3614,8 @@ H5C_get_entry_status(H5C_t * cache_ptr, size_t * size_ptr, hbool_t * in_cache_ptr, hbool_t * is_dirty_ptr, - hbool_t * is_protected_ptr) + hbool_t * is_protected_ptr, + hbool_t * is_pinned_ptr) { herr_t ret_value = SUCCEED; /* Return value */ H5C_cache_entry_t * entry_ptr = NULL; @@ -3451,6 +3662,11 @@ H5C_get_entry_status(H5C_t * cache_ptr, *is_protected_ptr = entry_ptr->is_protected; } + + if ( is_pinned_ptr != NULL ) { + + *is_pinned_ptr = entry_ptr->is_pinned; + } } done: @@ -3512,6 +3728,10 @@ done: * Added support for the new write_permitted field of * the H5C_t structure. * + * JRM -- 3/16/06 + * Added initialization for the new is_pinned field of the + * H5C_cache_entry_t structure. + * *------------------------------------------------------------------------- */ @@ -3693,6 +3913,8 @@ H5C_insert_entry(H5F_t * f, entry_ptr->is_protected = FALSE; + entry_ptr->is_pinned = FALSE; + H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL) /* New entries are presumed to be dirty, so this if statement is @@ -3776,6 +3998,9 @@ done: * Leave the old code in place for now (commented out) for * benchmarking. * + * JRM -- 4/13/06 + * Updated function to deal with pinned entries. + * *------------------------------------------------------------------------- */ @@ -3796,6 +4021,9 @@ H5C_mark_entries_as_clean(H5F_t * f, int initial_list_len; haddr_t addr; #if H5C_DO_SANITY_CHECKS + int pinned_entries_marked = 0; + int protected_entries_marked = 0; + int other_entries_marked = 0; haddr_t last_addr; #endif /* H5C_DO_SANITY_CHECKS */ H5C_cache_entry_t * clear_ptr = NULL; @@ -3895,17 +4123,31 @@ H5C_mark_entries_as_clean(H5F_t * f, HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't clear entry.") } } -#else /* modified code -- commented out for now */ +#else /* modified code */ } else { /* Mark the entry to be cleared on unprotect. We will * scan the LRU list shortly, and clear all those entries * not currently protected. */ entry_ptr->clear_on_unprotect = TRUE; +#if H5C_DO_SANITY_CHECKS + if ( entry_ptr->is_protected ) { + + protected_entries_marked++; + + } else if ( entry_ptr->is_pinned ) { + + pinned_entries_marked++; + + } else { + + other_entries_marked++; + } +#endif /* H5C_DO_SANITY_CHECKS */ } #endif /* end modified code */ } -#if 1 /* modified code -- commented out for now */ +#if 1 /* modified code */ /* Scan through the LRU list from back to front, and flush the * entries whose clear_on_unprotect flags are set. Observe that * any protected entries will not be on the LRU, and therefore @@ -3947,6 +4189,48 @@ H5C_mark_entries_as_clean(H5F_t * f, entries_examined++; } +#if H5C_DO_SANITY_CHECKS + HDassert( entries_cleared == other_entries_marked ); +#endif /* H5C_DO_SANITY_CHECKS */ + + /* It is also possible that some of the cleared entries are on the + * pinned list. Must scan that also. + */ + + entry_ptr = cache_ptr->pel_head_ptr; + + while ( entry_ptr != NULL ) + { + if ( entry_ptr->clear_on_unprotect ) { + + entry_ptr->clear_on_unprotect = FALSE; + clear_ptr = entry_ptr; + entry_ptr = entry_ptr->next; + entries_cleared++; + + if ( H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + clear_ptr->type, + clear_ptr->addr, + H5C__FLUSH_CLEAR_ONLY_FLAG, + &first_flush, + TRUE) < 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't clear entry.") + } + } else { + + entry_ptr = entry_ptr->next; + } + } + +#if H5C_DO_SANITY_CHECKS + HDassert( entries_cleared == pinned_entries_marked + other_entries_marked ); + HDassert( entries_cleared + protected_entries_marked == ce_array_len ); +#endif /* H5C_DO_SANITY_CHECKS */ + HDassert( ( entries_cleared == ce_array_len ) || ( (ce_array_len - entries_cleared) <= cache_ptr->pl_len ) ); @@ -3963,7 +4247,7 @@ H5C_mark_entries_as_clean(H5F_t * f, } HDassert( (entries_cleared + i) == ce_array_len ); #endif /* H5C_DO_SANITY_CHECKS */ -#endif /* modified code -- commented out for now */ +#endif /* modified code */ done: @@ -3983,6 +4267,103 @@ done: /*------------------------------------------------------------------------- + * Function: H5C_mark_pinned_entry_dirty + * + * Purpose: Mark a pinned entry as dirty. The target entry MUST be + * be pinned, and MUST be unprotected. + * + * If the entry has changed size, the function updates + * data structures for the size change. + * + * If the entry is not already dirty, the function places + * the entry on the skip list. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 3/22/06 + * + * Modifications: + * + * None + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_mark_pinned_entry_dirty(H5C_t * cache_ptr, + void * thing, + hbool_t size_changed, + size_t new_size) +{ + herr_t ret_value = SUCCEED; /* Return value */ + H5C_cache_entry_t * entry_ptr; + + FUNC_ENTER_NOAPI(H5C_mark_pinned_entry_dirty, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( thing ); + HDassert( ( size_changed == TRUE ) || ( size_changed == FALSE ) ); + + entry_ptr = (H5C_cache_entry_t *)thing; + + if ( ! ( entry_ptr->is_pinned ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \ + "Entry isn't pinned??") + } + + if ( entry_ptr->is_protected ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \ + "Entry is protected??") + } + + /* mark the entry as dirty if it isn't already */ + entry_ptr->is_dirty = TRUE; + + /* update for change in entry size if necessary */ + if ( ( size_changed ) && ( entry_ptr->size != new_size ) ) { + + /* update the protected entry list */ + H5C__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr->pel_len), \ + (cache_ptr->pel_size), \ + (entry_ptr->size), (new_size)); + + /* update the hash table */ + H5C__UPDATE_INDEX_FOR_SIZE_CHANGE((cache_ptr), (entry_ptr->size),\ + (new_size)); + + /* if the entry is in the skip list, update that too */ + if ( entry_ptr->in_slist ) { + + H5C__UPDATE_SLIST_FOR_SIZE_CHANGE((cache_ptr), (entry_ptr->size),\ + (new_size)); + } + + /* update statistics just before changing the entry size */ + H5C__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE((cache_ptr), (entry_ptr), \ + (new_size)); + + /* finally, update the entry size proper */ + entry_ptr->size = new_size; + } + + if ( ! (entry_ptr->in_slist) ) { + + H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr) + } + + H5C__UPDATE_STATS_FOR_DIRTY_PIN(cache_ptr, entry_ptr) + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_mark_pinned_entry_dirty() */ + + +/*------------------------------------------------------------------------- * * Function: H5C_rename_entry * @@ -4005,6 +4386,12 @@ done: * moving management of the is_dirty field of * H5C_cache_entry_t into the H5C code. * + * JRM -- 4/3/06 + * Updated function to disallow renaming of pinned entries. + * + * JRM -- 4/27/06 + * Updated function to support renaming of pinned entries. + * *------------------------------------------------------------------------- */ @@ -4039,14 +4426,20 @@ H5C_rename_entry(H5C_t * cache_ptr, H5C__SEARCH_INDEX(cache_ptr, old_addr, entry_ptr, FAIL) - if ( ( entry_ptr == NULL ) || ( entry_ptr->type != type ) ) + if ( ( entry_ptr == NULL ) || ( entry_ptr->type != type ) ) { /* the old item doesn't exist in the cache, so we are done. */ HGOTO_DONE(SUCCEED) + } HDassert( entry_ptr->addr == old_addr ); HDassert( entry_ptr->type == type ); - HDassert( !(entry_ptr->is_protected) ); + + if ( entry_ptr->is_protected ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTRENAME, FAIL, \ + "Target entry is protected.") + } H5C__SEARCH_INDEX(cache_ptr, new_addr, test_entry_ptr, FAIL) @@ -4091,14 +4484,7 @@ H5C_rename_entry(H5C_t * cache_ptr, H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL) - /* remove this if statement once this set of mods - * is up and running. -- JRM - */ - - if ( entry_ptr->is_dirty ) { - - H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr) - } + H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr) H5C__UPDATE_RP_FOR_RENAME(cache_ptr, entry_ptr, was_dirty, FAIL) @@ -4120,6 +4506,63 @@ done: /*------------------------------------------------------------------------- + * Function: H5C_pin_protected_entry() + * + * Purpose: Pin a protected cache entry. The entry must be protected + * at the time of call, and must be unpinned. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 4/26/06 + * + * Modifications: + * + * JRM -- 4/26/06 + * Modified routine to allow it to operate on protected + * entries. + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_pin_protected_entry(H5C_t * cache_ptr, + void * thing) +{ + herr_t ret_value = SUCCEED; /* Return value */ + H5C_cache_entry_t * entry_ptr; + + FUNC_ENTER_NOAPI(H5C_pin_protected_entry, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( thing ); + + entry_ptr = (H5C_cache_entry_t *)thing; + + HDassert( H5F_addr_defined(entry_ptr->addr) ); + + if ( ! ( entry_ptr->is_protected ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, "Entry isn't protected") + } + + if ( entry_ptr->is_pinned ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, "Entry is already pinned") + } + + entry_ptr->is_pinned = TRUE; + + H5C__UPDATE_STATS_FOR_UNPIN(cache_ptr, entry_ptr) + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_pin_protected_entry() */ + + +/*------------------------------------------------------------------------- * Function: H5C_protect * * Purpose: If the target entry is not in the cache, load it. If @@ -4242,7 +4685,7 @@ H5C_protect(H5F_t * f, entry_ptr = (H5C_cache_entry_t *)thing; - /* try to free up some space if necessay */ + /* try to free up some space if necessary */ if ( (cache_ptr->index_size + entry_ptr->size) > cache_ptr->max_cache_size ) { @@ -4851,6 +5294,9 @@ done: * Added code to use the prefix field of H5C_t to allow * tagging of statistics output. * + * JRM -- 3/21/06 + * Added code supporting the pinned entry related stats. + * *------------------------------------------------------------------------- */ @@ -4876,11 +5322,17 @@ H5C_stats(H5C_t * cache_ptr, int64_t total_renames = 0; int64_t total_size_increases = 0; int64_t total_size_decreases = 0; + int64_t total_pins = 0; + int64_t total_unpins = 0; + int64_t total_dirty_pins = 0; + int64_t total_pinned_flushes = 0; + int64_t total_pinned_clears = 0; int32_t aggregate_max_accesses = 0; int32_t aggregate_min_accesses = 1000000; int32_t aggregate_max_clears = 0; int32_t aggregate_max_flushes = 0; size_t aggregate_max_size = 0; + int32_t aggregate_max_pins = 0; double hit_rate; double average_successful_search_depth = 0.0; double average_failed_search_depth = 0.0; @@ -4911,6 +5363,11 @@ H5C_stats(H5C_t * cache_ptr, total_renames += cache_ptr->renames[i]; total_size_increases += cache_ptr->size_increases[i]; total_size_decreases += cache_ptr->size_decreases[i]; + total_pins += cache_ptr->pins[i]; + total_unpins += cache_ptr->unpins[i]; + total_dirty_pins += cache_ptr->dirty_pins[i]; + total_pinned_flushes += cache_ptr->pinned_flushes[i]; + total_pinned_clears += cache_ptr->pinned_clears[i]; #if H5C_COLLECT_CACHE_ENTRY_STATS if ( aggregate_max_accesses < cache_ptr->max_accesses[i] ) aggregate_max_accesses = cache_ptr->max_accesses[i]; @@ -4924,6 +5381,8 @@ H5C_stats(H5C_t * cache_ptr, aggregate_max_flushes = cache_ptr->max_flushes[i]; if ( aggregate_max_size < cache_ptr->max_size[i] ) aggregate_max_size = cache_ptr->max_size[i]; + if ( aggregate_max_pins < cache_ptr->max_pins[i] ) + aggregate_max_pins = cache_ptr->max_pins[i]; #endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ } @@ -4998,6 +5457,14 @@ H5C_stats(H5C_t * cache_ptr, (long)(cache_ptr->max_pl_len)); HDfprintf(stdout, + "%s current (max) PEL size / length = %ld (%ld) / %ld (%ld)\n", + cache_ptr->prefix, + (long)(cache_ptr->pel_size), + (long)(cache_ptr->max_pel_size), + (long)(cache_ptr->pel_len), + (long)(cache_ptr->max_pel_len)); + + HDfprintf(stdout, "%s current LRU list size / length = %ld / %ld\n", cache_ptr->prefix, (long)(cache_ptr->LRU_list_size), @@ -5039,6 +5506,18 @@ H5C_stats(H5C_t * cache_ptr, (long)total_size_increases, (long)total_size_decreases); + HDfprintf(stdout, + "%s Total entry pins (dirty) / unpins = %ld (%ld) / %ld\n", + cache_ptr->prefix, + (long)total_pins, + (long)total_dirty_pins, + (long)total_unpins); + + HDfprintf(stdout, "%s Total pinned flushes / clears = %ld / %ld\n", + cache_ptr->prefix, + (long)total_pinned_flushes, + (long)total_pinned_clears); + #if H5C_COLLECT_CACHE_ENTRY_STATS HDfprintf(stdout, "%s aggregate max / min accesses = %d / %d\n", @@ -5051,10 +5530,10 @@ H5C_stats(H5C_t * cache_ptr, (int)aggregate_max_clears, (int)aggregate_max_flushes); - HDfprintf(stdout, "%s aggregate max_size = %d\n", + HDfprintf(stdout, "%s aggregate max_size / max_pins = %d / %d\n", cache_ptr->prefix, - (int)aggregate_max_size); - + (int)aggregate_max_size, + (int)aggregate_max_pins); #endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ @@ -5103,6 +5582,18 @@ H5C_stats(H5C_t * cache_ptr, (long)(cache_ptr->size_increases[i]), (long)(cache_ptr->size_decreases[i])); + HDfprintf(stdout, + "%s entry pins / unpins = %ld / %ld\n", + cache_ptr->prefix, + (long)(cache_ptr->pins[i]), + (long)(cache_ptr->unpins[i])); + + HDfprintf(stdout, + "%s entry dirty pins/pin'd flushes = %ld / %ld\n", + cache_ptr->prefix, + (long)(cache_ptr->dirty_pins[i]), + (long)(cache_ptr->pinned_flushes[i])); + #if H5C_COLLECT_CACHE_ENTRY_STATS HDfprintf(stdout, @@ -5118,9 +5609,10 @@ H5C_stats(H5C_t * cache_ptr, cache_ptr->max_flushes[i]); HDfprintf(stdout, - "%s entry max_size = %d\n", + "%s entry max_size / max_pins = %d / %d\n", cache_ptr->prefix, - (int)(cache_ptr->max_size[i])); + (int)(cache_ptr->max_size[i]), + (int)(cache_ptr->max_pins[i])); #endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ @@ -5156,6 +5648,9 @@ done: * JRM - 9/8/05 * Updated for size increase / decrease statistics. * + * JRM - 3/20/06 + * Updated for pin / unpin related statistics. + * *------------------------------------------------------------------------- */ @@ -5179,6 +5674,11 @@ H5C_stats__reset(H5C_t * cache_ptr) cache_ptr->flushes[i] = 0; cache_ptr->evictions[i] = 0; cache_ptr->renames[i] = 0; + cache_ptr->pins[i] = 0; + cache_ptr->unpins[i] = 0; + cache_ptr->dirty_pins[i] = 0; + cache_ptr->pinned_flushes[i] = 0; + cache_ptr->pinned_clears[i] = 0; cache_ptr->size_increases[i] = 0; cache_ptr->size_decreases[i] = 0; } @@ -5199,6 +5699,9 @@ H5C_stats__reset(H5C_t * cache_ptr) cache_ptr->max_pl_len = 0; cache_ptr->max_pl_size = (size_t)0; + cache_ptr->max_pel_len = 0; + cache_ptr->max_pel_size = (size_t)0; + #if H5C_COLLECT_CACHE_ENTRY_STATS for ( i = 0; i <= cache_ptr->max_type_id; i++ ) @@ -5208,6 +5711,7 @@ H5C_stats__reset(H5C_t * cache_ptr) cache_ptr->max_clears[i] = 0; cache_ptr->max_flushes[i] = 0; cache_ptr->max_size[i] = (size_t)0; + cache_ptr->max_pins[i] = 0; } #endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ @@ -5219,6 +5723,61 @@ H5C_stats__reset(H5C_t * cache_ptr) /*------------------------------------------------------------------------- + * Function: H5C_unpin_entry() + * + * Purpose: Unpin a cache entry. The entry must be unprotected at + * the time of call, and must be pinned. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 3/22/06 + * + * Modifications: + * + * JRM -- 4/26/06 + * Modified routine to allow it to operate on protected + * entries. + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_unpin_entry(H5C_t * cache_ptr, + void * thing) +{ + herr_t ret_value = SUCCEED; /* Return value */ + H5C_cache_entry_t * entry_ptr; + + FUNC_ENTER_NOAPI(H5C_unpin_entry, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( thing ); + + entry_ptr = (H5C_cache_entry_t *)thing; + + if ( ! ( entry_ptr->is_pinned ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "Entry isn't pinned") + } + + if ( ! ( entry_ptr->is_protected ) ) { + + H5C__UPDATE_RP_FOR_UNPIN(cache_ptr, entry_ptr, FAIL) + } + + entry_ptr->is_pinned = FALSE; + + H5C__UPDATE_STATS_FOR_UNPIN(cache_ptr, entry_ptr) + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_unpin_entry() */ + + +/*------------------------------------------------------------------------- * Function: H5C_unprotect * * Purpose: Undo an H5C_protect() call -- specifically, mark the @@ -5294,6 +5853,10 @@ H5C_stats__reset(H5C_t * cache_ptr) * JRM -- 9/23/05 * Moved the size_changed parameter into flags. * + * JRM -- 3/21/06 + * Unpdated function to pin and unpin entries as directed via + * the new H5C__PIN_ENTRY_FLAG and H5C__UNPIN_ENTRY_FLAG flags. + * *------------------------------------------------------------------------- */ herr_t @@ -5311,6 +5874,8 @@ H5C_unprotect(H5F_t * f, hbool_t dirtied; hbool_t set_flush_marker; hbool_t size_changed; + hbool_t pin_entry; + hbool_t unpin_entry; #ifdef H5_HAVE_PARALLEL hbool_t clear_entry = FALSE; #endif /* H5_HAVE_PARALLEL */ @@ -5324,6 +5889,8 @@ H5C_unprotect(H5F_t * f, dirtied = ( (flags & H5C__DIRTIED_FLAG) != 0 ); set_flush_marker = ( (flags & H5C__SET_FLUSH_MARKER_FLAG) != 0 ); size_changed = ( (flags & H5C__SIZE_CHANGED_FLAG) != 0 ); + pin_entry = ( (flags & H5C__PIN_ENTRY_FLAG) != 0 ); + unpin_entry = ( (flags & H5C__UNPIN_ENTRY_FLAG) != 0 ); HDassert( cache_ptr ); HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); @@ -5336,6 +5903,7 @@ H5C_unprotect(H5F_t * f, HDassert( ( size_changed == TRUE ) || ( size_changed == FALSE ) ); HDassert( ( ! size_changed ) || ( dirtied ) ); HDassert( ( ! size_changed ) || ( new_size > 0 ) ); + HDassert( ! ( pin_entry && unpin_entry ) ); entry_ptr = (H5C_cache_entry_t *)thing; @@ -5415,6 +5983,32 @@ H5C_unprotect(H5F_t * f, entry_ptr->size = new_size; } + /* Pin or unpin the entry as requested. */ + if ( pin_entry ) { + + if ( entry_ptr->is_pinned ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, \ + "Entry already pinned???") + } + entry_ptr->is_pinned = TRUE; + H5C__UPDATE_STATS_FOR_PIN(cache_ptr, entry_ptr) + + } else if ( unpin_entry ) { + + if ( ! ( entry_ptr->is_pinned ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, \ + "Entry already unpinned???") + } + entry_ptr->is_pinned = FALSE; + H5C__UPDATE_STATS_FOR_UNPIN(cache_ptr, entry_ptr) + + } + + /* H5C__UPDATE_RP_FOR_UNPROTECT will places the unprotected entry on + * the pinned entry list if entry_ptr->is_pined is TRUE. + */ H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, FAIL) entry_ptr->is_protected = FALSE; @@ -5451,6 +6045,9 @@ H5C_unprotect(H5F_t * f, */ hbool_t dummy_first_flush = TRUE; + /* we can't delete a pinned entry */ + HDassert ( ! (entry_ptr->is_pinned ) ); + /* verify that the target entry is in the cache. */ H5C__SEARCH_INDEX(cache_ptr, addr, test_entry_ptr, FAIL) @@ -6837,6 +7434,314 @@ done: /*------------------------------------------------------------------------- + * Function: H5C_flush_invalidate_cache + * + * Purpose: Flush and destroy the entries contained in the target + * cache. + * + * If the cache contains protected entries, the function will + * fail, as protected entries cannot be either flushed or + * destroyed. However all unprotected entries should be + * flushed and destroyed before the function returns failure. + * + * While pinned entries can usually be flushed, they cannot + * be destroyed. However, they should be unpinned when all + * the entries that reference them have been destroyed (thus + * reduding the pinned entry's reference count to 0, allowing + * it to be unpinned). + * + * If pinned entries are present, the function makes repeated + * passes through the cache, flushing all dirty entries + * (including the pinned dirty entries where permitted) and + * destroying all unpinned entries. This process is repeated + * until either the cache is empty, or the number of pinned + * entries stops decreasing on each pass. + * + * The primary_dxpl_id and secondary_dxpl_id parameters + * specify the dxpl_ids used on the first write occasioned + * by the flush (primary_dxpl_id), and on all subsequent + * writes (secondary_dxpl_id). + * + * Return: Non-negative on success/Negative on failure or if there was + * a request to flush all items and something was protected. + * + * Programmer: John Mainzer + * 3/24/065 + * + * Modifications: + * + * None. + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_flush_invalidate_cache(H5F_t * f, + hid_t primary_dxpl_id, + hid_t secondary_dxpl_id, + H5C_t * cache_ptr, + unsigned flags) +{ + herr_t status; + herr_t ret_value = SUCCEED; + hbool_t first_flush = TRUE; + hbool_t first_pass = TRUE; + hbool_t have_pinned_entries; + int32_t protected_entries = 0; + int32_t i; + int32_t cur_pel_len; + int32_t old_pel_len; + unsigned cooked_flags; + H5SL_node_t * node_ptr = NULL; + H5C_cache_entry_t * entry_ptr = NULL; + H5C_cache_entry_t * next_entry_ptr = NULL; +#if H5C_DO_SANITY_CHECKS + int32_t actual_slist_len = 0; + int32_t initial_slist_len = 0; + size_t actual_slist_size = 0; + size_t initial_slist_size = 0; +#endif /* H5C_DO_SANITY_CHECKS */ + + FUNC_ENTER_NOAPI(H5C_flush_invalidate_cache, FAIL) + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( cache_ptr->skip_file_checks || f ); + HDassert( cache_ptr->slist_ptr ); + + /* Filter out the flags that are not relevant to the flush/invalidate. + * At present, only the H5C__FLUSH_CLEAR_ONLY_FLAG is kept. + */ + cooked_flags = flags & H5C__FLUSH_CLEAR_ONLY_FLAG; + + /* remove ageout markers if present */ + if ( cache_ptr->epoch_markers_active > 0 ) { + + status = H5C__autoadjust__ageout__remove_all_markers(cache_ptr); + + if ( status != SUCCEED ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "error removing all epoch markers.") + } + } + + cur_pel_len = cache_ptr->pel_len; + old_pel_len = cache_ptr->pel_len; + + while ( ( first_pass ) || + ( ( cur_pel_len < old_pel_len ) && ( protected_entries == 0 ) ) ) + { + have_pinned_entries = ( cur_pel_len > 0 ); + + /* first, try to flush-destroy any dirty entries */ + + if ( cache_ptr->slist_len == 0 ) { + + node_ptr = NULL; + HDassert( cache_ptr->slist_size == 0 ); + + } else { + + node_ptr = H5SL_first(cache_ptr->slist_ptr); + +#if H5C_DO_SANITY_CHECKS + /* Depending on circumstances, H5C_flush_single_entry() will + * remove dirty entries from the slist as it flushes them. + * Thus for sanity checks we must make note of the initial + * slist length and size before we do any flushes. + */ + initial_slist_len = cache_ptr->slist_len; + initial_slist_size = cache_ptr->slist_size; +#endif /* H5C_DO_SANITY_CHECKS */ + + } + + while ( node_ptr != NULL ) + { + /* Note that we now remove nodes from the slist as we flush + * the associated entries, instead of leaving them there + * until we are done, and then destroying all nodes in + * the slist. + * + * While this optimization is still easy if everything works, + * the addition of pinned entries and multiple passes + * through the cache to allow entries to unpin themselves + * complicates error recover greatly. + * + * Given these complications, I've decided to ommit this + * this optimization for now. It can be re-implemented + * later if needed. + */ + + entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr); + + /* increment node pointer now, before we delete its target + * from the slist. + */ + node_ptr = H5SL_next(node_ptr); + + HDassert( entry_ptr != NULL ); + HDassert( entry_ptr->in_slist ); + +#if H5C_DO_SANITY_CHECKS + actual_slist_len++; + actual_slist_size += entry_ptr->size; +#endif /* H5C_DO_SANITY_CHECKS */ + + if ( entry_ptr->is_protected ) { + + /* we have major problems -- but lets flush + * everything we can before we flag an error. + */ + protected_entries++; + + } else if ( entry_ptr->is_pinned ) { + + /* Test to see if we are can flush the entry now. + * If we can, go ahead and flush, but don't tell + * H5C_flush_single_entry() to destroy the entry + * as pinned entries can't be evicted. + */ + if ( TRUE ) { /* insert test here */ /* JRM */ + + + status = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + NULL, + entry_ptr->addr, + H5C__NO_FLAGS_SET, + &first_flush, + TRUE); + if ( status < 0 ) { + + /* This shouldn't happen -- if it does, we are toast + * so just scream and die. + */ + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "dirty pinned entry flush failed.") + } + } + } else { + + status = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + NULL, + entry_ptr->addr, + (cooked_flags | + H5C__FLUSH_INVALIDATE_FLAG), + &first_flush, + TRUE); + if ( status < 0 ) { + + /* This shouldn't happen -- if it does, we are toast so + * just scream and die. + */ + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "dirty entry flush destroy failed.") + } + } + } /* end while loop scanning skip list */ + +#if H5C_DO_SANITY_CHECKS + HDassert( actual_slist_len == initial_slist_len ); + HDassert( actual_slist_size == initial_slist_size ); +#endif /* H5C_DO_SANITY_CHECKS */ + + /* Since we are doing a destroy, we must make a pass through + * the hash table and try to flush - destroy all entries that + * remain. Note that all remaining entries entries must be + * clean, so this will not result in any writes to disk. + */ + for ( i = 0; i < H5C__HASH_TABLE_LEN; i++ ) + { + next_entry_ptr = cache_ptr->index[i]; + + while ( next_entry_ptr != NULL ) + { + entry_ptr = next_entry_ptr; + next_entry_ptr = entry_ptr->ht_next; + + if ( entry_ptr->is_protected ) { + + /* we have major problems -- but lets flush and destroy + * everything we can before we flag an error. + */ + + if ( ! entry_ptr->in_slist ) { + + protected_entries++; + HDassert( !(entry_ptr->is_dirty) ); + } + } else if ( ! ( entry_ptr->is_pinned ) ) { + + HDassert( !(entry_ptr->is_dirty) ); + HDassert( !(entry_ptr->in_slist) ); + + status = H5C_flush_single_entry(f, + primary_dxpl_id, + secondary_dxpl_id, + cache_ptr, + NULL, + entry_ptr->addr, + (cooked_flags | + H5C__FLUSH_INVALIDATE_FLAG), + &first_flush, + TRUE); + if ( status < 0 ) { + + /* This shouldn't happen -- if it does, we are toast so + * just scream and die. + */ + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "Clean entry flush destroy failed.") + } + } + /* We can't do anything if the entry is pinned. The + * hope is that the entry will be unpinned as the + * result of destroys of entries that reference it. + * + * We detect this by noting the change in the number + * of pinned entries from pass to pass. If it stops + * shrinking before it hits zero, we scream and die. + */ + } /* end while loop scanning hash table bin */ + } /* end for loop scanning hash table */ + + HDassert( protected_entries == cache_ptr->pl_len ); + + old_pel_len = cur_pel_len; + cur_pel_len = cache_ptr->pel_len; + + first_pass = FALSE; + + } /* main while loop */ + + HDassert( protected_entries <= cache_ptr->pl_len ); + + if ( protected_entries > 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "Cache has protected entries.") + + } else if ( cur_pel_len > 0 ) { + + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \ + "Can't unpin all pinned entries.") + + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_flush_invalidate_cache() */ + + +/*------------------------------------------------------------------------- * * Function: H5C_flush_single_entry * diff --git a/src/H5Cpkg.h b/src/H5Cpkg.h index 9a3cdcd..8d9a5a6 100644 --- a/src/H5Cpkg.h +++ b/src/H5Cpkg.h @@ -233,6 +233,55 @@ * This field is NULL if the list is empty. * * + * For very frequently used entries, the protect/unprotect overhead can + * become burdensome. To avoid this overhead, I have modified the cache + * to allow entries to be "pinned". A pinned entry is similar to a + * protected entry, in the sense that it cannot be evicted, and that + * the entry can be modified at any time. + * + * Pinning an entry has the following implications: + * + * 1) A pinned entry cannot be evicted. Thus unprotected + * pinned entries reside in the pinned entry list, instead + * of the LRU list(s) (or other lists maintained by the current + * replacement policy code). + * + * 2) A pinned entry can be accessed or modified at any time. + * Therefore, the cache must check with the entry owner + * before flushing it. If permission is denied, the + * cache just skips the entry in the flush. + * + * 3) A pinned entry can be marked as dirty (and possibly + * change size) while it is unprotected. + * + * 4) The flush-destroy code must allow pinned entries to + * be unpinned (and possibly unprotected) during the + * flush. + * + * Since pinned entries cannot be evicted, they must be kept on a pinned + * entry list, instead of being entrusted to the replacement policy code. + * + * Maintaining the pinned entry list requires the following fields: + * + * pel_len: Number of entries currently residing on the pinned + * entry list. + * + * pel_size: Number of bytes of cache entries currently residing on + * the pinned entry list. + * + * pel_head_ptr: Pointer to the head of the doubly linked list of pinned + * but not protected entries. Note that cache entries on + * this list are linked by their next and prev fields. + * + * This field is NULL if the list is empty. + * + * pel_tail_ptr: Pointer to the tail of the doubly linked list of pinned + * but not protected entries. Note that cache entries on + * this list are linked by their next and prev fields. + * + * This field is NULL if the list is empty. + * + * * The cache must have a replacement policy, and the fields supporting this * policy must be accessible from this structure. * @@ -504,6 +553,32 @@ * id equal to the array index has been renamed in the current * epoch. * + * pins: Array of int64 of length H5C__MAX_NUM_TYPE_IDS + 1. The cells + * are used to record the number of times an entry with type + * id equal to the array index has been pinned in the current + * epoch. + * + * unpins: Array of int64 of length H5C__MAX_NUM_TYPE_IDS + 1. The cells + * are used to record the number of times an entry with type + * id equal to the array index has been unpinned in the current + * epoch. + * + * dirty_pins: Array of int64 of length H5C__MAX_NUM_TYPE_IDS + 1. The cells + * are used to record the number of times an entry with type + * id equal to the array index has been marked dirty while pinned + * in the current epoch. + * + * pinned_flushes: Array of int64 of length H5C__MAX_NUM_TYPE_IDS + 1. The + * cells are used to record the number of times an entry + * with type id equal to the array index has been flushed while + * pinned in the current epoch. + * + * pinned_cleared: Array of int64 of length H5C__MAX_NUM_TYPE_IDS + 1. The + * cells are used to record the number of times an entry + * with type id equal to the array index has been cleared while + * pinned in the current epoch. + * + * * size_increases: Array of int64 of length H5C__MAX_NUM_TYPE_IDS + 1. * The cells are used to record the number of times an entry * with type id equal to the array index has increased in @@ -552,6 +627,12 @@ * max_pl_size: Largest value attained by the pl_size field in the * current epoch. * + * max_pel_len: Largest value attained by the pel_len field in the + * current epoch. + * + * max_pel_size: Largest value attained by the pel_size field in the + * current epoch. + * * The remaining stats are collected only when both H5C_COLLECT_CACHE_STATS * and H5C_COLLECT_CACHE_ENTRY_STATS are true. * @@ -580,6 +661,11 @@ * with type id equal to the array index that has resided in * the cache in the current epoch. * + * max_pins: Array of size_t of length H5C__MAX_NUM_TYPE_IDS + 1. The cells + * are used to record the maximum number of times that any single + * entry with type id equal to the array index that has been + * marked as pinned in the cache in the current epoch. + * * * Fields supporting testing: * @@ -644,6 +730,11 @@ struct H5C_t H5C_cache_entry_t * pl_head_ptr; H5C_cache_entry_t * pl_tail_ptr; + int32_t pel_len; + size_t pel_size; + H5C_cache_entry_t * pel_head_ptr; + H5C_cache_entry_t * pel_tail_ptr; + int32_t LRU_list_len; size_t LRU_list_size; H5C_cache_entry_t * LRU_head_ptr; @@ -687,6 +778,11 @@ struct H5C_t int64_t flushes[H5C__MAX_NUM_TYPE_IDS + 1]; int64_t evictions[H5C__MAX_NUM_TYPE_IDS + 1]; int64_t renames[H5C__MAX_NUM_TYPE_IDS + 1]; + int64_t pins[H5C__MAX_NUM_TYPE_IDS + 1]; + int64_t unpins[H5C__MAX_NUM_TYPE_IDS + 1]; + int64_t dirty_pins[H5C__MAX_NUM_TYPE_IDS + 1]; + int64_t pinned_flushes[H5C__MAX_NUM_TYPE_IDS + 1]; + int64_t pinned_clears[H5C__MAX_NUM_TYPE_IDS + 1]; int64_t size_increases[H5C__MAX_NUM_TYPE_IDS + 1]; int64_t size_decreases[H5C__MAX_NUM_TYPE_IDS + 1]; @@ -703,10 +799,12 @@ struct H5C_t int32_t max_slist_len; size_t max_slist_size; - int32_t max_pl_len; size_t max_pl_size; + int32_t max_pel_len; + size_t max_pel_size; + #if H5C_COLLECT_CACHE_ENTRY_STATS int32_t max_accesses[H5C__MAX_NUM_TYPE_IDS + 1]; @@ -714,6 +812,7 @@ struct H5C_t int32_t max_clears[H5C__MAX_NUM_TYPE_IDS + 1]; int32_t max_flushes[H5C__MAX_NUM_TYPE_IDS + 1]; size_t max_size[H5C__MAX_NUM_TYPE_IDS + 1]; + int32_t max_pins[H5C__MAX_NUM_TYPE_IDS + 1]; #endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ diff --git a/src/H5Cprivate.h b/src/H5Cprivate.h index c7d0313..43a93b3 100644 --- a/src/H5Cprivate.h +++ b/src/H5Cprivate.h @@ -243,11 +243,42 @@ typedef herr_t (*H5C_log_flush_func_t)(H5C_t * cache_ptr, * Note that protected entries are removed from the LRU lists * and inserted on the protected list. * + * is_pinned: Boolean flag indicating whether the entry has been pinned + * in the cache. + * + * For very hot entries, the protect / unprotect overhead + * can become excessive. Thus the cache has been extended + * to allow an entry to be "pinned" in the cache. + * + * Pinning an entry in the cache has several implications: + * + * 1) A pinned entry cannot be evicted. Thus unprotected + * pinned entries must be stored in the pinned entry + * list, instead of being managed by the replacement + * policy code (LRU at present). + * + * 2) A pinned entry can be accessed or modified at any time. + * Therefore, the cache must check with the entry owner + * before flushing it. If permission is denied, the + * cache does not flush the entry. + * + * 3) A pinned entry can be marked as dirty (and possibly + * change size) while it is unprotected. + * + * 4) The flush-destroy code must allow pinned entries to + * be unpinned (and possibly unprotected) during the + * flush. + * + * JRM -- 3/16/06 + * * in_slist: Boolean flag indicating whether the entry is in the skip list * As a general rule, entries are placed in the list when they * are marked dirty. However they may remain in the list after * being flushed. * + * Update: Dirty entries are now removed from the skip list + * when they are flushed. + * * flush_marker: Boolean flag indicating that the entry is to be flushed * the next time H5C_flush_cache() is called with the * H5AC__FLUSH_MARKED_ENTRIES_FLAG. The flag is reset when @@ -359,6 +390,9 @@ typedef herr_t (*H5C_log_flush_func_t)(H5C_t * cache_ptr, * flushes: int32_t containing the number of times this cache entry has * been flushed to file in its life time. * + * pins: int32_t containing the number of times this cache entry has + * been pinned in cache in its life time. + * ****************************************************************************/ typedef struct H5C_cache_entry_t @@ -368,6 +402,7 @@ typedef struct H5C_cache_entry_t const H5C_class_t * type; hbool_t is_dirty; hbool_t is_protected; + hbool_t is_pinned; hbool_t in_slist; hbool_t flush_marker; #ifdef H5_HAVE_PARALLEL @@ -393,6 +428,7 @@ typedef struct H5C_cache_entry_t int32_t accesses; int32_t clears; int32_t flushes; + int32_t pins; #endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ @@ -708,16 +744,18 @@ typedef struct H5C_auto_size_ctl_t /* These flags applies only to H5C_unprotect() */ #define H5C__DIRTIED_FLAG 0x0004 #define H5C__SIZE_CHANGED_FLAG 0x0008 +#define H5C__PIN_ENTRY_FLAG 0x0010 +#define H5C__UNPIN_ENTRY_FLAG 0x0020 /* These flags apply to H5C_flush_cache() & H5C_flush_single_entry() */ -#define H5C__FLUSH_INVALIDATE_FLAG 0x0010 -#define H5C__FLUSH_CLEAR_ONLY_FLAG 0x0020 -#define H5C__FLUSH_MARKED_ENTRIES_FLAG 0x0040 +#define H5C__FLUSH_INVALIDATE_FLAG 0x0040 +#define H5C__FLUSH_CLEAR_ONLY_FLAG 0x0080 +#define H5C__FLUSH_MARKED_ENTRIES_FLAG 0x0100 /* This flag applies to H5C_flush_cache() only. It is an error to use * it in combination with the H5C__FLUSH_INVALIDATE_FLAG */ -#define H5C__FLUSH_IGNORE_PROTECTED_FLAG 0x0080 +#define H5C__FLUSH_IGNORE_PROTECTED_FLAG 0x0200 H5_DLL H5C_t * H5C_create(size_t max_cache_size, @@ -773,7 +811,8 @@ H5_DLL herr_t H5C_get_entry_status(H5C_t * cache_ptr, size_t * size_ptr, hbool_t * in_cache_ptr, hbool_t * is_dirty_ptr, - hbool_t * is_protected_ptr); + hbool_t * is_protected_ptr, + hbool_t * is_pinned_ptr); H5_DLL herr_t H5C_insert_entry(H5F_t * f, hid_t primary_dxpl_id, @@ -791,11 +830,19 @@ H5_DLL herr_t H5C_mark_entries_as_clean(H5F_t * f, int32_t ce_array_len, haddr_t * ce_array_ptr); +H5_DLL herr_t H5C_mark_pinned_entry_dirty(H5C_t * cache_ptr, + void * thing, + hbool_t size_changed, + size_t new_size); + H5_DLL herr_t H5C_rename_entry(H5C_t * cache_ptr, const H5C_class_t * type, haddr_t old_addr, haddr_t new_addr); +H5_DLL herr_t H5C_pin_protected_entry(H5C_t * cache_ptr, + void * thing); + H5_DLL void * H5C_protect(H5F_t * f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, @@ -822,6 +869,8 @@ H5_DLL herr_t H5C_stats(H5C_t * cache_ptr, H5_DLL void H5C_stats__reset(H5C_t * cache_ptr); +H5_DLL herr_t H5C_unpin_entry(H5C_t * cache_ptr, void * thing); + H5_DLL herr_t H5C_unprotect(H5F_t * f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, diff --git a/src/H5Edefin.h b/src/H5Edefin.h index 1bdd4ba..75dc2ce 100644 --- a/src/H5Edefin.h +++ b/src/H5Edefin.h @@ -144,6 +144,9 @@ hid_t H5E_CANTINS_g = FAIL; /* Unable to insert metadata into cache hid_t H5E_CANTRENAME_g = FAIL; /* Unable to rename metadata */ hid_t H5E_CANTPROTECT_g = FAIL; /* Unable to protect metadata */ hid_t H5E_CANTUNPROTECT_g = FAIL; /* Unable to unprotect metadata */ +hid_t H5E_CANTPIN_g = FAIL; /* Unable to pin cache entry */ +hid_t H5E_CANTUNPIN_g = FAIL; /* Unable to un-pin cache entry */ +hid_t H5E_CANTMARKDIRTY_g = FAIL; /* Unable to mark a pinned entry as dirty */ hid_t H5E_CANTDIRTY_g = FAIL; /* Unable to mark metadata as dirty */ /* Parallel MPI errors */ diff --git a/src/H5Einit.h b/src/H5Einit.h index c35cab5..ee1a5cf 100644 --- a/src/H5Einit.h +++ b/src/H5Einit.h @@ -534,6 +534,21 @@ if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to unprotect metadata"))==NULL) HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") if((H5E_CANTUNPROTECT_g = H5I_register(H5I_ERROR_MSG, msg))<0) HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") +assert(H5E_CANTPIN_g==(-1)); +if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to pin cache entry"))==NULL) + HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") +if((H5E_CANTPIN_g = H5I_register(H5I_ERROR_MSG, msg))<0) + HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") +assert(H5E_CANTUNPIN_g==(-1)); +if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to un-pin cache entry"))==NULL) + HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") +if((H5E_CANTUNPIN_g = H5I_register(H5I_ERROR_MSG, msg))<0) + HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") +assert(H5E_CANTMARKDIRTY_g==(-1)); +if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to mark a pinned entry as dirty"))==NULL) + HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") +if((H5E_CANTMARKDIRTY_g = H5I_register(H5I_ERROR_MSG, msg))<0) + HGOTO_ERROR(H5E_ERROR, H5E_CANTREGISTER, FAIL, "can't register error message") assert(H5E_CANTDIRTY_g==(-1)); if((msg = H5E_create_msg(cls, H5E_MINOR, "Unable to mark metadata as dirty"))==NULL) HGOTO_ERROR(H5E_ERROR, H5E_CANTINIT, FAIL, "error message initialization failed") diff --git a/src/H5Epubgen.h b/src/H5Epubgen.h index 7d9e284..a05e5ed 100644 --- a/src/H5Epubgen.h +++ b/src/H5Epubgen.h @@ -235,6 +235,9 @@ H5_DLLVAR hid_t H5E_NOIDS_g; /* Out of IDs for group */ #define H5E_CANTRENAME (H5OPEN H5E_CANTRENAME_g) #define H5E_CANTPROTECT (H5OPEN H5E_CANTPROTECT_g) #define H5E_CANTUNPROTECT (H5OPEN H5E_CANTUNPROTECT_g) +#define H5E_CANTPIN (H5OPEN H5E_CANTPIN_g) +#define H5E_CANTUNPIN (H5OPEN H5E_CANTUNPIN_g) +#define H5E_CANTMARKDIRTY (H5OPEN H5E_CANTMARKDIRTY_g) #define H5E_CANTDIRTY (H5OPEN H5E_CANTDIRTY_g) H5_DLLVAR hid_t H5E_CANTFLUSH_g; /* Unable to flush data from cache */ H5_DLLVAR hid_t H5E_CANTSERIALIZE_g; /* Unable to serialize data from cache */ @@ -246,6 +249,9 @@ H5_DLLVAR hid_t H5E_CANTINS_g; /* Unable to insert metadata into cache */ H5_DLLVAR hid_t H5E_CANTRENAME_g; /* Unable to rename metadata */ H5_DLLVAR hid_t H5E_CANTPROTECT_g; /* Unable to protect metadata */ H5_DLLVAR hid_t H5E_CANTUNPROTECT_g; /* Unable to unprotect metadata */ +H5_DLLVAR hid_t H5E_CANTPIN_g; /* Unable to pin cache entry */ +H5_DLLVAR hid_t H5E_CANTUNPIN_g; /* Unable to un-pin cache entry */ +H5_DLLVAR hid_t H5E_CANTMARKDIRTY_g; /* Unable to mark a pinned entry as dirty */ H5_DLLVAR hid_t H5E_CANTDIRTY_g; /* Unable to mark metadata as dirty */ /* Parallel MPI errors */ diff --git a/src/H5Eterm.h b/src/H5Eterm.h index d7afed8..f2968f1 100644 --- a/src/H5Eterm.h +++ b/src/H5Eterm.h @@ -146,6 +146,9 @@ H5E_CANTINS_g= H5E_CANTRENAME_g= H5E_CANTPROTECT_g= H5E_CANTUNPROTECT_g= +H5E_CANTPIN_g= +H5E_CANTUNPIN_g= +H5E_CANTMARKDIRTY_g= H5E_CANTDIRTY_g= /* Parallel MPI errors */ diff --git a/src/H5err.txt b/src/H5err.txt index bd3e06a..014e7a7 100644 --- a/src/H5err.txt +++ b/src/H5err.txt @@ -155,6 +155,9 @@ MINOR, CACHE, H5E_CANTINS, Unable to insert metadata into cache MINOR, CACHE, H5E_CANTRENAME, Unable to rename metadata MINOR, CACHE, H5E_CANTPROTECT, Unable to protect metadata MINOR, CACHE, H5E_CANTUNPROTECT, Unable to unprotect metadata +MINOR, CACHE, H5E_CANTPIN, Unable to pin cache entry +MINOR, CACHE, H5E_CANTUNPIN, Unable to un-pin cache entry +MINOR, CACHE, H5E_CANTMARKDIRTY, Unable to mark a pinned entry as dirty MINOR, CACHE, H5E_CANTDIRTY, Unable to mark metadata as dirty # B-tree related errors |