diff options
Diffstat (limited to 'src')
40 files changed, 7934 insertions, 501 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 510db1e..b7cfeb2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -90,8 +90,10 @@ set (H5C_SOURCES ${HDF5_SRC_DIR}/H5C.c ${HDF5_SRC_DIR}/H5Cdbg.c ${HDF5_SRC_DIR}/H5Cepoch.c + ${HDF5_SRC_DIR}/H5Cimage.c ${HDF5_SRC_DIR}/H5Clog.c ${HDF5_SRC_DIR}/H5Cmpio.c + ${HDF5_SRC_DIR}/H5Cprefetched.c ${HDF5_SRC_DIR}/H5Cquery.c ${HDF5_SRC_DIR}/H5Ctag.c ${HDF5_SRC_DIR}/H5Ctest.c @@ -439,6 +441,7 @@ set (H5O_SOURCES ${HDF5_SRC_DIR}/H5Obogus.c ${HDF5_SRC_DIR}/H5Obtreek.c ${HDF5_SRC_DIR}/H5Ocache.c + ${HDF5_SRC_DIR}/H5Ocache_image.c ${HDF5_SRC_DIR}/H5Ochunk.c ${HDF5_SRC_DIR}/H5Ocont.c ${HDF5_SRC_DIR}/H5Ocopy.c @@ -107,38 +107,43 @@ hbool_t H5_coll_api_sanity_check_g = false; /* Local Variables */ /*******************/ -static const char *H5AC_entry_type_names[H5AC_NTYPES] = -{ - "B-tree nodes", - "symbol table nodes", - "local heap prefixes", - "local heap data blocks", - "global heaps", - "object headers", - "object header chunks", - "v2 B-tree headers", - "v2 B-tree internal nodes", - "v2 B-tree leaf nodes", - "fractal heap headers", - "fractal heap direct blocks", - "fractal heap indirect blocks", - "free space headers", - "free space sections", - "shared OH message master table", - "shared OH message index", - "extensible array headers", - "extensible array index blocks", - "extensible array super blocks", - "extensible array data blocks", - "extensible array data block pages", - "fixed array headers", - "fixed array data block", - "fixed array data block pages", - "superblock", - "driver info", - "epoch marker", /* internal to cache only */ - "proxy entry", - "test entry" /* for testing only -- not used for actual files */ +/* Metadata entry class list */ + +/* Remember to add new type ID to the H5AC_type_t enum in H5ACprivate.h when + * adding a new class. + */ + +static const H5AC_class_t *const H5AC_class_s[] = { + H5AC_BT, /* ( 0) B-tree nodes */ + H5AC_SNODE, /* ( 1) symbol table nodes */ + H5AC_LHEAP_PRFX, /* ( 2) local heap prefix */ + H5AC_LHEAP_DBLK, /* ( 3) local heap data block */ + H5AC_GHEAP, /* ( 4) global heap */ + H5AC_OHDR, /* ( 5) object header */ + H5AC_OHDR_CHK, /* ( 6) object header chunk */ + H5AC_BT2_HDR, /* ( 7) v2 B-tree header */ + H5AC_BT2_INT, /* ( 8) v2 B-tree internal node */ + H5AC_BT2_LEAF, /* ( 9) v2 B-tree leaf node */ + H5AC_FHEAP_HDR, /* (10) fractal heap header */ + H5AC_FHEAP_DBLOCK, /* (11) fractal heap direct block */ + H5AC_FHEAP_IBLOCK, /* (12) fractal heap indirect block */ + H5AC_FSPACE_HDR, /* (13) free space header */ + H5AC_FSPACE_SINFO, /* (14) free space sections */ + H5AC_SOHM_TABLE, /* (15) shared object header message master table */ + H5AC_SOHM_LIST, /* (16) shared message index stored as a list */ + H5AC_EARRAY_HDR, /* (17) extensible array header */ + H5AC_EARRAY_IBLOCK, /* (18) extensible array index block */ + H5AC_EARRAY_SBLOCK, /* (19) extensible array super block */ + H5AC_EARRAY_DBLOCK, /* (20) extensible array data block */ + H5AC_EARRAY_DBLK_PAGE, /* (21) extensible array data block page */ + H5AC_FARRAY_HDR, /* (22) fixed array header */ + H5AC_FARRAY_DBLOCK, /* (23) fixed array data block */ + H5AC_FARRAY_DBLK_PAGE, /* (24) fixed array data block page */ + H5AC_SUPERBLOCK, /* (25) file superblock */ + H5AC_DRVRINFO, /* (26) driver info block (supplements superblock) */ + H5AC_EPOCH_MARKER, /* (27) epoch marker - always internal to cache */ + H5AC_PROXY_ENTRY, /* (28) cache entry proxy */ + H5AC_PREFETCHED_ENTRY /* (29) prefetched entry - always internal to cache */ }; @@ -366,12 +371,13 @@ H5AC_term_package(void) *------------------------------------------------------------------------- */ herr_t -H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr) +H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr, H5AC_cache_image_config_t * image_config_ptr) { #ifdef H5_HAVE_PARALLEL char prefix[H5C__PREFIX_LEN] = ""; H5AC_aux_t * aux_ptr = NULL; #endif /* H5_HAVE_PARALLEL */ + struct H5C_cache_image_ctl_t int_ci_config = H5C__DEFAULT_CACHE_IMAGE_CTL; herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) @@ -380,11 +386,16 @@ H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr) HDassert(f); HDassert(NULL == f->shared->cache); HDassert(config_ptr != NULL) ; - HDcompile_assert(NELMTS(H5AC_entry_type_names) == H5AC_NTYPES); + HDassert(image_config_ptr != NULL) ; + HDassert(image_config_ptr->version == H5AC__CURR_CACHE_IMAGE_CONFIG_VERSION); + HDcompile_assert(NELMTS(H5AC_class_s) == H5AC_NTYPES); HDcompile_assert(H5C__MAX_NUM_TYPE_IDS == H5AC_NTYPES); + /* Validate configurations */ if(H5AC_validate_config(config_ptr) < 0) HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad cache configuration") + if(H5AC_validate_cache_image_config(image_config_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad cache image configuration") #ifdef H5_HAVE_PARALLEL if(H5F_HAS_FEATURE(f, H5FD_FEAT_HAS_MPI)) { @@ -426,6 +437,7 @@ H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr) aux_ptr->candidate_slist_ptr = NULL; aux_ptr->write_done = NULL; aux_ptr->sync_point_done = NULL; + aux_ptr->p0_image_len = 0; sprintf(prefix, "%d:", mpi_rank); @@ -448,19 +460,19 @@ H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr) if(aux_ptr->mpi_rank == 0) f->shared->cache = H5C_create(H5AC__DEFAULT_MAX_CACHE_SIZE, H5AC__DEFAULT_MIN_CLEAN_SIZE, (H5AC_NTYPES - 1), - (const char **)H5AC_entry_type_names, + H5AC_class_s, H5AC__check_if_write_permitted, TRUE, H5AC__log_flushed_entry, (void *)aux_ptr); else f->shared->cache = H5C_create(H5AC__DEFAULT_MAX_CACHE_SIZE, H5AC__DEFAULT_MIN_CLEAN_SIZE, (H5AC_NTYPES - 1), - (const char **)H5AC_entry_type_names, + H5AC_class_s, H5AC__check_if_write_permitted, TRUE, NULL, (void *)aux_ptr); else f->shared->cache = H5C_create(H5AC__DEFAULT_MAX_CACHE_SIZE, H5AC__DEFAULT_MIN_CLEAN_SIZE, (H5AC_NTYPES - 1), - (const char **)H5AC_entry_type_names, + H5AC_class_s, H5AC__check_if_write_permitted, TRUE, NULL, NULL); } /* end if */ else { @@ -471,7 +483,7 @@ H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr) */ f->shared->cache = H5C_create(H5AC__DEFAULT_MAX_CACHE_SIZE, H5AC__DEFAULT_MIN_CLEAN_SIZE, (H5AC_NTYPES - 1), - (const char **)H5AC_entry_type_names, + H5AC_class_s, H5AC__check_if_write_permitted, TRUE, NULL, NULL); #ifdef H5_HAVE_PARALLEL } /* end else */ @@ -500,6 +512,20 @@ H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr) if(H5AC_set_cache_auto_resize_config(f->shared->cache, config_ptr) < 0) HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "auto resize configuration failed") + /* don't need to get the current H5C image config here since the + * cache has just been created, and thus f->shared->cache->image_ctl + * must still set to its initial value (H5C__DEFAULT_CACHE_IMAGE_CTL). + * Note that this not true as soon as control returns to the application + * program, as some test code modifies f->shared->cache->image_ctl. + */ + int_ci_config.version = image_config_ptr->version; + int_ci_config.generate_image = image_config_ptr->generate_image; + int_ci_config.save_resize_status = image_config_ptr->save_resize_status; + int_ci_config.entry_ageout = image_config_ptr->entry_ageout; + + if(H5C_set_cache_image_config(f, f->shared->cache, &int_ci_config) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "auto resize configuration failed") + done: #ifdef H5_HAVE_PARALLEL /* if there is a failure, try to tidy up the auxilary structure */ @@ -978,6 +1004,41 @@ done: /*------------------------------------------------------------------------- + * Function: H5AC_load_cache_image_on_next_protect + * + * Purpose: Load the cache image block at the specified location, + * decode it, and insert its contents into the metadata + * cache. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 7/6/15 + * + *------------------------------------------------------------------------- + */ +herr_t +H5AC_load_cache_image_on_next_protect(H5F_t * f, haddr_t addr, hsize_t len, + hbool_t rw) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(f->shared->cache); + + if(H5C_load_cache_image_on_next_protect(f, addr, len, rw) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, FAIL, "call to H5C_load_cache_image_on_next_protect failed") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5AC_load_cache_image_on_next_protect() */ + + +/*------------------------------------------------------------------------- * Function: H5AC_mark_entry_dirty * * Purpose: Mark a pinned or protected entry as dirty. The target @@ -2094,7 +2155,7 @@ done: */ herr_t H5AC_get_cache_size(H5AC_t *cache_ptr, size_t *max_size_ptr, size_t *min_clean_size_ptr, - size_t *cur_size_ptr, int32_t *cur_num_entries_ptr) + size_t *cur_size_ptr, uint32_t *cur_num_entries_ptr) { herr_t ret_value = SUCCEED; /* Return value */ @@ -2391,6 +2452,61 @@ done: /*------------------------------------------------------------------------- + * Function: H5AC_validate_cache_image_config() + * + * Purpose: Run a sanity check on the contents of the supplied + * instance of H5AC_cache_image_config_t. + * + * Do nothing and return SUCCEED if no errors are detected, + * and flag an error and return FAIL otherwise. + * + * At present, this function operates by packing the data + * from the instance of H5AC_cache_image_config_t into an + * instance of H5C_cache_image_ctl_t, and then calling + * H5C_validate_cache_image_config(). If and when + * H5AC_cache_image_config_t and H5C_cache_image_ctl_t + * diverge, we may have to change this. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/25/15 + * + *------------------------------------------------------------------------- + */ +herr_t +H5AC_validate_cache_image_config(H5AC_cache_image_config_t *config_ptr) +{ + H5C_cache_image_ctl_t internal_config = H5C__DEFAULT_CACHE_IMAGE_CTL; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + /* Check args */ + if(config_ptr == NULL) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "NULL config_ptr on entry") + + if(config_ptr->version != H5AC__CURR_CACHE_IMAGE_CONFIG_VERSION) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Unknown image config version") + + /* don't need to get the current H5C image config here since the + * default values of fields not in the H5AC config will always be + * valid. + */ + internal_config.version = config_ptr->version; + internal_config.generate_image = config_ptr->generate_image; + internal_config.save_resize_status = config_ptr->save_resize_status; + internal_config.entry_ageout = config_ptr->entry_ageout; + + if(H5C_validate_cache_image_config(&internal_config) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "error(s) in new cache image config") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5AC_validate_cache_image_config() */ + + +/*------------------------------------------------------------------------- * * Function: H5AC__check_if_write_permitted * diff --git a/src/H5ACdbg.c b/src/H5ACdbg.c index 8d99c6f..e2f7b13 100644 --- a/src/H5ACdbg.c +++ b/src/H5ACdbg.c @@ -252,6 +252,207 @@ done: /*------------------------------------------------------------------------- * + * Function: H5AC_get_entry_ptr_from_addr() + * + * Purpose: Debugging function that attempts to look up an entry in the + * cache by its file address, and if found, returns a pointer + * to the entry in *entry_ptr_ptr. If the entry is not in the + * cache, *entry_ptr_ptr is set to NULL. + * + * WARNING: This call should be used only in debugging + * routines, and it should be avoided when + * possible. + * + * Further, if we ever multi-thread the cache, + * this routine will have to be either discarded + * or heavily re-worked. + * + * Finally, keep in mind that the entry whose + * pointer is obtained in this fashion may not + * be in a stable state. + * + * Note that this function is only defined if NDEBUG + * is not defined. + * + * As heavy use of this function is almost certainly a + * bad idea, the metadata cache tracks the number of + * successful calls to this function, and (if + * H5C_DO_SANITY_CHECKS is defined) displays any + * non-zero count on cache shutdown. + * + * This function is just a wrapper that calls the H5C + * version of the function. + * + * Return: FAIL if error is detected, SUCCEED otherwise. + * + * Programmer: John Mainzer, 5/30/14 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +herr_t +H5AC_get_entry_ptr_from_addr(const H5F_t *f, haddr_t addr, void **entry_ptr_ptr) +{ + H5C_t *cache_ptr; /* Ptr to cache */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + cache_ptr = f->shared->cache; + + if(H5C_get_entry_ptr_from_addr(cache_ptr, addr, entry_ptr_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C_get_entry_ptr_from_addr() failed") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5AC_get_entry_ptr_from_addr() */ +#endif /* NDEBUG */ + + +/*------------------------------------------------------------------------- + * Function: H5AC_flush_dependency_exists() + * + * Purpose: Test to see if a flush dependency relationship exists + * between the supplied parent and child. Both parties + * are indicated by addresses so as to avoid the necessity + * of protect / unprotect calls prior to this call. + * + * If either the parent or the child is not in the metadata + * cache, the function sets *fd_exists_ptr to FALSE. + * + * If both are in the cache, the childs list of parents is + * searched for the proposed parent. If the proposed parent + * is found in the childs parent list, the function sets + * *fd_exists_ptr to TRUE. In all other non-error cases, + * the function sets *fd_exists_ptr FALSE. + * + * Return: SUCCEED on success/FAIL on failure. Note that + * *fd_exists_ptr is undefined on failure. + * + * Programmer: John Mainzer + * 9/28/16 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +herr_t +H5AC_flush_dependency_exists(H5F_t *f, haddr_t parent_addr, haddr_t child_addr, + hbool_t *fd_exists_ptr) +{ + H5C_t *cache_ptr; /* Ptr to cache */ + herr_t ret_value = FAIL; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT_NOERR + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + cache_ptr = f->shared->cache; + + ret_value = H5C_flush_dependency_exists(cache_ptr, parent_addr, child_addr, fd_exists_ptr); + + FUNC_LEAVE_NOAPI(ret_value) +} /* H5AC_flush_dependency_exists() */ +#endif /* NDEBUG */ + + +/*------------------------------------------------------------------------- + * + * Function: H5AC_verify_entry_type() + * + * Purpose: Debugging function that attempts to look up an entry in the + * cache by its file address, and if found, test to see if its + * type field contains the expected value. + * + * If the specified entry is in cache, *in_cache_ptr is set + * to TRUE, and *type_ok_ptr is set to TRUE or FALSE depending + * on whether the entries type field matches the + * expected_type parameter + * + * If the target entry is not in cache, *in_cache_ptr is + * set to FALSE, and *type_ok_ptr is undefined. + * + * Note that this function is only defined if NDEBUG + * is not defined. + * + * This function is just a wrapper that calls the H5C + * version of the function. + * + * Return: FAIL if error is detected, SUCCEED otherwise. + * + * Programmer: John Mainzer, 5/30/14 + * + * Changes: None. + * + * JRM -- 9/17/16 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +herr_t +H5AC_verify_entry_type(const H5F_t *f, haddr_t addr, + const H5AC_class_t *expected_type, hbool_t *in_cache_ptr, + hbool_t *type_ok_ptr) +{ + H5C_t * cache_ptr; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + cache_ptr = f->shared->cache; + + if(H5C_verify_entry_type(cache_ptr, addr, expected_type, in_cache_ptr, type_ok_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C_verify_entry_type() failed") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5AC_verify_entry_type() */ +#endif /* NDEBUG */ + + +/*------------------------------------------------------------------------- + * Function: H5AC_get_serialization_in_progress + * + * Purpose: Return the current value of + * cache_ptr->serialization_in_progress. + * + * Return: Current value of cache_ptr->serialization_in_progress. + * + * Programmer: John Mainzer + * 8/24/15 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +hbool_t +H5AC_get_serialization_in_progress(H5F_t *f) +{ + H5C_t * cache_ptr; + hbool_t ret_value = FALSE; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT_NOERR + + /* Sanity check */ + HDassert(f); + HDassert(f->shared); + cache_ptr = f->shared->cache; + + /* Set return value */ + ret_value = H5C_get_serialization_in_progress(cache_ptr); + + FUNC_LEAVE_NOAPI(ret_value) +} /* H5AC_get_serialization_in_progress() */ +#endif /* NDEBUG */ + + +/*------------------------------------------------------------------------- + * * Function: H5AC_cache_is_clean() * * Purpose: Debugging function that verifies that all rings in the diff --git a/src/H5ACpkg.h b/src/H5ACpkg.h index dbbd8a0..816f1d5 100644 --- a/src/H5ACpkg.h +++ b/src/H5ACpkg.h @@ -351,6 +351,12 @@ H5FL_EXTERN(H5AC_aux_t); * this verification. The field is set to NULL when the * callback is not needed. * + * The following field supports the metadata cache image feature. + * + * p0_image_len: unsiged integer containing the length of the metadata cache + * image constructed by MPI process 0. This field should be 0 + * if the value is unknown, or if cache image is not enabled. + * ****************************************************************************/ #ifdef H5_HAVE_PARALLEL @@ -400,6 +406,9 @@ typedef struct H5AC_aux_t void (* sync_point_done)(int num_writes, haddr_t * written_entries_tbl); + + unsigned p0_image_len; + } H5AC_aux_t; /* struct H5AC_aux_t */ #endif /* H5_HAVE_PARALLEL */ diff --git a/src/H5ACprivate.h b/src/H5ACprivate.h index 3dd6079..9f28b18 100644 --- a/src/H5ACprivate.h +++ b/src/H5ACprivate.h @@ -88,7 +88,7 @@ typedef enum { H5AC_DRVRINFO_ID, /* (26) driver info block (supplements superblock) */ H5AC_EPOCH_MARKER_ID, /* (27) epoch marker - always internal to cache */ H5AC_PROXY_ENTRY_ID, /* (28) cache entry proxy */ - H5AC_TEST_ID, /* (29) test entry -- not used for actual files */ + H5AC_PREFETCHED_ENTRY_ID, /* (29) prefetched entry - always internal to cache */ H5AC_NTYPES /* Number of types, must be last */ } H5AC_type_t; @@ -111,14 +111,22 @@ typedef enum { * use the dump_stats parameter to takedown_cache(), or call * H5C_stats() directly. * JRM -- 4/12/15 + * + * Added the H5AC_DUMP_IMAGE_STATS_ON_CLOSE #define, which works much + * the same way as H5AC_DUMP_STATS_ON_CLOSE. However, the set of stats + * displayed is much smaller, and directed purely at the cache image feature. + * + * JRM -- 11/1/15 */ #if H5C_COLLECT_CACHE_STATS #define H5AC_DUMP_STATS_ON_CLOSE 0 +#define H5AC_DUMP_IMAGE_STATS_ON_CLOSE 0 #else /* H5C_COLLECT_CACHE_STATS */ #define H5AC_DUMP_STATS_ON_CLOSE 0 +#define H5AC_DUMP_IMAGE_STATS_ON_CLOSE 0 #endif /* H5C_COLLECT_CACHE_STATS */ @@ -319,7 +327,13 @@ H5_DLLVAR hid_t H5AC_rawdata_dxpl_id; } #endif /* H5_HAVE_PARALLEL */ - +#define H5AC__DEFAULT_CACHE_IMAGE_CONFIG \ +{ \ + /* int32_t version = */ H5AC__CURR_CACHE_IMAGE_CONFIG_VERSION, \ + /* hbool_t generate_image = */ FALSE, \ + /* hbool_t save_resize_status = */ FALSE, \ + /* int32_t entry_ageout = */ H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE \ +} /* * Library prototypes. */ @@ -344,7 +358,6 @@ H5_DLLVAR hid_t H5AC_rawdata_dxpl_id; #define H5AC__TAKE_OWNERSHIP_FLAG H5C__TAKE_OWNERSHIP_FLAG #define H5AC__FLUSH_LAST_FLAG H5C__FLUSH_LAST_FLAG #define H5AC__FLUSH_COLLECTIVELY_FLAG H5C__FLUSH_COLLECTIVELY_FLAG -#define H5AC__EVICT_ALLOW_LAST_PINS_FLAG H5C__EVICT_ALLOW_LAST_PINS_FLAG /* #defines of flags used to report entry status in the @@ -360,11 +373,44 @@ H5_DLLVAR hid_t H5AC_rawdata_dxpl_id; #define H5AC_ES__IS_CORKED 0x0040 #define H5AC_ES__IMAGE_IS_UP_TO_DATE 0x0080 +/* Metadata entry class declarations */ +H5_DLLVAR const H5AC_class_t H5AC_BT[1]; +H5_DLLVAR const H5AC_class_t H5AC_SNODE[1]; +H5_DLLVAR const H5AC_class_t H5AC_LHEAP_PRFX[1]; +H5_DLLVAR const H5AC_class_t H5AC_LHEAP_DBLK[1]; +H5_DLLVAR const H5AC_class_t H5AC_GHEAP[1]; +H5_DLLVAR const H5AC_class_t H5AC_OHDR[1]; +H5_DLLVAR const H5AC_class_t H5AC_OHDR_CHK[1]; +H5_DLLVAR const H5AC_class_t H5AC_BT2_HDR[1]; +H5_DLLVAR const H5AC_class_t H5AC_BT2_INT[1]; +H5_DLLVAR const H5AC_class_t H5AC_BT2_LEAF[1]; +H5_DLLVAR const H5AC_class_t H5AC_FHEAP_HDR[1]; +H5_DLLVAR const H5AC_class_t H5AC_FHEAP_DBLOCK[1]; +H5_DLLVAR const H5AC_class_t H5AC_FHEAP_IBLOCK[1]; +H5_DLLVAR const H5AC_class_t H5AC_FSPACE_HDR[1]; +H5_DLLVAR const H5AC_class_t H5AC_FSPACE_SINFO[1]; +H5_DLLVAR const H5AC_class_t H5AC_SOHM_TABLE[1]; +H5_DLLVAR const H5AC_class_t H5AC_SOHM_LIST[1]; +H5_DLLVAR const H5AC_class_t H5AC_EARRAY_HDR[1]; +H5_DLLVAR const H5AC_class_t H5AC_EARRAY_IBLOCK[1]; +H5_DLLVAR const H5AC_class_t H5AC_EARRAY_SBLOCK[1]; +H5_DLLVAR const H5AC_class_t H5AC_EARRAY_DBLOCK[1]; +H5_DLLVAR const H5AC_class_t H5AC_EARRAY_DBLK_PAGE[1]; +H5_DLLVAR const H5AC_class_t H5AC_FARRAY_HDR[1]; +H5_DLLVAR const H5AC_class_t H5AC_FARRAY_DBLOCK[1]; +H5_DLLVAR const H5AC_class_t H5AC_FARRAY_DBLK_PAGE[1]; +H5_DLLVAR const H5AC_class_t H5AC_SUPERBLOCK[1]; +H5_DLLVAR const H5AC_class_t H5AC_DRVRINFO[1]; +H5_DLLVAR const H5AC_class_t H5AC_EPOCH_MARKER[1]; +H5_DLLVAR const H5AC_class_t H5AC_PROXY_ENTRY[1]; +H5_DLLVAR const H5AC_class_t H5AC_PREFETCHED_ENTRY[1]; + /* 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_create(const H5F_t *f, H5AC_cache_config_t *config_ptr, + H5AC_cache_image_config_t * image_config_ptr); H5_DLL herr_t H5AC_get_entry_status(const H5F_t *f, haddr_t addr, unsigned *status_ptr); H5_DLL herr_t H5AC_insert_entry(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type, @@ -394,13 +440,18 @@ H5_DLL herr_t H5AC_remove_entry(void *entry); H5_DLL herr_t H5AC_get_cache_auto_resize_config(const H5AC_t * cache_ptr, H5AC_cache_config_t *config_ptr); H5_DLL herr_t H5AC_get_cache_size(H5AC_t *cache_ptr, size_t *max_size_ptr, - size_t *min_clean_size_ptr, size_t *cur_size_ptr, int32_t *cur_num_entries_ptr); + size_t *min_clean_size_ptr, size_t *cur_size_ptr, uint32_t *cur_num_entries_ptr); H5_DLL herr_t H5AC_get_cache_hit_rate(H5AC_t *cache_ptr, double *hit_rate_ptr); H5_DLL herr_t H5AC_reset_cache_hit_rate_stats(H5AC_t *cache_ptr); H5_DLL herr_t H5AC_set_cache_auto_resize_config(H5AC_t *cache_ptr, H5AC_cache_config_t *config_ptr); H5_DLL herr_t H5AC_validate_config(H5AC_cache_config_t *config_ptr); +/* Cache image routines */ +H5_DLL herr_t H5AC_load_cache_image_on_next_protect(H5F_t *f, haddr_t addr, + hsize_t len, hbool_t rw); +H5_DLL herr_t H5AC_validate_cache_image_config(H5AC_cache_image_config_t *config_ptr); + /* Tag & Ring routines */ H5_DLL herr_t H5AC_tag(hid_t dxpl_id, haddr_t metadata_tag, haddr_t *prev_tag); H5_DLL herr_t H5AC_flush_tagged_metadata(H5F_t * f, haddr_t metadata_tag, hid_t dxpl_id); @@ -433,6 +484,14 @@ H5_DLL herr_t H5AC_add_candidate(H5AC_t * cache_ptr, haddr_t addr); H5_DLL herr_t H5AC_stats(const H5F_t *f); H5_DLL herr_t H5AC_dump_cache(const H5F_t *f); #ifndef NDEBUG +H5_DLL herr_t H5AC_get_entry_ptr_from_addr(const H5F_t *f, haddr_t addr, + void **entry_ptr_ptr); +H5_DLL herr_t H5AC_flush_dependency_exists(H5F_t *f, haddr_t parent_addr, + haddr_t child_addr, hbool_t *fd_exists_ptr); +H5_DLL herr_t H5AC_verify_entry_type(const H5F_t *f, haddr_t addr, + const H5AC_class_t *expected_type, hbool_t *in_cache_ptr, + hbool_t *type_ok_ptr); +H5_DLL hbool_t H5AC_get_serialization_in_progress(H5F_t *f); H5_DLL hbool_t H5AC_cache_is_clean(const H5F_t *f, H5AC_ring_t inner_ring); #endif /* NDEBUG */ /* end debugging functions */ diff --git a/src/H5ACpublic.h b/src/H5ACpublic.h index dd16764..5fdb3f4 100644 --- a/src/H5ACpublic.h +++ b/src/H5ACpublic.h @@ -508,6 +508,67 @@ typedef struct H5AC_cache_config_t } H5AC_cache_config_t; +/**************************************************************************** + * + * structure H5AC_cache_image_config_t + * + * H5AC_cache_image_ctl_t is a public structure intended for use in public + * APIs. At least in its initial incarnation, it is a copy of struct + * H5C_cache_image_ctl_t. + * + * The fields of the structure are discussed individually below: + * + * version: Integer field containing the version number of this version + * of the H5C_image_ctl_t structure. Any instance of + * H5C_image_ctl_t passed to the cache must have a known + * version number, or an error will be flagged. + * + * generate_image: Boolean flag indicating whether a cache image should + * be created on file close. + * + * save_resize_status: Boolean flag indicating whether the cache image + * should include the adaptive cache resize configuration and status. + * Note that this field is ignored at present. + * + * entry_ageout: Integer field indicating the maximum number of + * times a prefetched entry can appear in subsequent cache images. + * This field exists to allow the user to avoid the buildup of + * infrequently used entries in long sequences of cache images. + * + * The value of this field must lie in the range + * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE (-1) to + * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX (100). + * + * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE means that no limit + * is imposed on number of times a prefeteched entry can appear + * in subsequent cache images. + * + * A value of 0 prevents prefetched entries from being included + * in cache images. + * + * Positive integers restrict prefetched entries to the specified + * number of appearances. + * + * Note that the number of subsequent cache images that a prefetched + * entry has appeared in is tracked in an 8 bit field. Thus, while + * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX can be increased from its + * current value, any value in excess of 255 will be the functional + * equivalent of H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE. + * + ****************************************************************************/ + +#define H5AC__CURR_CACHE_IMAGE_CONFIG_VERSION 1 + +#define H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE -1 +#define H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX 100 + +typedef struct H5AC_cache_image_config_t { + int32_t version; + hbool_t generate_image; + hbool_t save_resize_status; + int32_t entry_ageout; +} H5AC_cache_image_config_t; + #ifdef __cplusplus } #endif diff --git a/src/H5B2pkg.h b/src/H5B2pkg.h index 7b1ec4d..71fdfde 100644 --- a/src/H5B2pkg.h +++ b/src/H5B2pkg.h @@ -307,15 +307,6 @@ typedef struct H5B2_node_info_test_t { /* Package Private Variables */ /*****************************/ -/* H5B2 header inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_BT2_HDR[1]; - -/* H5B2 internal node inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_BT2_INT[1]; - -/* H5B2 leaf node inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_BT2_LEAF[1]; - /* Declare a free list to manage the H5B2_internal_t struct */ H5FL_EXTERN(H5B2_internal_t); diff --git a/src/H5Bpkg.h b/src/H5Bpkg.h index 41e0951..fb93b8a 100644 --- a/src/H5Bpkg.h +++ b/src/H5Bpkg.h @@ -73,9 +73,6 @@ typedef struct H5B_cache_ud_t { /* Package Private Variables */ /*****************************/ -/* H5B header inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_BT[1]; - /* Declare a free list to manage the haddr_t sequence information */ H5FL_SEQ_EXTERN(haddr_t); @@ -46,9 +46,9 @@ * - Change protect/unprotect to lock/unlock. * * - Flush entries in increasing address order in - * H5C_make_space_in_cache(). + * H5C__make_space_in_cache(). * - * - Also in H5C_make_space_in_cache(), use high and low water marks + * - Also in H5C__make_space_in_cache(), use high and low water marks * to reduce the number of I/O calls. * * - When flushing, attempt to combine contiguous entries to reduce @@ -155,11 +155,6 @@ static void * H5C_load_entry(H5F_t * f, haddr_t addr, void * udata); -static herr_t H5C_make_space_in_cache(H5F_t * f, - hid_t dxpl_id, - size_t space_needed, - hbool_t write_permitted); - static herr_t H5C__mark_flush_dep_dirty(H5C_cache_entry_t * entry); static herr_t H5C__mark_flush_dep_clean(H5C_cache_entry_t * entry); @@ -243,7 +238,7 @@ H5C_t * H5C_create(size_t max_cache_size, size_t min_clean_size, int max_type_id, - const char * (* type_name_table_ptr), + const H5C_class_t * const * class_table_ptr, H5C_write_permitted_func_t check_write_permitted, hbool_t write_permitted, H5C_log_flush_func_t log_flush, @@ -261,11 +256,11 @@ H5C_create(size_t max_cache_size, HDassert( max_type_id >= 0 ); HDassert( max_type_id < H5C__MAX_NUM_TYPE_IDS ); - HDassert( type_name_table_ptr ); + HDassert( class_table_ptr ); for ( i = 0; i <= max_type_id; i++ ) { - HDassert( (type_name_table_ptr)[i] ); - HDassert( HDstrlen(( type_name_table_ptr)[i]) > 0 ); + HDassert( (class_table_ptr)[i] ); + HDassert(HDstrlen((class_table_ptr)[i]->name) > 0); } /* end for */ if(NULL == (cache_ptr = H5FL_CALLOC(H5C_t))) @@ -297,7 +292,7 @@ H5C_create(size_t max_cache_size, cache_ptr->max_type_id = max_type_id; - cache_ptr->type_name_table_ptr = type_name_table_ptr; + cache_ptr->class_table_ptr = class_table_ptr; cache_ptr->max_cache_size = max_cache_size; cache_ptr->min_clean_size = min_clean_size; @@ -389,6 +384,7 @@ H5C_create(size_t max_cache_size, cache_ptr->resize_enabled = FALSE; cache_ptr->cache_full = FALSE; cache_ptr->size_decreased = FALSE; + cache_ptr->resize_in_progress = FALSE; (cache_ptr->resize_ctl).version = H5C__CURR_AUTO_SIZE_CTL_VER; (cache_ptr->resize_ctl).rpt_fcn = NULL; @@ -434,12 +430,35 @@ H5C_create(size_t max_cache_size, ((cache_ptr->epoch_markers)[i]).magic = H5C__H5C_CACHE_ENTRY_T_MAGIC; ((cache_ptr->epoch_markers)[i]).addr = (haddr_t)i; - ((cache_ptr->epoch_markers)[i]).type = &H5C__epoch_marker_class; + ((cache_ptr->epoch_markers)[i]).type = H5AC_EPOCH_MARKER; } + /* Initialize cache image generation on file close related fields. + * Initial value of image_ctl must match H5C__DEFAULT_CACHE_IMAGE_CTL + * in H5Cprivate.h. + */ + cache_ptr->image_ctl.version = H5C__CURR_CACHE_IMAGE_CTL_VER; + cache_ptr->image_ctl.generate_image = FALSE; + cache_ptr->image_ctl.save_resize_status = FALSE; + cache_ptr->image_ctl.entry_ageout = -1; + cache_ptr->image_ctl.flags = H5C_CI__ALL_FLAGS; + + cache_ptr->serialization_in_progress= FALSE; + cache_ptr->load_image = FALSE; + cache_ptr->image_loaded = FALSE; + cache_ptr->delete_image = FALSE; + cache_ptr->image_addr = HADDR_UNDEF; + cache_ptr->image_len = 0; + cache_ptr->image_data_len = 0; + cache_ptr->entries_loaded_counter = 0; cache_ptr->entries_inserted_counter = 0; cache_ptr->entries_relocated_counter = 0; + cache_ptr->entry_fd_height_change_counter = 0; + + cache_ptr->num_entries_in_image = 0; + cache_ptr->image_entries = NULL; + cache_ptr->image_buffer = NULL; /* initialize free space manager related fields: */ cache_ptr->rdfsm_settled = FALSE; @@ -456,6 +475,10 @@ H5C_create(size_t max_cache_size, cache_ptr->prefix[0] = '\0'; /* empty string */ +#ifndef NDEBUG + cache_ptr->get_entry_ptr_from_addr_counter = 0; +#endif /* NDEBUG */ + /* Set return value */ ret_value = cache_ptr; @@ -733,6 +756,21 @@ H5C_prep_for_file_close(H5F_t *f, hid_t dxpl_id) /* Make certain there aren't any protected entries */ HDassert(cache_ptr->pl_len == 0); + /* If the file is opened and closed without any access to + * any group or dataset, it is possible that the cache image (if + * it exists) has not been read yet. Do this now if required. + */ + if(cache_ptr->load_image) { + cache_ptr->load_image = FALSE; + if(H5C__load_cache_image(f, dxpl_id) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, FAIL, "Can't load cache image") + } /* end if */ + + /* Generate the cache image, if requested */ + if(cache_ptr->image_ctl.generate_image) + if(H5C__prep_image_for_file_close(f, dxpl_id) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL, "Can't create cache image") + done: FUNC_LEAVE_NOAPI(ret_value) } /* H5C_prep_for_file_close() */ @@ -775,10 +813,20 @@ H5C_dest(H5F_t * f, hid_t dxpl_id) HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); HDassert(cache_ptr->close_warning_received); +#if H5AC_DUMP_IMAGE_STATS_ON_CLOSE + if(H5C_image_stats(cache_ptr, TRUE) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't display cache image stats") +#endif /* H5AC_DUMP_IMAGE_STATS_ON_CLOSE */ + /* Flush and invalidate all cache entries */ if(H5C_flush_invalidate_cache(f, dxpl_id, H5C__NO_FLAGS_SET) < 0 ) HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache") + /* Generate & write cache image if requested */ + if(cache_ptr->image_ctl.generate_image) + if(H5C__generate_cache_image(f, dxpl_id, cache_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL, "Can't generate metadata cache image") + if(cache_ptr->slist_ptr != NULL) { H5SL_close(cache_ptr->slist_ptr); cache_ptr->slist_ptr = NULL; @@ -790,6 +838,12 @@ H5C_dest(H5F_t * f, hid_t dxpl_id) } /* end if */ #ifndef NDEBUG +#if H5C_DO_SANITY_CHECKS + if(cache_ptr->get_entry_ptr_from_addr_counter > 0) + HDfprintf(stdout, "*** %ld calls to H5C_get_entry_ptr_from_add(). ***\n", + cache_ptr->get_entry_ptr_from_addr_counter); +#endif /* H5C_DO_SANITY_CHECKS */ + cache_ptr->magic = 0; #endif /* NDEBUG */ @@ -868,7 +922,7 @@ H5C_expunge_entry(H5F_t *f, hid_t dxpl_id, const H5C_class_t *type, #if H5C_DO_EXTREME_SANITY_CHECKS if(H5C_validate_lru_list(cache_ptr) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "LRU extreme sanity check failed on entry.\n") + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "LRU extreme sanity check failed on entry") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ /* Look for entry in cache */ @@ -909,7 +963,7 @@ H5C_expunge_entry(H5F_t *f, hid_t dxpl_id, const H5C_class_t *type, done: #if H5C_DO_EXTREME_SANITY_CHECKS if(H5C_validate_lru_list(cache_ptr) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "LRU extreme sanity check failed on exit.\n") + HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "LRU extreme sanity check failed on exit") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) @@ -968,12 +1022,12 @@ H5C_flush_cache(H5F_t *f, hid_t dxpl_id, unsigned flags) { #if H5C_DO_SANITY_CHECKS int i; - int32_t index_len = 0; + uint32_t index_len = 0; size_t index_size = (size_t)0; size_t clean_index_size = (size_t)0; size_t dirty_index_size = (size_t)0; size_t slist_size = (size_t)0; - int32_t slist_len = 0; + uint32_t slist_len = 0; #endif /* H5C_DO_SANITY_CHECKS */ H5C_ring_t ring; H5C_t * cache_ptr; @@ -1020,7 +1074,7 @@ H5C_flush_cache(H5F_t *f, hid_t dxpl_id, unsigned flags) if((H5C_validate_protected_entry_list(cache_ptr) < 0) || (H5C_validate_pinned_entry_list(cache_ptr) < 0) || (H5C_validate_lru_list(cache_ptr) < 0)) - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry.\n") + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ ignore_protected = ( (flags & H5C__FLUSH_IGNORE_PROTECTED_FLAG) != 0 ); @@ -1173,16 +1227,8 @@ H5C_flush_to_min_clean(H5F_t * f, "cache write is not permitted!?!\n"); } #if 1 /* original code */ - result = H5C_make_space_in_cache(f, - dxpl_id, - (size_t)0, - write_permitted); - - if ( result < 0 ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "H5C_make_space_in_cache failed.") - } + if(H5C__make_space_in_cache(f, dxpl_id, (size_t)0, write_permitted) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C__make_space_in_cache failed") #else /* modified code -- commented out for now */ if ( cache_ptr->max_cache_size > cache_ptr->index_size ) { @@ -1336,8 +1382,9 @@ H5C_insert_entry(H5F_t * f, hbool_t set_flush_marker; hbool_t write_permitted = TRUE; size_t empty_space; - H5C_cache_entry_t *entry_ptr; + H5C_cache_entry_t *entry_ptr = NULL; H5C_cache_entry_t *test_entry_ptr; + hbool_t entry_tagged = FALSE; herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) @@ -1350,6 +1397,7 @@ H5C_insert_entry(H5F_t * f, HDassert( cache_ptr ); HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); HDassert( type ); + HDassert( type->mem_type == cache_ptr->class_table_ptr[type->id]->mem_type ); HDassert( type->image_len ); HDassert( H5F_addr_defined(addr) ); HDassert( thing ); @@ -1357,14 +1405,10 @@ H5C_insert_entry(H5F_t * f, #if H5C_DO_EXTREME_SANITY_CHECKS /* no need to verify that entry is not already in the index as */ /* we already make that check below. */ - - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_lru_list(cache_ptr) < 0 ) ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "an extreme sanity check failed on entry.\n"); - } + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0) || + (H5C_validate_lru_list(cache_ptr) < 0)) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ set_flush_marker = ( (flags & H5C__SET_FLUSH_MARKER_FLAG) != 0 ); @@ -1453,6 +1497,22 @@ H5C_insert_entry(H5F_t * f, entry_ptr->aux_next = NULL; entry_ptr->aux_prev = NULL; + /* initialize cache image related fields */ + entry_ptr->include_in_image = FALSE; + entry_ptr->lru_rank = 0; + entry_ptr->image_dirty = FALSE; + entry_ptr->fd_parent_count = 0; + entry_ptr->fd_parent_addrs = NULL; + entry_ptr->fd_child_count = 0; + entry_ptr->fd_dirty_child_count = 0; + entry_ptr->image_fd_height = 0; + entry_ptr->prefetched = FALSE; + entry_ptr->prefetch_type_id = 0; + entry_ptr->age = 0; +#ifndef NDEBUG /* debugging field */ + entry_ptr->serialization_count = 0; +#endif /* NDEBUG */ + #ifdef H5_HAVE_PARALLEL entry_ptr->coll_next = NULL; entry_ptr->coll_prev = NULL; @@ -1461,6 +1521,7 @@ H5C_insert_entry(H5F_t * f, /* Apply tag to newly inserted entry */ if(H5C__tag_entry(cache_ptr, entry_ptr, dxpl_id) < 0) HGOTO_ERROR(H5E_CACHE, H5E_CANTTAG, FAIL, "Cannot tag metadata entry") + entry_tagged = TRUE; H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) @@ -1497,7 +1558,7 @@ H5C_insert_entry(H5F_t * f, /* Note that space_needed is just the amount of space that * needed to insert the new entry without exceeding the cache - * size limit. The subsequent call to H5C_make_space_in_cache() + * size limit. The subsequent call to H5C__make_space_in_cache() * may evict the entries required to free more or less space * depending on conditions. It MAY be less if the cache is * currently undersized, or more if the cache is oversized. @@ -1520,8 +1581,8 @@ H5C_insert_entry(H5F_t * f, * no point in worrying about the third. */ - if(H5C_make_space_in_cache(f, dxpl_id, space_needed, write_permitted) < 0) - HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "H5C_make_space_in_cache failed.") + if(H5C__make_space_in_cache(f, dxpl_id, space_needed, write_permitted) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "H5C__make_space_in_cache failed.") } H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL) @@ -1533,10 +1594,10 @@ H5C_insert_entry(H5F_t * f, H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, FAIL) #if H5C_DO_EXTREME_SANITY_CHECKS - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_lru_list(cache_ptr) < 0 ) ) - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed just before done.\n") + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0) || + (H5C_validate_lru_list(cache_ptr) < 0)) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed just before done") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ /* If the entry's type has a 'notify' callback send a 'after insertion' @@ -1588,16 +1649,15 @@ H5C_insert_entry(H5F_t * f, done: #if H5C_DO_EXTREME_SANITY_CHECKS - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_lru_list(cache_ptr) < 0 ) ) - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit.\n") + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0) || + (H5C_validate_lru_list(cache_ptr) < 0)) + HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ - if(ret_value < 0 && entry_ptr->tag_info) { + if(ret_value < 0 && entry_tagged) if(H5C__untag_entry(cache_ptr, entry_ptr) < 0) HDONE_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove entry from tag list") - } FUNC_LEAVE_NOAPI(ret_value) } /* H5C_insert_entry() */ @@ -1920,7 +1980,7 @@ H5C_move_entry(H5C_t * cache_ptr, if((H5C_validate_protected_entry_list(cache_ptr) < 0) || (H5C_validate_pinned_entry_list(cache_ptr) < 0) || (H5C_validate_lru_list(cache_ptr) < 0)) - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry.\n") + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ H5C__SEARCH_INDEX(cache_ptr, old_addr, entry_ptr, FAIL) @@ -2024,7 +2084,7 @@ done: if((H5C_validate_protected_entry_list(cache_ptr) < 0) || (H5C_validate_pinned_entry_list(cache_ptr) < 0) || (H5C_validate_lru_list(cache_ptr) < 0)) - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit.\n") + HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) @@ -2070,12 +2130,9 @@ H5C_resize_entry(void *thing, size_t new_size) HGOTO_ERROR(H5E_CACHE, H5E_BADTYPE, FAIL, "Entry isn't pinned or protected??") #if H5C_DO_EXTREME_SANITY_CHECKS - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "an extreme sanity check failed on entry.\n"); - } + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0)) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ /* update for change in entry size if necessary */ @@ -2172,14 +2229,10 @@ H5C_resize_entry(void *thing, size_t new_size) } /* end if */ done: - #if H5C_DO_EXTREME_SANITY_CHECKS - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "an extreme sanity check failed on exit.\n"); - } + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0)) + HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) @@ -2275,13 +2328,10 @@ H5C_pin_protected_entry(void *thing) HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); #if H5C_DO_EXTREME_SANITY_CHECKS - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_lru_list(cache_ptr) < 0 ) ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "an extreme sanity check failed on entry.\n"); - } + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0) || + (H5C_validate_lru_list(cache_ptr) < 0)) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ @@ -2294,15 +2344,11 @@ H5C_pin_protected_entry(void *thing) HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, "Can't pin entry by client") done: - #if H5C_DO_EXTREME_SANITY_CHECKS - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_lru_list(cache_ptr) < 0 ) ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "an extreme sanity check failed on exit.\n"); - } + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0) || + (H5C_validate_lru_list(cache_ptr) < 0)) + HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) @@ -2329,32 +2375,6 @@ done: * * Programmer: John Mainzer - 6/2/04 * - * JRM -- 11/13/08 - * Modified function to call H5C_make_space_in_cache() when - * the min_clean_size is violated, not just when there isn't - * enough space for and entry that has just been loaded. - * - * The purpose of this modification is to avoid "metadata - * blizzards" in the write only case. In such instances, - * the cache was allowed to fill with dirty metadata. When - * we finally needed to evict an entry to make space, we had - * to flush out a whole cache full of metadata -- which has - * interesting performance effects. We hope to avoid (or - * perhaps more accurately hide) this effect by maintaining - * the min_clean_size, which should force us to start flushing - * entries long before we actually have to evict something - * to make space. - * - * JRM -- 9/1/14 - * Replace the old rw parameter with the flags parameter. - * This allows H5C_protect to accept flags other than - * H5C__READ_ONLY_FLAG. - * - * Added support for the H5C__FLUSH_LAST_FLAG. - * At present, this flag is only applied if the entry is - * not in cache, and is loaded into the cache as a result of - * this call. - * *------------------------------------------------------------------------- */ void * @@ -2393,17 +2413,23 @@ H5C_protect(H5F_t * f, HDassert( cache_ptr ); HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); HDassert( type ); + HDassert( type->mem_type == cache_ptr->class_table_ptr[type->id]->mem_type ); HDassert( H5F_addr_defined(addr) ); #if H5C_DO_EXTREME_SANITY_CHECKS - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_lru_list(cache_ptr) < 0 ) ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "an extreme sanity check failed on entry.\n") - } + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0) || + (H5C_validate_lru_list(cache_ptr) < 0)) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "an extreme sanity check failed on entry") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ + /* Load the cache image, if requested */ + if(cache_ptr->load_image) { + cache_ptr->load_image = FALSE; + if(H5C__load_cache_image(f, dxpl_id) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "Can't load cache image") + } /* end if */ + read_only = ( (flags & H5C__READ_ONLY_FLAG) != 0 ); flush_last = ( (flags & H5C__FLUSH_LAST_FLAG) != 0 ); @@ -2437,6 +2463,21 @@ H5C_protect(H5F_t * f, if(entry_ptr->ring != ring) HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "ring type mismatch occured for cache entry\n") + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + + if(entry_ptr->prefetched) { + /* This call removes the prefetched entry from the cache, + * and replaces it with an entry deserialized from the + * image of the prefetched entry. + */ + if(H5C__deserialize_prefetched_entry(f, dxpl_id, cache_ptr, &entry_ptr, type, addr, udata) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "can't deserialize prefetched entry.") + + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(!entry_ptr->prefetched); + HDassert(entry_ptr->addr == addr); + } /* end if */ + /* Check for trying to load the wrong type of entry from an address */ if(entry_ptr->type != type) HGOTO_ERROR(H5E_CACHE, H5E_BADTYPE, NULL, "incorrect cache entry type") @@ -2554,7 +2595,7 @@ H5C_protect(H5F_t * f, empty_space = cache_ptr->max_cache_size - cache_ptr->index_size; /* try to free up if necceary and if evictions are permitted. Note - * that if evictions are enabled, we will call H5C_make_space_in_cache() + * that if evictions are enabled, we will call H5C__make_space_in_cache() * regardless if the min_free_space requirement is not met. */ if ( ( cache_ptr->evictions_enabled ) && @@ -2589,7 +2630,7 @@ H5C_protect(H5F_t * f, /* Note that space_needed is just the amount of space that * needed to insert the new entry without exceeding the cache - * size limit. The subsequent call to H5C_make_space_in_cache() + * size limit. The subsequent call to H5C__make_space_in_cache() * may evict the entries required to free more or less space * depending on conditions. It MAY be less if the cache is * currently undersized, or more if the cache is oversized. @@ -2616,8 +2657,8 @@ H5C_protect(H5F_t * f, * see no point in worrying about the fourth. */ - if(H5C_make_space_in_cache(f, dxpl_id, space_needed, write_permitted) < 0 ) - HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C_make_space_in_cache failed 1.") + if(H5C__make_space_in_cache(f, dxpl_id, space_needed, write_permitted) < 0 ) + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C__make_space_in_cache failed 1.") } /* Insert the entry in the hash table. It can't be dirty yet, so @@ -2737,7 +2778,7 @@ H5C_protect(H5F_t * f, * bring the cache size down to the current maximum cache size. * * Also, if the min_clean_size requirement is not met, we - * should also call H5C_make_space_in_cache() to bring us + * should also call H5C__make_space_in_cache() to bring us * into complience. */ @@ -2754,8 +2795,8 @@ H5C_protect(H5F_t * f, if(cache_ptr->index_size > cache_ptr->max_cache_size) cache_ptr->cache_full = TRUE; - if(H5C_make_space_in_cache(f, dxpl_id, (size_t)0, write_permitted) < 0 ) - HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C_make_space_in_cache failed 2.") + if(H5C__make_space_in_cache(f, dxpl_id, (size_t)0, write_permitted) < 0 ) + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C__make_space_in_cache failed 2.") } } } @@ -2794,10 +2835,10 @@ H5C_protect(H5F_t * f, done: #if H5C_DO_EXTREME_SANITY_CHECKS - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_lru_list(cache_ptr) < 0 ) ) - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "an extreme sanity check failed on exit.\n") + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0) || + (H5C_validate_lru_list(cache_ptr) < 0)) + HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "an extreme sanity check failed on exit") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) @@ -3166,13 +3207,10 @@ H5C_unpin_entry(void *_entry_ptr) HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); #if H5C_DO_EXTREME_SANITY_CHECKS - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_lru_list(cache_ptr) < 0 ) ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "an extreme sanity check failed on entry.\n"); - } + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0) || + (H5C_validate_lru_list(cache_ptr) < 0)) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ @@ -3181,19 +3219,14 @@ H5C_unpin_entry(void *_entry_ptr) HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "Can't unpin entry from client") done: - #if H5C_DO_EXTREME_SANITY_CHECKS - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_lru_list(cache_ptr) < 0 ) ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "an extreme sanity check failed on exit.\n"); - } + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0) || + (H5C_validate_lru_list(cache_ptr) < 0)) + HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) - } /* H5C_unpin_entry() */ @@ -3280,13 +3313,10 @@ H5C_unprotect(H5F_t * f, was_clean = ! ( entry_ptr->is_dirty ); #if H5C_DO_EXTREME_SANITY_CHECKS - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_lru_list(cache_ptr) < 0 ) ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "an extreme sanity check failed on entry.\n"); - } + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0) || + (H5C_validate_lru_list(cache_ptr) < 0)) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ /* if the entry has multiple read only protects, just decrement @@ -3481,19 +3511,14 @@ H5C_unprotect(H5F_t * f, H5C__UPDATE_STATS_FOR_UNPROTECT(cache_ptr) done: - #if H5C_DO_EXTREME_SANITY_CHECKS - if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || - ( H5C_validate_lru_list(cache_ptr) < 0 ) ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "an extreme sanity check failed on exit.\n"); - } + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0) || + (H5C_validate_lru_list(cache_ptr) < 0)) { + HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) - } /* H5C_unprotect() */ @@ -3884,6 +3909,7 @@ H5C_create_flush_dependency(void * parent_thing, void * child_thing) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for flush dependency parent list") child_entry->flush_dep_parent_nalloc *= 2; } /* end else */ + cache_ptr->entry_fd_height_change_counter++; } /* end if */ /* Add the dependency to the child's parent array */ @@ -4092,6 +4118,7 @@ H5C__auto_adjust_cache_size(H5F_t * f, { H5C_t * cache_ptr = f->shared->cache; herr_t result; + hbool_t reentrant_call = FALSE; hbool_t inserted_epoch_marker = FALSE; size_t new_max_cache_size = 0; size_t old_max_cache_size = 0; @@ -4111,6 +4138,18 @@ H5C__auto_adjust_cache_size(H5F_t * f, HDassert( (double)0.0f <= (cache_ptr->resize_ctl).min_clean_fraction ); HDassert( (cache_ptr->resize_ctl).min_clean_fraction <= (double)100.0f ); + /* check to see if cache_ptr->resize_in_progress is TRUE. If it, this + * is a re-entrant call via a client callback called in the resize + * process. To avoid an infinite recursion, set reentrant_call to + * TRUE, and goto done. + */ + if(cache_ptr->resize_in_progress) { + reentrant_call = TRUE; + HGOTO_DONE(SUCCEED); + } /* end if */ + + cache_ptr->resize_in_progress = TRUE; + if ( !cache_ptr->resize_enabled ) { HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Auto cache resize disabled.") @@ -4409,6 +4448,12 @@ H5C__auto_adjust_cache_size(H5F_t * f, } done: + /* Sanity checks */ + HDassert(cache_ptr->resize_in_progress); + if(!reentrant_call) + cache_ptr->resize_in_progress = FALSE; + HDassert((!reentrant_call) || (cache_ptr->resize_in_progress)); + FUNC_LEAVE_NOAPI(ret_value) } /* H5C__auto_adjust_cache_size() */ @@ -5352,8 +5397,8 @@ H5C_flush_invalidate_cache(H5F_t *f, hid_t dxpl_id, unsigned flags) #if H5C_DO_SANITY_CHECKS { int32_t i; - int32_t index_len = 0; - int32_t slist_len = 0; + uint32_t index_len = 0; + uint32_t slist_len = 0; size_t index_size = (size_t)0; size_t clean_index_size = (size_t)0; size_t dirty_index_size = (size_t)0; @@ -5484,7 +5529,7 @@ H5C_flush_invalidate_ring(H5F_t * f, hid_t dxpl_id, H5C_ring_t ring, { H5C_t *cache_ptr; hbool_t restart_slist_scan; - int32_t protected_entries = 0; + uint32_t protected_entries = 0; int32_t i; int32_t cur_ring_pel_len; int32_t old_ring_pel_len; @@ -5925,7 +5970,7 @@ H5C_flush_ring(H5F_t *f, hid_t dxpl_id, H5C_ring_t ring, unsigned flags) hbool_t ignore_protected; hbool_t tried_to_flush_protected_entry = FALSE; hbool_t restart_slist_scan; - int32_t protected_entries = 0; + uint32_t protected_entries = 0; H5SL_node_t * node_ptr = NULL; H5C_cache_entry_t * entry_ptr = NULL; H5C_cache_entry_t * next_entry_ptr = NULL; @@ -5949,7 +5994,7 @@ H5C_flush_ring(H5F_t *f, hid_t dxpl_id, H5C_ring_t ring, unsigned flags) if((H5C_validate_protected_entry_list(cache_ptr) < 0) || (H5C_validate_pinned_entry_list(cache_ptr) < 0) || (H5C_validate_lru_list(cache_ptr) < 0)) - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry.\n") + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry") #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ ignore_protected = ( (flags & H5C__FLUSH_IGNORE_PROTECTED_FLAG) != 0 ); @@ -6188,34 +6233,6 @@ done: * * Programmer: John Mainzer, 5/5/04 * - * Changes: Refactored function to remove the type_ptr parameter. - * - * JRM -- 8/7/14 - * - * Added code to check for slist changes in pre_serialize and - * serialize calls, and set - * cache_ptr->slist_change_in_pre_serialize and - * cache_ptr->slist_change_in_serialize as appropriate. - * - * JRM -- 12/13/14 - * - * Refactored function to delay all modifications of the - * metadata cache data structures until after any calls - * to the pre-serialize or serialize callbacks. - * - * Need to do this, as some pre-serialize or serialize - * calls result in calls to the metadata cache and - * modifications to its data structures. Thus, at the - * time of any such call, the target entry flags and - * the metadata cache must all be consistant. - * - * Also added the entry_size_change_ptr parameter, which - * allows the function to report back any change in the size - * of the entry during the flush. Such size changes may - * occur during the pre-serialize callback. - * - * JRM -- 12/24/14 - * *------------------------------------------------------------------------- */ herr_t @@ -6233,6 +6250,8 @@ H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr, hbool_t destroy_entry; /* internal flag */ hbool_t generate_image; /* internal flag */ hbool_t was_dirty; + hbool_t suppress_image_entry_writes = FALSE; + hbool_t suppress_image_entry_frees = FALSE; haddr_t entry_addr = HADDR_UNDEF; herr_t ret_value = SUCCEED; /* Return value */ @@ -6275,6 +6294,28 @@ H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr, else write_entry = FALSE; + /* if we have received close warning, and we have been instructed to + * generate a metadata cache image, and we have actually constructed + * the entry images, set suppress_image_entry_frees to TRUE. + * + * Set suppress_image_entry_writes to TRUE if indicated by the + * image_ctl flags. + */ + if(cache_ptr->close_warning_received && cache_ptr->image_ctl.generate_image + && cache_ptr->num_entries_in_image > 0 && cache_ptr->image_entries) { + /* Sanity checks */ + HDassert(entry_ptr->image_up_to_date || !(entry_ptr->include_in_image)); + HDassert(entry_ptr->image_ptr || !(entry_ptr->include_in_image)); + HDassert((!clear_only) || !(entry_ptr->include_in_image)); + HDassert((!take_ownership) || !(entry_ptr->include_in_image)); + HDassert((!free_file_space) || !(entry_ptr->include_in_image)); + + suppress_image_entry_frees = TRUE; + + if(cache_ptr->image_ctl.flags & H5C_CI__SUPRESS_ENTRY_WRITES) + suppress_image_entry_writes = TRUE; + } /* end if */ + /* run initial sanity checks */ #if H5C_DO_SANITY_CHECKS if(entry_ptr->in_slist) { @@ -6329,6 +6370,9 @@ H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr, } /* end if */ if(!(entry_ptr->image_up_to_date)) { + /* Sanity check */ + HDassert(!entry_ptr->prefetched); + /* Generate the entry's image */ if(H5C__generate_image(f, cache_ptr, entry_ptr, dxpl_id) < 0) HGOTO_ERROR(H5E_CACHE, H5E_CANTGET, FAIL, "can't generate entry's image") @@ -6349,7 +6393,17 @@ H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr, HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Write when writes are always forbidden!?!?!") #endif /* H5C_DO_SANITY_CHECKS */ - if(((entry_ptr->type->flags) & H5C__CLASS_SKIP_WRITES) == 0) { + /* Write the image to disk unless the write is suppressed. + * + * This happens if both suppress_image_entry_writes and + * entry_ptr->include_in_image are TRUE, or if the + * H5AC__CLASS_SKIP_WRITES is set in the entry's type. This + * flag should only be used in test code + */ + if((!suppress_image_entry_writes || !entry_ptr->include_in_image) + && (((entry_ptr->type->flags) & H5C__CLASS_SKIP_WRITES) == 0)) { + H5FD_mem_t mem_type = H5FD_MEM_DEFAULT; + #ifdef H5_HAVE_PARALLEL if(cache_ptr->coll_write_list) { if(H5SL_insert(cache_ptr->coll_write_list, entry_ptr, &entry_ptr->addr) < 0) @@ -6357,7 +6411,15 @@ H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr, } /* end if */ else #endif /* H5_HAVE_PARALLEL */ - if(H5F_block_write(f, entry_ptr->type->mem_type, entry_ptr->addr, entry_ptr->size, dxpl_id, entry_ptr->image_ptr) < 0) + + if(entry_ptr->prefetched) { + HDassert(entry_ptr->type->id == H5AC_PREFETCHED_ENTRY_ID); + mem_type = cache_ptr->class_table_ptr[entry_ptr->prefetch_type_id]->mem_type; + } /* end if */ + else + mem_type = entry_ptr->type->mem_type; + + if(H5F_block_write(f, mem_type, entry_ptr->addr, entry_ptr->size, dxpl_id, entry_ptr->image_ptr) < 0) HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't write image to file.") } /* end if */ @@ -6502,10 +6564,29 @@ H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr, /* Sanity check */ HDassert(0 == entry_ptr->flush_dep_nparents); - /* Start by freeing the buffer for the on disk image */ - if(entry_ptr->image_ptr != NULL) + /* if both suppress_image_entry_frees and entry_ptr->include_in_image + * are true, simply set entry_ptr->image_ptr to NULL, as we have + * another pointer to the buffer in an instance of H5C_image_entry_t + * in cache_ptr->image_entries. + * + * Otherwise, free the buffer if it exists. + */ + if(suppress_image_entry_frees && entry_ptr->include_in_image) + entry_ptr->image_ptr = NULL; + else if(entry_ptr->image_ptr != NULL) entry_ptr->image_ptr = H5MM_xfree(entry_ptr->image_ptr); + /* If the entry is not a prefetched entry, verify that the flush + * dependency parents addresses array has been transfered. + * + * If the entry is prefetched, the free_isr routine will dispose of + * the flush dependency parents adresses array if necessary. + */ + if(!entry_ptr->prefetched) { + HDassert(0 == entry_ptr->fd_parent_count); + HDassert(NULL == entry_ptr->fd_parent_addrs); + } /* end if */ + /* Check whether we should free the space in the file that * the entry occupies */ @@ -6981,6 +7062,22 @@ H5C_load_entry(H5F_t * f, entry->coll_prev = NULL; #endif /* H5_HAVE_PARALLEL */ + /* initialize cache image related fields */ + entry->include_in_image = FALSE; + entry->lru_rank = 0; + entry->image_dirty = FALSE; + entry->fd_parent_count = 0; + entry->fd_parent_addrs = NULL; + entry->fd_child_count = 0; + entry->fd_dirty_child_count = 0; + entry->image_fd_height = 0; + entry->prefetched = FALSE; + entry->prefetch_type_id = 0; + entry->age = 0; +#ifndef NDEBUG /* debugging field */ + entry->serialization_count = 0; +#endif /* NDEBUG */ + H5C__RESET_CACHE_ENTRY_STATS(entry); ret_value = thing; @@ -7001,7 +7098,7 @@ done: /*------------------------------------------------------------------------- * - * Function: H5C_make_space_in_cache + * Function: H5C__make_space_in_cache * * Purpose: Attempt to evict cache entries until the index_size * is at least needed_space below max_cache_size. @@ -7066,19 +7163,17 @@ done: * *------------------------------------------------------------------------- */ -static herr_t -H5C_make_space_in_cache(H5F_t * f, - hid_t dxpl_id, - size_t space_needed, - hbool_t write_permitted) +herr_t +H5C__make_space_in_cache(H5F_t *f, hid_t dxpl_id, size_t space_needed, + hbool_t write_permitted) { H5C_t * cache_ptr = f->shared->cache; #if H5C_COLLECT_CACHE_STATS int32_t clean_entries_skipped = 0; int32_t total_entries_scanned = 0; #endif /* H5C_COLLECT_CACHE_STATS */ - int32_t entries_examined = 0; - int32_t initial_list_len; + uint32_t entries_examined = 0; + uint32_t initial_list_len; size_t empty_space; hbool_t prev_is_dirty = FALSE; hbool_t didnt_flush_entry = FALSE; @@ -7086,16 +7181,16 @@ H5C_make_space_in_cache(H5F_t * f, H5C_cache_entry_t * entry_ptr; H5C_cache_entry_t * prev_ptr; H5C_cache_entry_t * next_ptr; - int32_t num_corked_entries = 0; + uint32_t num_corked_entries = 0; herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_NOAPI_NOINIT + FUNC_ENTER_PACKAGE - HDassert( f ); - HDassert( cache_ptr ); - HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( cache_ptr->index_size == - (cache_ptr->clean_index_size + cache_ptr->dirty_index_size) ); + /* Sanity checks */ + HDassert(f); + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(cache_ptr->index_size == (cache_ptr->clean_index_size + cache_ptr->dirty_index_size)); if ( write_permitted ) { @@ -7358,10 +7453,8 @@ H5C_make_space_in_cache(H5F_t * f, } done: - FUNC_LEAVE_NOAPI(ret_value) - -} /* H5C_make_space_in_cache() */ +} /* H5C__make_space_in_cache() */ /*------------------------------------------------------------------------- @@ -8184,6 +8277,10 @@ H5C__assert_flush_dep_nocycle(const H5C_cache_entry_t * entry, * are about to delete the entry from the cache (i.e. on a * flush destroy). * + * Note: This routine is very similar to H5C__serialize_single_entry + * and changes to one should probably be reflected in the other. + * Ideally, one should be eliminated. + * * Return: Non-negative on success/Negative on failure * * Programmer: Mohamad Chaarawi @@ -8218,8 +8315,7 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr, /* Check for any flags set in the pre-serialize callback */ if(serialize_flags != H5C__SERIALIZE_NO_FLAGS_SET) { /* Check for unexpected flags from serialize callback */ - if(serialize_flags & ~(H5C__SERIALIZE_RESIZED_FLAG | - H5C__SERIALIZE_MOVED_FLAG)) + if(serialize_flags & ~(H5C__SERIALIZE_RESIZED_FLAG | H5C__SERIALIZE_MOVED_FLAG)) HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unknown serialize flag(s)") #ifdef H5_HAVE_PARALLEL @@ -8266,9 +8362,8 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr, /* Update statistics for resizing the entry */ H5C__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, entry_ptr, new_len); - /* update the hash table for the size change */ - H5C__UPDATE_INDEX_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, \ - new_len, entry_ptr, !(entry_ptr->is_dirty)); + /* Update the hash table for the size change */ + H5C__UPDATE_INDEX_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_len, entry_ptr, !(entry_ptr->is_dirty)); /* The entry can't be protected since we are in the process of * flushing it. Thus we must update the replacement policy data @@ -8281,10 +8376,11 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr, * for the flush or flush destroy yet, the entry should * be in the slist. Thus update it for the size change. */ + HDassert(entry_ptr->is_dirty); HDassert(entry_ptr->in_slist); H5C__UPDATE_SLIST_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_len); - /* finally, update the entry for its new size */ + /* Finally, update the entry for its new size */ entry_ptr->size = new_len; } /* end if */ @@ -8301,10 +8397,10 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr, H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr, FAIL); H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr, FALSE); - /* update the entry for its new address */ + /* Update the entry for its new address */ entry_ptr->addr = new_addr; - /* and then reinsert in the index and slist */ + /* And then reinsert in the index and slist */ H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL); H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL); } /* end if */ diff --git a/src/H5Cdbg.c b/src/H5Cdbg.c index 5697bff..7f4c8e0 100644 --- a/src/H5Cdbg.c +++ b/src/H5Cdbg.c @@ -35,6 +35,7 @@ /* Headers */ /***********/ #include "H5private.h" /* Generic Functions */ +#include "H5ACprivate.h" /* Metadata Cache */ #include "H5Cpkg.h" /* Cache */ #include "H5Eprivate.h" /* Error Handling */ @@ -385,6 +386,7 @@ H5C_stats(H5C_t * cache_ptr, size_t aggregate_max_size = 0; int32_t aggregate_max_pins = 0; double hit_rate; + double prefetch_use_rate; double average_successful_search_depth = 0.0f; double average_failed_search_depth = 0.0f; double average_entries_skipped_per_calls_to_msic = 0.0f; @@ -490,12 +492,12 @@ H5C_stats(H5C_t * cache_ptr, average_failed_search_depth); HDfprintf(stdout, - "%s current (max) index size / length = %ld (%ld) / %ld (%ld)\n", + "%s current (max) index size / length = %ld (%ld) / %lu (%lu)\n", cache_ptr->prefix, (long)(cache_ptr->index_size), (long)(cache_ptr->max_index_size), - (long)(cache_ptr->index_len), - (long)(cache_ptr->max_index_len)); + (unsigned long)(cache_ptr->index_len), + (unsigned long)(cache_ptr->max_index_len)); HDfprintf(stdout, "%s current (max) clean/dirty idx size = %ld (%ld) / %ld (%ld)\n", @@ -506,46 +508,46 @@ H5C_stats(H5C_t * cache_ptr, (long)(cache_ptr->max_dirty_index_size)); HDfprintf(stdout, - "%s current (max) slist size / length = %ld (%ld) / %ld (%ld)\n", + "%s current (max) slist size / length = %ld (%ld) / %lu (%lu)\n", cache_ptr->prefix, (long)(cache_ptr->slist_size), (long)(cache_ptr->max_slist_size), - (long)(cache_ptr->slist_len), - (long)(cache_ptr->max_slist_len)); + (unsigned long)(cache_ptr->slist_len), + (unsigned long)(cache_ptr->max_slist_len)); HDfprintf(stdout, - "%s current (max) PL size / length = %ld (%ld) / %ld (%ld)\n", + "%s current (max) PL size / length = %ld (%ld) / %lu (%lu)\n", cache_ptr->prefix, (long)(cache_ptr->pl_size), (long)(cache_ptr->max_pl_size), - (long)(cache_ptr->pl_len), - (long)(cache_ptr->max_pl_len)); + (unsigned long)(cache_ptr->pl_len), + (unsigned long)(cache_ptr->max_pl_len)); HDfprintf(stdout, - "%s current (max) PEL size / length = %ld (%ld) / %ld (%ld)\n", + "%s current (max) PEL size / length = %ld (%ld) / %lu (%lu)\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)); + (unsigned long)(cache_ptr->pel_len), + (unsigned long)(cache_ptr->max_pel_len)); HDfprintf(stdout, - "%s current LRU list size / length = %ld / %ld\n", + "%s current LRU list size / length = %ld / %lu\n", cache_ptr->prefix, (long)(cache_ptr->LRU_list_size), - (long)(cache_ptr->LRU_list_len)); + (unsigned long)(cache_ptr->LRU_list_len)); HDfprintf(stdout, - "%s current clean LRU size / length = %ld / %ld\n", + "%s current clean LRU size / length = %ld / %lu\n", cache_ptr->prefix, (long)(cache_ptr->cLRU_list_size), - (long)(cache_ptr->cLRU_list_len)); + (unsigned long)(cache_ptr->cLRU_list_len)); HDfprintf(stdout, - "%s current dirty LRU size / length = %ld / %ld\n", + "%s current dirty LRU size / length = %ld / %lu\n", cache_ptr->prefix, (long)(cache_ptr->dLRU_list_size), - (long)(cache_ptr->dLRU_list_len)); + (unsigned long)(cache_ptr->dLRU_list_len)); HDfprintf(stdout, "%s Total hits / misses / hit_rate = %ld / %ld / %f\n", @@ -648,6 +650,38 @@ H5C_stats(H5C_t * cache_ptr, (long long)(cache_ptr->LRU_scan_restarts), (long long)(cache_ptr->index_scan_restarts)); + HDfprintf(stdout, + "%s cache image creations/loads/size = %d / %d / %lld\n", + cache_ptr->prefix, + cache_ptr->images_created, + cache_ptr->images_loaded, + (long long)cache_ptr->last_image_size); + + HDfprintf(stdout, + "%s prefetches / dirty prefetches = %lld / %lld\n", + cache_ptr->prefix, + (long long)(cache_ptr->prefetches), + (long long)(cache_ptr->dirty_prefetches)); + + HDfprintf(stdout, + "%s prefetch hits/flushes/evictions = %lld / %lld / %lld\n", + cache_ptr->prefix, + (long long)(cache_ptr->prefetch_hits), + (long long)(cache_ptr->flushes[H5AC_PREFETCHED_ENTRY_ID]), + (long long)(cache_ptr->evictions[H5AC_PREFETCHED_ENTRY_ID])); + + if(cache_ptr->prefetches > 0) + prefetch_use_rate = + (double)100.0f * ((double)(cache_ptr->prefetch_hits)) / + ((double)(cache_ptr->prefetches)); + else + prefetch_use_rate = 0.0f; + + HDfprintf(stdout, + "%s prefetched entry use rate = %lf\n", + cache_ptr->prefix, + prefetch_use_rate); + #if H5C_COLLECT_CACHE_ENTRY_STATS HDfprintf(stdout, "%s aggregate max / min accesses = %d / %d\n", @@ -673,7 +707,7 @@ H5C_stats(H5C_t * cache_ptr, HDfprintf(stdout, "%s Stats on %s:\n", cache_ptr->prefix, - ((cache_ptr->type_name_table_ptr))[i]); + ((cache_ptr->class_table_ptr))[i]->name); if((cache_ptr->hits[i] > 0) || (cache_ptr->misses[i] > 0)) hit_rate = (double)100.0f * ((double)(cache_ptr->hits[i])) / @@ -869,6 +903,14 @@ H5C_stats__reset(H5C_t H5_ATTR_UNUSED * cache_ptr) cache_ptr->LRU_scan_restarts = 0; cache_ptr->index_scan_restarts = 0; + cache_ptr->images_created = 0; + cache_ptr->images_loaded = 0; + cache_ptr->last_image_size = (size_t)0; + + cache_ptr->prefetches = 0; + cache_ptr->dirty_prefetches = 0; + cache_ptr->prefetch_hits = 0; + #if H5C_COLLECT_CACHE_ENTRY_STATS for(i = 0; i <= cache_ptr->max_type_id; i++) { cache_ptr->max_accesses[i] = 0; @@ -952,6 +994,303 @@ H5C__dump_entry(H5C_t *cache_ptr, const H5C_cache_entry_t *entry_ptr, if(entry_ptr->flush_dep_nchildren) H5C__dump_children(cache_ptr, entry_ptr, FALSE, "Child", indent); } /* end H5C__dump_entry() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_flush_dependency_exists() + * + * Purpose: Test to see if a flush dependency relationship exists + * between the supplied parent and child. Both parties + * are indicated by addresses so as to avoid the necessity + * of protect / unprotect calls prior to this call. + * + * If either the parent or the child is not in the metadata + * cache, the function sets *fd_exists_ptr to FALSE. + * + * If both are in the cache, the childs list of parents is + * searched for the proposed parent. If the proposed parent + * is found in the childs parent list, the function sets + * *fd_exists_ptr to TRUE. In all other non-error cases, + * the function sets *fd_exists_ptr FALSE. + * + * Return: SUCCEED on success/FAIL on failure. Note that + * *fd_exists_ptr is undefined on failure. + * + * Programmer: John Mainzer + * 9/28/16 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +herr_t +H5C_flush_dependency_exists(H5C_t *cache_ptr, haddr_t parent_addr, haddr_t child_addr, + hbool_t *fd_exists_ptr) +{ + hbool_t fd_exists = FALSE; /* whether flush dependency exists */ + H5C_cache_entry_t * parent_ptr; /* Ptr to parent entry */ + H5C_cache_entry_t * child_ptr; /* Ptr to child entry */ + hbool_t ret_value = FALSE; /* Return value */ + + FUNC_ENTER_NOAPI(NULL) + + /* Sanity checks */ + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(H5F_addr_defined(parent_addr)); + HDassert(H5F_addr_defined(child_addr)); + HDassert(fd_exists_ptr); + + H5C__SEARCH_INDEX(cache_ptr, parent_addr, parent_ptr, FAIL) + H5C__SEARCH_INDEX(cache_ptr, child_addr, child_ptr, FAIL) + + if(parent_ptr && child_ptr) { + HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(child_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + + if(child_ptr->flush_dep_nparents > 0) { + unsigned u; /* Local index variable */ + + HDassert(child_ptr->flush_dep_parent); + HDassert(child_ptr->flush_dep_parent_nalloc >= child_ptr->flush_dep_nparents); + + for(u = 0; u < child_ptr->flush_dep_nparents; u++) { + if(child_ptr->flush_dep_parent[u] == parent_ptr) { + fd_exists = TRUE; + HDassert(parent_ptr->flush_dep_nchildren > 0); + break; + } /* end if */ + } /* end for */ + } /* end if */ + } /* end if */ + + *fd_exists_ptr = fd_exists; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C_flush_dependency_exists() */ +#endif /* NDEBUG */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_validate_index_list + * + * Purpose: Debugging function that scans the index list for errors. + * + * If an error is detected, the function generates a + * diagnostic and returns FAIL. If no error is detected, + * the function returns SUCCEED. + * + * Return: FAIL if error is detected, SUCCEED otherwise. + * + * Programmer: John Mainzer, 9/16/16 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +herr_t +H5C_validate_index_list(H5C_t *cache_ptr) +{ + H5C_cache_entry_t * entry_ptr = NULL; + uint32_t len = 0; + int32_t index_ring_len[H5C_RING_NTYPES]; + size_t size = 0; + size_t clean_size = 0; + size_t dirty_size = 0; + size_t index_ring_size[H5C_RING_NTYPES]; + size_t clean_index_ring_size[H5C_RING_NTYPES]; + size_t dirty_index_ring_size[H5C_RING_NTYPES]; + int i; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT + + /* Sanity checks */ + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + + for(i = 0; i < H5C_RING_NTYPES; i++) { + index_ring_len[i] = 0; + index_ring_size[i] = 0; + clean_index_ring_size[i] = 0; + dirty_index_ring_size[i] = 0; + } /* end if */ + + if(((cache_ptr->il_head == NULL) || (cache_ptr->il_tail == NULL)) + && (cache_ptr->il_head != cache_ptr->il_tail)) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 1 failed") + + if((cache_ptr->index_len == 1) && ((cache_ptr->il_head != cache_ptr->il_tail) + || (cache_ptr->il_head == NULL) || (cache_ptr->il_head->size != cache_ptr->index_size))) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 2 failed") + + if((cache_ptr->index_len >= 1) + && ((cache_ptr->il_head == NULL) + || (cache_ptr->il_head->il_prev != NULL) + || (cache_ptr->il_tail == NULL) + || (cache_ptr->il_tail->il_next != NULL))) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 3 failed") + + entry_ptr = cache_ptr->il_head; + while(entry_ptr != NULL) { + if((entry_ptr != cache_ptr->il_head) + && ((entry_ptr->il_prev == NULL) || (entry_ptr->il_prev->il_next != entry_ptr))) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 4 failed") + + if((entry_ptr != cache_ptr->il_tail) + && ((entry_ptr->il_next == NULL) || (entry_ptr->il_next->il_prev != entry_ptr))) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 5 failed") + + HDassert(entry_ptr->ring > 0); + HDassert(entry_ptr->ring < H5C_RING_NTYPES); + + len++; + index_ring_len[entry_ptr->ring] += 1; + + size += entry_ptr->size; + index_ring_size[entry_ptr->ring] += entry_ptr->size; + + if(entry_ptr->is_dirty) { + dirty_size += entry_ptr->size; + dirty_index_ring_size[entry_ptr->ring] += entry_ptr->size; + } /* end if */ + else { + clean_size += entry_ptr->size; + clean_index_ring_size[entry_ptr->ring] += entry_ptr->size; + } /* end else */ + + entry_ptr = entry_ptr->il_next; + } /* end while */ + + if((cache_ptr->index_len != len) || (cache_ptr->il_len != len) + || (cache_ptr->index_size != size) || (cache_ptr->il_size != size) + || (cache_ptr->clean_index_size != clean_size) + || (cache_ptr->dirty_index_size != dirty_size) + || (clean_size + dirty_size != size)) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 6 failed") + + size = 0; + clean_size = 0; + dirty_size = 0; + for(i = 0; i < H5C_RING_NTYPES; i++) { + size += clean_index_ring_size[i] + dirty_index_ring_size[i]; + clean_size += clean_index_ring_size[i]; + dirty_size += dirty_index_ring_size[i]; + } /* end for */ + + if((cache_ptr->index_size != size) + || (cache_ptr->clean_index_size != clean_size) + || (cache_ptr->dirty_index_size != dirty_size)) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 7 failed") + +done: + if(ret_value != SUCCEED) + HDassert(0); + + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C_validate_index_list() */ +#endif /* NDEBUG */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_get_entry_ptr_from_addr() + * + * Purpose: Debugging function that attempts to look up an entry in the + * cache by its file address, and if found, returns a pointer + * to the entry in *entry_ptr_ptr. If the entry is not in the + * cache, *entry_ptr_ptr is set to NULL. + * + * WARNING: This call should be used only in debugging + * routines, and it should be avoided when + * possible. + * + * Further, if we ever multi-thread the cache, + * this routine will have to be either discarded + * or heavily re-worked. + * + * Finally, keep in mind that the entry whose + * pointer is obtained in this fashion may not + * be in a stable state. + * + * Note that this function is only defined if NDEBUG + * is not defined. + * + * As heavy use of this function is almost certainly a + * bad idea, the metadata cache tracks the number of + * successful calls to this function, and (if + * H5C_DO_SANITY_CHECKS is defined) displays any + * non-zero count on cache shutdown. + * + * Return: FAIL if error is detected, SUCCEED otherwise. + * + * Programmer: John Mainzer, 5/30/14 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +herr_t +H5C_get_entry_ptr_from_addr(H5C_t *cache_ptr, haddr_t addr, void **entry_ptr_ptr) +{ + H5C_cache_entry_t * entry_ptr = NULL; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + /* Sanity checks */ + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(H5F_addr_defined(addr)); + HDassert(entry_ptr_ptr); + + H5C__SEARCH_INDEX(cache_ptr, addr, entry_ptr, FAIL) + + if(entry_ptr == NULL) + /* the entry doesn't exist in the cache -- report this + * and quit. + */ + *entry_ptr_ptr = NULL; + else { + *entry_ptr_ptr = entry_ptr; + + /* increment call counter */ + (cache_ptr->get_entry_ptr_from_addr_counter)++; + } /* end else */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C_get_entry_ptr_from_addr() */ +#endif /* NDEBUG */ + + +/*------------------------------------------------------------------------- + * Function: H5C_get_serialization_in_progress + * + * Purpose: Return the current value of + * cache_ptr->serialization_in_progress. + * + * Return: Current value of cache_ptr->serialization_in_progress. + * + * Programmer: John Mainzer + * 8/24/15 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +hbool_t +H5C_get_serialization_in_progress(const H5C_t *cache_ptr) +{ + FUNC_ENTER_NOAPI_NOINIT_NOERR + + /* Sanity check */ + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + + FUNC_LEAVE_NOAPI(cache_ptr->serialization_in_progress) +} /* H5C_get_serialization_in_progress() */ +#endif /* NDEBUG */ + + /*------------------------------------------------------------------------- * * Function: H5C_cache_is_clean() @@ -995,3 +1334,69 @@ H5C_cache_is_clean(const H5C_t *cache_ptr, H5C_ring_t inner_ring) } /* H5C_cache_is_clean() */ #endif /* NDEBUG */ + +/*------------------------------------------------------------------------- + * + * Function: H5C_verify_entry_type() + * + * Purpose: Debugging function that attempts to look up an entry in the + * cache by its file address, and if found, test to see if its + * type field contains the expted value. + * + * If the specified entry is in cache, *in_cache_ptr is set + * to TRUE, and *type_ok_ptr is set to TRUE or FALSE depending + * on whether the entries type field matches the expected_type + * parameter. + * + * If the target entry is not in cache, *in_cache_ptr is + * set to FALSE, and *type_ok_ptr is undefined. + * + * Note that this function is only defined if NDEBUG + * is not defined. + * + * Return: FAIL if error is detected, SUCCEED otherwise. + * + * Programmer: John Mainzer, 5/30/14 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +herr_t +H5C_verify_entry_type(H5C_t *cache_ptr, haddr_t addr, + const H5C_class_t *expected_type, hbool_t *in_cache_ptr, + hbool_t *type_ok_ptr) +{ + H5C_cache_entry_t * entry_ptr = NULL; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + /* Sanity checks */ + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(H5F_addr_defined(addr)); + HDassert(expected_type); + HDassert(in_cache_ptr); + HDassert(type_ok_ptr); + + H5C__SEARCH_INDEX(cache_ptr, addr, entry_ptr, FAIL) + + if(entry_ptr == NULL) + /* the entry doesn't exist in the cache -- report this + * and quit. + */ + *in_cache_ptr = FALSE; + else { + *in_cache_ptr = TRUE; + + if(entry_ptr->prefetched) + *type_ok_ptr = (expected_type->id == entry_ptr->prefetch_type_id); + else + *type_ok_ptr = (expected_type == entry_ptr->type); + } /* end else */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C_verify_entry_type() */ +#endif /* NDEBUG */ + diff --git a/src/H5Cepoch.c b/src/H5Cepoch.c index e576028..655d795 100644 --- a/src/H5Cepoch.c +++ b/src/H5Cepoch.c @@ -92,8 +92,7 @@ static herr_t H5C__epoch_marker_fsf_size(const void H5_ATTR_UNUSED * thing, /*******************/ -const H5C_class_t H5C__epoch_marker_class = -{ +const H5AC_class_t H5AC_EPOCH_MARKER[1] = {{ /* id = */ H5AC_EPOCH_MARKER_ID, /* name = */ "epoch marker", /* mem_type = */ H5FD_MEM_DEFAULT, /* value doesn't matter */ @@ -108,7 +107,7 @@ const H5C_class_t H5C__epoch_marker_class = /* notify = */ H5C__epoch_marker_notify, /* free_icr = */ H5C__epoch_marker_free_icr, /* fsf_size = */ H5C__epoch_marker_fsf_size, -}; +}}; /*************************************************************************** diff --git a/src/H5Cimage.c b/src/H5Cimage.c new file mode 100644 index 0000000..dec8283 --- /dev/null +++ b/src/H5Cimage.c @@ -0,0 +1,4100 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * 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://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*------------------------------------------------------------------------- + * + * Created: H5Cimage.c + * July 20, 2015 + * John Mainzer + * + * Purpose: Functions in this file are specific to the implementation + * of the metadata cache image feature. + * + *------------------------------------------------------------------------- + */ + +/****************/ +/* Module Setup */ +/****************/ + +#include "H5Cmodule.h" /* This source code file is part of the H5C module */ +#define H5F_FRIEND /*suppress error about including H5Fpkg */ + + +/***********/ +/* Headers */ +/***********/ +#include "H5private.h" /* Generic Functions */ +#ifdef H5_HAVE_PARALLEL +#define H5AC_FRIEND /*suppress error about including H5ACpkg */ +#include "H5ACpkg.h" /* Metadata cache */ +#endif /* H5_HAVE_PARALLEL */ +#include "H5Cpkg.h" /* Cache */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5Fpkg.h" /* Files */ +#include "H5FDprivate.h" /* File drivers */ +#include "H5FLprivate.h" /* Free Lists */ +#include "H5MFprivate.h" /* File memory management */ +#include "H5MMprivate.h" /* Memory management */ + + +/****************/ +/* Local Macros */ +/****************/ +#if H5C_DO_MEMORY_SANITY_CHECKS +#define H5C_IMAGE_EXTRA_SPACE 8 +#define H5C_IMAGE_SANITY_VALUE "DeadBeef" +#else /* H5C_DO_MEMORY_SANITY_CHECKS */ +#define H5C_IMAGE_EXTRA_SPACE 0 +#endif /* H5C_DO_MEMORY_SANITY_CHECKS */ + +/* Cache image buffer components, on disk */ +#define H5C__MDCI_BLOCK_SIGNATURE "MDCI" +#define H5C__MDCI_BLOCK_SIGNATURE_LEN 4 +#define H5C__MDCI_BLOCK_VERSION_0 0 + +/* Metadata cache image header flags -- max 8 bits */ +#define H5C__MDCI_HEADER_HAVE_RESIZE_STATUS 0x01 + +/* Metadata cache image entry flags -- max 8 bits */ +#define H5C__MDCI_ENTRY_DIRTY_FLAG 0x01 +#define H5C__MDCI_ENTRY_IN_LRU_FLAG 0x02 +#define H5C__MDCI_ENTRY_IS_FD_PARENT_FLAG 0x04 +#define H5C__MDCI_ENTRY_IS_FD_CHILD_FLAG 0x08 + +/* Limits on flush dependency values, stored in 16-bit values on disk */ +#define H5C__MDCI_MAX_FD_CHILDREN USHRT_MAX +#define H5C__MDCI_MAX_FD_PARENTS USHRT_MAX + +/* Values for image entry magic field */ +#define H5C_IMAGE_ENTRY_T_MAGIC 0x005CAC08 +#define H5C_IMAGE_ENTRY_T_BAD_MAGIC 0xBeefDead + +/* Maximum ring allowed in image */ +#define H5C_MAX_RING_IN_IMAGE 3 + + +/******************/ +/* Local Typedefs */ +/******************/ + + +/********************/ +/* Local Prototypes */ +/********************/ + +/* Helper routines */ +static size_t H5C__cache_image_block_entry_header_size(const H5F_t *f); +static size_t H5C__cache_image_block_header_size(const H5F_t *f); +static herr_t H5C__decode_cache_image_buffer(const H5F_t *f, H5C_t *cache_ptr); +static herr_t H5C__decode_cache_image_header(const H5F_t *f, + H5C_t *cache_ptr, const uint8_t **buf); +static herr_t H5C__decode_cache_image_entry(const H5F_t *f, + const H5C_t *cache_ptr, const uint8_t **buf, unsigned entry_num); +static herr_t H5C__destroy_pf_entry_child_flush_deps(H5C_t *cache_ptr, + H5C_cache_entry_t *pf_entry_ptr, H5C_cache_entry_t **fd_children); +static herr_t H5C__encode_cache_image_header(const H5F_t *f, + const H5C_t *cache_ptr, uint8_t **buf); +static herr_t H5C__encode_cache_image_entry(H5F_t *f, H5C_t *cache_ptr, + uint8_t **buf, unsigned entry_num); +static herr_t H5C__prep_for_file_close__compute_fd_heights(const H5C_t *cache_ptr); +static void H5C__prep_for_file_close__compute_fd_heights_real( + H5C_cache_entry_t *entry_ptr, uint32_t fd_height); +static herr_t H5C__prep_for_file_close__setup_image_entries_array(H5C_t *cache_ptr); +static herr_t H5C__prep_for_file_close__scan_entries(const H5F_t *f, + H5C_t *cache_ptr); +static herr_t H5C__reconstruct_cache_contents(H5F_t *f, hid_t dxpl_id, + H5C_t *cache_ptr); +static H5C_cache_entry_t *H5C__reconstruct_cache_entry(H5C_t *cache_ptr, + unsigned index); +static herr_t H5C__serialize_cache(H5F_t *f, hid_t dxpl_id); +static herr_t H5C__serialize_ring(H5F_t *f, hid_t dxpl_id, + H5C_ring_t ring); +static herr_t H5C__serialize_single_entry(H5F_t *f, hid_t dxpl_id, + H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr, + hbool_t *restart_list_scan_ptr); +static herr_t H5C__write_cache_image_superblock_msg(H5F_t *f, hid_t dxpl_id, + hbool_t create); +static herr_t H5C__read_cache_image(H5F_t * f, hid_t dxpl_id, const H5C_t *cache_ptr); +static herr_t H5C__write_cache_image(H5F_t *f, hid_t dxpl_id, const H5C_t *cache_ptr); +static herr_t H5C__construct_cache_image_buffer(H5F_t *f, H5C_t *cache_ptr); +static herr_t H5C__free_image_entries_array(H5C_t *cache_ptr); + + +/*********************/ +/* Package Variables */ +/*********************/ + +/* Declare a free list to manage H5C_cache_entry_t objects */ +H5FL_DEFINE(H5C_cache_entry_t); + + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + + +/*******************/ +/* Local Variables */ +/*******************/ + + +/*------------------------------------------------------------------------- + * Function: H5C_cache_image_status() + * + * Purpose: Examine the metadata cache associated with the supplied + * instance of H5F_t to determine whether the load of a + * cache image has either been queued ir executed, and if + * construction of a cache image has been requested. + * + * This done, it set *load_ci_ptr to TRUE if a cache image + * has either been loaded or a load has been requested, and + * to FALSE otherwise. + * + * Similarly, set *write_ci_ptr to TRUE if construction of + * a cache image has been requested, and to FALSE otherwise. + * + * Return: SUCCEED on success, and FAIL on failure. + * + * Programmer: John Mainzer + * 12/29/16 + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_cache_image_status(H5F_t * f, hbool_t *load_ci_ptr, hbool_t *write_ci_ptr) +{ + H5C_t * cache_ptr; + + FUNC_ENTER_NOAPI_NOINIT_NOERR + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + cache_ptr = f->shared->cache; + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(load_ci_ptr); + HDassert(write_ci_ptr); + + *load_ci_ptr = cache_ptr->load_image || cache_ptr->image_loaded; + *write_ci_ptr = cache_ptr->image_ctl.generate_image; + + FUNC_LEAVE_NOAPI(SUCCEED) +} /* H5C_cache_image_status() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__construct_cache_image_buffer() + * + * Purpose: Allocate a buffer of size cache_ptr->image_len, and + * load it with an image of the metadata cache image block. + * + * Note that by the time this function is called, the cache + * should have removed all entries from its data structures. + * + * Return: SUCCEED on success, and FAIL on failure. + * + * Programmer: John Mainzer + * 8/5/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__construct_cache_image_buffer(H5F_t * f, H5C_t *cache_ptr) +{ + uint8_t * p; /* Pointer into image buffer */ + uint32_t chksum; + unsigned u; /* Local index variable */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(cache_ptr == f->shared->cache); + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(cache_ptr->close_warning_received); + HDassert(cache_ptr->image_ctl.generate_image); + HDassert(cache_ptr->num_entries_in_image > 0); + HDassert(cache_ptr->index_len == 0); + HDassert(cache_ptr->image_data_len > 0); + HDassert(cache_ptr->image_data_len <= cache_ptr->image_len); + + /* Allocate the buffer in which to construct the cache image block */ + if(NULL == (cache_ptr->image_buffer = H5MM_malloc(cache_ptr->image_len + 1))) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for cache image buffer") + + /* Construct the cache image block header image */ + p = (uint8_t *)cache_ptr->image_buffer; + if(H5C__encode_cache_image_header(f, cache_ptr, &p) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTENCODE, FAIL, "header image construction failed") + HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) < cache_ptr->image_data_len); + + /* Construct the cache entry images */ + for(u = 0; u < cache_ptr->num_entries_in_image; u++) + if(H5C__encode_cache_image_entry(f, cache_ptr, &p, u) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTENCODE, FAIL, "entry image construction failed") + HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) < cache_ptr->image_data_len); + + /* Construct the adaptive resize status image -- not yet */ + + /* Compute the checksum and encode */ + chksum = H5_checksum_metadata(cache_ptr->image_buffer, (size_t)(cache_ptr->image_data_len - H5F_SIZEOF_CHKSUM), 0); + UINT32ENCODE(p, chksum); + HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) == cache_ptr->image_data_len); + HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) <= cache_ptr->image_len); + +#ifndef NDEBUG + /* validate the metadata cache image we just constructed by decoding it + * and comparing the result with the original data. + */ + { + uint32_t old_chksum; + const uint8_t * q; + H5C_t * fake_cache_ptr = NULL; + unsigned v; + herr_t status; /* Status from decoding */ + + fake_cache_ptr = (H5C_t *)H5MM_malloc(sizeof(H5C_t)); + HDassert(fake_cache_ptr); + fake_cache_ptr->magic = H5C__H5C_T_MAGIC; + + /* needed for sanity checks */ + fake_cache_ptr->image_len = cache_ptr->image_len; + q = (const uint8_t *)cache_ptr->image_buffer; + status = H5C__decode_cache_image_header(f, fake_cache_ptr, &q); + HDassert(status >= 0); + + HDassert(NULL != p); + HDassert(fake_cache_ptr->num_entries_in_image == cache_ptr->num_entries_in_image); + + fake_cache_ptr->image_entries = (H5C_image_entry_t *)H5MM_malloc(sizeof(H5C_image_entry_t) * + (size_t)(fake_cache_ptr->num_entries_in_image + 1)); + HDassert(fake_cache_ptr->image_entries); + + for(u = 0; u < fake_cache_ptr->num_entries_in_image; u++) { + (fake_cache_ptr->image_entries)[u].magic = H5C_IMAGE_ENTRY_T_MAGIC; + (fake_cache_ptr->image_entries)[u].image_ptr = NULL; + + /* touch up f->shared->cache to satisfy sanity checks... */ + f->shared->cache = fake_cache_ptr; + status = H5C__decode_cache_image_entry(f, fake_cache_ptr, &q, u); + HDassert(status >= 0); + + /* ...and then return f->shared->cache to its correct value */ + f->shared->cache = cache_ptr; + + /* verify expected contents */ + HDassert((cache_ptr->image_entries)[u].addr == (fake_cache_ptr->image_entries)[u].addr); + HDassert((cache_ptr->image_entries)[u].size == (fake_cache_ptr->image_entries)[u].size); + HDassert((cache_ptr->image_entries)[u].type_id == (fake_cache_ptr->image_entries)[u].type_id); + HDassert((cache_ptr->image_entries)[u].lru_rank == (fake_cache_ptr->image_entries)[u].lru_rank); + HDassert((cache_ptr->image_entries)[u].is_dirty == (fake_cache_ptr->image_entries)[u].is_dirty); + /* don't check image_fd_height as it is not stored in + * the metadata cache image block. + */ + HDassert((cache_ptr->image_entries)[u].fd_child_count == (fake_cache_ptr->image_entries)[u].fd_child_count); + HDassert((cache_ptr->image_entries)[u].fd_dirty_child_count == (fake_cache_ptr->image_entries)[u].fd_dirty_child_count); + HDassert((cache_ptr->image_entries)[u].fd_parent_count == (fake_cache_ptr->image_entries)[u].fd_parent_count); + + for(v = 0; v < (cache_ptr->image_entries)[u].fd_parent_count; v++) + HDassert((cache_ptr->image_entries)[u].fd_parent_addrs[v] == (fake_cache_ptr->image_entries)[u].fd_parent_addrs[v]); + + /* free the fd_parent_addrs array if it exists */ + if((fake_cache_ptr->image_entries)[u].fd_parent_addrs) { + HDassert((fake_cache_ptr->image_entries)[u].fd_parent_count > 0); + (fake_cache_ptr->image_entries)[u].fd_parent_addrs = (haddr_t *)H5MM_xfree((fake_cache_ptr->image_entries)[u].fd_parent_addrs); + (fake_cache_ptr->image_entries)[u].fd_parent_count = 0; + } /* end if */ + else + HDassert((fake_cache_ptr->image_entries)[u].fd_parent_count == 0); + + HDassert((cache_ptr->image_entries)[u].image_ptr); + HDassert((fake_cache_ptr->image_entries)[u].image_ptr); + HDassert(!HDmemcmp((cache_ptr->image_entries)[u].image_ptr, + (fake_cache_ptr->image_entries)[u].image_ptr, + (cache_ptr->image_entries)[u].size)); + + (fake_cache_ptr->image_entries)[u].image_ptr = H5MM_xfree((fake_cache_ptr->image_entries)[u].image_ptr); + } /* end for */ + + HDassert((size_t)(q - (const uint8_t *)cache_ptr->image_buffer) == cache_ptr->image_data_len - H5F_SIZEOF_CHKSUM); + + /* compute the checksum */ + old_chksum = chksum; + chksum = H5_checksum_metadata(cache_ptr->image_buffer, (size_t)(cache_ptr->image_data_len - H5F_SIZEOF_CHKSUM), 0); + HDassert(chksum == old_chksum); + + fake_cache_ptr->image_entries = (H5C_image_entry_t *)H5MM_xfree(fake_cache_ptr->image_entries); + fake_cache_ptr = (H5C_t *)H5MM_xfree(fake_cache_ptr); + } /* end block */ +#endif /* NDEBUG */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__construct_cache_image_buffer() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__generate_cache_image() + * + * Purpose: Generate the cache image and write it to the file, if + * directed. + * + * Return: SUCCEED on success, and FAIL on failure. + * + * Programmer: Quincey Koziol + * 1/26/17 + * + *------------------------------------------------------------------------- + */ +herr_t +H5C__generate_cache_image(H5F_t *f, hid_t dxpl_id, H5C_t *cache_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_PACKAGE + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(cache_ptr == f->shared->cache); + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + + /* Construct cache image */ + if(H5C__construct_cache_image_buffer(f, cache_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't create metadata cache image") + + /* Free image entries array */ + if(H5C__free_image_entries_array(cache_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't free image entries array") + + /* Write cache image block if so configured */ + if(cache_ptr->image_ctl.flags & H5C_CI__GEN_MDC_IMAGE_BLK) { + if(H5C__write_cache_image(f, dxpl_id, cache_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't write metadata cache image block to file") + + H5C__UPDATE_STATS_FOR_CACHE_IMAGE_CREATE(cache_ptr); + } /* end if */ + + /* Free cache image buffer */ + HDassert(cache_ptr->image_buffer); + cache_ptr->image_buffer = H5MM_xfree(cache_ptr->image_buffer); + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__generate_cache_image() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__deserialize_prefetched_entry() + * + * Purpose: Deserialize the supplied prefetched entry entry, and return + * a pointer to the deserialized entry in *entry_ptr_ptr. + * If successful, remove the prefetched entry from the cache, + * and free it. Insert the deserialized entry into the cache. + * + * Note that the on disk image of the entry is not freed -- + * a pointer to it is stored in the deserialized entries' + * image_ptr field, and its image_up_to_date field is set to + * TRUE unless the entry is dirtied by the deserialize call. + * + * If the prefetched entry is a flush dependency child, + * destroy that flush dependency prior to calling the + * deserialize callback. If appropriate, the flush dependency + * relationship will be recreated by the cache client. + * + * If the prefetched entry is a flush dependency parent, + * destroy the flush dependency relationship with all its + * children. As all these children must be prefetched entries, + * recreate these flush dependency relationships with + * deserialized entry after it is inserted in the cache. + * + * Since deserializing a prefetched entry is semantically + * equivalent to a load, issue an entry loaded nofification + * if the notify callback is defined. + * + * Return: SUCCEED on success, and FAIL on failure. + * + * Note that *entry_ptr_ptr is undefined on failure. + * + * Programmer: John Mainzer, 8/10/15 + * + *------------------------------------------------------------------------- + */ +herr_t +H5C__deserialize_prefetched_entry(H5F_t *f, hid_t dxpl_id, H5C_t *cache_ptr, + H5C_cache_entry_t **entry_ptr_ptr, const H5C_class_t *type, + haddr_t addr, void *udata) +{ + hbool_t dirty = FALSE; /* Flag indicating whether thing was + * dirtied during deserialize + */ + size_t len; /* Size of image in file */ + void * thing = NULL; /* Pointer to thing loaded */ + H5C_cache_entry_t * pf_entry_ptr; /* pointer to the prefetched entry */ + /* supplied in *entry_ptr_ptr. */ + H5C_cache_entry_t * ds_entry_ptr; /* Alias for thing loaded, as cache + * entry + */ + H5C_cache_entry_t** fd_children = NULL; /* Pointer to a dynamically */ + /* allocated array of pointers to */ + /* the flush dependency children of */ + /* the prefetched entry, or NULL if */ + /* that array does not exist. */ + unsigned flush_flags = (H5C__FLUSH_INVALIDATE_FLAG | + H5C__FLUSH_CLEAR_ONLY_FLAG); + int i; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_PACKAGE + + /* sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(f->shared->cache); + HDassert(f->shared->cache == cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(entry_ptr_ptr); + HDassert(*entry_ptr_ptr); + pf_entry_ptr = *entry_ptr_ptr; + HDassert(pf_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(pf_entry_ptr->type); + HDassert(pf_entry_ptr->type->id == H5AC_PREFETCHED_ENTRY_ID); + HDassert(pf_entry_ptr->prefetched); + HDassert(pf_entry_ptr->image_up_to_date); + HDassert(pf_entry_ptr->image_ptr); + HDassert(pf_entry_ptr->size > 0); + HDassert(pf_entry_ptr->addr == addr); + HDassert(type); + HDassert(type->id == pf_entry_ptr->prefetch_type_id); + HDassert(type->mem_type == cache_ptr->class_table_ptr[type->id]->mem_type); + + /* verify absence of prohibited or unsupported type flag combinations */ + HDassert(!(type->flags & H5C__CLASS_SKIP_READS)); + + /* Can't see how skip reads could be usefully combined with + * either the speculative read flag. Hence disallow. + */ + HDassert(!((type->flags & H5C__CLASS_SKIP_READS) && + (type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG))); + + HDassert(H5F_addr_defined(addr)); + HDassert(type->get_initial_load_size); + HDassert(type->deserialize); + + /* if *pf_entry_ptr is a flush dependency child, destroy all such + * relationships now. The client will restore the relationship(s) with + * the deserialized entry if appropriate. + */ + for(i = (int)(pf_entry_ptr->fd_parent_count) - 1; i >= 0; i--) { + HDassert(pf_entry_ptr->flush_dep_parent); + HDassert(pf_entry_ptr->flush_dep_parent[i]); + HDassert(pf_entry_ptr->flush_dep_parent[i]->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(pf_entry_ptr->flush_dep_parent[i]->flush_dep_nchildren > 0); + HDassert(pf_entry_ptr->fd_parent_addrs); + HDassert(pf_entry_ptr->flush_dep_parent[i]->addr == pf_entry_ptr->fd_parent_addrs[i]); + + if(H5C_destroy_flush_dependency(pf_entry_ptr->flush_dep_parent[i], pf_entry_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "can't destroy pf entry parent flush dependency") + + pf_entry_ptr->fd_parent_addrs[i] = HADDR_UNDEF; + } /* end for */ + HDassert(pf_entry_ptr->flush_dep_nparents == 0); + + /* If *pf_entry_ptr is a flush dependency parent, destroy its flush + * dependency relationships with all its children (which must be + * prefetched entries as well). + * + * These flush dependency relationships will have to be restored + * after the deserialized entry is inserted into the cache in order + * to transfer these relationships to the new entry. Hence save the + * pointers to the flush dependency children of *pf_enty_ptr for later + * use. + */ + if(pf_entry_ptr->fd_child_count > 0) { + if(NULL == (fd_children = (H5C_cache_entry_t **)H5MM_calloc(sizeof(H5C_cache_entry_t **) * (size_t)(pf_entry_ptr->fd_child_count + 1)))) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for fd child ptr array") + + if(H5C__destroy_pf_entry_child_flush_deps(cache_ptr, pf_entry_ptr, fd_children) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "can't destroy pf entry child flush dependency(s).") + } /* end if */ + + /* Since the size of the on disk image is known exactly, there is + * no need for either a call to the get_initial_load_size() callback, + * or retries if the H5C__CLASS_SPECULATIVE_LOAD_FLAG flag is set. + * Similarly, there is no need to clamp possible reads beyond + * EOF. + */ + len = pf_entry_ptr->size; + + /* Deserialize the prefetched on-disk image of the entry into the + * native memory form + */ + if(NULL == (thing = type->deserialize(pf_entry_ptr->image_ptr, len, udata, &dirty))) + HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, FAIL, "Can't deserialize image") + + ds_entry_ptr = (H5C_cache_entry_t *)thing; + + /* In general, an entry should be clean just after it is loaded. + * + * However, when this code is used in the metadata cache, it is + * possible that object headers will be dirty at this point, as + * the deserialize function will alter object headers if necessary to + * fix an old bug. + * + * In the following assert: + * + * HDassert( ( dirty == FALSE ) || ( type->id == 5 || type->id == 6 ) ); + * + * note that type ids 5 & 6 are associated with object headers in the + * metadata cache. + * + * When we get to using H5C for other purposes, we may wish to + * tighten up the assert so that the loophole only applies to the + * metadata cache. + * + * Note that at present, dirty can't be set to true with prefetched + * entries. However this may change, so include this functionality + * against that posibility. + * + * Also, note that it is possible for a prefetched entry to be dirty -- + * hence the value assigned to ds_entry_ptr->is_dirty below. + */ + + HDassert( ( dirty == FALSE ) || ( type->id == 5 || type->id == 6) ); + + ds_entry_ptr->magic = H5C__H5C_CACHE_ENTRY_T_MAGIC; + ds_entry_ptr->cache_ptr = f->shared->cache; + ds_entry_ptr->addr = addr; + ds_entry_ptr->size = len; + HDassert(ds_entry_ptr->size < H5C_MAX_ENTRY_SIZE); + ds_entry_ptr->image_ptr = pf_entry_ptr->image_ptr; + ds_entry_ptr->image_up_to_date = !dirty; + ds_entry_ptr->type = type; + ds_entry_ptr->is_dirty = dirty | + pf_entry_ptr->is_dirty; + ds_entry_ptr->dirtied = FALSE; + ds_entry_ptr->is_protected = FALSE; + ds_entry_ptr->is_read_only = FALSE; + ds_entry_ptr->ro_ref_count = 0; + ds_entry_ptr->is_pinned = FALSE; + ds_entry_ptr->in_slist = FALSE; + ds_entry_ptr->flush_marker = FALSE; +#ifdef H5_HAVE_PARALLEL + ds_entry_ptr->clear_on_unprotect = FALSE; + ds_entry_ptr->flush_immediately = FALSE; + ds_entry_ptr->coll_access = FALSE; +#endif /* H5_HAVE_PARALLEL */ + ds_entry_ptr->flush_in_progress = FALSE; + ds_entry_ptr->destroy_in_progress = FALSE; + + ds_entry_ptr->ring = pf_entry_ptr->ring; + + /* Initialize flush dependency height fields */ + ds_entry_ptr->flush_dep_parent = NULL; + ds_entry_ptr->flush_dep_nparents = 0; + ds_entry_ptr->flush_dep_parent_nalloc = 0; + ds_entry_ptr->flush_dep_nchildren = 0; + ds_entry_ptr->flush_dep_ndirty_children = 0; + ds_entry_ptr->flush_dep_nunser_children = 0; + + /* Initialize fields supporting the hash table: */ + ds_entry_ptr->ht_next = NULL; + ds_entry_ptr->ht_prev = NULL; + ds_entry_ptr->il_next = NULL; + ds_entry_ptr->il_prev = NULL; + + /* Initialize fields supporting replacement policies: */ + ds_entry_ptr->next = NULL; + ds_entry_ptr->prev = NULL; + ds_entry_ptr->aux_next = NULL; + ds_entry_ptr->aux_prev = NULL; +#ifdef H5_HAVE_PARALLEL + pf_entry_ptr->coll_next = NULL; + pf_entry_ptr->coll_prev = NULL; +#endif /* H5_HAVE_PARALLEL */ + + /* initialize cache image related fields */ + ds_entry_ptr->include_in_image = FALSE; + ds_entry_ptr->lru_rank = 0; + ds_entry_ptr->image_dirty = FALSE; + ds_entry_ptr->fd_parent_count = 0; + ds_entry_ptr->fd_parent_addrs = NULL; + ds_entry_ptr->fd_child_count = pf_entry_ptr->fd_child_count; + ds_entry_ptr->fd_dirty_child_count = 0; + ds_entry_ptr->image_fd_height = 0; + ds_entry_ptr->prefetched = FALSE; + ds_entry_ptr->prefetch_type_id = 0; + ds_entry_ptr->age = 0; + + H5C__RESET_CACHE_ENTRY_STATS(ds_entry_ptr); + + /* Apply to to the newly deserialized entry */ + if(H5C__tag_entry(cache_ptr, ds_entry_ptr, dxpl_id) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTTAG, FAIL, "Cannot tag metadata entry") + + /* We have successfully deserialized the prefetched entry. + * + * Before we return a pointer to the deserialized entry, we must remove + * the prefetched entry from the cache, discard it, and replace it with + * the deserialized entry. Note that we do not free the prefetched + * entries image, as that has been transferred to the deserialized + * entry. + * + * Also note that we have not yet restored any flush dependencies. This + * must wait until the deserialized entry is inserted in the cache. + * + * To delete the prefetched entry from the cache: + * + * 1) Set pf_entry_ptr->image_ptr to NULL. Since we have already + * transferred the buffer containing the image to *ds_entry_ptr, + * this is not a memory leak. + * + * 2) Call H5C__flush_single_entry() with the H5C__FLUSH_INVALIDATE_FLAG + * and H5C__FLUSH_CLEAR_ONLY_FLAG flags set. + */ + pf_entry_ptr->image_ptr = NULL; + if ( pf_entry_ptr->is_dirty ) { + HDassert(pf_entry_ptr->in_slist); + flush_flags |= H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG; + } /* end if */ + + if(H5C__flush_single_entry(f, dxpl_id, pf_entry_ptr, flush_flags) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, "can't expunge prefetched entry") + +#ifndef NDEGUG /* verify deletion */ + H5C__SEARCH_INDEX(cache_ptr, addr, pf_entry_ptr, FAIL); + + HDassert(NULL == pf_entry_ptr); +#endif /* NDEBUG */ + + /* Insert the deserialized entry into the cache. */ + H5C__INSERT_IN_INDEX(cache_ptr, ds_entry_ptr, FAIL) + + HDassert(!ds_entry_ptr->in_slist); + + if(ds_entry_ptr->is_dirty) + H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, ds_entry_ptr, FAIL) + + H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, ds_entry_ptr, FAIL) + + /* Deserializing a prefetched entry is the conceptual equivalent of + * loading it from file. If the deserialized entry has a notify callback, + * send an "after load" notice now that the deserialized entry is fully + * integrated into the cache. + */ + if(ds_entry_ptr->type->notify && + (ds_entry_ptr->type->notify)(H5C_NOTIFY_ACTION_AFTER_LOAD, ds_entry_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry loaded into cache") + + /* restore flush dependencies with the flush dependency children of + * of the prefetched entry. Note that we must protect *ds_entry_ptr + * before the call to avoid triggering sanity check failures, and + * then unprotect it afterwards. + */ + i = 0; + if(fd_children != NULL) { + int j; + hbool_t found; + + H5C__UPDATE_RP_FOR_PROTECT(cache_ptr, ds_entry_ptr, FAIL) + ds_entry_ptr->is_protected = TRUE; + while(fd_children[i] != NULL) { + /* Sanity checks */ + HDassert((fd_children[i])->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert((fd_children[i])->prefetched); + HDassert((fd_children[i])->fd_parent_count > 0); + HDassert((fd_children[i])->fd_parent_addrs); + + j = 0; + found = FALSE; + while((j < (int)((fd_children[i])->fd_parent_count)) && (!found)) { + if((fd_children[i])->fd_parent_addrs[j] == ds_entry_ptr->addr) + found = TRUE; + + j++; + } /* end while */ + HDassert(found); + + if(H5C_create_flush_dependency(ds_entry_ptr, fd_children[i]) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTDEPEND, FAIL, "Can't restore child flush dependency.") + + i++; + } /* end while */ + + H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, ds_entry_ptr, FAIL); + + ds_entry_ptr->is_protected = FALSE; + } /* end if ( fd_children != NULL ) */ + HDassert((unsigned)i == ds_entry_ptr->fd_child_count); + + ds_entry_ptr->fd_child_count = 0; + H5C__UPDATE_STATS_FOR_PREFETCH_HIT(cache_ptr) + + /* finally, pass ds_entry_ptr back to the caller */ + *entry_ptr_ptr = ds_entry_ptr; + +done: + if(fd_children) + fd_children = (H5C_cache_entry_t **)H5MM_xfree((void *)fd_children); + + /* Release resources on error */ + if(FAIL == ret_value) + if(thing && type->free_icr(thing) < 0) + HDONE_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "free_icr callback failed") + + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__deserialize_prefetched_entry() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__free_image_entries_array + * + * Purpose: If the image entries array exists, free the image + * associated with each entry, and then free the image + * entries array proper. + * + * Note that by the time this function is called, the cache + * should have removed all entries from its data structures. + * + * Return: SUCCEED on success, and FAIL on failure. + * + * Programmer: John Mainzer + * 8/4/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__free_image_entries_array(H5C_t * cache_ptr) +{ + FUNC_ENTER_STATIC_NOERR + + /* Sanity checks */ + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(cache_ptr->close_warning_received); + HDassert(cache_ptr->image_ctl.generate_image); + HDassert(cache_ptr->index_len == 0); + + /* Check for entries to free */ + if(cache_ptr->image_entries != NULL) { + unsigned u; /* Local index variable */ + + for(u = 0; u < cache_ptr->num_entries_in_image; u++) { + H5C_image_entry_t *ie_ptr; /* Image entry to release */ + + /* Get pointer to image entry */ + ie_ptr = &((cache_ptr->image_entries)[u]); + + /* Sanity checks */ + HDassert(ie_ptr); + HDassert(ie_ptr->magic == H5C_IMAGE_ENTRY_T_MAGIC); + HDassert(ie_ptr->image_ptr); + + /* Free the parent addrs array if appropriate */ + if(ie_ptr->fd_parent_addrs) { + HDassert(ie_ptr->fd_parent_count > 0); + + ie_ptr->fd_parent_addrs = (haddr_t *)H5MM_xfree(ie_ptr->fd_parent_addrs); + } /* end if */ + else + HDassert(ie_ptr->fd_parent_count == 0); + + /* Free the image */ + ie_ptr->image_ptr = H5MM_xfree(ie_ptr->image_ptr); + + /* Set magic field to bad magic so we can detect freed entries */ + ie_ptr->magic = H5C_IMAGE_ENTRY_T_BAD_MAGIC; + } /* end for */ + + /* Free the image entries array */ + cache_ptr->image_entries = (H5C_image_entry_t *)H5MM_xfree(cache_ptr->image_entries); + } /* end if */ + + FUNC_LEAVE_NOAPI(SUCCEED) +} /* H5C__free_image_entries_array() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_get_cache_image_config + * + * Purpose: Copy the current configuration for cache image generation + * on file close into the instance of H5C_cache_image_ctl_t + * pointed to by config_ptr. + * + * Return: SUCCEED on success, and FAIL on failure. + * + * Programmer: John Mainzer + * 7/3/15 + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_get_cache_image_config(const H5C_t * cache_ptr, + H5C_cache_image_ctl_t *config_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + if((cache_ptr == NULL) || (cache_ptr->magic != H5C__H5C_T_MAGIC)) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad cache_ptr on entry") + if(config_ptr == NULL) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad config_ptr on entry") + + *config_ptr = cache_ptr->image_ctl; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C_get_cache_image_config() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_image_stats + * + * Purpose: Prints statistics specific to the cache image. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 10/26/15 + * + *------------------------------------------------------------------------- + */ +herr_t +#if H5C_COLLECT_CACHE_STATS +H5C_image_stats(H5C_t * cache_ptr, hbool_t print_header) +#else /* H5C_COLLECT_CACHE_STATS */ +H5C_image_stats(H5C_t * cache_ptr, hbool_t H5_ATTR_UNUSED print_header) +#endif /* H5C_COLLECT_CACHE_STATS */ +{ +#if H5C_COLLECT_CACHE_STATS + int i; + int64_t total_hits = 0; + int64_t total_misses = 0; + double hit_rate; + double prefetch_use_rate; +#endif /* H5C_COLLECT_CACHE_STATS */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + if(!cache_ptr || cache_ptr->magic != H5C__H5C_T_MAGIC) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr") + +#if H5C_COLLECT_CACHE_STATS + for(i = 0; i <= cache_ptr->max_type_id; i++) { + total_hits += cache_ptr->hits[i]; + total_misses += cache_ptr->misses[i]; + } /* end for */ + + if((total_hits > 0) || (total_misses > 0)) + hit_rate = (double)100.0f * ((double)(total_hits)) / ((double)(total_hits + total_misses)); + else + hit_rate = 0.0f; + + if(cache_ptr->prefetches > 0) + prefetch_use_rate = (double)100.0f * ((double)(cache_ptr->prefetch_hits)) / + ((double)(cache_ptr->prefetches)); + else + prefetch_use_rate = 0.0f; + + if(print_header) { + HDfprintf(stdout, + "\nhit prefetches prefetch image pf hit\n"); + HDfprintf(stdout, + "rate: total: dirty: hits: flshs: evct: size: rate:\n"); + } /* end if */ + + HDfprintf(stdout, + "%3.1lf %5lld %5lld %5lld %5lld %5lld %5lld %3.1lf\n", + hit_rate, + (long long)(cache_ptr->prefetches), + (long long)(cache_ptr->dirty_prefetches), + (long long)(cache_ptr->prefetch_hits), + (long long)(cache_ptr->flushes[H5AC_PREFETCHED_ENTRY_ID]), + (long long)(cache_ptr->evictions[H5AC_PREFETCHED_ENTRY_ID]), + (long long)(cache_ptr->last_image_size), + prefetch_use_rate); +#endif /* H5C_COLLECT_CACHE_STATS */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C_image_stats() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__read_cache_image + * + * Purpose: Load the metadata cache image from the specified location + * in the file, and return it in the supplied buffer. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 8/16/15 + * + *------------------------------------------------------------------------- + */ +herr_t +H5C__read_cache_image(H5F_t *f, hid_t dxpl_id, const H5C_t *cache_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(f); + HDassert(cache_ptr); + HDassert(H5F_addr_defined(cache_ptr->image_addr)); + HDassert(cache_ptr->image_len > 0); + HDassert(cache_ptr->image_buffer); + +#ifdef H5_HAVE_PARALLEL +{ + H5AC_aux_t *aux_ptr = (H5AC_aux_t *)cache_ptr->aux_ptr; + int mpi_result; + + if((NULL == aux_ptr) || (aux_ptr->mpi_rank == 0)) { + HDassert((NULL == aux_ptr) || (aux_ptr->magic == H5AC__H5AC_AUX_T_MAGIC)); +#endif /* H5_HAVE_PARALLEL */ + + /* Read the buffer (if serial access, or rank 0 of parallel access) */ + if(H5F_block_read(f, H5FD_MEM_SUPER, cache_ptr->image_addr, cache_ptr->image_len, dxpl_id, cache_ptr->image_buffer) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_READERROR, FAIL, "Can't read metadata cache image block") + +#ifdef H5_HAVE_PARALLEL + if(aux_ptr) { + /* Broadcast cache image */ + if(MPI_SUCCESS != (mpi_result = MPI_Bcast(cache_ptr->image_buffer, (int)cache_ptr->image_len, MPI_BYTE, 0, aux_ptr->mpi_comm))) + HMPI_GOTO_ERROR(FAIL, "MPI_Bcast failed", mpi_result) + } /* end if */ + } /* end if */ + else if(aux_ptr) { + /* Retrieve the contents of the metadata cache image from process 0 */ + if(MPI_SUCCESS != (mpi_result = MPI_Bcast(cache_ptr->image_buffer, (int)cache_ptr->image_len, MPI_BYTE, 0, aux_ptr->mpi_comm))) + HMPI_GOTO_ERROR(FAIL, "can't receive cache image MPI_Bcast", mpi_result) + } /* end else-if */ +} /* end block */ +#endif /* H5_HAVE_PARALLEL */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__read_cache_image() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__load_cache_image + * + * Purpose: Read the cache image superblock extension message and + * delete it if so directed. + * + * Then load the cache image block at the specified location, + * decode it, and insert its contents into the metadata + * cache. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 7/6/15 + * + *------------------------------------------------------------------------- + */ +herr_t +H5C__load_cache_image(H5F_t *f, hid_t dxpl_id) +{ + H5C_t * cache_ptr; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_PACKAGE + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + cache_ptr = f->shared->cache; + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + + /* If the image address is defined, load the image, decode it, + * and insert its contents into the metadata cache. + * + * Note that under normal operating conditions, it is an error if the + * image address is HADDR_UNDEF. However, to facilitate testing, + * we allow this special value of the image address which means that + * no image exists, and that the load operation should be skipped + * silently. + */ + if(H5F_addr_defined(cache_ptr->image_addr)) { + /* Sanity checks */ + HDassert(cache_ptr->image_len > 0); + HDassert(cache_ptr->image_buffer == NULL); + + /* Allocate space for the image */ + if(NULL == (cache_ptr->image_buffer = H5MM_malloc(cache_ptr->image_len + 1))) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for cache image buffer") + + /* Load the image from file */ + if(H5C__read_cache_image(f, dxpl_id, cache_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_READERROR, FAIL, "Can't read metadata cache image block") + + /* Decode metadata cache image */ + if(H5C__decode_cache_image_buffer(f, cache_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTDECODE, FAIL, "Can't decode metadata cache image block") + + /* At this point, the image_data_len should be known */ + HDassert(cache_ptr->image_data_len > 0); + HDassert(cache_ptr->image_data_len <= cache_ptr->image_len); + + /* Insert image contents into cache */ + if(H5C__reconstruct_cache_contents(f, dxpl_id, cache_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTDECODE, FAIL, "Can't reconstruct cache contents from image block") + + /* Free the image buffer */ + cache_ptr->image_buffer = H5MM_xfree(cache_ptr->image_buffer); + + /* Update stats -- must do this now, as we are about + * to discard the size of the cache image. + */ + H5C__UPDATE_STATS_FOR_CACHE_IMAGE_LOAD(cache_ptr) + + /* Free the image entries array. Note that all on disk image + * image buffers and fd parent address arrays have been transferred + * to their respective prefetched entries so we can just free the + * array of H5C_image_entry_t. + */ +#ifndef NDEBUG + { + unsigned u; + + for(u = 0; u < cache_ptr->num_entries_in_image; u++) { + H5C_image_entry_t * ie_ptr; + + ie_ptr = &((cache_ptr->image_entries)[u]); + + HDassert(ie_ptr); + HDassert(ie_ptr->magic == H5C_IMAGE_ENTRY_T_MAGIC); + HDassert(ie_ptr->fd_parent_addrs == NULL); + HDassert(ie_ptr->image_ptr == NULL); + } /* end for */ + } /* end block */ +#endif /* NDEBUG */ + + cache_ptr->image_entries = (H5C_image_entry_t *)H5MM_xfree(cache_ptr->image_entries); + cache_ptr->num_entries_in_image = 0; + + cache_ptr->image_loaded = TRUE; + } /* end if */ + + /* If directed, free the on disk metadata cache image */ + if(cache_ptr->delete_image) { + if(H5F_super_ext_remove_msg(f, dxpl_id, H5O_MDCI_MSG_ID) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove metadata cache image message from superblock extension") + + /* Reset image block values */ + cache_ptr->image_len = 0; + cache_ptr->image_data_len = 0; + cache_ptr->image_addr = HADDR_UNDEF; + } /* end if */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__load_cache_image() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_load_cache_image_on_next_protect() + * + * Purpose: Note the fact that a metadata cache image superblock + * extension message exists, along with the base address + * and length of the metadata cache image block. + * + * Once this notification is received the metadata cache + * image block must be read, decoded, and loaded into the + * cache on the next call to H5C_protect(). + * + * Further, if the file is opened R/W, the metadata cache + * image superblock extension message must be deleted from + * the superblock extension and the image block freed + * + * Contrawise, if the file is openened R/O, the metadata + * cache image superblock extension message and image block + * must be left as is. Further, any dirty entries in the + * cache image block must be marked as clean to avoid + * attempts to write them on file close. + * + * Return: SUCCEED + * + * Programmer: John Mainzer + * 7/6/15 + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_load_cache_image_on_next_protect(H5F_t *f, haddr_t addr, hsize_t len, + hbool_t rw) +{ + H5C_t *cache_ptr; + + FUNC_ENTER_NOAPI_NOINIT_NOERR + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + cache_ptr = f->shared->cache; + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + + /* Set information needed to load cache image */ + cache_ptr->image_addr = addr, + cache_ptr->image_len = len; + cache_ptr->load_image = TRUE; + cache_ptr->delete_image = rw; + + FUNC_LEAVE_NOAPI(SUCCEED) +} /* H5C_load_cache_image_on_next_protect() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__image_entry_cmp + * + * Purpose: Comparison callback for qsort(3) on image entries. + * Entries are sorted first by flush dependency height, + * and then by LRU rank. + * + * Note: Entries with a _greater_ flush dependency height should + * be sorted earlier than entries with lower heights, since + * leafs in the flush dependency graph are at height 0, and their + * parents need to be earlier in the image, so that they can + * construct their flush dependencies when decoded. + * + * Return: An integer less than, equal to, or greater than zero if the + * first entry is considered to be respectively less than, + * equal to, or greater than the second. + * + * Programmer: Quincey Koziol + * 1/20/16 + * + *------------------------------------------------------------------------- + */ +static int +H5C__image_entry_cmp(const void *_entry1, const void *_entry2) +{ + const H5C_image_entry_t *entry1 = (const H5C_image_entry_t *)_entry1; /* Pointer to first image entry to compare */ + const H5C_image_entry_t *entry2 = (const H5C_image_entry_t *)_entry2; /* Pointer to second image entry to compare */ + int ret_value = 0; /* Return value */ + + FUNC_ENTER_STATIC_NOERR + + /* Sanity checks */ + HDassert(entry1); + HDassert(entry2); + + if(entry1->image_fd_height > entry2->image_fd_height) + ret_value = -1; + else if(entry1->image_fd_height < entry2->image_fd_height) + ret_value = 1; + else { + /* Sanity check */ + HDassert(entry1->lru_rank >= -1); + HDassert(entry2->lru_rank >= -1); + + if(entry1->lru_rank < entry2->lru_rank) + ret_value = -1; + else if(entry1->lru_rank > entry2->lru_rank) + ret_value = 1; + } /* end else */ + + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__image_entry_cmp() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__prep_image_for_file_close + * + * Purpose: The objective of the call is to allow the metadata cache + * to do any preparatory work prior to generation of a + * cache image. + * + * In particular, the cache must + * + * 1) serialize all its entries, + * + * 2) compute the size of the metadata cache image, + * + * 3) allocate space for the metadata cache image, and + * + * 4) setup the metadata cache image superblock extension + * message with the address and size of the metadata + * cache image. + * + * The parallel case is complicated by the fact that + * while all metadata caches must contain the same set of + * dirty entries, there is no such requirement for clean + * entries or the order that entries appear in the LRU. + * + * Thus, there is no requirement that different processes + * will construct cache images of the same size. + * + * This is not a major issue as long as all processes include + * the same set of dirty entries in the cache -- as they + * currently do (note that this will change when we implement + * the ageout feature). Since only the process zero cache + * writes the cache image, all that is necessary is to + * broadcast the process zero cache size for use in the + * superblock extension messages and cache image block + * allocations. + * + * Note: At present, cache image is disabled in the + * parallel case as the new collective metadata write + * code must be modified to support cache image. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 7/3/15 + * + *------------------------------------------------------------------------- + */ +herr_t +H5C__prep_image_for_file_close(H5F_t *f, hid_t dxpl_id) +{ + H5C_t * cache_ptr = NULL; + haddr_t eoa_frag_addr = HADDR_UNDEF; + hsize_t eoa_frag_size = 0; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_PACKAGE + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(f->shared->cache); + cache_ptr = f->shared->cache; + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + + /* Create the cache image super block extension message. + * + * Note that the base address and length of the metadata cache + * image are undefined at this point, and thus will have to be + * updated later. + * + * Create the super block extension message now so that space + * is allocated for it (if necessary) before we allocate space + * for the cache image block. + * + * To simplify testing, do this only if the + * H5C_CI__GEN_MDCI_SBE_MESG bit is set in + * cache_ptr->image_ctl.flags. + */ + if(cache_ptr->image_ctl.flags & H5C_CI__GEN_MDCI_SBE_MESG) + if(H5C__write_cache_image_superblock_msg(f, dxpl_id, TRUE) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "creation of cache image SB mesg failed.") + + /* Serialize the cache */ + if(H5C__serialize_cache(f, dxpl_id) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "serialization of the cache failed") + + /* Scan the cache and record data needed to construct the + * cache image. In particular, for each entry we must record: + * + * 1) rank in LRU (if entry is in LRU) + * + * 2) Whether the entry is dirty prior to flush of + * cache just prior to close. + * + * 3) Addresses of flush dependency parents (if any). + * + * 4) Number of flush dependency children (if any). + * + * In passing, also compute the size of the metadata cache + * image. With the recent modifications of the free space + * manager code, this size should be correct. + */ + if(H5C__prep_for_file_close__scan_entries(f, cache_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C__prep_for_file_close__scan_entries failed") + HDassert(HADDR_UNDEF == cache_ptr->image_addr); + +#ifdef H5_HAVE_PARALLEL + /* In the parallel case, overwrite the image_len with the + * value computed by process 0. + */ + if(cache_ptr->aux_ptr) { /* we have multiple processes */ + int mpi_result; + unsigned p0_image_len; + H5AC_aux_t * aux_ptr; + + aux_ptr = (H5AC_aux_t *)cache_ptr->aux_ptr; + if(aux_ptr->mpi_rank == 0) { + aux_ptr->p0_image_len = (unsigned)cache_ptr->image_data_len; + p0_image_len = aux_ptr->p0_image_len; + + if(MPI_SUCCESS != (mpi_result = MPI_Bcast(&p0_image_len, 1, MPI_UNSIGNED, 0, aux_ptr->mpi_comm))) + HMPI_GOTO_ERROR(FAIL, "MPI_Bcast failed", mpi_result) + + HDassert(p0_image_len == aux_ptr->p0_image_len); + } /* end if */ + else { + if(MPI_SUCCESS != (mpi_result = MPI_Bcast(&p0_image_len, 1, MPI_UNSIGNED, 0, aux_ptr->mpi_comm))) + HMPI_GOTO_ERROR(FAIL, "MPI_Bcast failed", mpi_result) + + aux_ptr->p0_image_len = p0_image_len; + } /* end else */ + + /* Allocate space for a cache image of size equal to that + * computed by the process 0. This may be different from + * cache_ptr->image_data_len if mpi_rank != 0. However, since + * cache image write is suppressed on all processes other than + * process 0, this doesn't matter. + * + * Note that we allocate the cache image directly from the file + * driver so as to avoid unsettling the free space managers. + */ + if(HADDR_UNDEF == (cache_ptr->image_addr = H5FD_alloc(f->shared->lf, dxpl_id, H5FD_MEM_SUPER, f, + (hsize_t)p0_image_len, &eoa_frag_addr, &eoa_frag_size))) + HGOTO_ERROR(H5E_CACHE, H5E_NOSPACE, FAIL, "can't allocate file space for metadata cache image") + } /* end if */ + else +#endif /* H5_HAVE_PARALLEL */ + /* Allocate the cache image block. Note that we allocate this + * this space directly from the file driver so as to avoid + * unsettling the free space managers. + */ + if(HADDR_UNDEF == (cache_ptr->image_addr = H5FD_alloc(f->shared->lf, dxpl_id, H5FD_MEM_SUPER, f, + (hsize_t)(cache_ptr->image_data_len), &eoa_frag_addr, &eoa_frag_size))) + HGOTO_ERROR(H5E_CACHE, H5E_NOSPACE, FAIL, "can't allocate file space for metadata cache image") + + /* For now, drop any fragment left over from the allocation of the + * image block on the ground. A fragment should only be returned + * if the underlying file alignment is greater than 1. + * + * Clean this up eventually by extending the size of the cache + * image block to the next alignement boundary, and then setting + * the image_data_len to the actual size of the cache_image. + * + * On the off chance that there is some other way to get a + * a fragment on a cache image allocation, leave the following + * assertion in the code so we will find out. + */ + HDassert((eoa_frag_size == 0) || (f->shared->alignment != 1)); + + /* Eventually it will be possible for the length of the cache image + * block on file to be greater than the size of the data it + * contains. However, for now they must be the same. Set + * cache_ptr->image_len accordingly. + */ + cache_ptr->image_len = cache_ptr->image_data_len; + + /* update the metadata cache image superblock extension + * message with the new cache image block base address and + * length. + * + * to simplify testing, do this only if the + * H5C_CI__GEN_MDC_IMAGE_BLK bit is set in + * cache_ptr->image_ctl.flags. + */ + if(cache_ptr->image_ctl.flags & H5C_CI__GEN_MDC_IMAGE_BLK) + if(H5C__write_cache_image_superblock_msg(f, dxpl_id, FALSE) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "update of cache image SB mesg failed.") + + /* At this point: + * + * 1) space in the file for the metadata cache image + * is allocated, + * + * 2) the metadata cache image superblock extension + * message exists and (if so configured) contains + * the correct data, + * + * 3) All entries in the cache that will appear in the + * cache image are serialized with up to date images. + * + * Since we just updated the cache image message, + * the super block extension message is dirty. However, + * since the superblock and the superblock extension + * can't be included in the cache image, this is a non- + * issue. + * + * 4) All entries in the cache that will be include in + * the cache are marked as such, and we have a count + * of same. + * + * 5) Flush dependency heights are calculated for all + * entries that will be included in the cache image. + * + * If there are any entries to be included in the metadata cache + * image, allocate, populate, and sort the image_entries array. + * + * If the metadata cache image will be empty, delete the + * metadata cache image superblock extension message, set + * cache_ptr->image_ctl.generate_image to FALSE. This will + * allow the file close to continue normally without the + * unecessary generation of the metadata cache image. + */ + if(cache_ptr->num_entries_in_image > 0) { + if(H5C__prep_for_file_close__setup_image_entries_array(cache_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTINIT, FAIL, "can't setup image entries array.") + + /* Sort the entries */ + HDqsort(cache_ptr->image_entries, (size_t)cache_ptr->num_entries_in_image, + sizeof(H5C_image_entry_t), H5C__image_entry_cmp); + } /* end if */ + else { /* cancel creation of metadata cache iamge */ + HDassert(cache_ptr->image_entries == NULL); + + /* To avoid breaking the control flow tests, only delete + * the mdci superblock extension message if the + * H5C_CI__GEN_MDC_IMAGE_BLK flag is set in + * cache_ptr->image_ctl.flags. + */ + if(cache_ptr->image_ctl.flags & H5C_CI__GEN_MDC_IMAGE_BLK) + if(H5F_super_ext_remove_msg(f, dxpl_id, H5O_MDCI_MSG_ID) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove MDC image msg from superblock ext.") + + cache_ptr->image_ctl.generate_image = FALSE; + } /* end else */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__prep_image_for_file_close() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_set_cache_image_config + * + * Purpose: If *config_ptr contains valid data, copy it into the + * image_ctl field of *cache_ptr. Make adjustments for + * changes in configuration as required. + * + * Fail if the new configuration is invalid. + * + * Return: SUCCEED on success, and FAIL on failure. + * + * Programmer: John Mainzer + * 7/3/15 + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_set_cache_image_config(const H5F_t *f, H5C_t *cache_ptr, + H5C_cache_image_ctl_t *config_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(f->shared->cache == f->shared->cache); + + /* Check arguments */ + if((cache_ptr == NULL) || (cache_ptr->magic != H5C__H5C_T_MAGIC)) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad cache_ptr on entry") + if(config_ptr == NULL) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "NULL config_ptr on entry") + if(config_ptr->version != H5C__CURR_CACHE_IMAGE_CTL_VER) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Unknown config version") + + /* check general configuration section of the config: */ + if(H5C_validate_cache_image_config(config_ptr) < 0) + HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "invalid cache image configuration") + + if(H5F_INTENT(f) & H5F_ACC_RDWR) /* file has been opened R/W */ + cache_ptr->image_ctl = *config_ptr; + else { /* file opened R/O -- suppress cache image silently */ + H5C_cache_image_ctl_t default_image_ctl = H5C__DEFAULT_CACHE_IMAGE_CTL; + + cache_ptr->image_ctl = default_image_ctl; + HDassert(!(cache_ptr->image_ctl.generate_image)); + } /* end else */ + +#ifdef H5_HAVE_PARALLEL + /* the collective metadata write code is not currently compatible + * with cache image. Until this is fixed, suppress cache image silently + * if there is more than one process. + * JRM -- 11/8/16 + */ + if(cache_ptr->aux_ptr) { + H5C_cache_image_ctl_t default_image_ctl = H5C__DEFAULT_CACHE_IMAGE_CTL; + + cache_ptr->image_ctl = default_image_ctl; + HDassert(!(cache_ptr->image_ctl.generate_image)); + } /* end if */ +#endif /* H5_HAVE_PARALLEL */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C_set_cache_image_config() */ + + +/*------------------------------------------------------------------------- + * Function: H5C_validate_cache_image_config() + * + * Purpose: Run a sanity check on the provided instance of struct + * H5AC_cache_image_config_t. + * + * Do nothing and return SUCCEED if no errors are detected, + * and flag an error and return FAIL otherwise. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/15/15 + * + *------------------------------------------------------------------------- + */ +herr_t +H5C_validate_cache_image_config(H5C_cache_image_ctl_t * ctl_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + if(ctl_ptr == NULL) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "NULL ctl_ptr on entry") + if(ctl_ptr->version != H5C__CURR_CACHE_IMAGE_CTL_VER) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown cache image control version") + + /* At present, we do not support inclusion of the adaptive resize + * configuration in the cache image. Thus the save_resize_status + * field must be FALSE. + */ + if(ctl_ptr->save_resize_status != FALSE) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "unexpected value in save_resize_status field") + + /* At present, we do not support prefetched entry ageouts. Thus + * the entry_ageout field must be set to + * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE. + */ + if(ctl_ptr->entry_ageout != H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "unexpected value in entry_ageout field") + + if((ctl_ptr->flags & ~H5C_CI__ALL_FLAGS) != 0) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "unknown flag set") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C_validate_cache_image_config() */ + + +/*************************************************************************/ +/**************************** Private Functions: *************************/ +/*************************************************************************/ + +/*------------------------------------------------------------------------- + * Function: H5C__cache_image_block_entry_header_size + * + * Purpose: Compute the size of the header of the metadata cache + * image block, and return the value. + * + * Return: Size of the header section of the metadata cache image + * block in bytes. + * + * Programmer: John Mainzer + * 7/27/15 + * + *------------------------------------------------------------------------- + */ +static size_t +H5C__cache_image_block_entry_header_size(const H5F_t * f) +{ + size_t ret_value = 0; /* Return value */ + + FUNC_ENTER_STATIC_NOERR + + /* Set return value */ + ret_value = (size_t)( 1 + /* type */ + 1 + /* flags */ + 1 + /* ring */ + 1 + /* age */ + 2 + /* dependency child count */ + 2 + /* dirty dep child count */ + 2 + /* dependency parent count */ + 4 + /* index in LRU */ + H5F_SIZEOF_ADDR(f) + /* entry offset */ + H5F_SIZEOF_SIZE(f) ); /* entry length */ + + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__cache_image_block_entry_header_size() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__cache_image_block_header_size + * + * Purpose: Compute the size of the header of the metadata cache + * image block, and return the value. + * + * Return: Size of the header section of the metadata cache image + * block in bytes. + * + * Programmer: John Mainzer + * 7/27/15 + * + *------------------------------------------------------------------------- + */ +static size_t +H5C__cache_image_block_header_size(const H5F_t * f) +{ + size_t ret_value = 0; /* Return value */ + + FUNC_ENTER_STATIC_NOERR + + /* Set return value */ + ret_value = (size_t)( 4 + /* signature */ + 1 + /* version */ + 1 + /* flags */ + H5F_SIZEOF_SIZE(f) + /* image data length */ + 4 ); /* num_entries */ + + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__cache_image_block_header_size() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__decode_cache_image_buffer() + * + * Purpose: Allocate a suitably size array of instances of + * H5C_image_entry_t and and set cache_ptr->image_entries + * to point to this array. Set cache_ptr->num_entries_in_image + * equal to the number of entries in this array. + * + * Decode the contents of cache_ptr->image_buffer into the + * array. + * + * Return: SUCCEED on success, and FAIL on failure. + * + * Programmer: John Mainzer + * 8/9/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__decode_cache_image_buffer(const H5F_t *f, H5C_t *cache_ptr) +{ + uint32_t read_chksum; + uint32_t computed_chksum; + const uint8_t * p; + unsigned u; /* Local index variable */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(cache_ptr == f->shared->cache); + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(cache_ptr->image_buffer); + HDassert(cache_ptr->image_len > 0); + HDassert(cache_ptr->image_data_len == 0); + HDassert(cache_ptr->image_entries == NULL); + HDassert(cache_ptr->num_entries_in_image == 0); + + /* Decode metadata cache image header */ + p = (uint8_t *)cache_ptr->image_buffer; + if(H5C__decode_cache_image_header(f, cache_ptr, &p) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTDECODE, FAIL, "cache image header decode failed") + HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) < cache_ptr->image_len); + + /* cache_ptr->image_data_len should be defined now */ + HDassert(cache_ptr->image_data_len > 0); + HDassert(cache_ptr->image_data_len <= cache_ptr->image_len); + + /* We should now have cache_ptr->num_entries_in_image -- allocate the + * image entries array. + */ + HDassert(cache_ptr->num_entries_in_image > 0); + if(NULL == (cache_ptr->image_entries = (H5C_image_entry_t *)H5MM_malloc(sizeof(H5C_image_entry_t) * (size_t)(cache_ptr->num_entries_in_image + 1)))) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed image entries array") + + /* Load the image entries */ + for(u = 0; u < cache_ptr->num_entries_in_image; u++) { + (cache_ptr->image_entries)[u].magic = H5C_IMAGE_ENTRY_T_MAGIC; + (cache_ptr->image_entries)[u].image_fd_height = 0; + (cache_ptr->image_entries)[u].image_ptr = NULL; + + if(H5C__decode_cache_image_entry(f, cache_ptr, &p, u) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTDECODE, FAIL, "entry image decode failed") + } /* end for */ + HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) < cache_ptr->image_len); + + /* (Load the adaptive cache resize status -- not yet) */ + + /* Verify the checksum */ + UINT32DECODE(p, read_chksum); + HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) == cache_ptr->image_data_len); + HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) <= cache_ptr->image_len); + computed_chksum = H5_checksum_metadata(cache_ptr->image_buffer, (size_t)(cache_ptr->image_data_len - H5F_SIZEOF_CHKSUM), 0); + if(read_chksum != computed_chksum) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "bad checksum on metadata cache image block") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__decode_cache_image_buffer() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__decode_cache_image_header() + * + * Purpose: Decode the metadata cache image buffer header from the + * supplied buffer and load the data into the supplied instance + * of H5C_t. Advances the buffer pointer to the first byte + * after the header image, or unchanged on failure. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 8/6/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__decode_cache_image_header(const H5F_t *f, H5C_t *cache_ptr, + const uint8_t **buf) +{ + uint8_t version; + uint8_t flags; + hbool_t have_resize_status = FALSE; + size_t actual_header_len; + size_t expected_header_len; + const uint8_t * p; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(buf); + HDassert(*buf); + + /* Point to buffer to decode */ + p = *buf; + + /* Check signature */ + if(HDmemcmp(p, H5C__MDCI_BLOCK_SIGNATURE, (size_t)H5C__MDCI_BLOCK_SIGNATURE_LEN)) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad metadata cache image header signature") + p += H5C__MDCI_BLOCK_SIGNATURE_LEN; + + /* Check version */ + version = *p++; + if(version != (uint8_t)H5C__MDCI_BLOCK_VERSION_0) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad metadata cache image version") + + /* Decode flags */ + flags = *p++; + if(flags & H5C__MDCI_HEADER_HAVE_RESIZE_STATUS) + have_resize_status = TRUE; + if(have_resize_status) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "MDC resize status not yet supported") + + /* Read image data length */ + H5F_DECODE_LENGTH(f, p, cache_ptr->image_data_len); + + /* For now -- will become <= eventually */ + if(cache_ptr->image_data_len != cache_ptr->image_len) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad metadata cache image data length") + + /* Read num entries */ + UINT32DECODE(p, cache_ptr->num_entries_in_image); + if(cache_ptr->num_entries_in_image == 0) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad metadata cache entry count") + + /* Verify expected length of header */ + actual_header_len = (size_t)(p - *buf); + expected_header_len = H5C__cache_image_block_header_size(f); + if(actual_header_len != expected_header_len) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad header image len.") + + /* Update buffer pointer */ + *buf = p; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__decode_cache_image_header() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__decode_cache_image_entry() + * + * Purpose: Decode the metadata cache image entry from the supplied + * buffer into the supplied instance of H5C_image_entry_t. + * This includes allocating a buffer for the entry image, + * loading it, and seting ie_ptr->image_ptr to point to + * the buffer. + * + * Advances the buffer pointer to the first byte + * after the entry, or unchanged on failure. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 8/6/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__decode_cache_image_entry(const H5F_t *f, const H5C_t *cache_ptr, + const uint8_t **buf, unsigned entry_num) +{ + hbool_t is_dirty = FALSE; +#ifndef NDEBUG /* only used in assertions */ + hbool_t in_lru = FALSE; + hbool_t is_fd_parent = FALSE; + hbool_t is_fd_child = FALSE; +#endif /* NDEBUG */ /* only used in assertions */ + haddr_t addr; + hsize_t size = 0; + void * image_ptr; + uint8_t flags = 0; + uint8_t type_id; + uint8_t ring; + uint8_t age; + uint16_t fd_child_count; + uint16_t fd_dirty_child_count; + uint16_t fd_parent_count; + haddr_t * fd_parent_addrs = NULL; + int32_t lru_rank; + H5C_image_entry_t * ie_ptr = NULL; + const uint8_t * p; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(cache_ptr == f->shared->cache); + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(buf); + HDassert(*buf); + HDassert(entry_num < cache_ptr->num_entries_in_image); + ie_ptr = &((cache_ptr->image_entries)[entry_num]); + HDassert(ie_ptr); + HDassert(ie_ptr->magic == H5C_IMAGE_ENTRY_T_MAGIC); + + /* Get pointer to buffer */ + p = *buf; + + /* Decode type id */ + type_id = *p++; + + /* Decode flags */ + flags = *p++; + if(flags & H5C__MDCI_ENTRY_DIRTY_FLAG) + is_dirty = TRUE; +#ifndef NDEBUG /* only used in assertions */ + if(flags & H5C__MDCI_ENTRY_IN_LRU_FLAG) + in_lru = TRUE; + if(flags & H5C__MDCI_ENTRY_IS_FD_PARENT_FLAG) + is_fd_parent = TRUE; + if(flags & H5C__MDCI_ENTRY_IS_FD_CHILD_FLAG) + is_fd_child = TRUE; +#endif /* NDEBUG */ /* only used in assertions */ + + /* Decode ring */ + ring = *p++; + HDassert(ring > (uint8_t)(H5C_RING_UNDEFINED)); + HDassert(ring < (uint8_t)(H5C_RING_NTYPES)); + + /* Decode age */ + age = *p++; + + /* Decode dependency child count */ + UINT16DECODE(p, fd_child_count); + HDassert((is_fd_parent && fd_child_count > 0) || (!is_fd_parent && fd_child_count == 0)); + + /* Decode dirty dependency child count */ + UINT16DECODE(p, fd_dirty_child_count); + if(fd_dirty_child_count > fd_child_count) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "invalid dirty flush dependency child count") + + /* Decode dependency parent count */ + UINT16DECODE(p, fd_parent_count); + HDassert((is_fd_child && fd_parent_count > 0) || (!is_fd_child && fd_parent_count == 0)); + + /* Decode index in LRU */ + INT32DECODE(p, lru_rank); + HDassert((in_lru && lru_rank >= 0) || (!in_lru && lru_rank == -1)); + + /* Decode entry offset */ + H5F_addr_decode(f, &p, &addr); + if(!H5F_addr_defined(addr)) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "invalid entry offset") + + /* Decode entry length */ + H5F_DECODE_LENGTH(f, p, size); + if(size == 0) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "invalid entry size") + + /* Verify expected length of entry image */ + if((size_t)(p - *buf) != H5C__cache_image_block_entry_header_size(f)) + HGOTO_ERROR(H5E_CACHE, H5E_BADSIZE, FAIL, "Bad entry image len") + + /* If parent count greater than zero, allocate array for parent + * addresses, and decode addresses into the array. + */ + if(fd_parent_count > 0) { + int i; /* Local index variable */ + + if(NULL == (fd_parent_addrs = (haddr_t *)H5MM_malloc((size_t)(fd_parent_count) * H5F_SIZEOF_ADDR(f)))) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for fd parent addrs buffer") + + for(i = 0; i < fd_parent_count; i++) { + H5F_addr_decode(f, &p, &(fd_parent_addrs[i])); + if(!H5F_addr_defined(fd_parent_addrs[i])) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "invalid flush dependency parent offset") + } /* end for */ + } /* end if */ + + /* Allocate buffer for entry image */ + if(NULL == (image_ptr = H5MM_malloc(size + H5C_IMAGE_EXTRA_SPACE))) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for on disk image buffer") + +#if H5C_DO_MEMORY_SANITY_CHECKS + HDmemcpy(((uint8_t *)image_ptr) + size, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE); +#endif /* H5C_DO_MEMORY_SANITY_CHECKS */ + + /* Copy the entry image from the cache image block */ + HDmemcpy(image_ptr, p, size); + p += size; + + /* Copy data into target */ + ie_ptr->addr = addr; + ie_ptr->size = size; + ie_ptr->ring = (H5C_ring_t)ring; + ie_ptr->age = (int32_t)age; + ie_ptr->type_id = (int32_t)type_id; + ie_ptr->lru_rank = lru_rank; + ie_ptr->is_dirty = is_dirty; + ie_ptr->fd_child_count = (uint64_t)fd_child_count; + ie_ptr->fd_dirty_child_count = (uint64_t)fd_dirty_child_count; + ie_ptr->fd_parent_count = (uint64_t)fd_parent_count; + ie_ptr->fd_parent_addrs = fd_parent_addrs; + ie_ptr->image_ptr = image_ptr; + + /* Update buffer pointer */ + *buf = p; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__decode_cache_image_entry() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__destroy_pf_entry_child_flush_deps() + * + * Purpose: Destroy all flush dependencies in this the supplied + * prefetched entry is the parent. Note that the children + * in these flush dependencies must be prefetched entries as + * well. + * + * As this action is part of the process of transferring all + * such flush dependencies to the deserialized version of the + * prefetched entry, ensure that the data necessary to complete + * the transfer is retained. + * + * Note: The current implementation of this function is + * quite inefficient -- mostly due to the current + * implementation of flush dependencies. This should + * be fixed at some point. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 8/11/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__destroy_pf_entry_child_flush_deps(H5C_t *cache_ptr, + H5C_cache_entry_t *pf_entry_ptr, H5C_cache_entry_t **fd_children) +{ + H5C_cache_entry_t * entry_ptr; + unsigned entries_visited = 0; + int fd_children_found = 0; + hbool_t found; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(pf_entry_ptr); + HDassert(pf_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(pf_entry_ptr->type); + HDassert(pf_entry_ptr->type->id == H5AC_PREFETCHED_ENTRY_ID); + HDassert(pf_entry_ptr->prefetched); + HDassert(pf_entry_ptr->fd_child_count > 0); + HDassert(fd_children); + + /* Scan each entry on the index list */ + entry_ptr = cache_ptr->il_head; + while(entry_ptr != NULL) { + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + + /* Here we look at entry_ptr->flush_dep_nparents and not + * entry_ptr->fd_parent_count as it is possible that some + * or all of the prefetched flush dependency child relationships + * have already been destroyed. + */ + if(entry_ptr->prefetched && (entry_ptr->flush_dep_nparents > 0)) { + unsigned u; /* Local index variable */ + + /* Re-init */ + u = 0; + found = FALSE; + + /* Sanity checks */ + HDassert(entry_ptr->type); + HDassert(entry_ptr->type->id == H5AC_PREFETCHED_ENTRY_ID); + HDassert(entry_ptr->fd_parent_count >= entry_ptr->flush_dep_nparents); + HDassert(entry_ptr->fd_parent_addrs); + HDassert(entry_ptr->flush_dep_parent); + + /* Look for correct entry */ + while(!found && (u < entry_ptr->fd_parent_count)) { + /* Sanity check entry */ + HDassert(entry_ptr->flush_dep_parent[u]); + HDassert(entry_ptr->flush_dep_parent[u]->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + + /* Correct entry? */ + if(pf_entry_ptr == entry_ptr->flush_dep_parent[u]) + found = TRUE; + + u++; + } /* end while */ + + if(found) { + HDassert(NULL == fd_children[fd_children_found]); + + /* Remove flush dependency */ + fd_children[fd_children_found] = entry_ptr; + fd_children_found++; + if(H5C_destroy_flush_dependency(pf_entry_ptr, entry_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "can't destroy pf entry child flush dependency") + +#ifndef NDEBUG + /* Sanity check -- verify that the address of the parent + * appears in entry_ptr->fd_parent_addrs. Must do a search, + * as with flush dependency creates and destroys, + * entry_ptr->fd_parent_addrs and entry_ptr->flush_dep_parent + * can list parents in different order. + */ + found = FALSE; + u = 0; + while(!found && u < entry_ptr->fd_parent_count) { + if(pf_entry_ptr->addr == entry_ptr->fd_parent_addrs[u]) + found = TRUE; + u++; + } /* end while */ + HDassert(found); +#endif /* NDEBUG */ + } /* end if */ + } /* end if */ + + entries_visited++; + entry_ptr = entry_ptr->il_next; + } /* end while */ + + /* Post-op sanity checks */ + HDassert(NULL == fd_children[fd_children_found]); + HDassert((unsigned)fd_children_found == pf_entry_ptr->fd_child_count); + HDassert(entries_visited == cache_ptr->index_len); + HDassert(!pf_entry_ptr->is_pinned); + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__destroy_pf_entry_child_flush_deps() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__encode_cache_image_header() + * + * Purpose: Encode the metadata cache image buffer header in the + * supplied buffer. Updates buffer pointer to the first byte + * after the header image in the buffer, or unchanged on failure. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 8/6/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__encode_cache_image_header(const H5F_t *f, const H5C_t *cache_ptr, + uint8_t **buf) +{ + size_t actual_header_len; + size_t expected_header_len; + uint8_t flags = 0; + uint8_t * p; /* Pointer into cache image buffer */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(cache_ptr->close_warning_received); + HDassert(cache_ptr->image_ctl.generate_image); + HDassert(cache_ptr->index_len == 0); + HDassert(cache_ptr->image_data_len > 0); + HDassert(cache_ptr->image_data_len <= cache_ptr->image_len); + HDassert(buf); + HDassert(*buf); + + /* Set pointer into buffer */ + p = *buf; + + /* write signature */ + HDmemcpy(p, H5C__MDCI_BLOCK_SIGNATURE, (size_t)H5C__MDCI_BLOCK_SIGNATURE_LEN); + p += H5C__MDCI_BLOCK_SIGNATURE_LEN; + + /* write version */ + *p++ = (uint8_t)H5C__MDCI_BLOCK_VERSION_0; + + /* setup and write flags */ + + /* at present we don't support saving resize status */ + HDassert(!cache_ptr->image_ctl.save_resize_status); + if(cache_ptr->image_ctl.save_resize_status) + flags |= H5C__MDCI_HEADER_HAVE_RESIZE_STATUS; + + *p++ = flags; + + /* Encode image data length */ + /* this must be true at present */ + HDassert(cache_ptr->image_len == cache_ptr->image_data_len); + H5F_ENCODE_LENGTH(f, p, cache_ptr->image_data_len); + + /* write num entries */ + UINT32ENCODE(p, cache_ptr->num_entries_in_image); + + /* verify expected length of header */ + actual_header_len = (size_t)(p - *buf); + expected_header_len = H5C__cache_image_block_header_size(f); + if(actual_header_len != expected_header_len) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad header image len") + + /* Update buffer pointer */ + *buf = p; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__encode_cache_image_header() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__encode_cache_image_entry() + * + * Purpose: Encode the metadata cache image buffer header in the + * supplied buffer. Updates buffer pointer to the first byte + * after the entry in the buffer, or unchanged on failure. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 8/6/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__encode_cache_image_entry(H5F_t *f, H5C_t *cache_ptr, uint8_t **buf, + unsigned entry_num) +{ + H5C_image_entry_t * ie_ptr; /* Pointer to entry to encode */ + uint8_t flags = 0; /* Flags for entry */ + uint8_t * p; /* Pointer into cache image buffer */ + unsigned u; /* Local index value */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(cache_ptr == f->shared->cache); + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(cache_ptr->close_warning_received); + HDassert(cache_ptr->image_ctl.generate_image); + HDassert(cache_ptr->index_len == 0); + HDassert(buf); + HDassert(*buf); + HDassert(entry_num < cache_ptr->num_entries_in_image); + ie_ptr = &((cache_ptr->image_entries)[entry_num]); + HDassert(ie_ptr->magic == H5C_IMAGE_ENTRY_T_MAGIC); + + /* Get pointer to buffer to encode into */ + p = *buf; + + /* Encode type */ + if((ie_ptr->type_id < 0) || (ie_ptr->type_id > 255)) + HGOTO_ERROR(H5E_CACHE, H5E_BADRANGE, FAIL, "type_id out of range.") + *p++ = (uint8_t)(ie_ptr->type_id); + + /* Compose and encode flags */ + if(ie_ptr->is_dirty) + flags |= H5C__MDCI_ENTRY_DIRTY_FLAG; + if(ie_ptr->lru_rank > 0) + flags |= H5C__MDCI_ENTRY_IN_LRU_FLAG; + if(ie_ptr->fd_child_count > 0) + flags |= H5C__MDCI_ENTRY_IS_FD_PARENT_FLAG; + if(ie_ptr->fd_parent_count > 0) + flags |= H5C__MDCI_ENTRY_IS_FD_CHILD_FLAG; + *p++ = flags; + + /* Encode ring */ + *p++ = (uint8_t)(ie_ptr->ring); + + /* Encode age */ + *p++ = (uint8_t)(ie_ptr->age); + + /* Validate and encode dependency child count */ + if(ie_ptr->fd_child_count > H5C__MDCI_MAX_FD_CHILDREN) + HGOTO_ERROR(H5E_CACHE, H5E_BADRANGE, FAIL, "fd_child_count out of range") + UINT16ENCODE(p, (uint16_t)(ie_ptr->fd_child_count)); + + /* Validate and encode dirty dependency child count */ + if(ie_ptr->fd_dirty_child_count > H5C__MDCI_MAX_FD_CHILDREN) + HGOTO_ERROR(H5E_CACHE, H5E_BADRANGE, FAIL, "fd_dirty_child_count out of range") + UINT16ENCODE(p, (uint16_t)(ie_ptr->fd_dirty_child_count)); + + /* Validate and encode dependency parent count */ + if(ie_ptr->fd_parent_count > H5C__MDCI_MAX_FD_PARENTS) + HGOTO_ERROR(H5E_CACHE, H5E_BADRANGE, FAIL, "fd_parent_count out of rang.") + UINT16ENCODE(p, (uint16_t)(ie_ptr->fd_parent_count)); + + /* Encode index in LRU */ + INT32ENCODE(p, ie_ptr->lru_rank); + + /* Encode entry offset */ + H5F_addr_encode(f, &p, ie_ptr->addr); + + /* Encode entry length */ + H5F_ENCODE_LENGTH(f, p, ie_ptr->size); + + /* Verify expected length of entry image */ + if((size_t)(p - *buf) != H5C__cache_image_block_entry_header_size(f)) + HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad entry image len") + + /* Encode dependency parent offsets -- if any */ + for(u = 0; u < ie_ptr->fd_parent_count; u++) + H5F_addr_encode(f, &p, ie_ptr->fd_parent_addrs[u]); + + /* Copy entry image */ + HDmemcpy(p, ie_ptr->image_ptr, ie_ptr->size); + p += ie_ptr->size; + + /* Update buffer pointer */ + *buf = p; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__encode_cache_image_entry() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__prep_for_file_close__compute_fd_heights + * + * Purpose: Recent modifications to flush dependency support in the + * metadata cache have removed the notion of flush dependency + * height. This is a problem for the cache image feature, + * as flush dependency height is used to order entries in the + * cache image so that flush dependency parents appear before + * flush dependency children. (Recall that the flush dependency + * height of an entry in a flush dependency relationship is the + * length of the longest path from the entry to a leaf entry -- + * that is an entry with flush dependency parents, but no + * flush dependency children. With the introduction of the + * possibility of multiple flush dependency parents, we have + * a flush partial dependency latice, not a flush dependency + * tree. But since the partial latice is acyclic, the concept + * of flush dependency height still makes sense. + * + * The purpose of this function is to compute the flush + * dependency height of all entries that appear in the cache + * image. + * + * At present, entries are included or excluded from the + * cache image depending upon the ring in which they reside. + * Thus there is no chance that one side of a flush dependency + * will be in the cache image, and the other side not. + * + * However, once we start placing a limit on the size of the + * cache image, or start excluding prefetched entries from + * the cache image if they haven't been accessed in some + * number of file close / open cycles, this will no longer + * be the case. + * + * In particular, if a flush dependency child is dirty, and + * one of its flush dependency parents is dirty and not in + * the cache image, then the flush dependency child cannot + * be in the cache image without violating flush ordering. + * + * Observe that a clean flush dependency child can be either + * in or out of the cache image without effect on flush + * dependencies. + * + * Similarly, a flush dependency parent can always be part + * of a cache image, regardless of whether it is clean or + * dirty -- but remember that a flush dependency parent can + * also be a flush dependency child. + * + * Finally, note that for purposes of the cache image, flush + * dependency height ends when a flush dependecy relation + * passes off the cache image. + * + * On exit, the flush dependency height of each entry in the + * cache image should be calculated and stored in the cache + * entry. Entries will be removed from the cache image if + * necessary to maintain flush ordering. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 9/6/16 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__prep_for_file_close__compute_fd_heights(const H5C_t *cache_ptr) +{ + H5C_cache_entry_t * entry_ptr; + H5C_cache_entry_t * parent_ptr; + unsigned entries_removed_from_image = 0; + unsigned external_parent_fd_refs_removed = 0; + unsigned external_child_fd_refs_removed = 0; + hbool_t done = FALSE; + unsigned u; /* Local index variable */ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + /* sanity checks */ + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + + /* Remove from the cache image all dirty entries that are + * flush dependency children of dirty entries that are not in the + * cache image. Must do this, as if we fail to do so, the parent + * will be written to file before the child. Since it is possible + * that the child will have dirty children of its own, this may take + * multiple passes through the index list. + */ + done = FALSE; + while(!done) { + done = TRUE; + entry_ptr = cache_ptr->il_head; + while(entry_ptr != NULL) { + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + + /* Should this entry be in the image */ + if(entry_ptr->image_dirty && entry_ptr->include_in_image && + (entry_ptr->fd_parent_count > 0)) { + HDassert(entry_ptr->flush_dep_parent != NULL); + for(u = 0; u < entry_ptr->flush_dep_nparents; u++ ) { + parent_ptr = entry_ptr->flush_dep_parent[u]; + + /* Sanity check parent */ + HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(entry_ptr->ring == parent_ptr->ring); + + if(parent_ptr->is_dirty && !parent_ptr->include_in_image && + entry_ptr->include_in_image) { + + /* Must remove child from image -- only do this once */ + entries_removed_from_image++; + entry_ptr->include_in_image = FALSE; + } /* end if */ + } /* for */ + } /* end if */ + + entry_ptr = entry_ptr->il_next; + } /* while ( entry_ptr != NULL ) */ + } /* while ( ! done ) */ + + /* at present, entries are included in the cache image if they reside + * in a specified set of rings. Thus it should be impossible for + * entries_removed_from_image to be positive. Assert that this is + * so. Note that this will change when we start aging entries out + * of the cache image. + */ + HDassert(entries_removed_from_image == 0); + + /* Next, remove from entries in the cache image, references to + * flush dependency parents or children that are not in the cache image. + */ + entry_ptr = cache_ptr->il_head; + while(entry_ptr != NULL) { + if(!entry_ptr->include_in_image && entry_ptr->flush_dep_nparents > 0) { + HDassert(entry_ptr->flush_dep_parent != NULL); + + for(u = 0; u < entry_ptr->flush_dep_nparents; u++ ) { + parent_ptr = entry_ptr->flush_dep_parent[u]; + + /* Sanity check parent */ + HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(entry_ptr->ring == parent_ptr->ring); + + if(parent_ptr->include_in_image) { + /* Must remove reference to child */ + HDassert(parent_ptr->fd_child_count > 0); + parent_ptr->fd_child_count--; + + if(entry_ptr->is_dirty) { + HDassert(parent_ptr->fd_dirty_child_count > 0); + parent_ptr->fd_dirty_child_count--; + } /* end if */ + + external_child_fd_refs_removed++; + } /* end if */ + } /* for */ + } /* end if */ + else if(entry_ptr->include_in_image && entry_ptr->flush_dep_nparents > 0) { + /* Sanity checks */ + HDassert(entry_ptr->flush_dep_parent != NULL); + HDassert(entry_ptr->flush_dep_nparents == entry_ptr->fd_parent_count); + HDassert(entry_ptr->fd_parent_addrs); + + for(u = 0; u < entry_ptr->flush_dep_nparents; u++ ) { + parent_ptr = entry_ptr->flush_dep_parent[u]; + + /* Sanity check parent */ + HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(entry_ptr->ring == parent_ptr->ring); + + if(!parent_ptr->include_in_image) { + /* Must remove reference to parent */ + HDassert(entry_ptr->fd_parent_count > 0); + parent_ptr->fd_child_count--; + + HDassert(parent_ptr->addr == entry_ptr->fd_parent_addrs[u]); + + entry_ptr->fd_parent_addrs[u] = HADDR_UNDEF; + external_parent_fd_refs_removed++; + } /* end if */ + } /* for */ + + /* Touch up fd_parent_addrs array if necessary */ + if(entry_ptr->fd_parent_count == 0) { + H5MM_xfree(entry_ptr->fd_parent_addrs); + entry_ptr->fd_parent_addrs = NULL; + } /* end if */ + else if(entry_ptr->flush_dep_nparents > entry_ptr->fd_parent_count) { + haddr_t * old_fd_parent_addrs = entry_ptr->fd_parent_addrs; + unsigned v; + + if(NULL == (entry_ptr->fd_parent_addrs = (haddr_t *)H5MM_calloc(sizeof(haddr_t) * (size_t)(entry_ptr->fd_parent_addrs)))) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for fd parent addr array") + + v = 0; + for(u = 0; u < entry_ptr->flush_dep_nparents; u++) { + if(old_fd_parent_addrs[u] != HADDR_UNDEF) { + entry_ptr->fd_parent_addrs[v] = old_fd_parent_addrs[u]; + v++; + } /* end if */ + } /* end for */ + + HDassert(v == entry_ptr->fd_parent_count); + } /* end else-if */ + } /* end else-if */ + + entry_ptr = entry_ptr->il_next; + } /* while (entry_ptr != NULL) */ + + /* At present, no extenal parent or child flush dependency links + * should exist -- hence the following assertions. This will change + * if we support ageout of entries in the cache image. + */ + HDassert(external_child_fd_refs_removed == 0); + HDassert(external_parent_fd_refs_removed == 0); + + /* At this point we should have removed all flush dependencies that + * cross cache image boundaries. Now compute the flush dependency + * heights for all entries in the image. + * + * Until I can think of a better way, do this via a depth first + * search implemented via a recursive function call. + * + * Note that entry_ptr->image_fd_height has already been initialized to 0 + * for all entries that may appear in the cache image. + */ + entry_ptr = cache_ptr->il_head; + while(entry_ptr != NULL ) { + if(entry_ptr->include_in_image && entry_ptr->fd_child_count == 0 && + entry_ptr->fd_parent_count > 0) { + for(u = 0; u < entry_ptr->fd_parent_count; u++ ) { + parent_ptr = entry_ptr->flush_dep_parent[u]; + + HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + if(parent_ptr->include_in_image && parent_ptr->image_fd_height <= 0) + H5C__prep_for_file_close__compute_fd_heights_real(parent_ptr, 1); + } /* end for */ + } /* end if */ + + entry_ptr = entry_ptr->il_next; + } /* while (entry_ptr != NULL) */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__prep_for_file_close__compute_fd_heights() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__prep_for_file_close__compute_fd_heights_real + * + * Purpose: H5C__prep_for_file_close__compute_fd_heights() prepares + * for the computation of flush dependency heights of all + * entries in the cache image, this function actually does + * it. + * + * The basic observation behind this function is as follows: + * + * Suppose you have an entry E with a flush dependency + * height of X. Then the parents of E must all have + * flush dependency X + 1 or greater. + * + * Use this observation to compute flush dependency height + * of all entries in the cache image via the following + * recursive algorithm: + * + * 1) On entry, set the flush dependency height of the + * supplied cache entry to the supplied value. + * + * 2) Examine all the flush dependency parents of the + * supplied entry. + * + * If the parent is in the cache image, and has flush + * dependency height less than or equal to the flush + * dependency height of the current entry, call the + * recursive routine on the parent with flush dependency + * height equal to the flush dependency height of the + * child plus 1. + * + * Otherwise do nothing. + * + * Observe that if the flush dependency height of all entries + * in the image is initialized to zero, and if this recursive + * function is called with flush dependency height 0 on all + * entries in the cache image with FD parents in the image, + * but without FD children in the image, the correct flush + * dependency height should be set for all entries in the + * cache image. + * + * Return: void + * + * Programmer: John Mainzer + * 9/6/16 + * + *------------------------------------------------------------------------- + */ +static void +H5C__prep_for_file_close__compute_fd_heights_real(H5C_cache_entry_t *entry_ptr, + uint32_t fd_height) +{ + FUNC_ENTER_STATIC_NOERR + + /* Sanity checks */ + HDassert(entry_ptr); + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(entry_ptr->include_in_image); + HDassert((entry_ptr->image_fd_height == 0) || (entry_ptr->image_fd_height < fd_height)); + HDassert(((fd_height == 0) && (entry_ptr->fd_child_count == 0)) || ((fd_height > 0) && (entry_ptr->fd_child_count > 0))); + + entry_ptr->image_fd_height = fd_height; + if(entry_ptr->flush_dep_nparents > 0) { + unsigned u; + + HDassert(entry_ptr->flush_dep_parent); + for(u = 0; u < entry_ptr->fd_parent_count; u++) { + H5C_cache_entry_t *parent_ptr; + + parent_ptr = entry_ptr->flush_dep_parent[u]; + HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + + if(parent_ptr->include_in_image && parent_ptr->image_fd_height <= fd_height) + H5C__prep_for_file_close__compute_fd_heights_real(parent_ptr, fd_height + 1); + } /* end for */ + } /* end if */ + + FUNC_LEAVE_NOAPI_VOID +} /* H5C__prep_for_file_close__compute_fd_heights_real() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__prep_for_file_close__setup_image_entries_array + * + * Purpose: Allocate space for the image_entries array, and load + * each instance of H5C_image_entry_t in the array with + * the data necessary to construct the metadata cache image. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 8/4/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__prep_for_file_close__setup_image_entries_array(H5C_t *cache_ptr) +{ + H5C_cache_entry_t * entry_ptr; + H5C_image_entry_t * image_entries = NULL; + uint32_t entries_visited = 0; + unsigned u; /* Local index variable */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(cache_ptr->close_warning_received); + HDassert(cache_ptr->pl_len == 0); + HDassert(cache_ptr->num_entries_in_image > 0); + HDassert(cache_ptr->image_entries == NULL); + + /* Allocate and initialize image_entries array */ + if(NULL == (image_entries = (H5C_image_entry_t *)H5MM_malloc(sizeof(H5C_image_entry_t) * (size_t)(cache_ptr->num_entries_in_image + 1)))) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for image_entries") + + for(u = 0; u <= cache_ptr->num_entries_in_image; u++) { + image_entries[u].magic = H5C_IMAGE_ENTRY_T_MAGIC; + image_entries[u].addr = HADDR_UNDEF; + image_entries[u].size = 0; + image_entries[u].ring = H5C_RING_UNDEFINED; + image_entries[u].age = 0; + image_entries[u].type_id = -1; + image_entries[u].lru_rank = 0; + image_entries[u].is_dirty = FALSE; + image_entries[u].image_fd_height = 0; + image_entries[u].fd_parent_count = 0; + image_entries[u].fd_parent_addrs = NULL; + image_entries[u].fd_child_count = 0; + image_entries[u].fd_dirty_child_count = 0; + image_entries[u].image_ptr = NULL; + } /* end for */ + + /* Scan each entry on the index list and populate the image_entries array */ + u = 0; + entry_ptr = cache_ptr->il_head; + while(entry_ptr != NULL) { + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + + if(entry_ptr->include_in_image) { + /* Since we have already serialized the cache, the following + * should hold. + */ + HDassert(entry_ptr->image_up_to_date); + HDassert(entry_ptr->image_ptr); + HDassert(entry_ptr->type); + + image_entries[u].addr = entry_ptr->addr; + image_entries[u].size = entry_ptr->size; + image_entries[u].ring = entry_ptr->ring; + + /* When a prefetched entry is included in the image, store + * its underlying type id in the image entry, not + * H5AC_PREFETCHED_ENTRY_ID. In passing, also increment + * the age (up to H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX). + */ + if(entry_ptr->type->id == H5AC_PREFETCHED_ENTRY_ID) { + image_entries[u].type_id = entry_ptr->prefetch_type_id; + image_entries[u].age = entry_ptr->age + 1; + + if(image_entries[u].age > H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX) + image_entries[u].age = H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX; + } /* end if */ + else { + image_entries[u].type_id = entry_ptr->type->id; + image_entries[u].age = 0; + } /* end else */ + + image_entries[u].lru_rank = entry_ptr->lru_rank; + image_entries[u].is_dirty = entry_ptr->is_dirty; + image_entries[u].image_fd_height = entry_ptr->image_fd_height; + image_entries[u].fd_parent_count = entry_ptr->fd_parent_count; + image_entries[u].fd_parent_addrs = entry_ptr->fd_parent_addrs; + image_entries[u].fd_child_count = entry_ptr->fd_child_count; + image_entries[u].fd_dirty_child_count = + entry_ptr->fd_dirty_child_count; + image_entries[u].image_ptr = entry_ptr->image_ptr; + + /* Null out entry_ptr->fd_parent_addrs and set + * entry_ptr->fd_parent_count to zero so that ownership of the + * flush dependency parents address array is transferred to the + * image entry. + */ + entry_ptr->fd_parent_count = 0; + entry_ptr->fd_parent_addrs = NULL; + + u++; + + HDassert(u <= cache_ptr->num_entries_in_image); + } /* end if */ + + entries_visited++; + + entry_ptr = entry_ptr->il_next; + } /* end while */ + + /* Sanity checks */ + HDassert(entries_visited == cache_ptr->index_len); + HDassert(u == cache_ptr->num_entries_in_image); + + HDassert(image_entries[u].fd_parent_addrs == NULL); + HDassert(image_entries[u].image_ptr == NULL); + + cache_ptr->image_entries = image_entries; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__prep_for_file_close__setup_image_entries_array() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__prep_for_file_close__scan_entries + * + * Purpose: Scan all entries in the metadata cache, and store all + * entry specific data required for construction of the + * metadata cache image block and likely to be discarded + * or modified during the cache flush on file close. + * + * In particular, make note of: + * entry rank in LRU + * whether the entry is dirty + * base address of entry flush dependency parent, + * if it exists. + * number of flush dependency children, if any. + * + * Also, determine which entries are to be included in the + * metadata cache image. At present, all entries other than + * the superblock, the superblock extension object header and + * its associated chunks (if any) are included. + * + * Finally, compute the size of the metadata cache image + * block. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 7/21/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__prep_for_file_close__scan_entries(const H5F_t *f, H5C_t *cache_ptr) +{ + H5C_cache_entry_t * entry_ptr; + hbool_t include_in_image; + unsigned entries_visited = 0; + int lru_rank = 1; + uint32_t num_entries_tentatively_in_image = 0; + uint32_t num_entries_in_image = 0; + size_t image_len; + size_t entry_header_len; + size_t fd_parents_list_len; + int i; + unsigned j; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(f->shared->sblock); + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(cache_ptr->close_warning_received); + HDassert(cache_ptr->pl_len == 0); + + /* Initialize image len to the size of the metadata cache image block + * header. + */ + image_len = H5C__cache_image_block_header_size(f); + entry_header_len = H5C__cache_image_block_entry_header_size(f); + + /* Scan each entry on the index list */ + entry_ptr = cache_ptr->il_head; + while(entry_ptr != NULL) { + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + + /* Since we have already serialized the cache, the following + * should hold. + */ + HDassert(entry_ptr->image_up_to_date); + HDassert(entry_ptr->image_ptr); + + /* Initially, we mark all entries in the rings included + * in the cache image as being included in the in the + * image. Depending on circumstances, we may exclude some + * of these entries later. + */ + if(entry_ptr->ring > H5C_MAX_RING_IN_IMAGE) + include_in_image = FALSE; + else + include_in_image = TRUE; + entry_ptr->include_in_image = include_in_image; + + if(include_in_image) { + entry_ptr->lru_rank = -1; + entry_ptr->image_dirty = entry_ptr->is_dirty; + entry_ptr->image_fd_height = 0; /* will compute this later */ + + /* Initially, include all flush dependency parents in the + * the list of flush dependencies to be stored in the + * image. We may remove some or all of these later. + */ + if(entry_ptr->flush_dep_nparents > 0) { + /* The parents addresses array may already exist -- reallocate + * as needed. + */ + if(entry_ptr->flush_dep_nparents == entry_ptr->fd_parent_count ) { + /* parent addresses array should already be allocated + * and of the correct size. + */ + HDassert(entry_ptr->fd_parent_addrs); + } /* end if */ + else if(entry_ptr->fd_parent_count > 0) { + HDassert(entry_ptr->fd_parent_addrs); + entry_ptr->fd_parent_addrs = (haddr_t *)H5MM_xfree(entry_ptr->fd_parent_addrs); + } /* end else-if */ + else { + HDassert(entry_ptr->fd_parent_count == 0); + HDassert(entry_ptr->fd_parent_addrs == NULL); + } /* end else */ + + entry_ptr->fd_parent_count = entry_ptr->flush_dep_nparents; + if(NULL == entry_ptr->fd_parent_addrs) + if(NULL == (entry_ptr->fd_parent_addrs = (haddr_t *)H5MM_malloc(sizeof(haddr_t) * (size_t)(entry_ptr->fd_parent_count)))) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for fd parent addrs buffer") + + for(i = 0; i < (int)(entry_ptr->fd_parent_count); i++) { + entry_ptr->fd_parent_addrs[i] = entry_ptr->flush_dep_parent[i]->addr; + HDassert(H5F_addr_defined(entry_ptr->fd_parent_addrs[i])); + } /* end for */ + } /* end if */ + else if(entry_ptr->fd_parent_count > 0) { + HDassert(entry_ptr->fd_parent_addrs); + entry_ptr->fd_parent_addrs = (haddr_t *)H5MM_xfree(entry_ptr->fd_parent_addrs); + } /* end else-if */ + else + HDassert(entry_ptr->fd_parent_addrs == NULL); + + /* Initially, all flush dependency children are included int + * the count of flush dependency child relationships to be + * represented in the cache image. Some or all of these + * may be dropped from the image later. + */ + if(entry_ptr->flush_dep_nchildren > 0) { + if(!entry_ptr->is_pinned) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "encountered unpinned fd parent?!?") + + entry_ptr->fd_child_count = entry_ptr->flush_dep_nchildren; + entry_ptr->fd_dirty_child_count = entry_ptr->flush_dep_ndirty_children; + } /* end if */ + + num_entries_tentatively_in_image++; + } /* end if */ + + entries_visited++; + entry_ptr = entry_ptr->il_next; + } /* end while */ + HDassert(entries_visited == cache_ptr->index_len); + + /* Now compute the flush dependency heights of all flush dependency + * relationships to be represented in the image. + * + * If all entries in the target rings are included in the + * image, the flush dependency heights are simply the heights + * of all flush dependencies in the target rings. + * + * However, if we restrict appearance in the cache image either + * by number of entries in the image, restrictions on the number + * of times a prefetched entry can appear in an image, or image + * size, it is possible that flush dependency parents or children + * of entries that are in the image may not be included in the + * the image. In this case, we must prune all flush dependency + * relationships that cross the image boundary, and all exclude + * from the image all dirty flush dependency children that have + * a dirty flush dependency parent that is not in the image. + * This is necessary to preserve the required flush ordering. + * + * These details are tended to by the following call to + * H5C__prep_for_file_close__compute_fd_heights(). Because the + * exact contents of the image cannot be known until after this + * call, computation of the image size is delayed. + */ + if(H5C__prep_for_file_close__compute_fd_heights(cache_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "computation of flush dependency heights failed?!?") + + /* At this point, all entries that will appear in the cache + * image should be marked correctly. Compute the size of the + * cache image. + */ + entries_visited = 0; + entry_ptr = cache_ptr->il_head; + while(entry_ptr != NULL) { + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + + if(entry_ptr->include_in_image) { + if(entry_ptr->fd_parent_count > 0) + fd_parents_list_len = (size_t)(H5F_SIZEOF_ADDR(f) * entry_ptr->fd_parent_count); + else + fd_parents_list_len = (size_t)0; + + image_len += entry_header_len + fd_parents_list_len + entry_ptr->size; + num_entries_in_image++; + } /* end if */ + + entries_visited++; + entry_ptr = entry_ptr->il_next; + } /* end while */ + HDassert(entries_visited == cache_ptr->index_len); + HDassert(num_entries_in_image <= num_entries_tentatively_in_image); + + j = 0; + for(i = H5C_MAX_RING_IN_IMAGE + 1; i <= H5C_RING_SB; i++) + j += cache_ptr->index_ring_len[i]; + + /* This will change */ + HDassert(entries_visited == (num_entries_tentatively_in_image + j)); + + cache_ptr->num_entries_in_image = num_entries_in_image; + entries_visited = 0; + + /* Now scan the LRU list to set the lru_rank fields of all entries + * on the LRU. + * + * Note that we start with rank 1, and increment by 1 with each + * entry on the LRU. + * + * Note that manually pinned entryies will have lru_rank -1, + * and no flush dependency. Putting these entries at the head of + * the reconstructed LRU should be appropriate. + */ + entry_ptr = cache_ptr->LRU_head_ptr; + while(entry_ptr != NULL) { + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(entry_ptr->type != NULL); + + /* to avoid confusion, don't set lru_rank on epoch markers. + * Note that we still increment the lru_rank, so that the holes + * in the sequence of entries on the LRU will indicate the + * locations of epoch markers (if any) when we reconstruct + * the LRU. + * + * Do not set lru_rank or increment lru_rank for entries + * that will not be included in the cache image. + */ + if(entry_ptr->type->id == H5AC_EPOCH_MARKER_ID) + lru_rank++; + else if(entry_ptr->include_in_image) { + entry_ptr->lru_rank = lru_rank; + lru_rank++; + } /* end else-if */ + + entries_visited++; + entry_ptr = entry_ptr->next; + } /* end while */ + HDassert(entries_visited == cache_ptr->LRU_list_len); + + image_len += H5F_SIZEOF_CHKSUM; + cache_ptr->image_data_len = image_len; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__prep_for_file_close__scan_entries() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__reconstruct_cache_contents() + * + * Purpose: Scan the image_entries array, and create a prefetched + * cache entry for every entry in the array. Insert the + * prefetched entries in the index and the LRU, and + * reconstruct any flush dependencies. Order the entries + * in the LRU as indicated by the stored lru_ranks. + * + * Return: SUCCEED on success, and FAIL on failure. + * + * Programmer: John Mainzer + * 8/14/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__reconstruct_cache_contents(H5F_t *f, hid_t dxpl_id, H5C_t *cache_ptr) +{ + H5C_cache_entry_t * pf_entry_ptr; /* Pointer to prefetched entry */ + H5C_cache_entry_t * parent_ptr; /* Pointer to parent of prefetched entry */ + unsigned u, v; /* Local index variable */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(cache_ptr == f->shared->cache); + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(cache_ptr->image_buffer); + HDassert(cache_ptr->image_len > 0); + HDassert(cache_ptr->image_entries != NULL); + HDassert(cache_ptr->num_entries_in_image > 0); + + /* Reconstruct entries in image */ + for(u = 0; u < cache_ptr->num_entries_in_image; u++) { + /* Create the prefetched entry described by the ith + * entry in cache_ptr->image_entrise. + */ + if(NULL == (pf_entry_ptr = H5C__reconstruct_cache_entry(cache_ptr, u))) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "reconstruction of cache entry failed") + + /* Note that we make no checks on available cache space before + * inserting the reconstructed entry into the metadata cache. + * + * This is OK since the cache must be almost empty at the beginning + * of the process, and since we check cache size at the end of the + * reconstruction process. + */ + + /* Insert the prefetched entry in the index */ + H5C__INSERT_IN_INDEX(cache_ptr, pf_entry_ptr, FAIL) + + /* If dirty, insert the entry into the slist. */ + if(pf_entry_ptr->is_dirty) + H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, pf_entry_ptr, FAIL) + + /* Append the entry to the LRU */ + H5C__UPDATE_RP_FOR_INSERT_APPEND(cache_ptr, pf_entry_ptr, FAIL) + + H5C__UPDATE_STATS_FOR_PREFETCH(cache_ptr, pf_entry_ptr->is_dirty) + + /* If the prefetched entry is the child in one or more flush + * dependency relationships, recreate those flush dependencies. + */ + for(v = 0; v < pf_entry_ptr->fd_parent_count; v++) { + /* Sanity checks */ + HDassert(pf_entry_ptr->fd_parent_addrs); + HDassert(H5F_addr_defined(pf_entry_ptr->fd_parent_addrs[v])); + + /* Find the parent entry */ + parent_ptr = NULL; + H5C__SEARCH_INDEX(cache_ptr, pf_entry_ptr->fd_parent_addrs[v], parent_ptr, FAIL) + if(parent_ptr == NULL) + HGOTO_ERROR(H5E_CACHE, H5E_NOTFOUND, FAIL, "fd parent not in cache?!?") + + /* Sanity checks */ + HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(parent_ptr->addr == pf_entry_ptr->fd_parent_addrs[v]); + HDassert(parent_ptr->lru_rank == -1); + + /* Must protect parent entry to set up a flush dependency. + * Do this now, and then uprotect when done. + */ + H5C__UPDATE_RP_FOR_PROTECT(cache_ptr, parent_ptr, FAIL) + parent_ptr->is_protected = TRUE; + + /* Setup the flush dependency */ + if(H5C_create_flush_dependency(parent_ptr, pf_entry_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTDEPEND, FAIL, "Can't restore flush dependency") + + /* And now unprotect */ + H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, parent_ptr, FAIL) + parent_ptr->is_protected = FALSE; + } /* end for */ + } /* end for */ + +#ifndef NDEBUG + /* Scan the image_entries array, and verify that each entry has + * the expected flush dependency status. + */ + for(u = 0; u < cache_ptr->num_entries_in_image; u++) { + H5C_image_entry_t * ie_ptr; + + ie_ptr = &(cache_ptr->image_entries[u]); + HDassert(ie_ptr->magic == H5C_IMAGE_ENTRY_T_MAGIC); + + /* Find the prefetched entry */ + pf_entry_ptr = NULL; + H5C__SEARCH_INDEX(cache_ptr, ie_ptr->addr, pf_entry_ptr, FAIL); + + HDassert(pf_entry_ptr); + HDassert(pf_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(pf_entry_ptr->prefetched); + HDassert(ie_ptr->fd_parent_count == pf_entry_ptr->fd_parent_count); + HDassert(pf_entry_ptr->fd_parent_count == pf_entry_ptr->flush_dep_nparents); + HDassert(ie_ptr->lru_rank == pf_entry_ptr->lru_rank); + + for(v = 0; v < pf_entry_ptr->fd_parent_count; v++) { + parent_ptr = pf_entry_ptr->flush_dep_parent[v]; + HDassert(parent_ptr); + HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(pf_entry_ptr->fd_parent_addrs); + HDassert(pf_entry_ptr->fd_parent_addrs[v] == parent_ptr->addr); + HDassert(parent_ptr->flush_dep_nchildren > 0); + } /* end for */ + + HDassert(ie_ptr->fd_child_count == pf_entry_ptr->fd_child_count); + HDassert(pf_entry_ptr->fd_child_count == pf_entry_ptr->flush_dep_nchildren); + HDassert(pf_entry_ptr->fd_dirty_child_count == pf_entry_ptr->flush_dep_ndirty_children); + } /* end for */ + + /* Scan the LRU, and verify the expected ordering of the + * prefetched entries. + */ + { + int lru_rank_holes = 0; + H5C_cache_entry_t *entry_ptr; + int i; /* Local index variable */ + + i = -1; + entry_ptr = cache_ptr->LRU_head_ptr; + while(entry_ptr != NULL) { + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(entry_ptr->type != NULL); + + if(entry_ptr->prefetched) { + HDassert(i <= entry_ptr->lru_rank); + HDassert((entry_ptr->lru_rank <= 2) || + (entry_ptr->lru_rank == i + 1) || + (entry_ptr->lru_rank == i + 2)); + + if((entry_ptr->lru_rank <= 2) && (entry_ptr->lru_rank == i + 2)) + lru_rank_holes++; + + i = entry_ptr->lru_rank; + } /* end if */ + + entry_ptr = entry_ptr->next; + } /* end while */ + + /* Holes of size 1 appear in the LRU ranking due to epoch + * markers. They are left in to allow re-insertion of the + * epoch markers on reconstruction of the cache -- thus + * the following sanity check will have to be revised when + * we add code to store and restore adaptive resize status. + */ + HDassert(lru_rank_holes <= H5C__MAX_EPOCH_MARKERS); + } /* end block */ +#endif /* NDEBUG */ + + /* Check to see if the cache is oversize, and evict entries as + * necessary to remain within limits. + */ + if(cache_ptr->index_size >= cache_ptr->max_cache_size) { + /* cache is oversized -- call H5C__make_space_in_cache() with zero + * space needed to repair the situation if possible. + */ + hbool_t write_permitted = FALSE; + + if(cache_ptr->check_write_permitted != NULL) { + if((cache_ptr->check_write_permitted)(f, &write_permitted) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, FAIL, "Can't get write_permitted") + } /* end if */ + else + write_permitted = cache_ptr->write_permitted; + + if(H5C__make_space_in_cache(f, dxpl_id, 0, write_permitted) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, FAIL, "H5C__make_space_in_cache failed") + } /* end if */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__reconstruct_cache_contents() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__reconstruct_cache_entry() + * + * Purpose: Allocate a prefetched metadata cache entry and initialize + * it from the indicated entry in the image_entries array. + * + * Return a pointer to the newly allocated cache entry, + * or NULL on failure. + * + * Return: Pointer to the new instance of H5C_cache_entry on success, + * or NULL on failure. + * + * Programmer: John Mainzer + * 8/14/15 + * + *------------------------------------------------------------------------- + */ +static H5C_cache_entry_t * +H5C__reconstruct_cache_entry(H5C_t *cache_ptr, unsigned index) +{ + H5C_cache_entry_t *pf_entry_ptr = NULL; /* Reconstructed cache entry */ + H5C_image_entry_t *ie_ptr; + H5C_cache_entry_t *ret_value = NULL; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(cache_ptr->image_entries != NULL); + HDassert(cache_ptr->num_entries_in_image > 0); + HDassert(index < cache_ptr->num_entries_in_image); + ie_ptr = &((cache_ptr->image_entries)[index]); + HDassert(ie_ptr->magic == H5C_IMAGE_ENTRY_T_MAGIC); + HDassert(H5F_addr_defined(ie_ptr->addr)); + HDassert(ie_ptr->size > 0); + HDassert(ie_ptr->image_ptr); + + /* Allocate space for the prefetched cache entry */ + if(NULL == (pf_entry_ptr = H5FL_CALLOC(H5C_cache_entry_t))) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "memory allocation failed for prefetched cache entry") + + /* Initialize the prefetched entry from the entry image */ + /* (Only need to set non-zero/NULL/FALSE fields, due to calloc() above) */ + pf_entry_ptr->magic = H5C__H5C_CACHE_ENTRY_T_MAGIC; + pf_entry_ptr->cache_ptr = cache_ptr; + pf_entry_ptr->addr = ie_ptr->addr; + pf_entry_ptr->size = ie_ptr->size; + pf_entry_ptr->ring = ie_ptr->ring; + pf_entry_ptr->age = ie_ptr->age; + pf_entry_ptr->image_ptr = ie_ptr->image_ptr; + pf_entry_ptr->image_up_to_date = TRUE; + pf_entry_ptr->type = H5AC_PREFETCHED_ENTRY; + + /* Force dirty entries to clean if the file read only -- must do + * this as otherwise the cache will attempt to write them on file + * close. Since the file is R/O, the metadata cache image superblock + * extension message and the cache image block will not be removed. + * Hence no danger in this. + */ + pf_entry_ptr->is_dirty = ie_ptr->is_dirty && cache_ptr->delete_image; + + /* Initialize cache image related fields */ + pf_entry_ptr->lru_rank = ie_ptr->lru_rank; + pf_entry_ptr->fd_parent_count = ie_ptr->fd_parent_count; + pf_entry_ptr->fd_parent_addrs = ie_ptr->fd_parent_addrs; + pf_entry_ptr->fd_child_count = ie_ptr->fd_child_count; + pf_entry_ptr->fd_dirty_child_count = ie_ptr->fd_dirty_child_count; + pf_entry_ptr->prefetched = TRUE; + pf_entry_ptr->prefetch_type_id = ie_ptr->type_id; + pf_entry_ptr->age = ie_ptr->age; + + /* Array of addresses of flush dependency parents is now transferred to + * the prefetched entry. Thus set ie_ptr->fd_parent_addrs to NULL. + */ + if(pf_entry_ptr->fd_parent_count > 0) { + HDassert(ie_ptr->fd_parent_addrs); + ie_ptr->fd_parent_addrs = NULL; + } /* end if */ + else + HDassert(ie_ptr->fd_parent_addrs == NULL); + + /* On disk image of entry is now transferred to the prefetched entry. + * Thus set ie_ptr->image_ptr to NULL. + */ + ie_ptr->image_ptr = NULL; + + /* Sanity checks */ + HDassert(pf_entry_ptr->size > 0 && pf_entry_ptr->size < H5C_MAX_ENTRY_SIZE); + + ret_value = pf_entry_ptr; + +done: + if(NULL == ret_value && pf_entry_ptr) + pf_entry_ptr = H5FL_FREE(H5C_cache_entry_t, pf_entry_ptr); + + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__reconstruct_cache_entry() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__serialize_cache + * + * Purpose: Serialize (i.e. construct an on disk image) for all entries + * in the metadata cache including clean entries. + * + * Note that flush dependencies and "flush me last" flags + * must be observed in the serialization process. + * + * Note also that entries may be loaded, flushed, evicted, + * expunged, relocated, resized, or removed from the cache + * during this process, just as these actions may occur during + * a regular flush. + * + * However, we are given that the cache will contain no protected + * entries on entry to this routine (although entries may be + * briefly protected and then unprotected during the serialize + * process). + * + * The objective of this routine is serialize all entries and + * to force all entries into their actual locations on disk. + * + * The initial need for this routine is to settle all entries + * in the cache prior to construction of the metadata cache + * image so that the size of the cache image can be calculated. + * However, I gather that other uses for the routine are + * under consideration. + * + * 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 + * 7/22/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__serialize_cache(H5F_t *f, hid_t dxpl_id) +{ +#if H5C_DO_SANITY_CHECKS + int i; + uint32_t index_len = 0; + size_t index_size = (size_t)0; + size_t clean_index_size = (size_t)0; + size_t dirty_index_size = (size_t)0; + size_t slist_size = (size_t)0; + uint32_t slist_len = 0; +#endif /* H5C_DO_SANITY_CHECKS */ + H5C_ring_t ring; + H5C_t * cache_ptr; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + cache_ptr = f->shared->cache; + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(cache_ptr->slist_ptr); + +#if H5C_DO_SANITY_CHECKS + HDassert(cache_ptr->index_ring_len[H5C_RING_UNDEFINED] == 0); + HDassert(cache_ptr->index_ring_size[H5C_RING_UNDEFINED] == (size_t)0); + HDassert(cache_ptr->clean_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0); + HDassert(cache_ptr->dirty_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0); + HDassert(cache_ptr->slist_ring_len[H5C_RING_UNDEFINED] == 0); + HDassert(cache_ptr->slist_ring_size[H5C_RING_UNDEFINED] == (size_t)0); + + for(i = H5C_RING_USER; i < H5C_RING_NTYPES; i++) { + index_len += cache_ptr->index_ring_len[i]; + index_size += cache_ptr->index_ring_size[i]; + clean_index_size += cache_ptr->clean_index_ring_size[i]; + dirty_index_size += cache_ptr->dirty_index_ring_size[i]; + + slist_len += cache_ptr->slist_ring_len[i]; + slist_size += cache_ptr->slist_ring_size[i]; + } /* end for */ + + HDassert(cache_ptr->index_len == index_len); + HDassert(cache_ptr->index_size == index_size); + HDassert(cache_ptr->clean_index_size == clean_index_size); + HDassert(cache_ptr->dirty_index_size == dirty_index_size); + HDassert(cache_ptr->slist_len == slist_len); + HDassert(cache_ptr->slist_size == slist_size); +#endif /* H5C_DO_SANITY_CHECKS */ + +#if H5C_DO_EXTREME_SANITY_CHECKS + if((H5C_validate_protected_entry_list(cache_ptr) < 0) || + (H5C_validate_pinned_entry_list(cache_ptr) < 0) || + (H5C_validate_lru_list(cache_ptr) < 0)) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry.\n") +#endif /* H5C_DO_EXTREME_SANITY_CHECKS */ + +#ifndef NDEBUG + /* if this is a debug build, set the serialization_count field of + * each entry in the cache to zero before we start the serialization. + * This allows us to detect the case in which any entry is serialized + * more than once (a performance issues), and more importantly, the + * case is which any flush depencency parent is serializes more than + * once (a correctness issue). + */ + { + H5C_cache_entry_t * scan_ptr = NULL; + + scan_ptr = cache_ptr->il_head; + while(scan_ptr != NULL) { + HDassert(scan_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + scan_ptr->serialization_count = 0; + scan_ptr = scan_ptr->il_next; + } /* end while */ + } /* end block */ +#endif /* NDEBUG */ + + /* set cache_ptr->serialization_in_progress to TRUE, and back + * to FALSE at the end of the function. Must maintain this flag + * to support H5C_get_serialization_in_progress(), which is in + * turn required to support sanity checking in some cache + * clients. + */ + HDassert(!cache_ptr->serialization_in_progress); + cache_ptr->serialization_in_progress = TRUE; + + /* Serialize each ring, starting from the outermost ring and + * working inward. + */ + ring = H5C_RING_USER; + while(ring < H5C_RING_NTYPES) { + HDassert(cache_ptr->close_warning_received); + switch(ring) { + case H5C_RING_USER: + break; + + case H5C_RING_RDFSM: + if(!cache_ptr->rdfsm_settled) { + hbool_t fsm_settled = FALSE; /* Whether the FSM was actually settled */ + + /* Settle raw data FSM */ + if(H5MF_settle_raw_data_fsm(f, dxpl_id, &fsm_settled) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "RD FSM settle failed") + + /* Only set the flag if the FSM was actually settled */ + if(fsm_settled) + cache_ptr->rdfsm_settled = TRUE; + } /* end if */ + break; + + case H5C_RING_MDFSM: + if(!cache_ptr->mdfsm_settled) { + hbool_t fsm_settled = FALSE; /* Whether the FSM was actually settled */ + + /* Settle metadata FSM */ + if(H5MF_settle_meta_data_fsm(f, dxpl_id, &fsm_settled) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "MD FSM settle failed") + + /* Only set the flag if the FSM was actually settled */ + if(fsm_settled) + cache_ptr->mdfsm_settled = TRUE; + } /* end if */ + break; + + case H5C_RING_SBE: + case H5C_RING_SB: + break; + + default: + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown ring?!?!") + break; + } /* end switch */ + + if(H5C__serialize_ring(f, dxpl_id, ring) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "serialize ring failed") + + ring++; + } /* end while */ + +#ifndef NDEBUG + /* Verify that no entry has been serialized more than once. + * FD parents with multiple serializations should have been caught + * elsewhere, so no specific check for them here. + */ + { + H5C_cache_entry_t * scan_ptr = NULL; + + scan_ptr = cache_ptr->il_head; + while(scan_ptr != NULL) { + HDassert(scan_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(scan_ptr->serialization_count <= 1); + + scan_ptr = scan_ptr->il_next; + } /* end while */ + } /* end block */ +#endif /* NDEBUG */ + +done: + cache_ptr->serialization_in_progress = FALSE; + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__serialize_cache() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__serialize_ring + * + * Purpose: Serialize the entries contained in the specified cache and + * ring. All entries in rings outside the specified ring + * must have been serialized on entry. + * + * If the cache contains protected entries in the specified + * ring, the function will fail, as protected entries cannot + * be serialized. However all unprotected entries in the + * target ring should be serialized before the function + * returns failure. + * + * If flush dependencies appear in the target ring, the + * function makes repeated passes through the index list + * serializing entries in flush dependency order. + * + * All entries outside the H5C_RING_SBE are marked for + * inclusion in the cache image. Entries in H5C_RING_SBE + * and below are marked for exclusion from the image. + * + * 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 + * 9/11/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__serialize_ring(H5F_t *f, hid_t dxpl_id, H5C_ring_t ring) +{ + hbool_t done = FALSE; + hbool_t restart_list_scan = FALSE; + H5C_t * cache_ptr; + H5C_cache_entry_t * entry_ptr; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + cache_ptr = f->shared->cache; + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(ring > H5C_RING_UNDEFINED); + HDassert(ring < H5C_RING_NTYPES); + + HDassert(cache_ptr->serialization_in_progress); + + /* The objective here is to serialize all entries in the cache ring + * in flush dependency order. + * + * The basic algorithm is to scan the cache index list looking for + * unserialized entries that are either not in a flush dependency + * relationship, or which have no unserialized children. Any such + * entry is serialized and its flush dependency parents (if any) are + * informed -- allowing them to decrement their userialized child counts. + * + * However, this algorithm is complicated by the ability + * of client serialization callbacks to perform operations on + * on the cache which can result in the insertion, deletion, + * relocation, resize, dirty, flush, eviction, or removal (via the + * take ownership flag) of entries. Changes in the flush dependency + * structure are also possible. + * + * On the other hand, the algorithm is simplified by the fact that + * we are serializing, not flushing. Thus, as long as all entries + * are serialized correctly, it doesn't matter if we have to go back + * and serialize an entry a second time. + * + * These possible actions result in the following modfications to + * tha basic algorithm: + * + * 1) In the event of an entry expunge, eviction or removal, we must + * restart the scan as it is possible that the next entry in our + * scan is no longer in the cache. Were we to examine this entry, + * we would be accessing deallocated memory. + * + * 2) A resize, dirty, or insertion of an entry may result in the + * the increment of a flush dependency parent's dirty and/or + * unserialized child count. In the context of serializing the + * the cache, this is a non-issue, as even if we have already + * serialized the parent, it will be marked dirty and its image + * marked out of date if appropriate when the child is serialized. + * + * However, this is a major issue for a flush, as were this to happen + * in a flush, it would violate the invarient that the flush dependency + * feature is intended to enforce. As the metadata cache has no + * control over the behavior of cache clients, it has no way of + * preventing this behaviour. However, it should detect it if at all + * possible. + * + * Do this by maintaining a count of the number of times each entry is + * serialized during a cache serialization. If any flush dependency + * parent is serialized more than once, throw an assertion failure. + * + * 3) An entry relocation will typically change the location of the + * entry in the index list. This shouldn't cause problems as we + * will scan the index list until we make a complete pass without + * finding anything to serialize -- making relocations of either + * the current or next entries irrelevant. + * + * Note that since a relocation may result in our skipping part of + * the index list, we must always do at least one more pass through + * the index list after an entry relocation. + * + * 4) Changes in the flush dependency structure are possible on + * entry insertion, load, expunge, evict, or remove. Destruction + * of a flush dependency has no effect, as it can only relax the + * flush dependencies. Creation of a flush dependency can create + * an unserialized child of a flush dependency parent where all + * flush dependency children were previously serialized. Should + * this child dirty the flush dependency parent when it is serialized, + * the parent will be re-serialized. + * + * Per the discussion of 2) above, this is a non issue for cache + * serialization, and a major problem for cache flush. Using the + * same detection mechanism, throw an assertion failure if this + * condition appears. + * + * Observe that either eviction or removal of entries as a result of + * a serialization is not a problem as long as the flush depencency + * tree does not change beyond the removal of a leaf. + */ + while(!done) { + done = TRUE; /* set to FALSE if any activity in inner loop */ + entry_ptr = cache_ptr->il_head; + while(entry_ptr != NULL) { + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + + /* Verify that either the entry is already serialized, or + * that it is assigned to either the target or an inner + * ring. + */ + HDassert((entry_ptr->ring >= ring) || (entry_ptr->image_up_to_date)); + + /* Skip flush me last entries or inner ring entries */ + if(!entry_ptr->flush_me_last && entry_ptr->ring == ring) { + + /* if we encounter an unserialized entry in the current + * ring that is not marked flush me last, we are not done. + */ + if(!entry_ptr->image_up_to_date) + done = FALSE; + + /* Serialize the entry if its image is not up to date + * and it has no unserialized flush dependency children. + */ + if(!entry_ptr->image_up_to_date && entry_ptr->flush_dep_nunser_children == 0) { + HDassert(entry_ptr->serialization_count == 0); + + /* Serialize the entry */ + if(H5C__serialize_single_entry(f, dxpl_id, cache_ptr, entry_ptr, &restart_list_scan) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "entry serialization failed") + + HDassert(entry_ptr->flush_dep_nunser_children == 0); + HDassert(entry_ptr->serialization_count == 0); + +#ifndef NDEBUG + entry_ptr->serialization_count++; +#endif /* NDEBUG */ + } /* end if */ + +#if H5C_COLLECT_CACHE_STATS + if(restart_list_scan) + H5C__UPDATE_STATS_FOR_INDEX_SCAN_RESTART(cache_ptr); +#endif /* H5C_COLLECT_CACHE_STATS */ + } /* end if */ + + if(restart_list_scan) { + restart_list_scan = FALSE; + entry_ptr = cache_ptr->il_head; + } /* end if */ + else + entry_ptr = entry_ptr->il_next; + } /* while ( entry_ptr != NULL ) */ + } /* while ( ! done ) */ + + + /* At this point, all entries not marked "flush me last" and in + * the current ring or outside it should be serialized and have up + * to date images. Scan the index list again to serialize the + * "flush me last" entries (if they are in the current ring) and to + * verify that all other entries have up to date images. + */ + entry_ptr = cache_ptr->il_head; + while(entry_ptr != NULL) { + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(entry_ptr->ring > H5C_RING_UNDEFINED); + HDassert(entry_ptr->ring < H5C_RING_NTYPES); + HDassert((entry_ptr->ring >= ring) || (entry_ptr->image_up_to_date)); + + if(entry_ptr->ring == ring) { + if(entry_ptr->flush_me_last) { + if(!entry_ptr->image_up_to_date) { + HDassert(entry_ptr->serialization_count == 0); + HDassert(entry_ptr->flush_dep_nunser_children == 0); + + /* Serialize the entry */ + if(H5C__serialize_single_entry(f, dxpl_id, cache_ptr, entry_ptr, &restart_list_scan) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "entry serialization failed") + else if(restart_list_scan) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "flush_me_last entry serialization triggered restart") + + HDassert(entry_ptr->flush_dep_nunser_children == 0); + HDassert(entry_ptr->serialization_count == 0); +#ifndef NDEBUG + entry_ptr->serialization_count++; +#endif /* NDEBUG */ + } /* end if */ + } /* end if */ + else { + HDassert(entry_ptr->image_up_to_date); + HDassert(entry_ptr->serialization_count <= 1); + HDassert(entry_ptr->flush_dep_nunser_children == 0); + } /* end else */ + } /* if ( entry_ptr->ring == ring ) */ + + entry_ptr = entry_ptr->il_next; + } /* while ( entry_ptr != NULL ) */ + +done: + HDassert(cache_ptr->serialization_in_progress); + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__serialize_ring() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__serialize_single_entry + * + * Purpose: Serialize the cache entry pointed to by the entry_ptr + * parameter. + * + * Note: This routine is very similar to H5C__generate_image + * and changes to one should probably be reflected in the other. + * Ideally, one should be eliminated. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer, 7/24/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__serialize_single_entry(H5F_t *f, hid_t dxpl_id, H5C_t *cache_ptr, + H5C_cache_entry_t *entry_ptr, hbool_t *restart_list_scan_ptr) +{ + unsigned serialize_flags = H5C__SERIALIZE_NO_FLAGS_SET; + haddr_t new_addr = HADDR_UNDEF; + haddr_t old_addr = HADDR_UNDEF; + size_t new_len = 0; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(f); + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(entry_ptr); + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(!entry_ptr->prefetched); + HDassert(!entry_ptr->image_up_to_date); + HDassert(entry_ptr->is_dirty); + HDassert(!entry_ptr->is_protected); + HDassert(!entry_ptr->flush_in_progress); + HDassert(entry_ptr->type); + HDassert(restart_list_scan_ptr); + HDassert(*restart_list_scan_ptr == FALSE); + + /* Set entry_ptr->flush_in_progress to TRUE so the the target entry + * will not be evicted out from under us. Must set it back to FALSE + * when we are done. + */ + entry_ptr->flush_in_progress = TRUE; + + /* Allocate buffer for the entry image if required. */ + if(NULL == entry_ptr->image_ptr) { + HDassert(entry_ptr->size > 0); + if(NULL == (entry_ptr->image_ptr = H5MM_malloc(entry_ptr->size + H5C_IMAGE_EXTRA_SPACE)) ) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for on disk image buffer") +#if H5C_DO_MEMORY_SANITY_CHECKS + HDmemcpy(((uint8_t *)entry_ptr->image_ptr) + image_size, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE); +#endif /* H5C_DO_MEMORY_SANITY_CHECKS */ + } /* end if */ + + /* Serialize the entry. Note that the entry need not be dirty. */ + + /* Reset cache_ptr->slist_changed so we can detect slist + * modifications in the pre_serialize call. + */ + cache_ptr->slist_changed = FALSE; + + /* Make note of the entry's current address */ + old_addr = entry_ptr->addr; + + /* Reset the counters so that we can detect insertions, loads, + * moves, and flush dependency height changes caused by the pre_serialize + * and serialize calls. + */ + cache_ptr->entries_loaded_counter = 0; + cache_ptr->entries_inserted_counter = 0; + cache_ptr->entries_relocated_counter = 0; + + /* Call client's pre-serialize callback, if there's one */ + if(entry_ptr->type->pre_serialize && + (entry_ptr->type->pre_serialize)(f, dxpl_id, (void *)entry_ptr, entry_ptr->addr, entry_ptr->size, &new_addr, &new_len, &serialize_flags) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to pre-serialize entry") + + /* Check for any flags set in the pre-serialize callback */ + if(serialize_flags != H5C__SERIALIZE_NO_FLAGS_SET) { + + /* Check for unexpected flags from serialize callback */ + if(serialize_flags & ~(H5C__SERIALIZE_RESIZED_FLAG | H5C__SERIALIZE_MOVED_FLAG)) + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unknown serialize flag(s)") + +#ifdef H5_HAVE_PARALLEL + /* In the parallel case, resizes and moves in the serialize + * operation can cause problems. If they occur, scream and die. + * + * At present, in the parallel case, the aux_ptr will only be + * set if there is more than one process. Thus we can use this + * to detect the parallel case. + * + * This works for now, but if we start using the aux_ptr for + * other purposes, we will have to change this test accordingly. + * + * NB: While this test detects entryies that attempt + * to resize or move themselves during a flush + * in the parallel case, it will not detect an + * entry that dirties, resizes, and/or moves + * other entries during its flush. + * + * From what Quincey tells me, this test is + * sufficient for now, as any flush routine that + * does the latter will also do the former. + * + * If that ceases to be the case, further + * tests will be necessary. + */ + if(cache_ptr->aux_ptr != NULL) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "resize/move in serialize occured in parallel case.") +#endif /* H5_HAVE_PARALLEL */ + + /* Resize the buffer if required */ + if(serialize_flags & H5C__SERIALIZE_RESIZED_FLAG) { + /* Sanity check */ + HDassert(new_len > 0); + + /* Allocate a new image buffer */ + if(NULL == (entry_ptr->image_ptr = H5MM_realloc(entry_ptr->image_ptr, new_len + H5C_IMAGE_EXTRA_SPACE))) + HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for on disk image buffer") +#if H5C_DO_MEMORY_SANITY_CHECKS + HDmemcpy(((uint8_t *)entry_ptr->image_ptr) + new_len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE); +#endif /* H5C_DO_MEMORY_SANITY_CHECKS */ + + /* Update the entry and the cache data structures for a resize. */ + H5C__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, entry_ptr, new_len) + + /* Update the hash table for the size change */ + H5C__UPDATE_INDEX_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_len, entry_ptr, !(entry_ptr->is_dirty)); + + /* The entry can't be protected since we are + * in the process of serializing the cache. Thus we must + * update the replacement policy data structures for the + * size change. The macro deals with the pinned case. + */ + H5C__UPDATE_RP_FOR_SIZE_CHANGE(cache_ptr, entry_ptr, new_len); + + /* It should be in the skip list, update the skip list for the + * size change. + */ + HDassert(entry_ptr->is_dirty); + HDassert(entry_ptr->in_slist); + H5C__UPDATE_SLIST_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_len) + + /* Finally, update the entry for its new size */ + entry_ptr->size = new_len; + } /* end if */ + + /* If required, update the entry and the cache data structures + * for a move + */ + if(serialize_flags & H5C__SERIALIZE_MOVED_FLAG) { + /* Since the entry has moved, it is probably no longer in + * the same place in its list. Thus at a minimum, we must set + * *restart_list_scan_ptr to TRUE. + */ + *restart_list_scan_ptr = TRUE; + + /* Update stats and the entries relocated counter */ + H5C__UPDATE_STATS_FOR_MOVE(cache_ptr, entry_ptr) + + /* We must update cache data structures for the change in address */ + if(entry_ptr->addr == old_addr) { + /* Delete the entry from the hash table and the slist */ + H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr, FAIL) + H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr, FALSE) + + /* Update the entry for its new address */ + entry_ptr->addr = new_addr; + + /* And then reinsert in the index and slist (if appropriate) */ + H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL) + H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL) + } /* end if */ + else /* Move is already done for us -- just do sanity checks */ + HDassert(entry_ptr->addr == new_addr); + } /* end if */ + } /* end if ( serialize_flags != H5C__SERIALIZE_NO_FLAGS_SET ) */ + + /* Reset cache_ptr->slist_changed so we can detect slist + * modifications in the serialize call. + */ + cache_ptr->slist_changed = FALSE; + + /* Serialize object into buffer */ + if(entry_ptr->type->serialize(f, entry_ptr->image_ptr, entry_ptr->size, (void *)entry_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to serialize entry") +#if H5C_DO_MEMORY_SANITY_CHECKS + HDassert(0 == HDmemcmp(((uint8_t *)entry_ptr->image_ptr) + image_len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE)); +#endif /* H5C_DO_MEMORY_SANITY_CHECKS */ + entry_ptr->image_up_to_date = TRUE; + + /* Propagate the fact that the entry is serialized up the + * flush dependency chain if appropriate. Since the image must + * have been out of date for this function to have been called + * (see assertion on entry), no need to check that -- only check + * for flush dependency parents. + */ + HDassert(entry_ptr->flush_dep_nunser_children == 0); + if(entry_ptr->flush_dep_nparents > 0) + if(H5C__mark_flush_dep_serialized(entry_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "Can't propagate flush dep serialized flag") + + /* Reset the flush_in progress flag */ + entry_ptr->flush_in_progress = FALSE; + + /* Set *restart_fd_scan_ptr to TRUE if appropriate, and if we + * haven't already done so. + */ + if(!(*restart_list_scan_ptr)) + if((cache_ptr->entries_loaded_counter > 0) || (cache_ptr->entries_inserted_counter > 0) || + (cache_ptr->entries_relocated_counter > 0)) + *restart_list_scan_ptr = TRUE; + +done: + HDassert((ret_value != SUCCEED) || (!entry_ptr->flush_in_progress)); + HDassert((ret_value != SUCCEED) || (entry_ptr->image_up_to_date)); + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__serialize_single_entry() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__write_cache_image_superblock_msg + * + * Purpose: Write the cache image superblock extension message, + * creating if specified. + * + * In general, the size and location of the cache image block + * will be unknow at the time that the cache image superblock + * message is created. A subsequent call to this routine will + * be used to write the correct data. + * + * Return: Non-negative on success/Negative on failure. + * + * Programmer: John Mainzer, 7/4/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__write_cache_image_superblock_msg(H5F_t *f, hid_t dxpl_id, hbool_t create) +{ + H5C_t * cache_ptr; + H5O_mdci_t mdci_msg; /* metadata cache image message */ + /* to insert in the superblock */ + /* extension. */ + unsigned mesg_flags = H5O_MSG_FLAG_FAIL_IF_UNKNOWN_ALWAYS; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(f); + HDassert(f->shared); + HDassert(f->shared->cache); + cache_ptr = f->shared->cache; + HDassert(cache_ptr); + HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC); + HDassert(cache_ptr->close_warning_received); + + /* Write data into the metadata cache image superblock extension message. + * Note that this data will be bogus when we first create the message. + * We will overwrite this data later in a second call to this function. + */ + mdci_msg.addr = cache_ptr->image_addr; +#ifdef H5_HAVE_PARALLEL + if(cache_ptr->aux_ptr) { /* we have multiple processes */ + H5AC_aux_t * aux_ptr; + + aux_ptr = (H5AC_aux_t *)cache_ptr->aux_ptr; + HDassert(aux_ptr->magic == H5AC__H5AC_AUX_T_MAGIC); + mdci_msg.size = aux_ptr->p0_image_len; + } /* end if */ + else +#endif /* H5_HAVE_PARALLEL */ + mdci_msg.size = cache_ptr->image_len; + + /* Write metadata cache image message to superblock extension */ + if(H5F_super_ext_write_msg(f, dxpl_id, H5O_MDCI_MSG_ID, &mdci_msg, create, mesg_flags) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_WRITEERROR, FAIL, "can't write metadata cache image message to superblock extension") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__write_cache_image_superblock_msg() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__write_cache_image + * + * Purpose: Write the supplied metadata cache image to the specified + * location in file. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 8/26/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__write_cache_image(H5F_t *f, hid_t dxpl_id, const H5C_t *cache_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(f); + HDassert(cache_ptr); + HDassert(H5F_addr_defined(cache_ptr->image_addr)); + HDassert(cache_ptr->image_len > 0); + HDassert(cache_ptr->image_buffer); + +#ifdef H5_HAVE_PARALLEL +{ + H5AC_aux_t *aux_ptr = (H5AC_aux_t *)cache_ptr->aux_ptr; + + if((NULL == aux_ptr) || (aux_ptr->mpi_rank == 0)) { + HDassert((NULL == aux_ptr) || (aux_ptr->magic == H5AC__H5AC_AUX_T_MAGIC)); +#endif /* H5_HAVE_PARALLEL */ + + /* Write the buffer (if serial access, or rank 0 for parallel access) */ + if(H5F_block_write(f, H5FD_MEM_SUPER, cache_ptr->image_addr, cache_ptr->image_len, dxpl_id, cache_ptr->image_buffer) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't write metadata cache image block to file.") +#ifdef H5_HAVE_PARALLEL + } /* end if */ +} /* end block */ +#endif /* H5_HAVE_PARALLEL */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* H5C__write_cache_image() */ + diff --git a/src/H5Cpkg.h b/src/H5Cpkg.h index 6e37bca..1656a32 100644 --- a/src/H5Cpkg.h +++ b/src/H5Cpkg.h @@ -209,7 +209,6 @@ if ( ( (entry_ptr) == NULL ) || \ ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ ( (head_ptr) != (tail_ptr) ) \ ) || \ - ( (len) < 0 ) || \ ( ( (len) == 1 ) && \ ( ( (head_ptr) != (tail_ptr) ) || \ ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \ @@ -494,7 +493,6 @@ if ( ( (entry_ptr) == NULL ) || \ ( ( ( (hd_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ ( (hd_ptr) != (tail_ptr) ) \ ) || \ - ( (len) < 0 ) || \ ( ( (len) == 1 ) && \ ( ( (hd_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \ ( (hd_ptr) == NULL ) || ( (hd_ptr)->size != (Size) ) \ @@ -514,7 +512,6 @@ if ( ( (entry_ptr) == NULL ) || \ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ ( (head_ptr) != (tail_ptr) ) \ ) || \ - ( (len) < 0 ) || \ ( ( (len) == 1 ) && \ ( ( (head_ptr) != (tail_ptr) ) || \ ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \ @@ -599,23 +596,6 @@ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_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. - * - * JRM -- 8/9/06 - * More pinned entry stats related updates. - * - * JRM -- 3/31/07 - * Updated H5C__UPDATE_STATS_FOR_PROTECT() to keep stats on - * read and write protects. - * - * MAM -- 1/15/09 - * Created H5C__UPDATE_MAX_INDEX_SIZE_STATS to contain - * common code within macros that update the maximum - * index, clean_index, and dirty_index statistics fields. - * ***********************************************************************/ #define H5C__UPDATE_CACHE_HIT_RATE_STATS(cache_ptr, hit) \ @@ -702,6 +682,31 @@ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ #define H5C__UPDATE_STATS_FOR_INDEX_SCAN_RESTART(cache_ptr) \ ((cache_ptr)->index_scan_restarts)++; +#define H5C__UPDATE_STATS_FOR_CACHE_IMAGE_CREATE(cache_ptr) \ +{ \ + (cache_ptr)->images_created++; \ +} + +#define H5C__UPDATE_STATS_FOR_CACHE_IMAGE_LOAD(cache_ptr) \ +{ \ + /* make sure image len is still good */ \ + HDassert((cache_ptr)->image_len > 0); \ + (cache_ptr)->images_loaded++; \ + (cache_ptr)->last_image_size = (cache_ptr)->image_len; \ +} + +#define H5C__UPDATE_STATS_FOR_PREFETCH(cache_ptr, dirty) \ +{ \ + (cache_ptr)->prefetches++; \ + if ( dirty ) \ + (cache_ptr)->dirty_prefetches++; \ +} + +#define H5C__UPDATE_STATS_FOR_PREFETCH_HIT(cache_ptr) \ +{ \ + (cache_ptr)->prefetch_hits++; \ +} + #if H5C_COLLECT_CACHE_ENTRY_STATS #define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) \ @@ -926,6 +931,10 @@ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \ #define H5C__UPDATE_STATS_FOR_SLIST_SCAN_RESTART(cache_ptr) #define H5C__UPDATE_STATS_FOR_LRU_SCAN_RESTART(cache_ptr) #define H5C__UPDATE_STATS_FOR_INDEX_SCAN_RESTART(cache_ptr) +#define H5C__UPDATE_STATS_FOR_CACHE_IMAGE_CREATE(cache_ptr) +#define H5C__UPDATE_STATS_FOR_CACHE_IMAGE_LOAD(cache_ptr) +#define H5C__UPDATE_STATS_FOR_PREFETCH(cache_ptr, dirty) +#define H5C__UPDATE_STATS_FOR_PREFETCH_HIT(cache_ptr) #endif /* H5C_COLLECT_CACHE_STATS */ @@ -2239,6 +2248,120 @@ if ( ( (cache_ptr)->index_size != \ /*------------------------------------------------------------------------- * + * Macro: H5C__UPDATE_RP_FOR_INSERT_APPEND + * + * Purpose: Update the replacement policy data structures for an + * insertion of the specified cache entry. + * + * Unlike H5C__UPDATE_RP_FOR_INSERTION below, mark the + * new entry as the LEAST recently used entry, not the + * most recently used. + * + * For now at least, this macro should only be used in + * the reconstruction of the metadata cache from a cache + * image block. + * + * 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, 8/15/15 + * + *------------------------------------------------------------------------- + */ + +#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS + +#define H5C__UPDATE_RP_FOR_INSERT_APPEND(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_read_only) ); \ + HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + 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 tail of the LRU list. */ \ + \ + H5C__DLL_APPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \ + (cache_ptr)->LRU_tail_ptr, \ + (cache_ptr)->LRU_list_len, \ + (cache_ptr)->LRU_list_size, (fail_val)) \ + \ + /* insert the entry at the tail of the clean or dirty LRU list as \ + * appropriate. \ + */ \ + \ + if ( entry_ptr->is_dirty ) { \ + H5C__AUX_DLL_APPEND((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_APPEND((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. */ \ + } \ +} + +#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + +#define H5C__UPDATE_RP_FOR_INSERT_APPEND(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_read_only) ); \ + HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \ + HDassert( (entry_ptr)->size > 0 ); \ + \ + 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 tail of the LRU list. */ \ + \ + H5C__DLL_APPEND((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. */ \ + } \ +} + +#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */ + + +/*------------------------------------------------------------------------- + * * Macro: H5C__UPDATE_RP_FOR_INSERTION * * Purpose: Update the replacement policy data structures for an @@ -2500,7 +2623,6 @@ if ( ( (cache_ptr)->index_size != \ (cache_ptr)->pel_tail_ptr, \ (cache_ptr)->pel_len, \ (cache_ptr)->pel_size, (fail_val)) \ - HDassert( (cache_ptr)->pel_len >= 0 ); \ \ } else { \ \ @@ -2901,7 +3023,6 @@ if ( ( (cache_ptr)->index_size != \ 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)) \ - HDassert( (cache_ptr)->pel_len >= 0 ); \ \ /* modified LRU specific code */ \ \ @@ -3433,10 +3554,8 @@ typedef struct H5C_tag_info_t { * types are stored in the type_name_table discussed below, and * indexed by the ids. * - * type_name_table_ptr: Pointer to an array of pointer to char of length - * max_type_id + 1. The strings pointed to by the entries - * in the array are the names of the entry types associated - * with the indexing type IDs. + * class_table_ptr: Pointer to an array of H5C_class_t of length + * max_type_id + 1. Entry classes for the cache. * * max_cache_size: Nominal maximum number of bytes that may be stored in the * cache. This value should be viewed as a soft limit, as the @@ -3986,7 +4105,22 @@ typedef struct H5C_tag_info_t { * * size_decreased: Boolean flag set to TRUE whenever the maximum cache * size is decreased. The flag triggers a call to - * H5C_make_space_in_cache() on the next call to H5C_protect(). + * H5C__make_space_in_cache() on the next call to H5C_protect(). + * + * resize_in_progress: As the metadata cache has become re-entrant, it is + * possible that a protect may trigger a call to + * H5C__auto_adjust_cache_size(), which may trigger a flush, + * which may trigger a protect, which will result in another + * call to H5C__auto_adjust_cache_size(). + * + * The resize_in_progress boolean flag is used to detect this, + * and to prevent the infinite recursion that would otherwise + * occur. + * + * Note that this issue is not hypothetical -- this field + * was added 12/29/15 to fix a bug exposed in the testing + * of changes to the file driver info superblock extension + * management code needed to support rings. * * resize_ctl: Instance of H5C_auto_size_ctl_t containing configuration * data for automatic cache resizing. @@ -4063,6 +4197,77 @@ typedef struct H5C_tag_info_t { * this field will be reset every automatic resize epoch. * * + * Metadata cache image management related fields. + * + * image_ctl: Instance of H5C_cache_image_ctl_t containing configuration + * data for generation of a cache image on file close. + * + * serialization_in_progress: Boolean field that is set to TRUE iff + * the cache is in the process of being serialized. This + * field is needed to support the H5C_serialization_in_progress() + * call, which is in turn required for sanity checks in some + * cache clients. + * + * load_image: Boolean flag indicating that the metadata cache image + * superblock extension message exists and should be + * read, and the image block read and decoded on the next + * call to H5C_protect(). + * + * image_loaded: Boolean flag indicating that the metadata cache has + * loaded the metadata cache image as directed by the + * MDC cache image superblock extension message. + * + * delete_image: Boolean flag indicating whether the metadata cache image + * superblock message should be deleted and the cache image + * file space freed after they have been read and decoded. + * + * This flag should be set to TRUE iff the file is opened + * R/W and there is a cache image to be read. + * + * image_addr: haddr_t containing the base address of the on disk + * metadata cache image, or HADDR_UNDEF if that value is + * undefined. Note that this field is used both in the + * construction and write, and the read and decode of + * metadata cache image blocks. + * + * image_len: hsize_t containing the size of the on disk metadata cache + * image, or zero if that value is undefined. Note that this + * field is used both in the construction and write, and the + * read and decode of metadata cache image blocks. + * + * image_data_len: size_t containing the number of bytes of data in the + * on disk metadata cache image, or zero if that value is + * undefined. + * + * In most cases, this value is the same as the image_len + * above. It exists to allow for metadata cache image blocks + * that are larger than the actual image. Thus in all + * cases image_data_len <= image_len. + * + * To create the metadata cache image, we must first serialize all the + * entries in the metadata cache. This is done by a scan of the index. + * As entries must be serialized in increasing flush dependency height + * order, we scan the index repeatedly, once for each flush dependency + * height in increasing order. + * + * This operation is complicated by the fact that entries other the the + * target may be inserted, loaded, relocated, or removed from the cache + * (either by eviction or the take ownership flag) as the result of a + * pre_serialize or serialize callback. While entry removals are not + * a problem for the scan of the index, insertions, loads, and relocations + * are. Hence the entries loaded, inserted, and relocated counters + * listed below have been implemented to allow these conditions to be + * detected and dealt with by restarting the scan. + * + * The serialization operation is further complicated by the fact that + * the flush dependency height of a given entry may increase (as the + * result of an entry load or insert) or decrease (as the result of an + * entry removal -- via either eviction or the take ownership flag). The + * entry_fd_height_change_counter field is maintained to allow detection + * of this condition, and a restart of the scan when it occurs. + * + * Note that all these new fields would work just as well as booleans. + * * entries_loaded_counter: Number of entries loaded into the cache * since the last time this field was reset. * @@ -4072,6 +4277,29 @@ typedef struct H5C_tag_info_t { * entries relocated_counter: Number of entries whose base address has * been changed since the last time this field was reset. * + * entry_fd_height_change_counter: Number of entries whose flush dependency + * height has changed since the last time this field was reset. + * + * The following fields are used assemble the cache image prior to + * writing it to disk. + * + * num_entries_in_image: Unsigned integer field containing the number of entries + * to be copied into the metadata cache image. Note that + * this value will be less than the number of entries in + * the cache, and the superblock and its related entries + * are not written to the metadata cache image. + * + * image_entries: Pointer to a dynamically allocated array of instance of + * H5C_image_entry_t of length num_entries_in_image, or NULL + * if that array does not exist. This array is used to + * assemble entry data to be included in the image, and to + * sort them by flush dependency height and LRU rank. + * + * image_buffer: Pointer to the dynamically allocated buffer of length + * image_len in which the metadata cache image is assembled, + * or NULL if that buffer does not exist. + * + * * Free Space Manager Related fields: * * The free space managers must be informed when we are about to close @@ -4285,23 +4513,63 @@ typedef struct H5C_tag_info_t { * max_pel_size: Largest value attained by the pel_size field in the * current epoch. * - * calls_to_msic: Total number of calls to H5C_make_space_in_cache + * calls_to_msic: Total number of calls to H5C__make_space_in_cache * * total_entries_skipped_in_msic: Number of clean entries skipped while - * enforcing the min_clean_fraction in H5C_make_space_in_cache(). + * enforcing the min_clean_fraction in H5C__make_space_in_cache(). * * total_entries_scanned_in_msic: Number of clean entries skipped while - * enforcing the min_clean_fraction in H5C_make_space_in_cache(). + * enforcing the min_clean_fraction in H5C__make_space_in_cache(). * * max_entries_skipped_in_msic: Maximum number of clean entries skipped - * in any one call to H5C_make_space_in_cache(). + * in any one call to H5C__make_space_in_cache(). * * max_entries_scanned_in_msic: Maximum number of entries scanned over - * in any one call to H5C_make_space_in_cache(). + * in any one call to H5C__make_space_in_cache(). * * entries_scanned_to_make_space: Number of entries scanned only when looking * for entries to evict in order to make space in cache. * + * + * The following fields track statistics on cache images. + * + * images_created: Integer field containing the number of cache images + * created since the last time statistics were reset. + * + * At present, this field must always be either 0 or 1. + * Further, since cache images are only created at file + * close, this field should only be set at that time. + * + * images_loaded: Integer field containing the number of cache images + * loaded since the last time statistics were reset. + * + * At present, this field must always be either 0 or 1. + * Further, since cache images are only loaded at the + * time of the first protect or on file close, this value + * should only change on those events. + * + * last_image_size: Size of the most recently loaded metadata cache image + * loaded into the cache, or zero if no image has been + * loaded. + * + * At present, at most one cache image can be loaded into + * the metadata cache for any given file, and this image + * will be loaded either on the first protect, or on file + * close if no entry is protected before then. + * + * + * Fields for tracking prefetched entries. Note that flushes and evictions + * of prefetched entries are tracked in the flushes and evictions arrays + * discused above. + * + * prefetches: Number of prefetched entries that are loaded to the + * cache. + * + * dirty_prefetches: Number of dirty prefetched entries that are loaded + * into the cache. + * + * prefetch_hits: Number of prefetched entries that are actually used. + * * * As entries are now capable of moving, loading, dirtying, and deleting * other entries in their pre_serialize and serialize callbacks, it has @@ -4372,6 +4640,11 @@ typedef struct H5C_tag_info_t { * field is intended to allow marking of output of with * the processes mpi rank. * + * get_entry_ptr_from_addr_counter: Counter used to track the number of + * times the H5C_get_entry_ptr_from_addr() function has been + * called successfully. This field is only defined when + * NDEBUG is not #defined. + * ****************************************************************************/ struct H5C_t { uint32_t magic; @@ -4382,7 +4655,7 @@ struct H5C_t { FILE * log_file_ptr; void * aux_ptr; int32_t max_type_id; - const char * (* type_name_table_ptr); + const H5C_class_t * const *class_table_ptr; size_t max_cache_size; size_t min_clean_size; H5C_write_permitted_func_t check_write_permitted; @@ -4392,16 +4665,16 @@ struct H5C_t { hbool_t close_warning_received; /* Fields for maintaining [hash table] index of entries */ - int32_t index_len; + uint32_t index_len; size_t index_size; - int32_t index_ring_len[H5C_RING_NTYPES]; + uint32_t index_ring_len[H5C_RING_NTYPES]; size_t index_ring_size[H5C_RING_NTYPES]; size_t clean_index_size; size_t clean_index_ring_size[H5C_RING_NTYPES]; size_t dirty_index_size; size_t dirty_index_ring_size[H5C_RING_NTYPES]; H5C_cache_entry_t * index[H5C__HASH_TABLE_LEN]; - int32_t il_len; + uint32_t il_len; size_t il_size; H5C_cache_entry_t * il_head; H5C_cache_entry_t * il_tail; @@ -4413,12 +4686,12 @@ struct H5C_t { /* Fields for maintaining list of in-order entries, for flushing */ hbool_t slist_changed; - int32_t slist_len; + uint32_t slist_len; size_t slist_size; - int32_t slist_ring_len[H5C_RING_NTYPES]; + uint32_t slist_ring_len[H5C_RING_NTYPES]; size_t slist_ring_size[H5C_RING_NTYPES]; H5SL_t * slist_ptr; - int32_t num_last_entries; + uint32_t num_last_entries; #if H5C_DO_SANITY_CHECKS int64_t slist_len_increase; int64_t slist_size_increase; @@ -4429,38 +4702,38 @@ struct H5C_t { hbool_t ignore_tags; /* Fields for tracking protected entries */ - int32_t pl_len; + uint32_t pl_len; size_t pl_size; H5C_cache_entry_t * pl_head_ptr; H5C_cache_entry_t * pl_tail_ptr; /* Fields for tracking pinned entries */ - int32_t pel_len; + uint32_t pel_len; size_t pel_size; H5C_cache_entry_t * pel_head_ptr; H5C_cache_entry_t * pel_tail_ptr; /* Fields for complete LRU list of entries */ - int32_t LRU_list_len; + uint32_t LRU_list_len; size_t LRU_list_size; H5C_cache_entry_t * LRU_head_ptr; H5C_cache_entry_t * LRU_tail_ptr; /* Fields for clean LRU list of entries */ - int32_t cLRU_list_len; + uint32_t cLRU_list_len; size_t cLRU_list_size; H5C_cache_entry_t * cLRU_head_ptr; H5C_cache_entry_t * cLRU_tail_ptr; /* Fields for dirty LRU list of entries */ - int32_t dLRU_list_len; + uint32_t dLRU_list_len; size_t dLRU_list_size; H5C_cache_entry_t * dLRU_head_ptr; H5C_cache_entry_t * dLRU_tail_ptr; #ifdef H5_HAVE_PARALLEL /* Fields for collective metadata reads */ - int32_t coll_list_len; + uint32_t coll_list_len; size_t coll_list_size; H5C_cache_entry_t * coll_head_ptr; H5C_cache_entry_t * coll_tail_ptr; @@ -4477,6 +4750,7 @@ struct H5C_t { hbool_t resize_enabled; hbool_t cache_full; hbool_t size_decreased; + hbool_t resize_in_progress; H5C_auto_size_ctl_t resize_ctl; /* Fields for epoch markers used in automatic cache size adjustment */ @@ -4492,9 +4766,23 @@ struct H5C_t { int64_t cache_hits; int64_t cache_accesses; + /* fields supporting generation of a cache image on file close */ + H5C_cache_image_ctl_t image_ctl; + hbool_t serialization_in_progress; + hbool_t load_image; + hbool_t image_loaded; + hbool_t delete_image; + haddr_t image_addr; + hsize_t image_len; + hsize_t image_data_len; int64_t entries_loaded_counter; int64_t entries_inserted_counter; int64_t entries_relocated_counter; + int64_t entry_fd_height_change_counter; + uint32_t num_entries_in_image; + H5C_image_entry_t * image_entries; + void * image_buffer; + /* Free Space Manager Related fields */ hbool_t rdfsm_settled; hbool_t mdfsm_settled; @@ -4532,21 +4820,21 @@ struct H5C_t { int64_t total_successful_ht_search_depth; int64_t failed_ht_searches; int64_t total_failed_ht_search_depth; - int32_t max_index_len; + uint32_t max_index_len; size_t max_index_size; size_t max_clean_index_size; size_t max_dirty_index_size; /* Fields for in-order skip list */ - int32_t max_slist_len; + uint32_t max_slist_len; size_t max_slist_size; /* Fields for protected entry list */ - int32_t max_pl_len; + uint32_t max_pl_len; size_t max_pl_size; /* Fields for pinned entry list */ - int32_t max_pel_len; + uint32_t max_pel_len; size_t max_pel_size; /* Fields for tracking 'make space in cache' (msic) operations */ @@ -4562,6 +4850,16 @@ struct H5C_t { int64_t LRU_scan_restarts; int64_t index_scan_restarts; + /* Fields for tracking cache image operations */ + int32_t images_created; + int32_t images_loaded; + hsize_t last_image_size; + + /* Fields for tracking prefetched entries */ + int64_t prefetches; + int64_t dirty_prefetches; + int64_t prefetch_hits; + #if H5C_COLLECT_CACHE_ENTRY_STATS int32_t max_accesses[H5C__MAX_NUM_TYPE_IDS + 1]; int32_t min_accesses[H5C__MAX_NUM_TYPE_IDS + 1]; @@ -4573,6 +4871,10 @@ struct H5C_t { #endif /* H5C_COLLECT_CACHE_STATS */ char prefix[H5C__PREFIX_LEN]; + +#ifndef NDEBUG + int64_t get_entry_ptr_from_addr_counter; +#endif /* NDEBUG */ }; /* Define typedef for tagged cache entry iteration callbacks */ @@ -4583,19 +4885,24 @@ typedef int (*H5C_tag_iter_cb_t)(H5C_cache_entry_t *entry, void *ctx); /* Package Private Variables */ /*****************************/ -/* Metadata cache epoch class */ -H5_DLLVAR const H5C_class_t H5C__epoch_marker_class; - /******************************/ /* Package Private Prototypes */ /******************************/ +H5_DLL herr_t H5C__prep_image_for_file_close(H5F_t *f, hid_t dxpl_id); +H5_DLL herr_t H5C__deserialize_prefetched_entry(H5F_t * f, hid_t dxpl_id, + H5C_t * cache_ptr, H5C_cache_entry_t** entry_ptr_ptr, + const H5C_class_t * type, haddr_t addr, void * udata); /* General routines */ H5_DLL herr_t H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr, unsigned flags); +H5_DLL herr_t H5C__generate_cache_image(H5F_t *f, hid_t dxpl_id, H5C_t *cache_ptr); +H5_DLL herr_t H5C__load_cache_image(H5F_t *f, hid_t dxpl_id); H5_DLL herr_t H5C__mark_flush_dep_serialized(H5C_cache_entry_t * entry_ptr); H5_DLL herr_t H5C__mark_flush_dep_unserialized(H5C_cache_entry_t * entry_ptr); +H5_DLL herr_t H5C__make_space_in_cache(H5F_t * f, hid_t dxpl_id, + size_t space_needed, hbool_t write_permitted); H5_DLL herr_t H5C__flush_marked_entries(H5F_t * f, hid_t dxpl_id); H5_DLL herr_t H5C__iter_tagged_entries(H5C_t *cache, haddr_t tag, hbool_t match_global, H5C_tag_iter_cb_t cb, void *cb_ctx); diff --git a/src/H5Cprefetched.c b/src/H5Cprefetched.c new file mode 100644 index 0000000..9b11006 --- /dev/null +++ b/src/H5Cprefetched.c @@ -0,0 +1,352 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * 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://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*------------------------------------------------------------------------- + * + * Created: H5Cprefetched.c + * December 28 2016 + * Quincey Koziol + * + * Purpose: Metadata cache prefetched entry callbacks. + * + *------------------------------------------------------------------------- + */ + + +/***********/ +/* Headers */ +/***********/ +#include "H5private.h" /* Generic Functions */ +#include "H5ACprivate.h" /* Metadata cache */ +#include "H5FLprivate.h" /* Free Lists */ +#include "H5MMprivate.h" /* Memory management */ + + +/****************/ +/* Local Macros */ +/****************/ + + +/******************/ +/* Local Typedefs */ +/******************/ + + +/********************/ +/* Local Prototypes */ +/********************/ + +/**************************************************************************** + * + * Declarations for prefetched cache entry callbacks. + * + ****************************************************************************/ +static herr_t H5C__prefetched_entry_get_initial_load_size(void *udata_ptr, + size_t *image_len_ptr); +static herr_t H5C__prefetched_entry_get_final_load_size(const void *image_ptr, + size_t image_len, void *udata_ptr, size_t *actual_len_ptr); +static htri_t H5C__prefetched_entry_verify_chksum(const void *image_ptr, + size_t len, void *udata_ptr); +static void * H5C__prefetched_entry_deserialize(const void *image_ptr, + size_t len, void *udata, hbool_t *dirty_ptr); +static herr_t H5C__prefetched_entry_image_len(const void *thing, + size_t *image_len_ptr); +static herr_t H5C__prefetched_entry_pre_serialize(H5F_t *f, + hid_t dxpl_id, void *thing, haddr_t addr, size_t len, + haddr_t *new_addr_ptr, size_t *new_len_ptr, unsigned *flags_ptr); +static herr_t H5C__prefetched_entry_serialize(const H5F_t *f, void *image_ptr, + size_t len, void *thing); +static herr_t H5C__prefetched_entry_notify(H5C_notify_action_t action, + void *thing); +static herr_t H5C__prefetched_entry_free_icr(void *thing); +static herr_t H5C__prefetched_entry_fsf_size(const void *thing, + size_t *fsf_size_ptr); + + +/*********************/ +/* Package Variables */ +/*********************/ + +/* Declare external the free list for H5C_cache_entry_t's */ +H5FL_EXTERN(H5C_cache_entry_t); + + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + + +/*******************/ +/* Local Variables */ +/*******************/ + + +const H5AC_class_t H5AC_PREFETCHED_ENTRY[1] = {{ + /* id = */ H5AC_PREFETCHED_ENTRY_ID, + /* name = */ "prefetched entry", + /* mem_type = */ H5FD_MEM_DEFAULT, /* value doesn't matter */ + /* flags = */ H5AC__CLASS_NO_FLAGS_SET, + /* get_initial_load_size = */ H5C__prefetched_entry_get_initial_load_size, + /* get_final_load_size = */ H5C__prefetched_entry_get_final_load_size, + /* verify_chksum = */ H5C__prefetched_entry_verify_chksum, + /* deserialize = */ H5C__prefetched_entry_deserialize, + /* image_len = */ H5C__prefetched_entry_image_len, + /* pre_serialize = */ H5C__prefetched_entry_pre_serialize, + /* serialize = */ H5C__prefetched_entry_serialize, + /* notify = */ H5C__prefetched_entry_notify, + /* free_icr = */ H5C__prefetched_entry_free_icr, + /* fsf_size = */ H5C__prefetched_entry_fsf_size, +}}; + + + +/*************************************************************************** + * With two exceptions, these functions should never be called, and thus + * there is little point in documenting them separately as they all simply + * throw an error. + * + * See header comments for the two exceptions (free_icr and notify). + * + * JRM - 8/13/15 + * + ***************************************************************************/ + +static herr_t +H5C__prefetched_entry_get_initial_load_size(void H5_ATTR_UNUSED *udata_ptr, + size_t H5_ATTR_UNUSED *image_len_ptr) +{ + FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */ + + HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn."); + + FUNC_LEAVE_NOAPI(FAIL) +} /* end H5C__prefetched_entry_get_initial_load_size() */ + +static herr_t +H5C__prefetched_entry_get_final_load_size(const void H5_ATTR_UNUSED *image_ptr, + size_t H5_ATTR_UNUSED image_len, void H5_ATTR_UNUSED *udata_ptr, + size_t H5_ATTR_UNUSED *actual_len_ptr) +{ + FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */ + + HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn."); + + FUNC_LEAVE_NOAPI(FAIL) +} /* end H5C__prefetched_entry_get_final_load_size() */ + +static htri_t +H5C__prefetched_entry_verify_chksum(const void H5_ATTR_UNUSED *image_ptr, + size_t H5_ATTR_UNUSED len, void H5_ATTR_UNUSED *udata_ptr) +{ + FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */ + + HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn."); + + FUNC_LEAVE_NOAPI(FAIL) +} /* end H5C__prefetched_verify_chksum() */ + + +static void * +H5C__prefetched_entry_deserialize(const void H5_ATTR_UNUSED * image_ptr, + size_t H5_ATTR_UNUSED len, void H5_ATTR_UNUSED * udata, + hbool_t H5_ATTR_UNUSED * dirty_ptr) +{ + FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */ + + HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn."); + + FUNC_LEAVE_NOAPI(NULL) +} /* end H5C__prefetched_entry_deserialize() */ + + +static herr_t +H5C__prefetched_entry_image_len(const void H5_ATTR_UNUSED *thing, + size_t H5_ATTR_UNUSED *image_len_ptr) +{ + FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */ + + HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn."); + + FUNC_LEAVE_NOAPI(FAIL) +} /* end H5C__prefetched_entry_image_len() */ + + +static herr_t +H5C__prefetched_entry_pre_serialize(H5F_t H5_ATTR_UNUSED *f, + hid_t H5_ATTR_UNUSED dxpl_id, void H5_ATTR_UNUSED *thing, + haddr_t H5_ATTR_UNUSED addr, size_t H5_ATTR_UNUSED len, + haddr_t H5_ATTR_UNUSED *new_addr_ptr, + size_t H5_ATTR_UNUSED *new_len_ptr, + unsigned H5_ATTR_UNUSED *flags_ptr) +{ + FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */ + + HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn."); + + FUNC_LEAVE_NOAPI(FAIL) +} /* end H5C__prefetched_entry_pre_serialize() */ + + +static herr_t +H5C__prefetched_entry_serialize(const H5F_t H5_ATTR_UNUSED *f, + void H5_ATTR_UNUSED *image_ptr, + size_t H5_ATTR_UNUSED len, void H5_ATTR_UNUSED *thing) +{ + FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */ + + HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn."); + + FUNC_LEAVE_NOAPI(FAIL) +} /* end H5C__prefetched_entry_serialize() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__prefetched_entry_notify + * + * Purpose: On H5AC_NOTIFY_ACTION_BEFORE_EVICT, check to see if the + * target entry is a child in a flush dependency relationship. + * If it is, destroy that flush dependency relationship. + * + * Ignore on all other notifications. + * + * Return: Success: SUCCEED + * Failure: FAIL + * + * Programmer: John Mainzer + * 8/13/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__prefetched_entry_notify(H5C_notify_action_t action, void *_thing) +{ + H5C_cache_entry_t * entry_ptr = (H5C_cache_entry_t *)_thing; + unsigned u; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(entry_ptr); + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(entry_ptr->prefetched); + + switch(action) { + case H5C_NOTIFY_ACTION_AFTER_INSERT: + case H5C_NOTIFY_ACTION_AFTER_LOAD: + case H5C_NOTIFY_ACTION_AFTER_FLUSH: + case H5C_NOTIFY_ACTION_ENTRY_DIRTIED: + case H5C_NOTIFY_ACTION_ENTRY_CLEANED: + case H5C_NOTIFY_ACTION_CHILD_DIRTIED: + case H5C_NOTIFY_ACTION_CHILD_CLEANED: + case H5C_NOTIFY_ACTION_CHILD_UNSERIALIZED: + case H5C_NOTIFY_ACTION_CHILD_SERIALIZED: + /* do nothing */ + break; + + case H5C_NOTIFY_ACTION_BEFORE_EVICT: + for(u = 0; u < entry_ptr->flush_dep_nparents; u++) { + H5C_cache_entry_t * parent_ptr; + + /* Sanity checks */ + HDassert(entry_ptr->flush_dep_parent); + parent_ptr = entry_ptr->flush_dep_parent[u]; + HDassert(parent_ptr); + HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(parent_ptr->flush_dep_nchildren > 0); + + /* Destroy flush dependency with flush dependency parent */ + if(H5C_destroy_flush_dependency(parent_ptr, entry_ptr) < 0) + HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "unable to destroy prefetched entry flush dependency") + + if(parent_ptr->prefetched) { + /* In prefetched entries, the fd_child_count field is + * used in sanity checks elsewhere. Thus update this + * field to reflect the destruction of the flush + * dependency relationship. + */ + HDassert(parent_ptr->fd_child_count > 0); + (parent_ptr->fd_child_count)--; + } /* end if */ + } /* end for */ + break; + + default: + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unknown action from metadata cache") + break; + } /* end switch */ + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5C__prefetched_entry_notify() */ + + +/*------------------------------------------------------------------------- + * Function: H5C__prefetched_entry_free_icr + * + * Purpose: Free the in core representation of the prefetched entry. + * Verify that the image buffer associated with the entry + * has been either transferred or freed. + * + * Return: Success: SUCCEED + * Failure: FAIL + * + * Programmer: John Mainzer + * 8/13/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5C__prefetched_entry_free_icr(void *_thing) +{ + H5C_cache_entry_t *entry_ptr = (H5C_cache_entry_t *)_thing; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(entry_ptr); + HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_BAD_MAGIC); + HDassert(entry_ptr->prefetched); + + /* Release array for flush dependency parent addresses */ + if(entry_ptr->fd_parent_addrs != NULL) { + HDassert(entry_ptr->fd_parent_count > 0); + entry_ptr->fd_parent_addrs = (haddr_t *)H5MM_xfree((void *)entry_ptr->fd_parent_addrs); + } /* end if */ + else + HDassert(entry_ptr->fd_parent_count == 0); + + if(entry_ptr->image_ptr != NULL) + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "prefetched entry image buffer still attatched?") + + entry_ptr = H5FL_FREE(H5C_cache_entry_t, entry_ptr); + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5C__prefetched_entry_free_icr() */ + + +static herr_t +H5C__prefetched_entry_fsf_size(const void H5_ATTR_UNUSED *thing, + size_t H5_ATTR_UNUSED *fsf_size_ptr) +{ + FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */ + + HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn."); + + FUNC_LEAVE_NOAPI(FAIL) +} /* end H5C__prefetched_entry_fsf_size() */ + diff --git a/src/H5Cprivate.h b/src/H5Cprivate.h index 654ce35..47573f9 100644 --- a/src/H5Cprivate.h +++ b/src/H5Cprivate.h @@ -114,6 +114,7 @@ /* Cache configuration versions */ #define H5C__CURR_AUTO_SIZE_CTL_VER 1 #define H5C__CURR_AUTO_RESIZE_RPT_FCN_VER 1 +#define H5C__CURR_CACHE_IMAGE_CTL_VER 1 /* Default configuration settings */ #define H5C__DEF_AR_UPPER_THRESHHOLD 0.9999f @@ -994,16 +995,16 @@ typedef int H5C_ring_t; * just before the entry is freed. * * This is necessary, as the LRU list can be changed out - * from under H5C_make_space_in_cache() by the serialize + * from under H5C__make_space_in_cache() by the serialize * callback which may change the size of an existing entry, * and/or load a new entry while serializing the target entry. * * This in turn can cause a recursive call to - * H5C_make_space_in_cache() which may either flush or evict + * H5C__make_space_in_cache() which may either flush or evict * the next entry that the first invocation of that function * was about to examine. * - * The magic field allows H5C_make_space_in_cache() to + * The magic field allows H5C__make_space_in_cache() to * detect this case, and re-start its scan from the bottom * of the LRU when this situation occurs. * @@ -1338,6 +1339,187 @@ typedef int H5C_ring_t; * In either case, when there is no previous item, it should * be NULL. * + * Fields supporting the cache image feature: + * + * The following fields are used to store data about the entry which must + * be stored in the cache image block, but which will typically be either + * lost or heavily altered in the process of serializing the cache and + * preparing its contents to be copied into the cache image block. + * + * Some fields are also used in loading the contents of the metadata cache + * image back into the cache, and in managing such entries until they are + * either protected by the library (at which point they become regular + * entries) or are evicted. See discussion of the prefetched field for + * further details. + * + * include_in_image: Boolean flag indicating whether this entry should + * be included in the metadata cache image. This field should + * always be false prior to the H5C_prep_for_file_close() call. + * During that call, it should be set to TRUE for all entries + * that are to be included in the metadata cache image. At + * present, only the superblock, the superblock extension + * object header and its chunks (if any) are omitted from + * the image. + * + * lru_rank: Rank of the entry in the LRU just prior to file close. + * + * Note that the first entry on the LRU has lru_rank 1, + * and that entries not on the LRU at that time will have + * either lru_rank -1 (if pinned) or 0 (if loaded during + * the process of flushing the cache. + * + * image_dirty: Boolean flag indicating whether the entry should be marked + * as dirty in the metadata cache image. The flag is set to + * TRUE iff the entry is dirty when H5C_prep_for_file_close() + * is called. + * + * fd_parent_count: If the entry is a child in one or more flush dependency + * relationships, this field contains the number of flush + * dependency parents. + * + * In all other cases, the field is set to zero. + * + * Note that while this count is initially taken from the + * flush dependency fields above, if the entry is in the + * cache image (i.e. include_in_image is TRUE), any parents + * that are not in the image are removed from this count and + * from the fd_parent_addrs array below. + * + * Finally observe that if the entry is dirty and in the + * cache image, and its parent is dirty and not in the cache + * image, then the entry must be removed from the cache image + * to avoid violating the flush dependency flush ordering. + * + * fd_parent_addrs: If the entry is a child in one or more flush dependency + * relationship when H5C_prep_for_file_close() is called, this + * field must contain a pointer to an array of size + * fd_parent_count containing the on disk addresses of the + * parent. + * + * In all other cases, the field is set to NULL. + * + * Note that while this list of addresses is initially taken + * from the flush dependency fields above, if the entry is in the + * cache image (i.e. include_in_image is TRUE), any parents + * that are not in the image are removed from this list, and + * and from the fd_parent_count above. + * + * Finally observe that if the entry is dirty and in the + * cache image, and its parent is dirty and not in the cache + * image, then the entry must be removed from the cache image + * to avoid violating the flush dependency flush ordering. + * + * fd_child_count: If the entry is a parent in a flush dependency + * relationship, this field contains the number of flush + * dependency children. + * + * In all other cases, the field is set to zero. + * + * Note that while this count is initially taken from the + * flush dependency fields above, if the entry is in the + * cache image (i.e. include_in_image is TRUE), any children + * that are not in the image are removed from this count. + * + * fd_dirty_child_count: If the entry is a parent in a flush dependency + * relationship, this field contains the number of dirty flush + * dependency children. + * + * In all other cases, the field is set to zero. + * + * Note that while this count is initially taken from the + * flush dependency fields above, if the entry is in the + * cache image (i.e. include_in_image is TRUE), any dirty + * children that are not in the image are removed from this + * count. + * + * image_fd_height: Flush dependency height of the entry in the cache image. + * + * The flush dependency height of any entry involved in a + * flush dependency relationship is defined to be the + * longest flush dependency path from that entry to an entry + * with no flush depenency children. + * + * Since the image_fd_height is used to order entries in the + * cache image so that fd parents preceed fd children, for + * purposes of this field, and entry is at flush dependency + * level 0 if it either has no children, or if all of its + * children are not in the cache image. + * + * Note that if a child in a flush dependency relationship is + * dirty and in the cache image, and its parent is dirty and + * not in the cache image, then the child must be excluded + * from the cache image to maintain flush ordering. + * + * prefetched: Boolean flag indicating that the on disk image of the entry + * has been loaded into the cache prior any request for the + * entry by the rest of the library. + * + * As of this writing (8/10/15), this can only happen through + * the load of a cache image block, although other scenarios + * are contemplated for the use of this feature. Note that + * unlike the usual prefetch situation, this means that a + * pre fetched entry can be dirty, and/or can be a party to + * flush dependency relationship(s). This complicates matters + * somewhat. + * + * The essential feature of a pre-fetched entry is that it + * consists only of a buffer containing the on disk image of + * the entry. Thus it must be deserialized before it can + * be passed back to the library on a protect call. This + * task is handled by H5C_deserialized_prefetched_entry(). + * In essence, this routine calls the deserialize callback + * provided in the protect call with the on disk image, + * deletes the prefetched entry from the cache, and replaces + * it with the deserialized entry returned by the deserialize + * callback. + * + * Further, if the prefetched entry is a flush dependency parent, + * all its flush dependency children (which must also be + * pre-fetched entries), must be tranfered to the new cache + * entry returned by the deserailization callback. + * + * Finally, if the prefetched entry is a flush dependency child, + * this flush dependency must be destroyed prior to the + * deserialize call. + * + * In addition to the above special processing on the first + * protect call on a prefetched entry (after which is no longer + * a prefetched entry), prefetched entries also require special + * tretment on flush and evict. + * + * On flush, a dirty prefetched entry must simply be written + * to disk and marked clean without any call to any client + * callback. + * + * On eviction, if a prefetched entry is a flush dependency + * child, that flush dependency relationship must be destroyed + * just prior to the eviction. If the flush dependency code + * is working properly, it should be impossible for any entry + * that is a flush dependency parent to be evicted. + * + * prefetch_type_id: Integer field containing the type ID of the prefetched + * entry. This ID must match the ID of the type provided in any + * protect call on the prefetched entry. + * + * The value of this field is undefined in prefetched is FALSE. + * + * age: Number of times a prefetched entry has appeared in + * subsequent cache images. The field exists to allow + * imposition of a limit on how many times a prefetched + * entry can appear in subsequent cache images without being + * converted to a regular entry. + * + * This field must be zero if prefetched is FALSE. + * + * serialization_count: Integer field used to maintain a count of the + * number of times each entry is serialized during cache + * serialization. While no entry should be serialized more than + * once in any serialization call, throw an assertion if any + * flush depencency parent is serialized more than once during + * a single cache serialization. + * + * This is a debugging field, and thus is maintained only if + * NDEBUG is undefined. * * Fields supporting tagged entries: * @@ -1433,6 +1615,23 @@ typedef struct H5C_cache_entry_t { struct H5C_cache_entry_t *coll_prev; #endif /* H5_HAVE_PARALLEL */ + /* fields supporting cache image */ + hbool_t include_in_image; + int32_t lru_rank; + hbool_t image_dirty; + uint64_t fd_parent_count; + haddr_t *fd_parent_addrs; + uint64_t fd_child_count; + uint64_t fd_dirty_child_count; + uint32_t image_fd_height; + hbool_t prefetched; + int prefetch_type_id; + int32_t age; + +#ifndef NDEBUG /* debugging field */ + int serialization_count; +#endif /* NDEBUG */ + /* fields supporting tag lists */ struct H5C_cache_entry_t *tl_next; struct H5C_cache_entry_t *tl_prev; @@ -1447,6 +1646,168 @@ typedef struct H5C_cache_entry_t { #endif /* H5C_COLLECT_CACHE_ENTRY_STATS */ } H5C_cache_entry_t; + +/**************************************************************************** + * + * structure H5C_image_entry_t + * + * Instances of the H5C_image_entry_t structure are used to store data on + * metadata cache entries used in the construction of the metadata cache + * image block. In essence this structure is a greatly simplified version + * of H5C_cache_entry_t. + * + * The fields of this structure are discussed individually below: + * + * JRM - 8/5/15 + * + * magic: Unsigned 32 bit integer that must always be set to + * H5C__H5C_IMAGE_ENTRY_T_MAGIC when the entry is valid. + * The field must be set to H5C__H5C_IMAGE__ENTRY_T_BAD_MAGIC + * just before the entry is freed. + * + * addr: Base address of the cache entry on disk. + * + * size: Length of the cache entry on disk in bytes. + * + * ring: Instance of H5C_ring_t indicating the flush ordering ring + * to which this entry is assigned. + * + * age: Number of times this prefetech entry has appeared in + * the current sequence of cache images. This field is + * initialized to 0 if the instance of H5C_image_entry_t + * is constructed from a regular entry. + * + * If the instance is constructed from a prefetched entry + * currently residing in the metadata cache, the field is + * set to 1 + the age of the prefetched entry, or to + * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX if that sum exceeds + * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX. + * + * type_id: Integer field containing the type ID of the entry. + * + * lru_rank: Rank of the entry in the LRU just prior to file close. + * + * Note that the first entry on the LRU has lru_rank 1, + * and that entries not on the LRU at that time will have + * either lru_rank -1 (if pinned) or 0 (if loaded during + * the process of flushing the cache. + * + * is_dirty: Boolean flag indicating whether the contents of the cache + * entry has been modified since the last time it was written + * to disk as a regular piece of metadata. + * + * image_fd_height: Flush dependency height of the entry in the cache image. + * + * The flush dependency height of any entry involved in a + * flush dependency relationship is defined to be the + * longest flush dependency path from that entry to an entry + * with no flush depenency children. + * + * Since the image_fd_height is used to order entries in the + * cache image so that fd parents preceed fd children, for + * purposes of this field, an entry is at flush dependency + * level 0 if it either has no children, or if all of its + * children are not in the cache image. + * + * Note that if a child in a flush dependency relationship is + * dirty and in the cache image, and its parent is dirty and + * not in the cache image, then the child must be excluded + * from the cache image to maintain flush ordering. + * + * fd_parent_count: If the entry is a child in one or more flush dependency + * relationships, this field contains the number of flush + * dependency parents. + * + * In all other cases, the field is set to zero. + * + * Note that while this count is initially taken from the + * flush dependency fields in the associated instance of + * H5C_cache_entry_t, if the entry is in the cache image + * (i.e. include_in_image is TRUE), any parents that are + * not in the image are removed from this count and + * from the fd_parent_addrs array below. + * + * Finally observe that if the entry is dirty and in the + * cache image, and its parent is dirty and not in the cache + * image, then the entry must be removed from the cache image + * to avoid violating the flush dependency flush ordering. + * This should have happened before the construction of + * the instance of H5C_image_entry_t. + * + * fd_parent_addrs: If the entry is a child in one or more flush dependency + * relationship when H5C_prep_for_file_close() is called, this + * field must contain a pointer to an array of size + * fd_parent_count containing the on disk addresses of the + * parents. + * + * In all other cases, the field is set to NULL. + * + * Note that while this list of addresses is initially taken + * from the flush dependency fields in the associated instance of + * H5C_cache_entry_t, if the entry is in the cache image + * (i.e. include_in_image is TRUE), any parents that are not + * in the image are removed from this list, and from the + * fd_parent_count above. + * + * Finally observe that if the entry is dirty and in the + * cache image, and its parent is dirty and not in the cache + * image, then the entry must be removed from the cache image + * to avoid violating the flush dependency flush ordering. + * This should have happened before the construction of + * the instance of H5C_image_entry_t. + * + * fd_child_count: If the entry is a parent in a flush dependency + * relationship, this field contains the number of flush + * dependency children. + * + * In all other cases, the field is set to zero. + * + * Note that while this count is initially taken from the + * flush dependency fields in the associated instance of + * H5C_cache_entry_t, if the entry is in the cache image + * (i.e. include_in_image is TRUE), any children + * that are not in the image are removed from this count. + * + * fd_dirty_child_count: If the entry is a parent in a flush dependency + * relationship, this field contains the number of dirty flush + * dependency children. + * + * In all other cases, the field is set to zero. + * + * Note that while this count is initially taken from the + * flush dependency fields in the associated instance of + * H5C_cache_entry_t, if the entry is in the cache image + * (i.e. include_in_image is TRUE), any dirty children + * that are not in the image are removed from this count. + * + * image_ptr: Pointer to void. When not NULL, this field points to a + * dynamically allocated block of size bytes in which the + * on disk image of the metadata cache entry is stored. + * + * If the entry is dirty, the pre-serialize and serialize + * callbacks must be used to update this image before it is + * written to disk + * + * + ****************************************************************************/ + +typedef struct H5C_image_entry_t { + uint32_t magic; + haddr_t addr; + size_t size; + H5C_ring_t ring; + int32_t age; + int32_t type_id; + int32_t lru_rank; + hbool_t is_dirty; + unsigned image_fd_height; + uint64_t fd_parent_count; + haddr_t *fd_parent_addrs; + uint64_t fd_child_count; + uint64_t fd_dirty_child_count; + void *image_ptr; +} H5C_image_entry_t; + /**************************************************************************** * * structure H5C_auto_size_ctl_t @@ -1736,12 +2097,98 @@ typedef struct H5C_auto_size_ctl_t { double empty_reserve; } H5C_auto_size_ctl_t; +/**************************************************************************** + * + * structure H5C_cache_image_ctl_t + * + * Instances of H5C_image_ctl_t are used to get and set the control + * fields for generation of a metadata cache image on file close. + * + * At present control of construction of a cache image is via a FAPL + * property at file open / create. + * + * The fields of the structure are discussed individually below: + * + * version: Integer field containing the version number of this version + * of the H5C_image_ctl_t structure. Any instance of + * H5C_image_ctl_t passed to the cache must have a known + * version number, or an error will be flagged. + * + * generate_image: Boolean flag indicating whether a cache image should + * be created on file close. + * + * save_resize_status: Boolean flag indicating whether the cache image + * should include the adaptive cache resize configuration and status. + * Note that this field is ignored at present. + * + * entry_ageout: Integer field indicating the maximum number of + * times a prefetched entry can appear in subsequent cache images. + * This field exists to allow the user to avoid the buildup of + * infrequently used entries in long sequences of cache images. + * + * The value of this field must lie in the range + * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE (-1) to + * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX (100). + * + * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE means that no limit + * is imposed on number of times a prefeteched entry can appear + * in subsequent cache images. + * + * A value of 0 prevents prefetched entries from being included + * in cache images. + * + * Positive integers restrict prefetched entries to the specified + * number of appearances. + * + * Note that the number of subsequent cache images that a prefetched + * entry has appeared in is tracked in an 8 bit field. Thus, while + * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX can be increased from its + * current value, any value in excess of 255 will be the functional + * equivalent of H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE. + * + * flags: Unsigned integer containing flags controling which aspects of the + * cache image functinality is actually executed. The primary impetus + * behind this field is to allow developement of tests for partial + * implementations that will require little if any modification to run + * with the full implementation. In normal operation, all flags should + * be set. + * + ****************************************************************************/ + +#define H5C_CI__GEN_MDCI_SBE_MESG ((unsigned)0x0001) +#define H5C_CI__GEN_MDC_IMAGE_BLK ((unsigned)0x0002) +#define H5C_CI__SUPRESS_ENTRY_WRITES ((unsigned)0x0004) +#define H5C_CI__WRITE_CACHE_IMAGE ((unsigned)0x0008) + +/* This #define must set all defined H5C_CI flags. It is + * used in the default value for instances of H5C_cache_image_ctl_t. + * This value will only be modified in test code. + */ +#define H5C_CI__ALL_FLAGS ((unsigned)0x000F) + +#define H5C__DEFAULT_CACHE_IMAGE_CTL \ +{ \ + /* version = */ H5C__CURR_CACHE_IMAGE_CTL_VER, \ + /* generate_image = */ FALSE, \ + /* save_resize_status = */ FALSE, \ + /* entry_ageout = */ H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE, \ + /* flags = */ H5C_CI__ALL_FLAGS \ +} + +typedef struct H5C_cache_image_ctl_t { + int32_t version; + hbool_t generate_image; + hbool_t save_resize_status; + int32_t entry_ageout; + unsigned flags; +} H5C_cache_image_ctl_t; + /***************************************/ /* Library-private Function Prototypes */ /***************************************/ H5_DLL H5C_t *H5C_create(size_t max_cache_size, size_t min_clean_size, - int max_type_id, const char *(*type_name_table_ptr), + int max_type_id, const H5C_class_t * const *class_table_ptr, H5C_write_permitted_func_t check_write_permitted, hbool_t write_permitted, H5C_log_flush_func_t log_flush, void *aux_ptr); H5_DLL herr_t H5C_set_up_logging(H5C_t *cache_ptr, const char log_location[], hbool_t start_immediately); @@ -1770,9 +2217,11 @@ herr_t H5C_verify_tag(int id, haddr_t tag); H5_DLL herr_t H5C_flush_to_min_clean(H5F_t *f, hid_t dxpl_id); H5_DLL herr_t H5C_get_cache_auto_resize_config(const H5C_t *cache_ptr, H5C_auto_size_ctl_t *config_ptr); +H5_DLL herr_t H5C_get_cache_image_config(const H5C_t * cache_ptr, + H5C_cache_image_ctl_t *config_ptr); H5_DLL herr_t H5C_get_cache_size(H5C_t *cache_ptr, size_t *max_size_ptr, size_t *min_clean_size_ptr, size_t *cur_size_ptr, - int32_t *cur_num_entries_ptr); + uint32_t *cur_num_entries_ptr); H5_DLL herr_t H5C_get_cache_hit_rate(H5C_t *cache_ptr, double *hit_rate_ptr); H5_DLL herr_t H5C_get_entry_status(const H5F_t *f, haddr_t addr, size_t *size_ptr, hbool_t *in_cache_ptr, hbool_t *is_dirty_ptr, @@ -1783,8 +2232,11 @@ H5_DLL herr_t H5C_get_evictions_enabled(const H5C_t *cache_ptr, hbool_t *evictio H5_DLL void * H5C_get_aux_ptr(const H5C_t *cache_ptr); H5_DLL FILE *H5C_get_trace_file_ptr(const H5C_t *cache_ptr); H5_DLL FILE *H5C_get_trace_file_ptr_from_entry(const H5C_cache_entry_t *entry_ptr); +H5_DLL herr_t H5C_image_stats(H5C_t * cache_ptr, hbool_t print_header); H5_DLL herr_t H5C_insert_entry(H5F_t *f, hid_t dxpl_id, const H5C_class_t *type, haddr_t addr, void *thing, unsigned int flags); +H5_DLL herr_t H5C_load_cache_image_on_next_protect(H5F_t *f, haddr_t addr, + hsize_t len, hbool_t rw); H5_DLL herr_t H5C_mark_entry_dirty(void *thing); H5_DLL herr_t H5C_mark_entry_clean(void *thing); H5_DLL herr_t H5C_mark_entry_unserialized(void *thing); @@ -1799,6 +2251,8 @@ H5_DLL void * H5C_protect(H5F_t *f, hid_t dxpl_id, const H5C_class_t *type, H5_DLL herr_t H5C_reset_cache_hit_rate_stats(H5C_t *cache_ptr); H5_DLL herr_t H5C_resize_entry(void *thing, size_t new_size); H5_DLL herr_t H5C_set_cache_auto_resize_config(H5C_t *cache_ptr, H5C_auto_size_ctl_t *config_ptr); +H5_DLL herr_t H5C_set_cache_image_config(const H5F_t *f, H5C_t *cache_ptr, + H5C_cache_image_ctl_t *config_ptr); H5_DLL herr_t H5C_set_evictions_enabled(H5C_t *cache_ptr, hbool_t evictions_enabled); H5_DLL herr_t H5C_set_prefix(H5C_t *cache_ptr, char *prefix); H5_DLL herr_t H5C_set_trace_file_ptr(H5C_t *cache_ptr, FILE *trace_file_ptr); @@ -1810,6 +2264,7 @@ H5_DLL herr_t H5C_unpin_entry(void *thing); H5_DLL herr_t H5C_destroy_flush_dependency(void *parent_thing, void *child_thing); H5_DLL herr_t H5C_unprotect(H5F_t *f, hid_t dxpl_id, haddr_t addr, void *thing, unsigned int flags); +H5_DLL herr_t H5C_validate_cache_image_config(H5C_cache_image_ctl_t * ctl_ptr); H5_DLL herr_t H5C_validate_resize_config(H5C_auto_size_ctl_t *config_ptr, unsigned int tests); H5_DLL herr_t H5C_ignore_tags(H5C_t *cache_ptr); @@ -1819,6 +2274,8 @@ H5_DLL herr_t H5C_cork(H5C_t *cache_ptr, haddr_t obj_addr, unsigned action, hboo H5_DLL herr_t H5C_get_entry_ring(const H5F_t *f, haddr_t addr, H5C_ring_t *ring); H5_DLL herr_t H5C_unsettle_entry_ring(void *thing); H5_DLL herr_t H5C_remove_entry(void *thing); +H5_DLL herr_t H5C_cache_image_status(H5F_t * f, hbool_t *load_ci_ptr, + hbool_t *write_ci_ptr); #ifdef H5_HAVE_PARALLEL H5_DLL herr_t H5C_apply_candidate_list(H5F_t *f, hid_t dxpl_id, @@ -1832,7 +2289,16 @@ H5_DLL herr_t H5C_mark_entries_as_clean(H5F_t *f, hid_t dxpl_id, int32_t ce_arra #endif /* H5_HAVE_PARALLEL */ #ifndef NDEBUG /* debugging functions */ +H5_DLL hbool_t H5C_get_serialization_in_progress(const H5C_t *cache_ptr); H5_DLL hbool_t H5C_cache_is_clean(const H5C_t *cache_ptr, H5C_ring_t inner_ring); +H5_DLL herr_t H5C_get_entry_ptr_from_addr(H5C_t *cache_ptr, haddr_t addr, + void **entry_ptr_ptr); +H5_DLL herr_t H5C_flush_dependency_exists(H5C_t *cache_ptr, haddr_t parent_addr, + haddr_t child_addr, hbool_t *fd_exists_ptr); +H5_DLL herr_t H5C_verify_entry_type(H5C_t *cache_ptr, haddr_t addr, + const H5C_class_t *expected_type, hbool_t *in_cache_ptr, + hbool_t *type_ok_ptr); +H5_DLL herr_t H5C_validate_index_list(H5C_t *cache_ptr); #endif /* NDEBUG */ #endif /* !_H5Cprivate_H */ diff --git a/src/H5Cquery.c b/src/H5Cquery.c index f5409f7..03aadb0 100644 --- a/src/H5Cquery.c +++ b/src/H5Cquery.c @@ -131,7 +131,7 @@ H5C_get_cache_size(H5C_t * cache_ptr, size_t * max_size_ptr, size_t * min_clean_size_ptr, size_t * cur_size_ptr, - int32_t * cur_num_entries_ptr) + uint32_t * cur_num_entries_ptr) { herr_t ret_value = SUCCEED; /* Return value */ diff --git a/src/H5EApkg.h b/src/H5EApkg.h index 093403c..7540ff2 100644 --- a/src/H5EApkg.h +++ b/src/H5EApkg.h @@ -378,21 +378,6 @@ typedef struct H5EA__ctx_cb_t { /* Package Private Variables */ /*****************************/ -/* H5EA header inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_EARRAY_HDR[1]; - -/* H5EA index block inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_EARRAY_IBLOCK[1]; - -/* H5EA index block inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_EARRAY_SBLOCK[1]; - -/* H5EA data block inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_EARRAY_DBLOCK[1]; - -/* H5EA data block page inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_EARRAY_DBLK_PAGE[1]; - /* Internal extensible array testing class */ H5_DLLVAR const H5EA_class_t H5EA_CLS_TEST[1]; @@ -445,6 +445,8 @@ done: hid_t H5Fcreate(const char *filename, unsigned flags, hid_t fcpl_id, hid_t fapl_id) { + hbool_t ci_load = FALSE; /* whether MDC ci load requested */ + hbool_t ci_write = FALSE; /* whether MDC CI write requested */ H5F_t *new_file = NULL; /*file struct for new file */ hid_t dxpl_id = H5AC_ind_read_dxpl_id; /*dxpl used by library */ hid_t ret_value; /*return value */ @@ -490,6 +492,14 @@ H5Fcreate(const char *filename, unsigned flags, hid_t fcpl_id, hid_t fapl_id) if(NULL == (new_file = H5F_open(filename, flags, fcpl_id, fapl_id, dxpl_id))) HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "unable to create file") + /* check to see if both SWMR and cache image are requested. Fail if so */ + if ( H5C_cache_image_status(new_file, &ci_load, &ci_write) < 0 ) + HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "can't get MDC CI status") + + if ( ( ( ci_load ) || ( ci_write ) ) && + ( flags & (H5F_ACC_SWMR_READ | H5F_ACC_SWMR_WRITE) ) ) + HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "can't have both SWMR and cache image.") + /* Get an atom for the file */ if((ret_value = H5I_register(H5I_FILE, new_file, TRUE)) < 0) HGOTO_ERROR(H5E_ATOM, H5E_CANTREGISTER, FAIL, "unable to atomize file") @@ -498,9 +508,29 @@ H5Fcreate(const char *filename, unsigned flags, hid_t fcpl_id, hid_t fapl_id) new_file->file_id = ret_value; done: +#if 0 + /* Quincy: please review this. With the original cleanup code (below) + * my code (above) to disable the combination of SWMR and cache image, + * triggering results in an assertion failure in H5F_close() either + * immediately or on library shutdown depending on the exact location + * of my code. + * + * I noticed that the issue did not appear in H5Fopen(). As that function + * calls H5F_try_close() instead of H5F_close(), I tried copying that + * cleanup code here. It seems to work -- but since I'm not familiar + * with this section of the code, it would be good if you would check me. + * + * Please delete the old version if you buy the fix. + * + * JRM -- 12/29/16 + */ if(ret_value < 0 && new_file) if(H5F_close(new_file) < 0) HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "problems closing file") +#else + if(ret_value < 0 && new_file && H5F_try_close(new_file, NULL) < 0) + HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "problems closing file") +#endif FUNC_LEAVE_API(ret_value) } /* end H5Fcreate() */ @@ -549,6 +579,8 @@ done: hid_t H5Fopen(const char *filename, unsigned flags, hid_t fapl_id) { + hbool_t ci_load = FALSE; /* whether MDC ci load requested */ + hbool_t ci_write = FALSE; /* whether MDC CI write requested */ H5F_t *new_file = NULL; /*file struct for new file */ hid_t dxpl_id = H5AC_ind_read_dxpl_id; /*dxpl used by library */ hid_t ret_value; /*return value */ @@ -578,6 +610,14 @@ H5Fopen(const char *filename, unsigned flags, hid_t fapl_id) if(NULL == (new_file = H5F_open(filename, flags, H5P_FILE_CREATE_DEFAULT, fapl_id, dxpl_id))) HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "unable to open file") + /* check to see if both SWMR and cache image are requested. Fail if so */ + if ( H5C_cache_image_status(new_file, &ci_load, &ci_write) < 0 ) + HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "can't get MDC CI status") + + if ( ( ( ci_load ) || ( ci_write ) ) && + ( flags & (H5F_ACC_SWMR_READ | H5F_ACC_SWMR_WRITE) ) ) + HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "can't have both SWMR and cache image") + /* Get an atom for the file */ if((ret_value = H5I_register(H5I_FILE, new_file, TRUE)) < 0) HGOTO_ERROR(H5E_ATOM, H5E_CANTREGISTER, FAIL, "unable to atomize file handle") @@ -1177,7 +1217,7 @@ H5Fget_mdc_size(hid_t file_id, size_t *max_size_ptr, size_t *min_clean_size_ptr, size_t *cur_size_ptr, int *cur_num_entries_ptr) { H5F_t *file; /* File object for file ID */ - int32_t cur_num_entries; + uint32_t cur_num_entries; herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_API(FAIL) @@ -1593,6 +1633,8 @@ done: herr_t H5Fstart_swmr_write(hid_t file_id) { + hbool_t ci_load = FALSE; /* whether MDC ci load requested */ + hbool_t ci_write = FALSE; /* whether MDC CI write requested */ H5F_t *file = NULL; /* File info */ size_t grp_dset_count=0; /* # of open objects: groups & datasets */ size_t nt_attr_count=0; /* # of opened named datatypes + opened attributes */ @@ -1626,6 +1668,13 @@ H5Fstart_swmr_write(hid_t file_id) HDassert(file->shared->sblock->status_flags & H5F_SUPER_WRITE_ACCESS); + /* check to see if cache image is enabled. Fail if so */ + if ( H5C_cache_image_status(file, &ci_load, &ci_write) < 0 ) + HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "can't get MDC CI status") + + if ( ( ci_load ) || ( ci_write ) ) + HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "can't have both SWMR and MDC cache image.") + /* Flush data buffers */ if(H5F_flush(file, H5AC_ind_read_dxpl_id, FALSE) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTFLUSH, FAIL, "unable to flush file's cached information") @@ -1868,7 +1917,48 @@ H5Fget_mdc_logging_status(hid_t file_id, hbool_t *is_enabled, done: FUNC_LEAVE_API(ret_value) -} /* H5Fstop_mdc_logging() */ +} /* H5Fget_mdc_logging_status() */ + + +/*------------------------------------------------------------------------- + * Function: H5Fset_latest_format + * + * Purpose: Enable switching the "latest format" flag while a file is open. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Quincey Koziol + * Monday, September 21, 2015 + *------------------------------------------------------------------------- + */ +herr_t +H5Fset_latest_format(hid_t file_id, hbool_t latest_format) +{ + H5F_t *f; /* File */ + unsigned latest_flags; /* Latest format flags for file */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_API(FAIL) + H5TRACE2("e", "ib", file_id, latest_format); + + /* Check args */ + if(NULL == (f = (H5F_t *)H5I_object_verify(file_id, H5I_FILE))) + HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "not a file ID") + + /* Check if the value is changing */ + latest_flags = H5F_USE_LATEST_FLAGS(f, H5F_LATEST_ALL_FLAGS); + if(latest_format != (H5F_LATEST_ALL_FLAGS == latest_flags)) { + /* Call the flush routine, for this file */ + if(H5F_flush(f, H5AC_ind_read_dxpl_id, FALSE) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTFLUSH, FAIL, "unable to flush file's cached information") + + /* Toggle the 'latest format' flag */ + H5F_SET_LATEST_FLAGS(f, latest_format ? H5F_LATEST_ALL_FLAGS : 0); + } /* end if */ + +done: + FUNC_LEAVE_API(ret_value) +} /* end H5Fset_latest_format() */ /*------------------------------------------------------------------------- diff --git a/src/H5FApkg.h b/src/H5FApkg.h index ccef562..63eacff 100644 --- a/src/H5FApkg.h +++ b/src/H5FApkg.h @@ -249,15 +249,6 @@ typedef struct H5FA_dblk_page_cache_ud_t { /* Package Private Variables */ /*****************************/ -/* H5FA header inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_FARRAY_HDR[1]; - -/* H5FA data block inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_FARRAY_DBLOCK[1]; - -/* H5FA data block page inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_FARRAY_DBLK_PAGE[1]; - /* Internal fixed array testing class */ H5_DLLVAR const H5FA_class_t H5FA_CLS_TEST[1]; diff --git a/src/H5FSpkg.h b/src/H5FSpkg.h index f07ffad..4411236 100644 --- a/src/H5FSpkg.h +++ b/src/H5FSpkg.h @@ -199,12 +199,6 @@ struct H5FS_t { /* Package Private Variables */ /*****************************/ -/* H5FS header inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_FSPACE_HDR[1]; - -/* H5FS section info inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_FSPACE_SINFO[1]; - /* Declare a free list to manage the H5FS_node_t struct */ H5FL_EXTERN(H5FS_node_t); diff --git a/src/H5Fint.c b/src/H5Fint.c index 8ad97a8..4783a36 100644 --- a/src/H5Fint.c +++ b/src/H5Fint.c @@ -188,6 +188,8 @@ H5F_get_access_plist(H5F_t *f, hbool_t app_ref) if(H5P_set(new_plist, H5F_ACS_COLL_MD_WRITE_FLAG_NAME, &(f->coll_md_write)) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't set collective metadata read flag") #endif /* H5_HAVE_PARALLEL */ + if(H5P_set(new_plist, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_NAME, &(f->shared->mdc_initCacheImageCfg)) < 0) + HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set initial metadata cache resize config.") /* Prepare the driver property */ driver_prop.driver_id = f->shared->lf->driver_id; @@ -667,6 +669,8 @@ H5F_new(H5F_file_t *shared, unsigned flags, hid_t fcpl_id, hid_t fapl_id, H5FD_t if(H5P_get(plist, H5F_ACS_COLL_MD_WRITE_FLAG_NAME, &(f->coll_md_write)) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get collective metadata write flag") #endif /* H5_HAVE_PARALLEL */ + if(H5P_get(plist, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_NAME, &(f->shared->mdc_initCacheImageCfg)) < 0) + HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get initial metadata cache resize config") /* Get the VFD values to cache */ f->shared->maxaddr = H5FD_get_maxaddr(lf); @@ -750,7 +754,7 @@ H5F_new(H5F_file_t *shared, unsigned flags, hid_t fcpl_id, hid_t fapl_id, H5FD_t * The cache might be created with a different number of elements and * the access property list should be updated to reflect that. */ - if(H5AC_create(f, &(f->shared->mdc_initCacheCfg)) < 0) + if(H5AC_create(f, &(f->shared->mdc_initCacheCfg), &(f->shared->mdc_initCacheImageCfg)) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, NULL, "unable to create metadata cache") /* Create the file's "open object" information */ @@ -2671,3 +2675,33 @@ H5F_set_coll_md_read(H5F_t *f, H5P_coll_md_read_flag_t cmr) } /* H5F_set_coll_md_read() */ #endif /* H5_HAVE_PARALLEL */ + +/*------------------------------------------------------------------------- + * Function: H5F_set_latest_flags + * + * Purpose: Set the latest_flags field with a new value. + * + * Return: Success: SUCCEED + * Failure: FAIL + * + * Programmer: Quincey Koziol + * 4/26/16 + * + *------------------------------------------------------------------------- + */ +herr_t +H5F_set_latest_flags(H5F_t *f, unsigned flags) +{ + /* Use FUNC_ENTER_NOAPI_NOINIT_NOERR here to avoid performance issues */ + FUNC_ENTER_NOAPI_NOINIT_NOERR + + /* Sanity check */ + HDassert(f); + HDassert(f->shared); + HDassert(0 == ((~flags) & H5F_LATEST_ALL_FLAGS)); + + f->shared->latest_flags = flags; + + FUNC_LEAVE_NOAPI(SUCCEED) +} /* H5F_set_latest_flags() */ + diff --git a/src/H5Fio.c b/src/H5Fio.c index e215666..afe1278 100644 --- a/src/H5Fio.c +++ b/src/H5Fio.c @@ -302,7 +302,7 @@ H5F__evict_cache_entries(H5F_t *f, hid_t dxpl_id) #ifndef NDEBUG { unsigned status = 0; - int32_t cur_num_entries; + uint32_t cur_num_entries; /* Retrieve status of the superblock */ if(H5AC_get_entry_status(f, (haddr_t)0, &status) < 0) diff --git a/src/H5Fpkg.h b/src/H5Fpkg.h index 11665f4..93a3978 100644 --- a/src/H5Fpkg.h +++ b/src/H5Fpkg.h @@ -287,6 +287,12 @@ struct H5F_file_t { /* metadata cache. This structure is */ /* fixed at creation time and should */ /* not change thereafter. */ + H5AC_cache_image_config_t + mdc_initCacheImageCfg; /* initial configuration for the */ + /* generate metadata cache image on */ + /* close option. This structure is */ + /* fixed at creation time and should */ + /* not change thereafter. */ hbool_t use_mdc_logging; /* Set when metadata logging is desired */ hbool_t start_mdc_log_on_access; /* set when mdc logging should */ /* begin on file access/create */ @@ -369,9 +375,6 @@ H5FL_EXTERN(H5F_t); /* Declare a free list to manage the H5F_file_t struct */ H5FL_EXTERN(H5F_file_t); -H5_DLLVAR const H5AC_class_t H5AC_SUPERBLOCK[1]; -H5_DLLVAR const H5AC_class_t H5AC_DRVRINFO[1]; - /******************************/ /* Package Private Prototypes */ @@ -400,7 +403,8 @@ H5_DLL herr_t H5F__super_free(H5F_super_t *sblock); /* Superblock extension related routines */ H5_DLL herr_t H5F_super_ext_open(H5F_t *f, haddr_t ext_addr, H5O_loc_t *ext_ptr); -H5_DLL herr_t H5F_super_ext_write_msg(H5F_t *f, hid_t dxpl_id, unsigned id, void *mesg, hbool_t may_create); +H5_DLL herr_t H5F_super_ext_write_msg(H5F_t *f, hid_t dxpl_id, unsigned id, + void *mesg, hbool_t may_create, unsigned mesg_flags); H5_DLL herr_t H5F_super_ext_remove_msg(H5F_t *f, hid_t dxpl_id, unsigned id); H5_DLL herr_t H5F_super_ext_close(H5F_t *f, H5O_loc_t *ext_ptr, hid_t dxpl_id, hbool_t was_created); diff --git a/src/H5Fprivate.h b/src/H5Fprivate.h index bcc56c6..7d288fa 100644 --- a/src/H5Fprivate.h +++ b/src/H5Fprivate.h @@ -317,6 +317,7 @@ #define H5F_SET_GRP_BTREE_SHARED(F, RC) (((F)->shared->grp_btree_shared = (RC)) ? SUCCEED : FAIL) #define H5F_USE_TMP_SPACE(F) ((F)->shared->use_tmp_space) #define H5F_IS_TMP_ADDR(F, ADDR) (H5F_addr_le((F)->shared->tmp_addr, (ADDR))) +#define H5F_SET_LATEST_FLAGS(F, FL) ((F)->shared->latest_flags = (FL)) #ifdef H5_HAVE_PARALLEL #define H5F_COLL_MD_READ(F) ((F)->coll_md_read) #endif /* H5_HAVE_PARALLEL */ @@ -367,6 +368,7 @@ #define H5F_SET_GRP_BTREE_SHARED(F, RC) (H5F_set_grp_btree_shared((F), (RC))) #define H5F_USE_TMP_SPACE(F) (H5F_use_tmp_space(F)) #define H5F_IS_TMP_ADDR(F, ADDR) (H5F_is_tmp_addr((F), (ADDR))) +#define H5F_SET_LATEST_FLAGS(F, FL) (H5F_set_latest_flags((F), (FL))) #ifdef H5_HAVE_PARALLEL #define H5F_COLL_MD_READ(F) (H5F_coll_md_read(F)) #endif /* H5_HAVE_PARALLEL */ @@ -481,6 +483,7 @@ #define H5F_ACS_EVICT_ON_CLOSE_FLAG_NAME "evict_on_close_flag" /* Whether or not the metadata cache will evict objects on close */ #define H5F_ACS_CORE_WRITE_TRACKING_PAGE_SIZE_NAME "core_write_tracking_page_size" /* The page size in kiB when core VFD write tracking is enabled */ #define H5F_ACS_COLL_MD_WRITE_FLAG_NAME "collective_metadata_write" /* property indicating whether metadata writes are done collectively or not */ +#define H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_NAME "mdc_initCacheImageCfg" /* Initial metadata cache image creation configuration */ /* ======================== File Mount properties ====================*/ #define H5F_MNT_SYM_LOCAL_NAME "local" /* Whether absolute symlinks local to file. */ @@ -700,6 +703,7 @@ H5_DLL struct H5UC_t *H5F_grp_btree_shared(const H5F_t *f); H5_DLL herr_t H5F_set_grp_btree_shared(H5F_t *f, struct H5UC_t *rc); H5_DLL hbool_t H5F_use_tmp_space(const H5F_t *f); H5_DLL hbool_t H5F_is_tmp_addr(const H5F_t *f, haddr_t addr); +H5_DLL herr_t H5F_set_latest_flags(H5F_t *f, unsigned flags); #ifdef H5_HAVE_PARALLEL H5_DLL H5P_coll_md_read_flag_t H5F_coll_md_read(const H5F_t *f); H5_DLL void H5F_set_coll_md_read(H5F_t *f, H5P_coll_md_read_flag_t flag); diff --git a/src/H5Fpublic.h b/src/H5Fpublic.h index a79da75..c57a821 100644 --- a/src/H5Fpublic.h +++ b/src/H5Fpublic.h @@ -246,6 +246,7 @@ H5_DLL herr_t H5Fstart_swmr_write(hid_t file_id); H5_DLL ssize_t H5Fget_free_sections(hid_t file_id, H5F_mem_t type, size_t nsects, H5F_sect_info_t *sect_info/*out*/); H5_DLL herr_t H5Fclear_elink_file_cache(hid_t file_id); +H5_DLL herr_t H5Fset_latest_format(hid_t file_id, hbool_t latest_format); H5_DLL herr_t H5Fstart_mdc_logging(hid_t file_id); H5_DLL herr_t H5Fstop_mdc_logging(hid_t file_id); H5_DLL herr_t H5Fget_mdc_logging_status(hid_t file_id, diff --git a/src/H5Fsuper.c b/src/H5Fsuper.c index 893ce26..e203c38 100644 --- a/src/H5Fsuper.c +++ b/src/H5Fsuper.c @@ -290,7 +290,7 @@ H5F__update_super_ext_driver_msg(H5F_t *f, hid_t dxpl_id) */ drvinfo.len = driver_size; drvinfo.buf = dbuf; - if(H5F_super_ext_write_msg(f, dxpl_id, H5O_DRVINFO_ID, &drvinfo, FALSE) < 0) + if(H5F_super_ext_write_msg(f, dxpl_id, H5O_DRVINFO_ID, &drvinfo, FALSE, H5O_MSG_NO_FLAGS_SET) < 0) HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, "unable to update driver info header message") } /* end if driver_size > 0 */ } /* end if !H5F_HAS_FEATURE(f, H5FD_FEAT_IGNORE_DRVRINFO) */ @@ -692,6 +692,34 @@ H5F__super_read(H5F_t *f, hid_t dxpl_id, hbool_t initial_read) f->shared->fs_addr[u] = fsinfo.fs_addr[u-1]; } /* end if */ + /* Check for the extension having a 'metadata cache image' message */ + if((status = H5O_msg_exists(&ext_loc, H5O_MDCI_MSG_ID, dxpl_id)) < 0) + HGOTO_ERROR(H5E_FILE, H5E_EXISTS, FAIL, "unable to read object header") + if(status) { + hbool_t rw = ((rw_flags & H5AC__READ_ONLY_FLAG) == 0); + H5O_mdci_t mdci_msg; + + /* if the metadata cache image superblock extension message exists, + * read its contents and pass the data on to the metadata cache. + * Given this data, the cache will load and decode the metadata + * cache image block, decoded it and load its contents into the + * the cache on the test protect call. + * + * Further, if the file is opened R/W, the metadata cache will + * delete the metadata cache image superblock extension and free + * the cache image block. Don't do this now as f->shared + * is not fully setup, which complicates matters. + */ + + /* Retrieve the 'metadata cache image message' structure */ + if(NULL == H5O_msg_read(&ext_loc, H5O_MDCI_MSG_ID, &mdci_msg, dxpl_id)) + HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to get metadata cache image message") + + /* Indicate to the cache that there's an image to load on first protect call */ + if(H5AC_load_cache_image_on_next_protect(f, mdci_msg.addr, mdci_msg.size, rw) < 0) + HGOTO_ERROR(H5E_FILE, H5E_CANTLOAD, FAIL, "call to H5AC_load_cache_image_on_next_protect failed"); + } /* end if */ + /* Close superblock extension */ if(H5F_super_ext_close(f, &ext_loc, dxpl_id, FALSE) < 0) HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEOBJ, FAIL, "unable to close file's superblock extension") @@ -737,7 +765,7 @@ H5F__super_read(H5F_t *f, hid_t dxpl_id, hbool_t initial_read) HDassert(f->shared->sblock == NULL); f->shared->sblock = sblock; #endif /* JRM */ - if(H5F_super_ext_write_msg(f, dxpl_id, H5O_DRVINFO_ID, &drvinfo, FALSE) < 0) + if(H5F_super_ext_write_msg(f, dxpl_id, H5O_DRVINFO_ID, &drvinfo, FALSE, H5O_MSG_NO_FLAGS_SET) < 0) HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, "error in writing message to superblock extension") #if 1 /* bug fix test code -- tidy this up if all goes well */ /* JRM */ @@ -1365,7 +1393,8 @@ done: *------------------------------------------------------------------------- */ herr_t -H5F_super_ext_write_msg(H5F_t *f, hid_t dxpl_id, unsigned id, void *mesg, hbool_t may_create) +H5F_super_ext_write_msg(H5F_t *f, hid_t dxpl_id, unsigned id, void *mesg, + hbool_t may_create, unsigned mesg_flags) { H5P_genplist_t *dxpl = NULL; /* DXPL for setting ring */ H5AC_ring_t orig_ring = H5AC_RING_INV; /* Original ring value */ @@ -1410,7 +1439,7 @@ H5F_super_ext_write_msg(H5F_t *f, hid_t dxpl_id, unsigned id, void *mesg, hbool_ HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "Message should not exist") /* Create the message with ID in the superblock extension */ - if(H5O_msg_create(&ext_loc, id, H5O_MSG_FLAG_DONTSHARE, H5O_UPDATE_TIME, mesg, dxpl_id) < 0) + if(H5O_msg_create(&ext_loc, id, (mesg_flags | H5O_MSG_FLAG_DONTSHARE), H5O_UPDATE_TIME, mesg, dxpl_id) < 0) HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "unable to create the message in object header") } /* end if */ else { @@ -1418,7 +1447,7 @@ H5F_super_ext_write_msg(H5F_t *f, hid_t dxpl_id, unsigned id, void *mesg, hbool_ HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "Message should exist") /* Update the message with ID in the superblock extension */ - if(H5O_msg_write(&ext_loc, id, H5O_MSG_FLAG_DONTSHARE, H5O_UPDATE_TIME, mesg, dxpl_id) < 0) + if(H5O_msg_write(&ext_loc, id, (mesg_flags | H5O_MSG_FLAG_DONTSHARE), H5O_UPDATE_TIME, mesg, dxpl_id) < 0) HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "unable to write the message in object header") } /* end else */ diff --git a/src/H5Gpkg.h b/src/H5Gpkg.h index c994da3..1e8ad31 100644 --- a/src/H5Gpkg.h +++ b/src/H5Gpkg.h @@ -312,9 +312,6 @@ typedef struct H5G_copy_file_ud_t { */ H5_DLLVAR H5B_class_t H5B_SNODE[1]; -/* The cache subclass */ -H5_DLLVAR const H5AC_class_t H5AC_SNODE[1]; - /* The v2 B-tree class for indexing 'name' field on links */ H5_DLLVAR const H5B2_class_t H5G_BT2_NAME[1]; diff --git a/src/H5HFcache.c b/src/H5HFcache.c index 302fe04..8e5ed76 100644 --- a/src/H5HFcache.c +++ b/src/H5HFcache.c @@ -116,14 +116,17 @@ static herr_t H5HF__cache_dblock_free_icr(void *thing); /* Debugging Function Prototypes */ #ifndef NDEBUG -static herr_t H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, H5HF_hdr_t *hdr, - hbool_t *clean); -static herr_t H5HF__cache_verify_iblock_descendants_clean(H5F_t *f, - H5HF_indirect_t *iblock, unsigned *iblock_status, hbool_t *clean); -static herr_t H5HF__cache_verify_iblocks_dblocks_clean(H5F_t *f, - H5HF_indirect_t *iblock, hbool_t *clean, hbool_t *has_dblocks); -static herr_t H5HF__cache_verify_descendant_iblocks_clean(H5F_t *f, - H5HF_indirect_t *iblock, hbool_t *clean, hbool_t *has_iblocks); +static herr_t H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, hid_t dxpl_id, + H5HF_hdr_t * hdr, hbool_t *fd_clean, hbool_t *clean); +static herr_t H5HF__cache_verify_iblock_descendants_clean(H5F_t *f, + hid_t dxpl_id, haddr_t fd_parent_addr, H5HF_indirect_t *iblock, + unsigned *iblock_status, hbool_t * fd_clean, hbool_t *clean); +static herr_t H5HF__cache_verify_iblocks_dblocks_clean(H5F_t *f, + haddr_t fd_parent_addr, H5HF_indirect_t *iblock, hbool_t *fd_clean, + hbool_t *clean, hbool_t *has_dblocks); +static herr_t H5HF__cache_verify_descendant_iblocks_clean(H5F_t *f, + hid_t dxpl_id, haddr_t fd_parent_addr, H5HF_indirect_t *iblock, + hbool_t *fd_clean, hbool_t *clean, hbool_t *has_iblocks); #endif /* NDEBUG */ @@ -687,6 +690,7 @@ H5HF__cache_hdr_pre_serialize(H5F_t *f, hid_t H5_ATTR_UNUSED dxpl_id, #ifndef NDEBUG { hbool_t descendants_clean = TRUE; + hbool_t fd_children_clean = TRUE; /* Verify that flush dependencies are working correctly. Do this * by verifying that either: @@ -701,10 +705,22 @@ H5HF__cache_hdr_pre_serialize(H5F_t *f, hid_t H5_ATTR_UNUSED dxpl_id, * constraint is met by default. * * Do this with a call to H5HF__cache_verify_hdr_descendants_clean(). + * + * Note that decendants need not be clean if the pre_serialize call + * is made during a cache serialization instead of an entry or cache + * flush. + * + * Note also that with the recent change in the definition of flush + * dependency, not all decendants need be clean -- only direct flush + * dependency children. + * + * Finally, observe that the H5HF__cache_verify_hdr_descendants_clean() + * call still looks for dirty descendants. At present we do not check + * this value. */ - if(H5HF__cache_verify_hdr_descendants_clean((H5F_t *)f, hdr, &descendants_clean) < 0) + if(H5HF__cache_verify_hdr_descendants_clean((H5F_t *)f, dxpl_id, hdr, &fd_children_clean, &descendants_clean) < 0) HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify hdr descendants clean.") - HDassert(descendants_clean); + HDassert(fd_children_clean); } #endif /* NDEBUG */ @@ -1176,8 +1192,9 @@ H5HF__cache_iblock_image_len(const void *_thing, size_t *image_len) * and if so, to move it to real file space before the entry is * serialized. * - * In debug compiles, this function also verifies that all children - * of this indirect block are either clean or are not in cache. + * In debug compiles, this function also verifies that all + * immediate flush dependency children of this indirect block + * are either clean or are not in cache. * * Return: Success: SUCCEED * Failure: FAIL @@ -1217,10 +1234,12 @@ H5HF__cache_iblock_pre_serialize(H5F_t *f, hid_t dxpl_id, void *_thing, #ifndef NDEBUG { hbool_t descendants_clean = TRUE; + hbool_t fd_children_clean = TRUE; unsigned iblock_status = 0; /* verify that flush dependencies are working correctly. Do this - * by verifying that all children of this iblock are clean. + * by verifying that all immediate flush dependency children of this + * iblock are clean. */ if(H5AC_get_entry_status(f, iblock->addr, &iblock_status) < 0) HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get iblock status") @@ -1230,9 +1249,9 @@ H5HF__cache_iblock_pre_serialize(H5F_t *f, hid_t dxpl_id, void *_thing, * there is no need to check to see if it is pinned or protected, or to * protect it if it is not. */ - if(H5HF__cache_verify_iblock_descendants_clean((H5F_t *)f, iblock, &iblock_status, &descendants_clean) < 0) + if(H5HF__cache_verify_iblock_descendants_clean((H5F_t *)f, dxpl_id, iblock->addr, iblock, &iblock_status, &fd_children_clean, &descendants_clean) < 0) HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify descendants clean.") - HDassert(descendants_clean); + HDassert(fd_children_clean); } #endif /* NDEBUG */ @@ -2585,6 +2604,54 @@ done: * instance of H5HF_hdr_t are clean. Set *clean to * TRUE if this is the case, and to FALSE otherwise. * + * Update -- 8/24/15 + * + * With the advent of the metadata cache image feature, it is + * possible for the pre-serialize and serialize calls to be + * invoked outside of a flush. While this serialization + * observes flush dependencies for the order of serialization, + * the entries are not written to disk, and hence dirty entries + * remain dirty. + * + * To address this, updated the sanity checks in this function + * to treat entries whose images are up to date as clean if + * a cache serialization is in progress. + * + * Update -- 9/29/16 + * + * The implementation of flush dependencies has been changed. + * Prior to this change, a flush dependency parent could be + * flushed if and only if all its flush dependency decendants + * were clean. In the new definition, a flush dependency + * parent can be flushed if all its immediate flush dependency + * children are clean, regardless of any other dirty + * decendants. + * + * Further, metadata cache entries are now allowed to have + * multiple flush dependency parents. + * + * This means that the fractal heap is no longer ncessarily + * flushed from the bottom up. + * + * For example, it is now possible for a dirty fractal heap + * header to be flushed before a dirty dblock, as long as the + * there in an interviening iblock, and the header has no + * dirty immediate flush dependency children. + * + * Also, I gather that under some circumstances, a dblock + * will be direct a flush dependency child both of the iblock + * that points to it, and of the fractal heap header. + * + * As a result of these changes, the functionality of these + * sanity checking routines has been modified significantly. + * Instead of scanning the fractal heap from a starting point + * down, and verifying that there were no dirty entries, the + * functions now scan downward from the starting point and + * verify that there are no dirty flush dependency children + * of the specified flush dependency parent. In passing, + * they also walk the data structure, and verify it. + * + * * Return: Non-negative on success/Negative on failure * * Programmer: John Mainzer @@ -2594,9 +2661,10 @@ done: */ #ifndef NDEBUG static herr_t -H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, H5HF_hdr_t *hdr, - hbool_t *clean) +H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, hid_t dxpl_id, + H5HF_hdr_t *hdr, hbool_t *fd_clean, hbool_t *clean) { + hbool_t fd_exists = FALSE; /* whether flush dependency exists. */ haddr_t hdr_addr; /* Address of header */ unsigned hdr_status = 0; /* Header cache entry status */ herr_t ret_value = SUCCEED; /* Return value */ @@ -2608,6 +2676,7 @@ H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, H5HF_hdr_t *hdr, HDassert(hdr); HDassert(hdr->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); HDassert(hdr->cache_info.type == H5AC_FHEAP_HDR); + HDassert(fd_clean); HDassert(clean); hdr_addr = hdr->cache_info.addr; HDassert(hdr_addr == hdr->heap_addr); @@ -2672,15 +2741,165 @@ H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, H5HF_hdr_t *hdr, root_iblock_in_cache = ( (root_iblock_status & H5AC_ES__IN_CACHE) != 0); HDassert(root_iblock_in_cache || (root_iblock == NULL)); - if(!root_iblock_in_cache) /* we are done */ - *clean = TRUE; - else if(root_iblock_status & H5AC_ES__IS_DIRTY) - *clean = FALSE; + if(!root_iblock_in_cache) { /* we are done */ + *clean = TRUE; + *fd_clean = TRUE; + } /* end if */ + else if((root_iblock_status & H5AC_ES__IS_DIRTY) && + (((root_iblock_status & H5AC_ES__IMAGE_IS_UP_TO_DATE) == 0) || + (!H5AC_get_serialization_in_progress(f)))) { + *clean = FALSE; + + /* verify that a flush dependency exists between the header and + * the root inode. + */ + if(H5AC_flush_dependency_exists(f, hdr->heap_addr, root_iblock_addr, &fd_exists) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency") + HDassert(fd_exists); + + *fd_clean = FALSE; + } /* end else-if */ + else { /* must examine children */ + hbool_t unprotect_root_iblock = FALSE; + + /* At this point, the root iblock may be pinned, protected, + * both, or neither, and we may or may not have a pointer + * to root iblock in memory. + * + * Before we call H5HF__cache_verify_iblock_descendants_clean(), + * we must ensure that the root iblock is either pinned or + * protected or both, and that we have a pointer to it. + * Do this as follows: + */ + if(root_iblock == NULL) { /* we don't have ptr to root iblock */ + if(0 == (root_iblock_status & H5AC_ES__IS_PROTECTED)) { + /* just protect the root iblock -- this will give us + * the pointer we need to proceed, and ensure that + * it is locked into the metadata cache for the + * duration. + * + * Note that the udata is only used in the load callback. + * While the fractal heap makes heavy use of the udata + * in this case, since we know that the entry is in cache, + * we can pass NULL udata. + * + * The tag specified in the dxpl we received + * as a parameter (via dxpl_id) may not be correct. + * Grab the (hopefully) correct tag from the header, + * and load it into the dxpl via the H5_BEGIN_TAG and + * H5_END_TAG macros. Note that any error bracked by + * these macros must be reported with HGOTO_ERROR_TAG. + */ + H5_BEGIN_TAG(dxpl_id, hdr->heap_addr, FAIL) + + if(NULL == (root_iblock = (H5HF_indirect_t *)H5AC_protect(f, dxpl_id, H5AC_FHEAP_IBLOCK, root_iblock_addr, NULL, H5AC__READ_ONLY_FLAG))) + HGOTO_ERROR_TAG(H5E_HEAP, H5E_CANTPROTECT, FAIL, "H5AC_protect() faild.") + + H5_END_TAG(FAIL) + + unprotect_root_iblock = TRUE; + } /* end if */ + else { + /* the root iblock is protected, and we have no + * legitimate way of getting a pointer to it. + * + * We square this circle by using the + * H5AC_get_entry_ptr_from_addr() to get the needed + * pointer. + * + * WARNING: This call should be used only in debugging + * routines, and it should be avoided there when + * possible. + * + * Further, if we ever multi-thread the cache, + * this routine will have to be either discarded + * or heavily re-worked. + * + * Finally, keep in mind that the entry whose + * pointer is obtained in this fashion may not + * be in a stable state. + * + * Assuming that the flush dependency code is working + * as it should, the only reason for the root iblock to + * be unpinned is if none of its children are in cache. + * This unfortunately means that if it is protected and + * not pinned, the fractal heap is in the process of loading + * or inserting one of its children. The obvious + * implication is that there is a significant chance that + * the root iblock is in an unstable state. + * + * All this suggests that using + * H5AC_get_entry_ptr_from_addr() to obtain the pointer + * to the protected root iblock is questionable here. + * However, since this is test/debugging code, I expect + * that we will use this approach until it causes problems, + * or we think of a better way. + */ + if(H5AC_get_entry_ptr_from_addr(f, root_iblock_addr, (void **)(&root_iblock)) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "H5AC_get_entry_ptr_from_addr() failed.") + HDassert(root_iblock); + } /* end else */ + } /* end if */ + else { /* root_iblock != NULL */ + /* we have the pointer to the root iblock. Protect it + * if it is neither pinned nor protected -- otherwise we + * are ready to go. + */ + H5HF_indirect_t * iblock = NULL; + + if(((root_iblock_status & H5AC_ES__IS_PINNED) == 0) && + ((root_iblock_status & H5AC_ES__IS_PROTECTED) == 0)) { + /* the root iblock is neither pinned nor protected -- hence + * we must protect it before we proceed + * + * Note that the udata is only used in the load callback. + * While the fractal heap makes heavy use of the udata + * in this case, since we know that the entry is in cache, + * we can pass NULL udata. + * + * The tag associated specified in the dxpl we received + * as a parameter (via dxpl_id) may not be correct. + * Grab the (hopefully) correct tag from the header, + * and load it into the dxpl via the H5_BEGIN_TAG and + * H5_END_TAG macros. Note that any error bracked by + * these macros must be reported with HGOTO_ERROR_TAG. + */ + H5_BEGIN_TAG(dxpl_id, hdr->heap_addr, FAIL) + + if(NULL == (iblock = (H5HF_indirect_t *)H5AC_protect(f, dxpl_id, H5AC_FHEAP_IBLOCK, root_iblock_addr, NULL, H5AC__READ_ONLY_FLAG))) + HGOTO_ERROR_TAG(H5E_HEAP, H5E_CANTPROTECT, FAIL, "H5AC_protect() faild.") + + H5_END_TAG(FAIL) + + unprotect_root_iblock = TRUE; + HDassert(iblock == root_iblock); + } /* end if */ + } /* end else */ + + /* at this point, one way or another, the root iblock is locked + * in memory for the duration of the call. Do some sanity checks, + * and then call H5HF__cache_verify_iblock_descendants_clean(). + */ + HDassert(root_iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(root_iblock->cache_info.type == H5AC_FHEAP_IBLOCK); + + if(H5HF__cache_verify_iblock_descendants_clean(f, dxpl_id, hdr->heap_addr, root_iblock, &root_iblock_status, fd_clean, clean) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify root iblock & descendants clean.") + + /* Unprotect the root indirect block if required */ + if(unprotect_root_iblock) { + HDassert(root_iblock); + if(H5AC_unprotect(f, dxpl_id, H5AC_FHEAP_IBLOCK, root_iblock_addr, root_iblock, H5AC__NO_FLAGS_SET) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "H5AC_unprotect() faild.") + } /* end if */ + } /* end else */ } /* end if */ else if((hdr->man_dtable.curr_root_rows == 0) && (HADDR_UNDEF != hdr->man_dtable.table_addr)) { haddr_t root_dblock_addr; unsigned root_dblock_status = 0; + hbool_t in_cache; + hbool_t type_ok; /* this is scenario 2 -- we have a root dblock */ root_dblock_addr = hdr->man_dtable.table_addr; @@ -2688,25 +2907,48 @@ H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, H5HF_hdr_t *hdr, HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get root dblock status") if(root_dblock_status & H5AC_ES__IN_CACHE) { + if(H5AC_verify_entry_type(f, root_dblock_addr, &H5AC_FHEAP_DBLOCK[0], &in_cache, &type_ok) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check dblock type") + HDassert(in_cache); + if(!type_ok) + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "root dblock addr doesn't refer to a dblock?!?") + /* If a root dblock is in cache, it must have a flush - * dependency relationship with the header. + * dependency relationship with the header, and it + * may not be the parent in any flush dependency + * relationship. + * + * We don't test this fully, but we will verify that + * the root iblock is a child in a flush dependency + * relationship with the header. */ - if(0 == (root_dblock_status & H5AC_ES__IS_FLUSH_DEP_CHILD)) - HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "root dblock in cache and not a flush dep child.") + if(H5AC_flush_dependency_exists(f, hdr->heap_addr, root_dblock_addr, &fd_exists) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency") + if(!fd_exists) + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "root dblock is not a flush dep parent of header.") + if(0 != (root_dblock_status & H5AC_ES__IS_FLUSH_DEP_PARENT)) HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "root dblock in cache and is a flush dep parent.") - if(root_dblock_status & H5AC_ES__IS_DIRTY) - *clean = FALSE; - } /* end if */ - else /* root dblock not in cache */ - *clean = TRUE; + *clean = !((root_dblock_status & H5AC_ES__IS_DIRTY) && + (((root_dblock_status & + H5AC_ES__IMAGE_IS_UP_TO_DATE) == 0) || + (!H5AC_get_serialization_in_progress(f)))); + + *fd_clean = *clean; + } /* end if */ + else { /* root dblock not in cache */ + *fd_clean = TRUE; + *clean = TRUE; + } /* end else */ } /* end else-if */ - else - /* this is scenario 3 -- the fractal heap is empty, and we - * have nothing to do. - */ - *clean = TRUE; + else { + /* this is scenario 3 -- the fractal heap is empty, and we + * have nothing to do. + */ + *fd_clean = TRUE; + *clean = TRUE; + } /* end else */ done: FUNC_LEAVE_NOAPI(ret_value) @@ -2741,6 +2983,40 @@ done: * H5HF__cache_verify_descendant_iblocks_clean() are * recursive co-routines. * + * Update -- 9/29/16 + * + * The implementation of flush dependencies has been changed. + * Prior to this change, a flush dependency parent could be + * flushed if and only if all its flush dependency decendants + * were clean. In the new definition, a flush dependency + * parent can be flushed if all its immediate flush dependency + * children are clean, regardless of any other dirty + * decendants. + * + * Further, metadata cache entries are now allowed to have + * multiple flush dependency parents. + * + * This means that the fractal heap is no longer ncessarily + * flushed from the bottom up. + * + * For example, it is now possible for a dirty fractal heap + * header to be flushed before a dirty dblock, as long as the + * there in an interviening iblock, and the header has no + * dirty immediate flush dependency children. + * + * Also, I gather that under some circumstances, a dblock + * will be direct a flush dependency child both of the iblock + * that points to it, and of the fractal heap header. + * + * As a result of these changes, the functionality of these + * sanity checking routines has been modified significantly. + * Instead of scanning the fractal heap from a starting point + * down, and verifying that there were no dirty entries, the + * functions now scan downward from the starting point and + * verify that there are no dirty flush dependency children + * of the specified flush dependency parent. In passing, + * they also walk the data structure, and verify it. + * * Return: Non-negative on success/Negative on failure * * Programmer: John Mainzer @@ -2750,8 +3026,9 @@ done: */ #ifndef NDEBUG static herr_t -H5HF__cache_verify_iblock_descendants_clean(H5F_t *f, H5HF_indirect_t *iblock, - unsigned *iblock_status, hbool_t *clean) +H5HF__cache_verify_iblock_descendants_clean(H5F_t *f, hid_t dxpl_id, + haddr_t fd_parent_addr, H5HF_indirect_t *iblock, unsigned *iblock_status, + hbool_t * fd_clean, hbool_t *clean) { hbool_t has_dblocks = FALSE; hbool_t has_iblocks = FALSE; @@ -2761,17 +3038,19 @@ H5HF__cache_verify_iblock_descendants_clean(H5F_t *f, H5HF_indirect_t *iblock, /* Sanity checks */ HDassert(f); + HDassert(H5F_addr_defined(fd_parent_addr)); HDassert(iblock); HDassert(iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); HDassert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK); HDassert(iblock_status); - HDassert(clean); - HDassert(*clean); + HDassert(fd_clean); + HDassert(*fd_clean); + HDassert(clean); /* note that *clean need not be TRUE */ - if((*clean) && H5HF__cache_verify_iblocks_dblocks_clean(f, iblock, clean, &has_dblocks) < 0) + if((*fd_clean) && H5HF__cache_verify_iblocks_dblocks_clean(f, fd_parent_addr, iblock, fd_clean, clean, &has_dblocks) < 0) HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify dblocks clean.") - if((*clean) && H5HF__cache_verify_descendant_iblocks_clean(f, iblock, clean, &has_iblocks) < 0) + if((*fd_clean) && H5HF__cache_verify_descendant_iblocks_clean(f, dxpl_id, fd_parent_addr, iblock, fd_clean, clean, &has_iblocks) < 0) HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify iblocks clean.") /* verify that flush dependency setup is plausible */ @@ -2808,6 +3087,53 @@ done: * during the call. Caller must ensure that this is * the case before the call. * + * Update -- 8/24/15 + * + * With the advent of the metadata cache image feature, it is + * possible for the pre-serialize and serialize calls to be + * invoked outside of a flush. While this serialization + * observes flush dependencies for the order of serialization, + * the entries are not written to disk, and hence dirty entries + * remain dirty. + * + * To address this, updated the sanity checks in this function + * to treat entries whose images are up to date as clean if + * a cache serialization is in progress. + * + * Update -- 9/29/16 + * + * The implementation of flush dependencies has been changed. + * Prior to this change, a flush dependency parent could be + * flushed if and only if all its flush dependency decendants + * were clean. In the new definition, a flush dependency + * parent can be flushed if all its immediate flush dependency + * children are clean, regardless of any other dirty + * decendants. + * + * Further, metadata cache entries are now allowed to have + * multiple flush dependency parents. + * + * This means that the fractal heap is no longer ncessarily + * flushed from the bottom up. + * + * For example, it is now possible for a dirty fractal heap + * header to be flushed before a dirty dblock, as long as the + * there in an interviening iblock, and the header has no + * dirty immediate flush dependency children. + * + * Also, I gather that under some circumstances, a dblock + * will be direct a flush dependency child both of the iblock + * that points to it, and of the fractal heap header. + * + * As a result of these changes, the functionality of these + * sanity checking routines has been modified significantly. + * Instead of scanning the fractal heap from a starting point + * down, and verifying that there were no dirty entries, the + * functions now scan downward from the starting point and + * verify that there are no dirty flush dependency children + * of the specified flush dependency parent. In passing, + * they also walk the data structure, and verify it. + * * Return: Non-negative on success/Negative on failure * * Programmer: John Mainzer @@ -2817,53 +3143,82 @@ done: */ #ifndef NDEBUG static herr_t -H5HF__cache_verify_iblocks_dblocks_clean(H5F_t *f, H5HF_indirect_t *iblock, - hbool_t *clean, hbool_t *has_dblocks) +H5HF__cache_verify_iblocks_dblocks_clean(H5F_t *f, haddr_t fd_parent_addr, + H5HF_indirect_t *iblock, hbool_t *fd_clean, hbool_t *clean, + hbool_t *has_dblocks) { unsigned num_direct_rows; unsigned max_dblock_index; unsigned i; + haddr_t iblock_addr; herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_STATIC /* Sanity checks */ HDassert(f); + HDassert(H5F_addr_defined(fd_parent_addr)); HDassert(iblock); HDassert(iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); HDassert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK); - HDassert(clean); - HDassert(*clean); + HDassert(fd_clean); + HDassert(*fd_clean); + HDassert(clean); /* note that *clean need not be true */ HDassert(has_dblocks); i = 0; num_direct_rows = MIN(iblock->nrows, iblock->hdr->man_dtable.max_direct_rows); HDassert(num_direct_rows <= iblock->nrows); max_dblock_index = (num_direct_rows * iblock->hdr->man_dtable.cparam.width) - 1; - while((*clean) && (i <= max_dblock_index)) { + iblock_addr = iblock->addr; + HDassert(H5F_addr_defined(iblock_addr)); + + while((*fd_clean) && (i <= max_dblock_index)) { haddr_t dblock_addr; dblock_addr = iblock->ents[i].addr; if(H5F_addr_defined(dblock_addr)) { - unsigned dblock_status = 0; + hbool_t in_cache; + hbool_t type_ok; + + if(H5AC_verify_entry_type(f, dblock_addr, &H5AC_FHEAP_DBLOCK[0], &in_cache, &type_ok) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check dblock type") + + if(in_cache) { /* dblock is in cache */ + hbool_t fd_exists; + unsigned dblock_status = 0; + + if(!type_ok) + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "dblock addr doesn't refer to a dblock?!?") + + if(H5AC_get_entry_status(f, dblock_addr, &dblock_status) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get dblock status") + + HDassert(dblock_status & H5AC_ES__IN_CACHE); - if(H5AC_get_entry_status(f, dblock_addr, &dblock_status) < 0) - HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get dblock status") - if(dblock_status & H5AC_ES__IN_CACHE) { *has_dblocks = TRUE; - if(dblock_status & H5AC_ES__IS_DIRTY) + if((dblock_status & H5AC_ES__IS_DIRTY) && + (((dblock_status & H5AC_ES__IMAGE_IS_UP_TO_DATE) == 0) || + (!H5AC_get_serialization_in_progress(f)))) { *clean = FALSE; + + if(H5AC_flush_dependency_exists(f, fd_parent_addr, dblock_addr, &fd_exists) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency") + + if(fd_exists) + *fd_clean = FALSE; + } /* end if */ - /* If a child dblock is in cache, it must have a flush - * dependency relationship with this iblock, and it - * may not be the parent in any flush dependency - * relationship. + /* If a child dblock is in cache, it must have a flush + * dependency relationship with this iblock. Test this + * here. */ - if(0 == (dblock_status & H5AC_ES__IS_FLUSH_DEP_CHILD)) - HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "dblock in cache and not a flush dep child.") - if(0 != (dblock_status & H5AC_ES__IS_FLUSH_DEP_PARENT)) - HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "dblock in cache and is a flush dep parent.") + if(H5AC_flush_dependency_exists(f, iblock_addr, dblock_addr, &fd_exists) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency") + + if(!fd_exists) + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "dblock in cache and not a flush dep child of iblock.") } /* end if */ } /* end if */ @@ -2896,6 +3251,54 @@ done: * during the call. Caller must ensure that this is * the case before the call. * + * Update -- 8/24/15 + * + * With the advent of the metadata cache image feature, it is + * possible for the pre-serialize and serialize calls to be + * invoked outside of a flush. While this serialization + * observes flush dependencies for the order of serialization, + * the entries are not written to disk, and hence dirty entries + * remain dirty. + * + * To address this, updated the sanity checks in this function + * to treat entries whose images are up to date as clean if + * a cache serialization is in progress. + * + * Update -- 9/29/16 + * + * The implementation of flush dependencies has been changed. + * Prior to this change, a flush dependency parent could be + * flushed if and only if all its flush dependency decendants + * were clean. In the new definition, a flush dependency + * parent can be flushed if all its immediate flush dependency + * children are clean, regardless of any other dirty + * decendants. + * + * Further, metadata cache entries are now allowed to have + * multiple flush dependency parents. + * + * This means that the fractal heap is no longer ncessarily + * flushed from the bottom up. + * + * For example, it is now possible for a dirty fractal heap + * header to be flushed before a dirty dblock, as long as the + * there in an interviening iblock, and the header has no + * dirty immediate flush dependency children. + * + * Also, I gather that under some circumstances, a dblock + * will be direct a flush dependency child both of the iblock + * that points to it, and of the fractal heap header. + * + * As a result of these changes, the functionality of these + * sanity checking routines has been modified significantly. + * Instead of scanning the fractal heap from a starting point + * down, and verifying that there were no dirty entries, the + * functions now scan downward from the starting point and + * verify that there are no dirty flush dependency children + * of the specified flush dependency parent. In passing, + * they also walk the data structure, and verify it. + * + * * Return: Non-negative on success/Negative on failure * * Programmer: John Mainzer @@ -2905,33 +3308,38 @@ done: */ #ifndef NDEBUG static herr_t -H5HF__cache_verify_descendant_iblocks_clean(H5F_t *f, H5HF_indirect_t *iblock, +H5HF__cache_verify_descendant_iblocks_clean(H5F_t *f, hid_t dxpl_id, + haddr_t fd_parent_addr, H5HF_indirect_t *iblock, hbool_t *fd_clean, hbool_t *clean, hbool_t *has_iblocks) { unsigned first_iblock_index; unsigned last_iblock_index; unsigned num_direct_rows; unsigned i; + haddr_t iblock_addr; herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_STATIC /* Sanity checks */ HDassert(f); + HDassert(H5F_addr_defined(fd_parent_addr)); HDassert(iblock); HDassert(iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); HDassert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK); - HDassert(clean); - HDassert(*clean); + HDassert(fd_clean); + HDassert(*fd_clean); + HDassert(clean); /* note that *clean need not be true */ HDassert(has_iblocks); num_direct_rows = MIN(iblock->nrows, iblock->hdr->man_dtable.max_direct_rows); HDassert(num_direct_rows <= iblock->nrows); + iblock_addr = iblock->addr; first_iblock_index = num_direct_rows * iblock->hdr->man_dtable.cparam.width; last_iblock_index = (iblock->nrows * iblock->hdr->man_dtable.cparam.width) - 1; i = first_iblock_index; - while((*clean) && (i <= last_iblock_index)) { + while((*fd_clean) && (i <= last_iblock_index)) { haddr_t child_iblock_addr = iblock->ents[i].addr; if(H5F_addr_defined(child_iblock_addr)) { @@ -2941,9 +3349,157 @@ H5HF__cache_verify_descendant_iblocks_clean(H5F_t *f, H5HF_indirect_t *iblock, HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get iblock status") if(child_iblock_status & H5AC_ES__IN_CACHE) { + hbool_t fd_exists; + *has_iblocks = TRUE; - if(child_iblock_status & H5AC_ES__IS_DIRTY) - *clean = FALSE; + + if((child_iblock_status & H5AC_ES__IS_DIRTY) && + (((child_iblock_status & H5AC_ES__IMAGE_IS_UP_TO_DATE) == 0) || + (!H5AC_get_serialization_in_progress(f)))) { + + *clean = FALSE; + + if(H5AC_flush_dependency_exists(f, fd_parent_addr, child_iblock_addr, &fd_exists) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency") + + if(fd_exists) + *fd_clean = FALSE; + } /* end if */ + + /* if the child iblock is in cache and *fd_clean is TRUE, + * we must continue to explore down the fractal heap tree + * structure to verify that all descendant blocks that are + * flush dependency children of the entry at parent_addr are + * either clean, or not in the metadata cache. We do this + * with a recursive call to + * H5HF__cache_verify_iblock_descendants_clean(). + * However, we can't make this call unless the child iblock + * is somehow locked into the cache -- typically via either + * pinning or protecting. + * + * If the child iblock is pinned, we can look up its pointer + * on the current iblock's pinned child iblock list, and + * and use that pointer in the recursive call. + * + * If the entry is unprotected and unpinned, we simply + * protect it. + * + * If, however, the the child iblock is already protected, + * but not pinned, we have a bit of a problem, as we have + * no legitimate way of looking up its pointer in memory. + * + * To solve this problem, I have added a new metadata cache + * call to obtain the pointer. + * + * WARNING: This call should be used only in debugging + * routines, and it should be avoided there when + * possible. + * + * Further, if we ever multi-thread the cache, + * this routine will have to be either discarded + * or heavily re-worked. + * + * Finally, keep in mind that the entry whose + * pointer is obtained in this fashion may not + * be in a stable state. + * + * Assuming that the flush dependency code is working + * as it should, the only reason for the child entry to + * be unpinned is if none of its children are in cache. + * This unfortunately means that if it is protected and + * not pinned, the fractal heap is in the process of loading + * or inserting one of its children. The obvious implication + * is that there is a significant chance that the child + * iblock is in an unstable state. + * + * All this suggests that using the new call to obtain the + * pointer to the protected child iblock is questionable + * here. However, since this is test/debugging code, I + * expect that we will use this approach until it causes + * problems, or we think of a better way. + */ + if(*fd_clean) { + H5HF_indirect_t *child_iblock = NULL; + hbool_t unprotect_child_iblock = FALSE; + + if(0 == (child_iblock_status & H5AC_ES__IS_PINNED)) { + /* child iblock is not pinned */ + if(0 == (child_iblock_status & H5AC_ES__IS_PROTECTED)) { + /* child iblock is unprotected, and unpinned */ + /* protect it. Note that the udata is only */ + /* used in the load callback. While the */ + /* fractal heap makes heavy use of the udata */ + /* in this case, since we know that the */ + /* entry is in cache, we can pass NULL udata */ + /* */ + /* The tag associated specified in the dxpl */ + /* we received as a parameter (via dxpl_id) */ + /* may not be correct. */ + /* */ + /* Grab the (hopefully) correct tag from the */ + /* parent iblock, and load it into the dxpl */ + /* via the H5_BEGIN_TAG and H5_END_TAG */ + /* macros. Note that any error bracked by */ + /* these macros must be reported with */ + /* HGOTO_ERROR_TAG. */ + + H5_BEGIN_TAG(dxpl_id, iblock->hdr->heap_addr, FAIL) + + if(NULL == (child_iblock = (H5HF_indirect_t *) H5AC_protect(f, dxpl_id, H5AC_FHEAP_IBLOCK, child_iblock_addr, NULL, H5AC__READ_ONLY_FLAG))) + HGOTO_ERROR_TAG(H5E_HEAP, H5E_CANTPROTECT, FAIL, "H5AC_protect() faild.") + + H5_END_TAG(FAIL) + + unprotect_child_iblock = TRUE; + } /* end if */ + else { + /* child iblock is protected -- use */ + /* H5AC_get_entry_ptr_from_addr() to get a */ + /* pointer to the entry. This is very slimy -- */ + /* come up with a better solution. */ + if(H5AC_get_entry_ptr_from_addr(f, child_iblock_addr, (void **)(&child_iblock)) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "H5AC_get_entry_ptr_from_addr() faild.") + HDassert(child_iblock); + } /* end else */ + } /* end if */ + else { + /* child iblock is pinned -- look it up in the */ + /* parent iblocks child_iblocks array. */ + HDassert(iblock->child_iblocks); + child_iblock = iblock->child_iblocks[i - first_iblock_index]; + } /* end else */ + + /* At this point, one way or another we should have + * a pointer to the child iblock. Verify that we + * that we have the correct one. + */ + HDassert(child_iblock); + HDassert(child_iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(child_iblock->cache_info.type == H5AC_FHEAP_IBLOCK); + HDassert(child_iblock->addr == child_iblock_addr); + + /* now make the recursive call */ + if(H5HF__cache_verify_iblock_descendants_clean(f, dxpl_id, fd_parent_addr, child_iblock, &child_iblock_status, fd_clean, clean) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify child iblock clean.") + + /* if iblock_addr != fd_parent_addr, verify that a flush + * dependency relationship exists between iblock and + * the child iblock. + */ + if(fd_parent_addr != iblock_addr) { + if(H5AC_flush_dependency_exists(f, iblock_addr, child_iblock_addr, &fd_exists) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency") + + if(!fd_exists) + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "iblock is not a flush dep parent of child_iblock.") + } /* end if */ + + /* if we protected the child iblock, unprotect it now */ + if(unprotect_child_iblock) { + if(H5AC_unprotect(f, dxpl_id, H5AC_FHEAP_IBLOCK, child_iblock_addr, child_iblock, H5AC__NO_FLAGS_SET) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "H5AC_unprotect() faild.") + } /* end if */ + } /* end if */ } /* end if */ } /* end if */ diff --git a/src/H5HFpkg.h b/src/H5HFpkg.h index 6abae65..9c1d9a6 100644 --- a/src/H5HFpkg.h +++ b/src/H5HFpkg.h @@ -559,15 +559,6 @@ typedef struct H5HF_dblock_cache_ud_t { /* Package Private Variables */ /*****************************/ -/* H5HF header inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_FHEAP_HDR[1]; - -/* H5HF indirect block inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_FHEAP_IBLOCK[1]; - -/* H5HF direct block inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_FHEAP_DBLOCK[1]; - /* The v2 B-tree class for tracking indirectly accessed 'huge' objects */ H5_DLLVAR const H5B2_class_t H5HF_HUGE_BT2_INDIR[1]; diff --git a/src/H5HGpkg.h b/src/H5HGpkg.h index e566ece..a5c80ea 100644 --- a/src/H5HGpkg.h +++ b/src/H5HGpkg.h @@ -40,9 +40,6 @@ /* Package Private Variables */ /*****************************/ -/* The cache subclass */ -H5_DLLVAR const H5AC_class_t H5AC_GHEAP[1]; - /* Declare extern the free list to manage the H5HG_t struct */ H5FL_EXTERN(H5HG_heap_t); diff --git a/src/H5HLpkg.h b/src/H5HLpkg.h index 7075b2a..06db696 100644 --- a/src/H5HLpkg.h +++ b/src/H5HLpkg.h @@ -39,12 +39,6 @@ /* Package Private Variables */ /*****************************/ -/* The local heap prefix cache subclass */ -H5_DLLVAR const H5AC_class_t H5AC_LHEAP_PRFX[1]; - -/* The local heap data block cache subclass */ -H5_DLLVAR const H5AC_class_t H5AC_LHEAP_DBLK[1]; - /* Declare extern the free list to manage the H5HL_free_t struct */ H5FL_EXTERN(H5HL_free_t); @@ -1489,7 +1489,7 @@ H5MF_settle_raw_data_fsm(H5F_t *f, hid_t dxpl_id, hbool_t *fsm_settled) fsinfo.fs_addr[type-1] = HADDR_UNDEF; fsinfo.strategy = f->shared->fs_strategy; fsinfo.threshold = f->shared->fs_threshold; - if(H5F_super_ext_write_msg(f, dxpl_id, H5O_FSINFO_ID, &fsinfo, TRUE) < 0) + if(H5F_super_ext_write_msg(f, dxpl_id, H5O_FSINFO_ID, &fsinfo, TRUE, H5O_MSG_NO_FLAGS_SET) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_WRITEERROR, FAIL, "error in writing message to superblock extension") @@ -2113,7 +2113,7 @@ HDfprintf(stderr, "%s: Entering\n", FUNC); fsinfo.threshold = f->shared->fs_threshold; /* Write the free space manager message -- message must already exist */ - if(H5F_super_ext_write_msg(f, dxpl_id, H5O_FSINFO_ID, &fsinfo, FALSE) < 0) + if(H5F_super_ext_write_msg(f, dxpl_id, H5O_FSINFO_ID, &fsinfo, FALSE, H5O_MSG_NO_FLAGS_SET) < 0) HGOTO_ERROR(H5E_RESOURCE, H5E_WRITEERROR, FAIL, "error in writing message to superblock extension") /* Final close of free-space managers */ @@ -130,11 +130,12 @@ const H5O_msg_class_t *const H5O_msg_class_g[] = { H5O_MSG_AINFO, /*0x0015 Attribute information */ H5O_MSG_REFCOUNT, /*0x0016 Object's ref. count */ H5O_MSG_FSINFO, /*0x0017 Free-space manager info */ - H5O_MSG_UNKNOWN, /*0x0018 Placeholder for unknown message */ + H5O_MSG_MDCI, /*0x0018 Metadata cache image */ + H5O_MSG_UNKNOWN, /*0x0019 Placeholder for unknown message */ #ifdef H5O_ENABLE_BOGUS - H5O_MSG_BOGUS_INVALID, /*0x0019 "Bogus invalid" (for testing) */ + H5O_MSG_BOGUS_INVALID, /*0x001A "Bogus invalid" (for testing) */ #else /* H5O_ENABLE_BOGUS */ - NULL, /*0x0019 "Bogus invalid" (for testing) */ + NULL, /*0x001A "Bogus invalid" (for testing) */ #endif /* H5O_ENABLE_BOGUS */ }; diff --git a/src/H5Ocache_image.c b/src/H5Ocache_image.c new file mode 100644 index 0000000..65f6aa2 --- /dev/null +++ b/src/H5Ocache_image.c @@ -0,0 +1,337 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * 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://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*------------------------------------------------------------------------- + * + * Created: H5Ocache_image.c + * June 21, 2015 + * John Mainzer + * + * Purpose: A message indicating that a metadata cache image block + * of the indicated length exists at the specified offset + * in the HDF5 file. + * + * The mdci_msg only appears in the superblock extension. + * + *------------------------------------------------------------------------- + */ + +#include "H5Omodule.h" /* This source code file is part of the H5O module */ + + +#include "H5private.h" /* Generic Functions */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5FLprivate.h" /* Free Lists */ +#include "H5Opkg.h" /* Object headers */ +#include "H5MFprivate.h" /* File space management */ + +/* Callbacks for message class */ +static void *H5O__mdci_decode(H5F_t *f, hid_t dxpl_id, H5O_t *open_oh, + unsigned mesg_flags, unsigned *ioflags, const uint8_t *p); +static herr_t H5O__mdci_encode(H5F_t *f, hbool_t disable_shared, + uint8_t *p, const void *_mesg); +static void *H5O__mdci_copy(const void *_mesg, void *_dest); +static size_t H5O__mdci_size(const H5F_t *f, hbool_t disable_shared, + const void *_mesg); +static herr_t H5O__mdci_free(void *mesg); +static herr_t H5O__mdci_delete(H5F_t *f, hid_t dxpl_id, H5O_t *open_oh, + void *_mesg); +static herr_t H5O__mdci_debug(H5F_t *f, hid_t dxpl_id, const void *_mesg, + FILE *stream, int indent, int fwidth); + +/* This message derives from H5O message class */ +const H5O_msg_class_t H5O_MSG_MDCI[1] = {{ + H5O_MDCI_MSG_ID, /* message id number */ + "mdci", /* message name for debugging */ + sizeof(H5O_mdci_t), /* native message size */ + 0, /* messages are sharable? */ + H5O__mdci_decode, /* decode message */ + H5O__mdci_encode, /* encode message */ + H5O__mdci_copy, /* copy method */ + H5O__mdci_size, /* size of mdc image message */ + NULL, /* reset method */ + H5O__mdci_free, /* free method */ + H5O__mdci_delete, /* file delete method */ + NULL, /* link method */ + NULL, /* set share method */ + NULL, /* can share method */ + NULL, /* pre copy native value to file */ + NULL, /* copy native value to file */ + NULL, /* post copy native value to file */ + NULL, /* get creation index */ + NULL, /* set creation index */ + H5O__mdci_debug /* debugging */ +}}; + +/* Only one version of the metadata cache image message at present */ +#define H5O_MDCI_VERSION_0 0 + +/* Declare the free list for H5O_mdci_t's */ +H5FL_DEFINE(H5O_mdci_t); + + + +/*------------------------------------------------------------------------- + * Function: H5O__mdci_decode + * + * Purpose: Decode a metadata cache image message and return a + * pointer to a newly allocated H5O_mdci_t struct. + * + * Return: Success: Ptr to new message in native struct. + * Failure: NULL + * + * Programmer: John Mainzer + * 6/22/15 + * + *------------------------------------------------------------------------- + */ +static void * +H5O__mdci_decode(H5F_t *f, hid_t H5_ATTR_UNUSED dxpl_id, + H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNUSED mesg_flags, + unsigned H5_ATTR_UNUSED *ioflags, const uint8_t *p) +{ + H5O_mdci_t *mesg; /* Native message */ + void *ret_value = NULL; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity check */ + HDassert(f); + HDassert(p); + + /* Version of message */ + if(*p++ != H5O_MDCI_VERSION_0) + HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "bad version number for message") + + /* Allocate space for message */ + if(NULL == (mesg = (H5O_mdci_t *)H5FL_MALLOC(H5O_mdci_t))) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for metadata cache image message") + + /* Decode */ + H5F_addr_decode(f, &p, &(mesg->addr)); + H5F_DECODE_LENGTH(f, p, mesg->size); + + /* Set return value */ + ret_value = (void *)mesg; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5O__mdci_decode() */ + + +/*------------------------------------------------------------------------- + * Function: H5O__mdci_encode + * + * Purpose: Encode metadata cache image message + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/22/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5O__mdci_encode(H5F_t *f, hbool_t H5_ATTR_UNUSED disable_shared, + uint8_t *p, const void *_mesg) +{ + const H5O_mdci_t *mesg = (const H5O_mdci_t *)_mesg; + + FUNC_ENTER_STATIC_NOERR + + /* Sanity check */ + HDassert(f); + HDassert(p); + HDassert(mesg); + + /* encode */ + *p++ = H5O_MDCI_VERSION_0; + H5F_addr_encode(f, &p, mesg->addr); + H5F_ENCODE_LENGTH(f, p, mesg->size); + + FUNC_LEAVE_NOAPI(SUCCEED) +} /* end H5O__mdci_encode() */ + + +/*------------------------------------------------------------------------- + * Function: H5O__mdci_copy + * + * Purpose: Copies a message from _MESG to _DEST, allocating _DEST if + * necessary. + * + * Return: Success: Ptr to _DEST + * Failure: NULL + * + * Programmer: John Mainzer + * 6/22/15 + * + *------------------------------------------------------------------------- + */ +static void * +H5O__mdci_copy(const void *_mesg, void *_dest) +{ + const H5O_mdci_t *mesg = (const H5O_mdci_t *)_mesg; + H5O_mdci_t *dest = (H5O_mdci_t *) _dest; + void *ret_value = NULL; /* Return value */ + + FUNC_ENTER_STATIC + + /* check args */ + HDassert(mesg); + if(!dest && NULL == (dest = H5FL_MALLOC(H5O_mdci_t))) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") + + /* copy */ + *dest = *mesg; + + /* Set return value */ + ret_value = dest; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5O_mdci__copy() */ + + +/*------------------------------------------------------------------------- + * Function: H5O__mdci_size + * + * Purpose: Returns the size of the raw message in bytes not counting + * the message type or size fields, but only the data fields. + * This function doesn't take into account alignment. + * + * Return: Success: Message data size in bytes without alignment. + * + * Failure: zero + * + * Programmer: John Mainzer + * 6/22/15 + * + *------------------------------------------------------------------------- + */ +static size_t +H5O__mdci_size(const H5F_t *f, hbool_t H5_ATTR_UNUSED disable_shared, + const void H5_ATTR_UNUSED *_mesg) +{ + size_t ret_value = 0; /* Return value */ + + FUNC_ENTER_STATIC_NOERR + + /* Set return value */ + ret_value = (size_t)( 1 + /* Version number */ + H5F_SIZEOF_ADDR(f) + /* addr of metadata cache */ + /* image block */ + H5F_SIZEOF_SIZE(f) ); /* length of metadata cache */ + /* image block */ + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5O__mdci_size() */ + + +/*------------------------------------------------------------------------- + * Function: H5O__mdci_free + * + * Purpose: Free the message + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/22/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5O__mdci_free(void *mesg) +{ + FUNC_ENTER_STATIC_NOERR + + HDassert(mesg); + + mesg = H5FL_FREE(H5O_mdci_t, mesg); + + FUNC_LEAVE_NOAPI(SUCCEED) +} /* end H5O__mdci_free() */ + + +/*------------------------------------------------------------------------- + * Function: H5O__mdci_delete + * + * Purpose: Free file space referenced by message + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Quincey Koziol + * Wednesday, March 19, 2003 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5O__mdci_delete(H5F_t *f, hid_t dxpl_id, H5O_t *open_oh, void *_mesg) +{ + H5O_mdci_t *mesg = (H5O_mdci_t *)_mesg; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* check args */ + HDassert(f); + HDassert(open_oh); + HDassert(mesg); + + /* Free file space for cache image */ + if(H5F_addr_defined(mesg->addr)) + if(H5MF_xfree(f, H5FD_MEM_SUPER, dxpl_id, mesg->addr, mesg->size) < 0) + HGOTO_ERROR(H5E_OHDR, H5E_CANTFREE, FAIL, "unable to free file space for cache image block") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5O__mdci_delete() */ + + +/*------------------------------------------------------------------------- + * Function: H5O__mdci_debug + * + * Purpose: Prints debugging info. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 6/22/15 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5O__mdci_debug(H5F_t H5_ATTR_UNUSED *f, hid_t H5_ATTR_UNUSED dxpl_id, + const void *_mesg, FILE * stream, int indent, int fwidth) +{ + const H5O_mdci_t *mdci = (const H5O_mdci_t *) _mesg; + + FUNC_ENTER_STATIC_NOERR + + /* check args */ + HDassert(f); + HDassert(mdci); + HDassert(stream); + HDassert(indent >= 0); + HDassert(fwidth >= 0); + + HDfprintf(stream, "%*s%-*s %a\n", indent, "", fwidth, + "Metadata Cache Image Block address:", mdci->addr); + + HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth, + "Metadata Cache Image Block size in bytes:", mdci->size); + + FUNC_LEAVE_NOAPI(SUCCEED) +} /* end H5O__mdci_debug() */ + diff --git a/src/H5Opkg.h b/src/H5Opkg.h index ef49535..e1fa0be 100644 --- a/src/H5Opkg.h +++ b/src/H5Opkg.h @@ -31,7 +31,7 @@ #define H5O_NMESGS 8 /*initial number of messages */ #define H5O_NCHUNKS 2 /*initial number of chunks */ #define H5O_MIN_SIZE 22 /* Min. obj header data size (must be big enough for a message prefix and a continuation message) */ -#define H5O_MSG_TYPES 26 /* # of types of messages */ +#define H5O_MSG_TYPES 27 /* # of types of messages */ #define H5O_MAX_CRT_ORDER_IDX 65535 /* Max. creation order index value */ /* Versions of object header structure */ @@ -419,12 +419,6 @@ typedef struct H5O_chk_cache_ud_t { } H5O_chk_cache_ud_t; -/* H5O object header inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_OHDR[1]; - -/* H5O object header chunk inherits cache-like properties from H5AC */ -H5_DLLVAR const H5AC_class_t H5AC_OHDR_CHK[1]; - /* Header message ID to class mapping */ H5_DLLVAR const H5O_msg_class_t *const H5O_msg_class_g[H5O_MSG_TYPES]; @@ -544,7 +538,10 @@ H5_DLLVAR const H5O_msg_class_t H5O_MSG_REFCOUNT[1]; /* Free-space Manager Info message. (0x0017) */ H5_DLLVAR const H5O_msg_class_t H5O_MSG_FSINFO[1]; -/* Placeholder for unknown message. (0x0018) */ +/* Metadata Cache Image message. (0x0018) */ +H5_DLLVAR const H5O_msg_class_t H5O_MSG_MDCI[1]; + +/* Placeholder for unknown message. (0x0019) */ H5_DLLVAR const H5O_msg_class_t H5O_MSG_UNKNOWN[1]; diff --git a/src/H5Oprivate.h b/src/H5Oprivate.h index f6df874..71c512c 100644 --- a/src/H5Oprivate.h +++ b/src/H5Oprivate.h @@ -69,6 +69,7 @@ typedef struct H5O_t H5O_t; #define H5O_FIRST (-2) /* Operate on first message of type */ /* Flags needed when encoding messages */ +#define H5O_MSG_NO_FLAGS_SET 0x00u #define H5O_MSG_FLAG_CONSTANT 0x01u #define H5O_MSG_FLAG_SHARED 0x02u #define H5O_MSG_FLAG_DONTSHARE 0x04u @@ -204,7 +205,8 @@ typedef struct H5O_copy_t { #define H5O_AINFO_ID 0x0015 /* Attribute info message. */ #define H5O_REFCOUNT_ID 0x0016 /* Reference count message. */ #define H5O_FSINFO_ID 0x0017 /* File space info message. */ -#define H5O_UNKNOWN_ID 0x0018 /* Placeholder message ID for unknown message. */ +#define H5O_MDCI_MSG_ID 0x0018 /* Metadata Cache Image Message */ +#define H5O_UNKNOWN_ID 0x0019 /* Placeholder message ID for unknown message. */ /* (this should never exist in a file) */ /* * Note: Must increment H5O_MSG_TYPES in H5Opkg.h and update H5O_msg_class_g @@ -214,7 +216,7 @@ typedef struct H5O_copy_t { * * (this should never exist in a file) */ -#define H5O_BOGUS_INVALID_ID 0x0019 /* "Bogus invalid" Message. */ +#define H5O_BOGUS_INVALID_ID 0x001A /* "Bogus invalid" Message. */ /* Shared object message types. * Shared objects can be committed, in which case the shared message contains @@ -794,6 +796,16 @@ typedef struct H5O_fsinfo_t { haddr_t fs_addr[H5FD_MEM_NTYPES-1]; /* Addresses of free space managers */ } H5O_fsinfo_t; +/* + * Metadata Cache Image Message. + * Contains base address and length of the metadata cache image. + * (Data structure in memory) + */ +typedef struct H5O_mdci_t { + haddr_t addr; /* address of MDC image block */ + hsize_t size; /* size of MDC image block */ +} H5O_mdci_t; + /* Typedef for "application" iteration operations */ typedef herr_t (*H5O_operator_t)(const void *mesg/*in*/, unsigned idx, void *operator_data/*in,out*/); diff --git a/src/H5Pfapl.c b/src/H5Pfapl.c index 91a0e03..7ea0fe0 100644 --- a/src/H5Pfapl.c +++ b/src/H5Pfapl.c @@ -225,6 +225,13 @@ #define H5F_ACS_COLL_MD_WRITE_FLAG_ENC H5P__encode_hbool_t #define H5F_ACS_COLL_MD_WRITE_FLAG_DEC H5P__decode_hbool_t #endif /* H5_HAVE_PARALLEL */ +/* Definitions for the initial metadata cache image configuration */ +#define H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_SIZE sizeof(H5AC_cache_image_config_t) +#define H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_DEF H5AC__DEFAULT_CACHE_IMAGE_CONFIG +#define H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_ENC H5P__facc_cache_image_config_enc +#define H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_DEC H5P__facc_cache_image_config_dec +#define H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_CMP H5P__facc_cache_image_config_cmp + /******************/ /* Local Typedefs */ @@ -279,6 +286,11 @@ static herr_t H5P_facc_mdc_log_location_copy(const char *name, size_t size, void static int H5P_facc_mdc_log_location_cmp(const void *value1, const void *value2, size_t size); static herr_t H5P_facc_mdc_log_location_close(const char *name, size_t size, void *value); +/* Metadata cache image property callbacks */ +static int H5P__facc_cache_image_config_cmp(const void *_config1, const void *_config2, size_t H5_ATTR_UNUSED size); +static herr_t H5P__facc_cache_image_config_enc(const void *value, void **_pp, size_t *size); +static herr_t H5P__facc_cache_image_config_dec(const void **_pp, void *_value); + /*********************/ /* Package Variables */ @@ -346,6 +358,7 @@ static const hbool_t H5F_def_evict_on_close_flag_g = H5F_ACS_EVICT_ON_CLOSE_FLAG static const H5P_coll_md_read_flag_t H5F_def_coll_md_read_flag_g = H5F_ACS_COLL_MD_READ_FLAG_DEF; /* Default setting for the collective metedata read flag */ static const hbool_t H5F_def_coll_md_write_flag_g = H5F_ACS_COLL_MD_WRITE_FLAG_DEF; /* Default setting for the collective metedata write flag */ #endif /* H5_HAVE_PARALLEL */ +static const H5AC_cache_image_config_t H5F_def_mdc_initCacheImageCfg_g = H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_DEF; /* Default metadata cache image settings */ /*------------------------------------------------------------------------- @@ -555,6 +568,12 @@ H5P__facc_reg_prop(H5P_genclass_t *pclass) HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL, "can't insert property into class") #endif /* H5_HAVE_PARALLEL */ + /* Register the initial metadata cache image configuration */ + if(H5P_register_real(pclass, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_NAME, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_SIZE, &H5F_def_mdc_initCacheImageCfg_g, + NULL, NULL, NULL, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_ENC, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_DEC, + NULL, NULL, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_CMP, NULL) < 0) + HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL, "can't insert property into class") + done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5P__facc_reg_prop() */ @@ -1543,6 +1562,101 @@ done: /*------------------------------------------------------------------------- + * Function: H5Pset_mdc_image_config + * + * Purpose: Set the initial metadata cache image configuration in the + * target FAPL. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: J. Mainzer + * Thursday, June 25, 2015 + * + *------------------------------------------------------------------------- + */ +herr_t +H5Pset_mdc_image_config(hid_t plist_id, H5AC_cache_image_config_t *config_ptr) +{ + H5P_genplist_t *plist; /* Property list pointer */ + herr_t ret_value = SUCCEED; /* return value */ + + FUNC_ENTER_API(FAIL) + H5TRACE2("e", "i*x", plist_id, config_ptr); + + /* Get the plist structure */ + if(NULL == (plist = H5P_object_verify(plist_id,H5P_FILE_ACCESS))) + HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID") + + /* validate the new configuration */ + if(H5AC_validate_cache_image_config(config_ptr) < 0) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid metadata cache image configuration") + + /* set the modified metadata cache image config */ + + /* If we ever support multiple versions of H5AC_cache_image_config_t, we + * will have to test the version and do translation here. + */ + + if(H5P_set(plist, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_NAME, config_ptr) < 0) + HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set metadata cache image initial config") + +done: + FUNC_LEAVE_API(ret_value) +} /* H5Pset_mdc_image_config() */ + + +/*------------------------------------------------------------------------- + * Function: H5Pget_mdc_image_config + * + * Purpose: Retrieve the metadata cache initial image configuration + * from the target FAPL. + * + * Observe that the function will fail if config_ptr is + * NULL, or if config_ptr->version specifies an unknown + * version of H5AC_cache_image_config_t. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: J. Mainzer + * Friday, June 26, 2015 + * + *------------------------------------------------------------------------- + */ +herr_t +H5Pget_mdc_image_config(hid_t plist_id, H5AC_cache_image_config_t *config_ptr) +{ + H5P_genplist_t *plist; /* Property list pointer */ + herr_t ret_value = SUCCEED; /* return value */ + + FUNC_ENTER_API(FAIL) + H5TRACE2("e", "i*x", plist_id, config_ptr); + + /* Get the plist structure */ + if(NULL == (plist = H5P_object_verify(plist_id,H5P_FILE_ACCESS))) + HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID") + + /* validate the config_ptr */ + if(config_ptr == NULL) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL config_ptr on entry.") + + if(config_ptr->version != H5AC__CURR_CACHE_IMAGE_CONFIG_VERSION) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Unknown image config version.") + + /* If we ever support multiple versions of H5AC_cache_config_t, we + * will have to get the cannonical version here, and then translate + * to the version of the structure supplied. + */ + + /* Get the current initial metadata cache resize configuration */ + if(H5P_get(plist, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_NAME, config_ptr) < 0) + HGOTO_ERROR(H5E_PLIST, H5E_CANTGET,FAIL, "can't get metadata cache initial image config") + +done: + FUNC_LEAVE_API(ret_value) +} /* H5Pget_mdc_image_config() */ + + +/*------------------------------------------------------------------------- * Function: H5Pset_mdc_config * * Purpose: Set the initial metadata cache resize configuration in the @@ -2754,6 +2868,147 @@ done: /*------------------------------------------------------------------------- + * Function: H5P__facc_cache_image_config_cmp + * + * Purpose: Compare two cache image configurations. + * + * Return: positive if VALUE1 is greater than VALUE2, negative if VALUE2 is + * greater than VALUE1 and zero if VALUE1 and VALUE2 are equal. + * + * Programmer: John Mainzer + * June 26, 2015 + * + *------------------------------------------------------------------------- + */ +static int +H5P__facc_cache_image_config_cmp(const void *_config1, const void *_config2, size_t H5_ATTR_UNUSED size) +{ + const H5AC_cache_image_config_t *config1 = (const H5AC_cache_image_config_t *)_config1; /* Create local aliases for values */ + const H5AC_cache_image_config_t *config2 = (const H5AC_cache_image_config_t *)_config2; /* Create local aliases for values */ + int ret_value = 0; /* Return value */ + + FUNC_ENTER_STATIC_NOERR + + /* Check for a property being set */ + if(config1 == NULL && config2 != NULL) HGOTO_DONE(-1); + if(config1 != NULL && config2 == NULL) HGOTO_DONE(1); + + if(config1->version < config2->version) HGOTO_DONE(-1); + if(config1->version > config2->version) HGOTO_DONE(1); + + if(config1->generate_image < config2->generate_image) HGOTO_DONE(-1); + if(config1->generate_image > config2->generate_image) HGOTO_DONE(1); + + if(config1->save_resize_status < config2->save_resize_status) HGOTO_DONE(-1); + if(config1->save_resize_status > config2->save_resize_status) HGOTO_DONE(1); + + if(config1->entry_ageout < config2->entry_ageout) HGOTO_DONE(-1); + if(config1->entry_ageout > config2->entry_ageout) HGOTO_DONE(1); + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5P__facc_cache_image_config_cmp() */ + + +/*------------------------------------------------------------------------- + * Function: H5P__facc_cache_image_config_enc + * + * Purpose: Callback routine which is called whenever the default + * cache image config property in the file creation + * property list is encoded. + * + * Return: Success: Non-negative + * Failure: Negative + * + * Programmer: John Mainzer + * June 26, 2015 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5P__facc_cache_image_config_enc(const void *value, void **_pp, size_t *size) +{ + const H5AC_cache_image_config_t *config = (const H5AC_cache_image_config_t *)value; /* Create local aliases for value */ + uint8_t **pp = (uint8_t **)_pp; + + FUNC_ENTER_STATIC_NOERR + + /* Sanity check */ + HDassert(value); + + if(NULL != *pp) { + /* Encode type sizes (as a safety check) */ + *(*pp)++ = (uint8_t)sizeof(unsigned); + + INT32ENCODE(*pp, (int32_t)config->version); + + H5_ENCODE_UNSIGNED(*pp, config->generate_image); + + H5_ENCODE_UNSIGNED(*pp, config->save_resize_status); + + INT32ENCODE(*pp, (int32_t)config->entry_ageout); + } /* end if */ + + /* Compute encoded size of fixed-size values */ + *size += (1 + (2 * sizeof(unsigned)) + (2 * sizeof(int32_t))); + + FUNC_LEAVE_NOAPI(SUCCEED) +} /* end H5P__facc_cache_image_config_enc() */ + + +/*------------------------------------------------------------------------- + * Function: H5P__facc_cache_image_config_dec + * + * Purpose: Callback routine which is called whenever the default + * cache image config property in the file creation property + * list is decoded. + * + * Return: Success: Non-negative + * Failure: Negative + * + * Programmer: John Mainzer + * June 26, 2015 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5P__facc_cache_image_config_dec(const void **_pp, void *_value) +{ + H5AC_cache_image_config_t *config = (H5AC_cache_image_config_t *)_value; + const uint8_t **pp = (const uint8_t **)_pp; + unsigned enc_size; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(pp); + HDassert(*pp); + HDassert(config); + HDcompile_assert(sizeof(size_t) <= sizeof(uint64_t)); + + /* Set property to default value */ + HDmemcpy(config, &H5F_def_mdc_initCacheImageCfg_g, sizeof(H5AC_cache_image_config_t)); + + /* Decode type sizes */ + enc_size = *(*pp)++; + if(enc_size != sizeof(unsigned)) + HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "unsigned value can't be decoded") + + INT32DECODE(*pp, config->version); + + H5_DECODE_UNSIGNED(*pp, config->generate_image); + + H5_DECODE_UNSIGNED(*pp, config->save_resize_status); + + INT32DECODE(*pp, config->entry_ageout); + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5P__facc_cache_image_config_dec() */ + + +/*------------------------------------------------------------------------- * Function: H5P__facc_file_image_info_set * * Purpose: Copies a file image property when it's set for a property list diff --git a/src/H5Ppublic.h b/src/H5Ppublic.h index c736d7b..5aa8301 100644 --- a/src/H5Ppublic.h +++ b/src/H5Ppublic.h @@ -365,6 +365,8 @@ H5_DLL herr_t H5Pget_all_coll_metadata_ops(hid_t plist_id, hbool_t *is_collectiv H5_DLL herr_t H5Pset_coll_metadata_write(hid_t plist_id, hbool_t is_collective); H5_DLL herr_t H5Pget_coll_metadata_write(hid_t plist_id, hbool_t *is_collective); #endif /* H5_HAVE_PARALLEL */ +H5_DLL herr_t H5Pset_mdc_image_config(hid_t plist_id, H5AC_cache_image_config_t *config_ptr); +H5_DLL herr_t H5Pget_mdc_image_config(hid_t plist_id, H5AC_cache_image_config_t *config_ptr /*out*/); /* Dataset creation property list (DCPL) routines */ H5_DLL herr_t H5Pset_layout(hid_t plist_id, H5D_layout_t layout); diff --git a/src/H5SMpkg.h b/src/H5SMpkg.h index 3b13e23..342543d 100644 --- a/src/H5SMpkg.h +++ b/src/H5SMpkg.h @@ -256,8 +256,6 @@ H5FL_ARR_EXTERN(H5SM_index_header_t); H5FL_EXTERN(H5SM_list_t); H5FL_ARR_EXTERN(H5SM_sohm_t); -H5_DLLVAR const H5AC_class_t H5AC_SOHM_TABLE[1]; -H5_DLLVAR const H5AC_class_t H5AC_SOHM_LIST[1]; H5_DLLVAR const H5B2_class_t H5SM_INDEX[1]; /****************************/ diff --git a/src/Makefile.am b/src/Makefile.am index 939e151..69b54b4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -46,7 +46,8 @@ libhdf5_la_SOURCES= H5.c H5checksum.c H5dbg.c H5system.c H5timer.c H5trace.c \ H5B.c H5Bcache.c H5Bdbg.c \ H5B2.c H5B2cache.c H5B2dbg.c H5B2hdr.c H5B2int.c H5B2internal.c \ H5B2leaf.c H5B2stat.c H5B2test.c \ - H5C.c H5Cdbg.c H5Cepoch.c H5Clog.c H5Cquery.c H5Ctag.c H5Ctest.c \ + H5C.c H5Cdbg.c H5Cepoch.c H5Cimage.c H5Clog.c H5Cprefetched.c \ + H5Cquery.c H5Ctag.c H5Ctest.c \ H5CS.c \ H5D.c H5Dbtree.c H5Dbtree2.c H5Dchunk.c H5Dcompact.c H5Dcontig.c H5Ddbg.c \ H5Ddeprec.c H5Dearray.c H5Defl.c H5Dfarray.c H5Dfill.c H5Dint.c \ @@ -81,7 +82,8 @@ libhdf5_la_SOURCES= H5.c H5checksum.c H5dbg.c H5system.c H5timer.c H5trace.c \ H5MF.c H5MFaggr.c H5MFdbg.c H5MFsection.c \ H5MM.c H5MP.c H5MPtest.c \ H5O.c H5Oainfo.c H5Oalloc.c H5Oattr.c \ - H5Oattribute.c H5Obogus.c H5Obtreek.c H5Ocache.c H5Ochunk.c \ + H5Oattribute.c H5Obogus.c H5Obtreek.c H5Ocache.c H5Ocache_image.c \ + H5Ochunk.c \ H5Ocont.c H5Ocopy.c H5Odbg.c H5Odrvinfo.c H5Odtype.c H5Oefl.c \ H5Ofill.c H5Oflush.c H5Ofsinfo.c H5Oginfo.c \ H5Olayout.c \ |