hdf5/src/H5Cimage.c
Larry Knox 89fbe00dec Merge pull request #426 in HDFFV/hdf5 from ~LRKNOX/hdf5_lrk:hdf5_1_10 to hdf5_1_10
* commit '54957d37f5aa73912763dbb6e308555e863c43f4':
  Commit copyright header change for src/H5PLpkg.c which was added after running script to make changes.
  Add new files in release_docs to MANIFEST. Cimmit changes to Makefile.in(s) and H5PL.c that resulted from running autogen.sh.
  Merge pull request #407 in HDFFV/hdf5 from ~LRKNOX/hdf5_lrk:hdf5_1_10_1 to hdf5_1_10_1
  Change copyright headers to replace url referring to file to be removed and replace it with new url for COPYING file.
2017-04-25 16:05:36 -05:00

3570 lines
133 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* 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 COPYING file, which can be found at the root of the source code *
* distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. *
* 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 H5C_RING_MDFSM
/******************/
/* 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_header(const H5F_t *f,
H5C_t *cache_ptr, const uint8_t **buf);
#ifndef NDEBUG /* only used in assertions */
static herr_t H5C__decode_cache_image_entry(const H5F_t *f,
const H5C_t *cache_ptr, const uint8_t **buf, unsigned entry_num);
#endif /* NDEBUG */ /* only used in assertions */
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(const H5F_t *f,
H5C_t *cache_ptr, const uint8_t **buf);
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, 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_pending()
*
* Purpose: Tests to see if the load of a metadata cache image
* load is pending (i.e. will be executed on the next
* protect or insert)
*
* Returns TRUE if a cache image load is pending, and FALSE
* if not. Throws an assertion failure on error.
*
* Return: TRUE if a cache image load is pending, and FALSE otherwise.
*
* Programmer: John Mainzer, 6/18/16
*
*-------------------------------------------------------------------------
*/
hbool_t
H5C_cache_image_pending(const H5C_t *cache_ptr)
{
hbool_t ret_value = TRUE; /* Return value */
FUNC_ENTER_NOAPI_NOINIT_NOERR
/* Sanity checks */
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
ret_value = (cache_ptr->load_image && !cache_ptr->image_loaded);
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_cache_image_pending() */
/*-------------------------------------------------------------------------
* 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 or 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.
*/
HDassert(pf_entry_ptr->fd_parent_count == pf_entry_ptr->flush_dep_nparents);
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;
ds_entry_ptr->prefetched_dirty = pf_entry_ptr->prefetched_dirty;
#ifndef NDEBUG /* debugging field */
ds_entry_ptr->serialization_count = 0;
#endif /* NDEBUG */
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) {
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);
#ifndef NDEBUG
{
int j;
hbool_t found;
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);
}
#endif /* NDEBUG */
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_force_cache_image_load()
*
* Purpose: On rare occasions, it is necessary to run
* H5MF_tidy_self_referential_fsm_hack() prior to the first
* metadata cache access. This is a problem as if there is a
* cache image at the end of the file, that routine will
* discard it.
*
* We solve this issue by calling this function, which will
* load the cache image and then call
* H5MF_tidy_self_referential_fsm_hack() to discard it.
*
* Return: SUCCEED on success, and FAIL on failure.
*
* Programmer: John Mainzer
* 1/11/17
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_force_cache_image_load(H5F_t *f, hid_t dxpl_id)
{
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;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(cache_ptr->load_image);
/* 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, FAIL, "can't load cache image")
} /* end if */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_force_cache_image_load() */
/*-------------------------------------------------------------------------
* 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, 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")
H5C__UPDATE_STATS_FOR_CACHE_IMAGE_READ(cache_ptr)
#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")
/* Reconstruct cache contents, from image */
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)
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, hbool_t *image_generated)
{
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);
HDassert(image_generated);
/* If the file is opened and closed without any access to
* any group or data set, 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 */
/* Before we start to generate the cache image (if requested), verify
* that the superblock supports superblock extension messages, and
* silently cancel any request for a cache image if it does not.
*
* Ideally, we would do this when the cache image is requested,
* but the necessary information is not necessary available at that
* time -- hence this last minute check.
*
* Note that under some error conditions, the superblock will be
* undefined in this case as well -- if so, assume that the
* superblock does not support superblock extension messages.
*/
if((NULL == f->shared->sblock) ||
(f->shared->sblock->super_vers < HDF5_SUPERBLOCK_VERSION_2)) {
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 */
/* Generate the cache image, if requested */
if(cache_ptr->image_ctl.generate_image) {
/* 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")
/* Make note of the eoa after allocation of the cache image
* block. This value is used for sanity checking when we
* shutdown the self referential free space managers after
* we destroy the metadata cache.
*/
HDassert(HADDR_UNDEF == f->shared->eoa_post_mdci_fsalloc);
if(HADDR_UNDEF == (f->shared->eoa_post_mdci_fsalloc = H5FD_get_eoa(f->shared->lf, H5FD_MEM_DEFAULT)) )
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to get file size")
/* 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 image */
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 */
/* Indicate that a cache image was generated */
*image_generated = TRUE;
} /* end if */
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.
*
* If the file is open read only, silently
* force the cache image configuration to its default
* (which disables construction of a cache image).
*
* Note that in addition to being inapplicable in the
* read only case, cache image is also inapplicable if
* the superblock does not support superblock extension
* messages. Unfortunately, this information need not
* be available at this point. Thus we check for this
* later, in H5C_prep_for_file_close() and cancel the
* cache image request if appropriate.
*
* 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")
/* Validate the config: */
if(H5C_validate_cache_image_config(config_ptr) < 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "invalid cache image configuration")
#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 */
else {
#endif /* H5_HAVE_PARALLEL */
/* A cache image can only be generated if the file is opened read / write
* and the superblock supports superblock extension messages.
*
* However, the superblock version is not available at this point --
* hence we can only check the former requirement now. Do the latter
* check just before we construct the image..
*
* If the file is opened read / write, apply the supplied configuration.
*
* If it is not, set the image configuration to the default, which has
* the effect of silently disabling the cache image if it was requested.
*/
if(H5F_INTENT(f) & H5F_ACC_RDWR)
cache_ptr->image_ctl = *config_ptr;
else {
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
} /* end else */
#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_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() */
#ifndef NDEBUG
/*-------------------------------------------------------------------------
* 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;
hbool_t in_lru = FALSE; /* Only used in assertions */
hbool_t is_fd_parent = FALSE; /* Only used in assertions */
hbool_t is_fd_child = FALSE; /* 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;
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;
/* 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() */
#endif /* NDEBUG */
/*-------------------------------------------------------------------------
* 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 range")
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_calloc(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")
/* Initialize (non-zero/NULL/FALSE) fields */
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].ring = H5C_RING_UNDEFINED;
image_entries[u].type_id = -1;
} /* 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 buffer, and create a prefetched
* cache entry for every entry in the buffer. 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 */
const uint8_t * p; /* Pointer into image buffer */
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);
/* 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);
/* The image_data_len and # of entries should be defined now */
HDassert(cache_ptr->image_data_len > 0);
HDassert(cache_ptr->image_data_len <= cache_ptr->image_len);
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(f, cache_ptr, &p)))
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 cache entries, and verify that each entry has
* the expected flush dependency status.
*/
pf_entry_ptr = cache_ptr->il_head;
while(pf_entry_ptr != NULL) {
HDassert(pf_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert((pf_entry_ptr->prefetched && pf_entry_ptr->type == H5AC_PREFETCHED_ENTRY)
|| (!pf_entry_ptr->prefetched && pf_entry_ptr->type != H5AC_PREFETCHED_ENTRY));
if(pf_entry_ptr->type == H5AC_PREFETCHED_ENTRY)
HDassert(pf_entry_ptr->fd_parent_count == pf_entry_ptr->flush_dep_nparents);
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 */
if(pf_entry_ptr->type == H5AC_PREFETCHED_ENTRY) {
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 if */
pf_entry_ptr = pf_entry_ptr->il_next;
} /* end while */
/* 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(entry_ptr->lru_rank != 0);
HDassert((entry_ptr->lru_rank == -1) ||
(entry_ptr->lru_rank > i));
if ( ( entry_ptr->lru_rank > 1 ) &&
( entry_ptr->lru_rank > i + 1 ) )
lru_rank_holes += entry_ptr->lru_rank - (i + 1);
i = entry_ptr->lru_rank;
} /* end if */
entry_ptr = entry_ptr->next;
} /* end while */
/* Holes in the sequences of LRU ranks can appear 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 image buffer.
*
* 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(const H5F_t *f, H5C_t *cache_ptr,
const uint8_t **buf)
{
H5C_cache_entry_t *pf_entry_ptr = NULL; /* Reconstructed cache entry */
uint8_t flags = 0;
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 */
const uint8_t * p;
hbool_t file_is_rw;
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->num_entries_in_image > 0);
HDassert(buf && *buf);
/* Key R/W access off of whether the image will be deleted */
file_is_rw = cache_ptr->delete_image;
/* 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")
/* Get pointer to buffer */
p = *buf;
/* Decode type id */
pf_entry_ptr->prefetch_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 */
/* 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 for subsequent opens.
*
* However, if the dirty entry (marked clean for purposes of the R/O
* file open) is evicted and then referred to, the cache will read
* either invalid or obsolete data from the file. Handle this by
* setting the prefetched_dirty field, and hiding such entries from
* the eviction candidate selection algorithm.
*/
pf_entry_ptr->is_dirty = (is_dirty && file_is_rw);
/* Decode ring */
pf_entry_ptr->ring = *p++;
HDassert(pf_entry_ptr->ring > (uint8_t)(H5C_RING_UNDEFINED));
HDassert(pf_entry_ptr->ring < (uint8_t)(H5C_RING_NTYPES));
/* Decode age */
pf_entry_ptr->age = *p++;
/* Decode dependency child count */
UINT16DECODE(p, pf_entry_ptr->fd_child_count);
HDassert((is_fd_parent && pf_entry_ptr->fd_child_count > 0) || (!is_fd_parent && pf_entry_ptr->fd_child_count == 0));
/* Decode dirty dependency child count */
UINT16DECODE(p, pf_entry_ptr->fd_dirty_child_count);
if(!file_is_rw)
pf_entry_ptr->fd_dirty_child_count = 0;
if(pf_entry_ptr->fd_dirty_child_count > pf_entry_ptr->fd_child_count)
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, "invalid dirty flush dependency child count")
/* Decode dependency parent count */
UINT16DECODE(p, pf_entry_ptr->fd_parent_count);
HDassert((is_fd_child && pf_entry_ptr->fd_parent_count > 0) || (!is_fd_child && pf_entry_ptr->fd_parent_count == 0));
/* Decode index in LRU */
INT32DECODE(p, pf_entry_ptr->lru_rank);
HDassert((in_lru && pf_entry_ptr->lru_rank >= 0) || (!in_lru && pf_entry_ptr->lru_rank == -1));
/* Decode entry offset */
H5F_addr_decode(f, &p, &pf_entry_ptr->addr);
if(!H5F_addr_defined(pf_entry_ptr->addr))
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, "invalid entry offset")
/* Decode entry length */
H5F_DECODE_LENGTH(f, p, pf_entry_ptr->size);
if(pf_entry_ptr->size == 0)
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, "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, NULL, "Bad entry image len")
/* If parent count greater than zero, allocate array for parent
* addresses, and decode addresses into the array.
*/
if(pf_entry_ptr->fd_parent_count > 0) {
unsigned u; /* Local index variable */
if(NULL == (pf_entry_ptr->fd_parent_addrs = (haddr_t *)H5MM_malloc((size_t)(pf_entry_ptr->fd_parent_count) * H5F_SIZEOF_ADDR(f))))
HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "memory allocation failed for fd parent addrs buffer")
for(u = 0; u < pf_entry_ptr->fd_parent_count; u++) {
H5F_addr_decode(f, &p, &(pf_entry_ptr->fd_parent_addrs[u]));
if(!H5F_addr_defined(pf_entry_ptr->fd_parent_addrs[u]))
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, "invalid flush dependency parent offset")
} /* end for */
} /* end if */
/* Allocate buffer for entry image */
if(NULL == (pf_entry_ptr->image_ptr = H5MM_malloc(pf_entry_ptr->size + H5C_IMAGE_EXTRA_SPACE)))
HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "memory allocation failed for on disk image buffer")
#if H5C_DO_MEMORY_SANITY_CHECKS
HDmemcpy(((uint8_t *)pf_entry_ptr->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(pf_entry_ptr->image_ptr, p, pf_entry_ptr->size);
p += pf_entry_ptr->size;
/* Initialize the rest of the fields in the prefetched entry */
/* (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->image_up_to_date = TRUE;
pf_entry_ptr->type = H5AC_PREFETCHED_ENTRY;
pf_entry_ptr->prefetched = TRUE;
pf_entry_ptr->prefetched_dirty = is_dirty && (!file_is_rw);
/* Sanity checks */
HDassert(pf_entry_ptr->size > 0 && pf_entry_ptr->size < H5C_MAX_ENTRY_SIZE);
/* Update buffer pointer */
*buf = p;
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__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() */