/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright by The HDF Group. * * 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://www.hdfgroup.org/licenses. * * If you do not have access to either file, you may request a copy from * * help@hdfgroup.org. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Purpose: Free space tracking functions. * * Note: (Used to be in the H5HFflist.c file, prior to the date above) */ /****************/ /* Module Setup */ /****************/ #include "H5FSmodule.h" /* This source code file is part of the H5FS module */ /***********/ /* Headers */ /***********/ #include "H5private.h" /* Generic Functions */ #include "H5ACprivate.h" /* Metadata cache */ #include "H5Eprivate.h" /* Error handling */ #include "H5FLprivate.h" /* Free Lists */ #include "H5FSpkg.h" /* File free space */ #include "H5MFprivate.h" /* File memory management */ #include "H5MMprivate.h" /* Memory management */ #include "H5SLprivate.h" /* Skip Lists */ /****************/ /* Local Macros */ /****************/ /******************/ /* Local Typedefs */ /******************/ /********************/ /* Package Typedefs */ /********************/ /********************/ /* Local Prototypes */ /********************/ /* Section info routines */ static herr_t H5FS__sinfo_free_sect_cb(void *item, void *key, void *op_data); static herr_t H5FS__sinfo_free_node_cb(void *item, void *key, void *op_data); /*********************/ /* Package Variables */ /*********************/ /* Declare a free list to manage the H5FS_section_class_t sequence information */ H5FL_SEQ_DEFINE(H5FS_section_class_t); /* Declare a free list to manage the H5FS_t struct */ H5FL_DEFINE(H5FS_t); /*****************************/ /* Library Private Variables */ /*****************************/ /*******************/ /* Local Variables */ /*******************/ /*------------------------------------------------------------------------- * Function: H5FS_create * * Purpose: Allocate & initialize file free space info * * Return: Success: Pointer to free space structure * Failure: NULL * *------------------------------------------------------------------------- */ H5FS_t * H5FS_create(H5F_t *f, haddr_t *fs_addr, const H5FS_create_t *fs_create, uint16_t nclasses, const H5FS_section_class_t *classes[], void *cls_init_udata, hsize_t alignment, hsize_t threshold) { H5FS_t *fspace = NULL; /* New free space structure */ H5FS_t *ret_value = NULL; /* Return value */ FUNC_ENTER_NOAPI(NULL) #ifdef H5FS_DEBUG fprintf(stderr, "%s: Creating free space manager, nclasses = %Zu\n", __func__, nclasses); #endif /* H5FS_DEBUG */ /* Check arguments. */ assert(fs_create->shrink_percent); assert(fs_create->shrink_percent < fs_create->expand_percent); assert(fs_create->max_sect_size); assert(nclasses == 0 || classes); /* * Allocate free space structure */ if (NULL == (fspace = H5FS__new(f, nclasses, classes, cls_init_udata))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for free space free list"); /* Initialize creation information for free space manager */ fspace->client = fs_create->client; fspace->shrink_percent = fs_create->shrink_percent; fspace->expand_percent = fs_create->expand_percent; fspace->max_sect_addr = fs_create->max_sect_addr; fspace->max_sect_size = fs_create->max_sect_size; fspace->swmr_write = (H5F_INTENT(f) & H5F_ACC_SWMR_WRITE) > 0; fspace->alignment = alignment; fspace->align_thres = threshold; /* Check if the free space tracker is supposed to be persistent */ if (fs_addr) { /* Allocate space for the free space header */ if (HADDR_UNDEF == (fspace->addr = H5MF_alloc(f, H5FD_MEM_FSPACE_HDR, (hsize_t)fspace->hdr_size))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "file allocation failed for free space header"); /* Cache the new free space header (pinned) */ if (H5AC_insert_entry(f, H5AC_FSPACE_HDR, fspace->addr, fspace, H5AC__PIN_ENTRY_FLAG) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTINIT, NULL, "can't add free space header to cache"); /* Return free space header address to caller, if desired */ *fs_addr = fspace->addr; } /* end if */ /* Set the reference count to 1, since we inserted the entry in the cache pinned */ fspace->rc = 1; /* Set the return value */ ret_value = fspace; #ifdef H5FS_DEBUG fprintf(stderr, "%s: fspace = %p, fspace->addr = %" PRIuHADDR "\n", __func__, (void *)fspace, fspace->addr); #endif /* H5FS_DEBUG */ done: if (!ret_value && fspace) if (H5FS__hdr_dest(fspace) < 0) HDONE_ERROR(H5E_FSPACE, H5E_CANTFREE, NULL, "unable to destroy free space header"); #ifdef H5FS_DEBUG fprintf(stderr, "%s: Leaving, ret_value = %p\n", __func__, (void *)ret_value); #endif /* H5FS_DEBUG */ FUNC_LEAVE_NOAPI(ret_value) } /* H5FS_create() */ /*------------------------------------------------------------------------- * Function: H5FS_open * * Purpose: Open an existing file free space info structure on disk * * Return: Success: Pointer to free space structure * Failure: NULL * *------------------------------------------------------------------------- */ H5FS_t * H5FS_open(H5F_t *f, haddr_t fs_addr, uint16_t nclasses, const H5FS_section_class_t *classes[], void *cls_init_udata, hsize_t alignment, hsize_t threshold) { H5FS_t *fspace = NULL; /* New free space structure */ H5FS_hdr_cache_ud_t cache_udata; /* User-data for metadata cache callback */ H5FS_t *ret_value = NULL; /* Return value */ FUNC_ENTER_NOAPI(NULL) #ifdef H5FS_DEBUG fprintf(stderr, "%s: Opening free space manager, fs_addr = %" PRIuHADDR ", nclasses = %Zu\n", __func__, fs_addr, nclasses); #endif /* H5FS_DEBUG */ /* Check arguments. */ assert(H5_addr_defined(fs_addr)); assert(nclasses); assert(classes); /* Initialize user data for protecting the free space manager */ cache_udata.f = f; cache_udata.nclasses = nclasses; cache_udata.classes = classes; cache_udata.cls_init_udata = cls_init_udata; cache_udata.addr = fs_addr; /* Protect the free space header */ if (NULL == (fspace = (H5FS_t *)H5AC_protect(f, H5AC_FSPACE_HDR, fs_addr, &cache_udata, H5AC__READ_ONLY_FLAG))) HGOTO_ERROR(H5E_FSPACE, H5E_CANTPROTECT, NULL, "unable to load free space header"); #ifdef H5FS_DEBUG fprintf(stderr, "%s: fspace->sect_addr = %" PRIuHADDR "\n", __func__, fspace->sect_addr); fprintf(stderr, "%s: fspace->sect_size = %" PRIuHSIZE "\n", __func__, fspace->sect_size); fprintf(stderr, "%s: fspace->alloc_sect_size = %" PRIuHSIZE "\n", __func__, fspace->alloc_sect_size); fprintf(stderr, "%s: fspace->sinfo = %p\n", __func__, (void *)fspace->sinfo); fprintf(stderr, "%s: fspace->rc = %u\n", __func__, fspace->rc); #endif /* H5FS_DEBUG */ /* Increment the reference count on the free space manager header */ assert(fspace->rc <= 1); if (H5FS__incr(fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTINC, NULL, "unable to increment ref. count on free space header"); fspace->alignment = alignment; fspace->align_thres = threshold; /* Unlock free space header */ if (H5AC_unprotect(f, H5AC_FSPACE_HDR, fs_addr, fspace, H5AC__NO_FLAGS_SET) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTUNPROTECT, NULL, "unable to release free space header"); /* Set return value */ ret_value = fspace; done: FUNC_LEAVE_NOAPI(ret_value) } /* H5FS_open() */ /*------------------------------------------------------------------------- * Function: H5FS_delete * * Purpose: Delete a free space manager on disk * * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ herr_t H5FS_delete(H5F_t *f, haddr_t fs_addr) { H5FS_t *fspace = NULL; /* Free space header loaded from file */ H5FS_hdr_cache_ud_t cache_udata; /* User-data for metadata cache callback */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) #ifdef H5FS_DEBUG fprintf(stderr, "%s: Deleting free space manager, fs_addr = %" PRIuHADDR "\n", __func__, fs_addr); #endif /* H5FS_DEBUG */ /* Check arguments. */ assert(f); assert(H5_addr_defined(fs_addr)); /* Initialize user data for protecting the free space manager */ /* (no class information necessary for delete) */ cache_udata.f = f; cache_udata.nclasses = 0; cache_udata.classes = NULL; cache_udata.cls_init_udata = NULL; cache_udata.addr = fs_addr; #ifdef H5FS_DEBUG { unsigned fspace_status = 0; /* Free space section info's status in the metadata cache */ /* Sanity check */ assert(H5_addr_defined(fs_addr)); /* Check the free space section info's status in the metadata cache */ if (H5AC_get_entry_status(f, fs_addr, &fspace_status) < 0) HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "unable to check metadata cache status for free space section info"); fprintf(stderr, "%s: fspace_status = %0x: ", __func__, fspace_status); if (fspace_status) { bool printed = false; if (fspace_status & H5AC_ES__IN_CACHE) { fprintf(stderr, "H5AC_ES__IN_CACHE"); printed = true; } /* end if */ if (fspace_status & H5AC_ES__IS_DIRTY) { fprintf(stderr, "%sH5AC_ES__IS_DIRTY", (printed ? " | " : "")); printed = true; } /* end if */ if (fspace_status & H5AC_ES__IS_PROTECTED) { fprintf(stderr, "%sH5AC_ES__IS_PROTECTED", (printed ? " | " : "")); printed = true; } /* end if */ if (fspace_status & H5AC_ES__IS_PINNED) { fprintf(stderr, "%sH5AC_ES__IS_PINNED", (printed ? " | " : "")); printed = true; } /* end if */ if (fspace_status & H5AC_ES__IS_FLUSH_DEP_PARENT) { fprintf(stderr, "%sH5AC_ES__IS_FLUSH_DEP_PARENT", (printed ? " | " : "")); printed = true; } /* end if */ if (fspace_status & H5AC_ES__IS_FLUSH_DEP_CHILD) { fprintf(stderr, "%sH5AC_ES__IS_FLUSH_DEP_CHILD", (printed ? " | " : "")); printed = true; } /* end if */ } /* end if */ fprintf(stderr, "\n"); } #endif /* H5FS_DEBUG */ /* Protect the free space header */ if (NULL == (fspace = (H5FS_t *)H5AC_protect(f, H5AC_FSPACE_HDR, fs_addr, &cache_udata, H5AC__NO_FLAGS_SET))) HGOTO_ERROR(H5E_FSPACE, H5E_CANTPROTECT, FAIL, "unable to protect free space header"); /* Sanity check */ assert(fspace->sinfo == NULL); /* Delete serialized section storage, if there are any */ #ifdef H5FS_DEBUG fprintf(stderr, "%s: fspace->sect_addr = %" PRIuHADDR "\n", __func__, fspace->sect_addr); #endif /* H5FS_DEBUG */ if (fspace->serial_sect_count > 0) { unsigned sinfo_status = 0; /* Free space section info's status in the metadata cache */ /* Sanity check */ assert(H5_addr_defined(fspace->sect_addr)); assert(fspace->alloc_sect_size > 0); /* Check the free space section info's status in the metadata cache */ if (H5AC_get_entry_status(f, fspace->sect_addr, &sinfo_status) < 0) HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "unable to check metadata cache status for free space section info"); /* If the free space section info is in the cache, expunge it now */ if (sinfo_status & H5AC_ES__IN_CACHE) { /* Sanity checks on direct block */ assert(!(sinfo_status & H5AC_ES__IS_PINNED)); assert(!(sinfo_status & H5AC_ES__IS_PROTECTED)); #ifdef H5FS_DEBUG fprintf(stderr, "%s: Expunging free space section info from cache\n", __func__); #endif /* H5FS_DEBUG */ /* Evict the free space section info from the metadata cache */ /* (Free file space) */ { unsigned cache_flags = H5AC__NO_FLAGS_SET; /* if the indirect block is in real file space, tell * the cache to free its file space. */ if (!H5F_IS_TMP_ADDR(f, fspace->sect_addr)) cache_flags |= H5AC__FREE_FILE_SPACE_FLAG; if (H5AC_expunge_entry(f, H5AC_FSPACE_SINFO, fspace->sect_addr, cache_flags) < 0) HGOTO_ERROR(H5E_HEAP, H5E_CANTREMOVE, FAIL, "unable to remove free space section info from cache"); } /* end block */ #ifdef H5FS_DEBUG fprintf(stderr, "%s: Done expunging free space section info from cache\n", __func__); #endif /* H5FS_DEBUG */ } /* end if */ else { #ifdef H5FS_DEBUG fprintf(stderr, "%s: Deleting free space section info from file\n", __func__); #endif /* H5FS_DEBUG */ /* Release the space in the file */ if (!H5F_IS_TMP_ADDR(f, fspace->sect_addr)) if (H5MF_xfree(f, H5FD_MEM_FSPACE_SINFO, fspace->sect_addr, fspace->alloc_sect_size) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTFREE, FAIL, "unable to release free space sections"); } /* end else */ } /* end if */ done: if (fspace && H5AC_unprotect(f, H5AC_FSPACE_HDR, fs_addr, fspace, H5AC__DELETED_FLAG | H5AC__FREE_FILE_SPACE_FLAG) < 0) HDONE_ERROR(H5E_FSPACE, H5E_CANTUNPROTECT, FAIL, "unable to release free space header"); FUNC_LEAVE_NOAPI(ret_value) } /* H5FS_delete() */ /*------------------------------------------------------------------------- * Function: H5FS_close * * Purpose: Destroy & deallocate free list structure, serializing sections * in the bins * * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ herr_t H5FS_close(H5F_t *f, H5FS_t *fspace) { herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) /* Check arguments. */ assert(f); assert(fspace); #ifdef H5FS_DEBUG fprintf(stderr, "%s: Entering, fspace = %p, fspace->addr = %" PRIuHADDR ", fspace->sinfo = %p\n", __func__, (void *)fspace, fspace->addr, (void *)fspace->sinfo); #endif /* H5FS_DEBUG */ /* Check if section info is valid */ /* (i.e. the header "owns" the section info and it's not in the cache) */ if (fspace->sinfo) { #ifdef H5FS_DEBUG fprintf(stderr, "%s: fspace->tot_sect_count = %" PRIuHSIZE ", fspace->serial_sect_count = %" PRIuHSIZE ", fspace->sect_addr = %" PRIuHADDR ", fspace->rc = %u\n", __func__, fspace->tot_sect_count, fspace->serial_sect_count, fspace->sect_addr, fspace->rc); fprintf(stderr, "%s: fspace->alloc_sect_size = %" PRIuHSIZE ", fspace->sect_size = %" PRIuHSIZE "\n", __func__, fspace->alloc_sect_size, fspace->sect_size); #endif /* H5FS_DEBUG */ /* If there are sections to serialize, update them */ /* (if the free space manager is persistent) */ if (fspace->serial_sect_count > 0 && H5_addr_defined(fspace->addr)) { #ifdef H5FS_DEBUG fprintf(stderr, "%s: Real sections to store in file\n", __func__); #endif /* H5FS_DEBUG */ if (fspace->sinfo->dirty) { /* Check if the section info is "floating" */ if (!H5_addr_defined(fspace->sect_addr)) { /* Sanity check */ assert(fspace->sect_size > 0); /* Allocate space for the section info in file */ if (H5F_USE_TMP_SPACE(f)) { if (HADDR_UNDEF == (fspace->sect_addr = H5MF_alloc_tmp(f, fspace->sect_size))) HGOTO_ERROR(H5E_FSPACE, H5E_NOSPACE, FAIL, "file allocation failed for free space sections"); } /* end if */ else { if (HADDR_UNDEF == (fspace->sect_addr = H5MF_alloc(f, H5FD_MEM_FSPACE_SINFO, fspace->sect_size))) HGOTO_ERROR(H5E_FSPACE, H5E_NOSPACE, FAIL, "file allocation failed for free space sections"); } /* end if */ fspace->alloc_sect_size = (size_t)fspace->sect_size; /* Mark free space header as dirty */ if (H5AC_mark_entry_dirty(fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTMARKDIRTY, FAIL, "unable to mark free space header as dirty"); } /* end if */ } /* end if */ else /* Sanity check that section info has address */ assert(H5_addr_defined(fspace->sect_addr)); /* Cache the free space section info */ if (H5AC_insert_entry(f, H5AC_FSPACE_SINFO, fspace->sect_addr, fspace->sinfo, H5AC__NO_FLAGS_SET) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTINIT, FAIL, "can't add free space sections to cache"); } /* end if */ else { #ifdef H5FS_DEBUG fprintf(stderr, "%s: NOT storing section info in file\n", __func__); #endif /* H5FS_DEBUG */ /* Check if space for the section info is allocated */ if (H5_addr_defined(fspace->sect_addr)) { /* Sanity check */ /* (section info should only be in the file if the header is */ assert(H5_addr_defined(fspace->addr)); #ifdef H5FS_DEBUG fprintf(stderr, "%s: Section info allocated though\n", __func__); #endif /* H5FS_DEBUG */ /* Check if the section info is for the free space in the file */ /* (NOTE: This is the "bootstrapping" special case for the * free space manager, to avoid freeing the space for the * section info and re-creating it as a section in the * manager. -QAK) */ if (fspace->client == H5FS_CLIENT_FILE_ID) { htri_t status; /* "can absorb" status for section into */ #ifdef H5FS_DEBUG fprintf(stderr, "%s: Section info is for file free space\n", __func__); #endif /* H5FS_DEBUG */ /* Try to shrink the file or absorb the section info into a block aggregator */ if (H5F_IS_TMP_ADDR(f, fspace->sect_addr)) { #ifdef H5FS_DEBUG fprintf(stderr, "%s: Section info in temp. address space went 'go away'\n", __func__); #endif /* H5FS_DEBUG */ /* Reset section info in header */ fspace->sect_addr = HADDR_UNDEF; fspace->alloc_sect_size = 0; /* Mark free space header as dirty */ if (H5AC_mark_entry_dirty(fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTMARKDIRTY, FAIL, "unable to mark free space header as dirty"); } /* end if */ else { if ((status = H5MF_try_shrink(f, H5FD_MEM_FSPACE_SINFO, fspace->sect_addr, fspace->alloc_sect_size)) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTMERGE, FAIL, "can't check for absorbing section info"); else if (status == false) { /* Section info can't "go away", but it's free. Allow * header to record it */ #ifdef H5FS_DEBUG fprintf(stderr, "%s: Section info can't 'go away', header will own it\n", __func__); #endif /* H5FS_DEBUG */ } /* end if */ else { #ifdef H5FS_DEBUG fprintf(stderr, "%s: Section info went 'go away'\n", __func__); #endif /* H5FS_DEBUG */ /* Reset section info in header */ fspace->sect_addr = HADDR_UNDEF; fspace->alloc_sect_size = 0; /* Mark free space header as dirty */ if (H5AC_mark_entry_dirty(fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTMARKDIRTY, FAIL, "unable to mark free space header as dirty"); } /* end else */ } /* end else */ } /* end if */ else { haddr_t old_sect_addr = fspace->sect_addr; /* Previous location of section info in file */ hsize_t old_alloc_sect_size = fspace->alloc_sect_size; /* Previous size of section info in file */ #ifdef H5FS_DEBUG fprintf(stderr, "%s: Section info is NOT for file free space\n", __func__); #endif /* H5FS_DEBUG */ /* Reset section info in header */ fspace->sect_addr = HADDR_UNDEF; fspace->alloc_sect_size = 0; /* Mark free space header as dirty */ if (H5AC_mark_entry_dirty(fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTMARKDIRTY, FAIL, "unable to mark free space header as dirty"); /* Free previous serialized sections disk space */ if (!H5F_IS_TMP_ADDR(f, old_sect_addr)) { if (H5MF_xfree(f, H5FD_MEM_FSPACE_SINFO, old_sect_addr, old_alloc_sect_size) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTFREE, FAIL, "unable to free free space sections"); } /* end if */ } /* end else */ } /* end if */ /* Destroy section info */ if (H5FS__sinfo_dest(fspace->sinfo) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTCLOSEOBJ, FAIL, "unable to destroy free space section info"); } /* end else */ /* Reset the header's pointer to the section info */ fspace->sinfo = NULL; } /* end if */ else { /* Just sanity checks... */ if (fspace->serial_sect_count > 0) /* Sanity check that section info has address */ assert(H5_addr_defined(fspace->sect_addr)); } /* end else */ /* Decrement the reference count on the free space manager header */ if (H5FS__decr(fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTDEC, FAIL, "unable to decrement ref. count on free space header"); done: #ifdef H5FS_DEBUG fprintf(stderr, "%s: Leaving, ret_value = %d, fspace->rc = %u\n", __func__, ret_value, fspace->rc); #endif /* H5FS_DEBUG */ FUNC_LEAVE_NOAPI(ret_value) } /* H5FS_close() */ /*------------------------------------------------------------------------- * Function: H5FS__new * * Purpose: Create new free space manager structure * * Return: Success: non-NULL, pointer to new free space manager struct * Failure: NULL * *------------------------------------------------------------------------- */ H5FS_t * H5FS__new(const H5F_t *f, uint16_t nclasses, const H5FS_section_class_t *classes[], void *cls_init_udata) { H5FS_t *fspace = NULL; /* Free space manager */ size_t u; /* Local index variable */ H5FS_t *ret_value = NULL; /* Return value */ FUNC_ENTER_PACKAGE /* Check arguments. */ assert(nclasses == 0 || (nclasses > 0 && classes)); /* * Allocate free space structure */ if (NULL == (fspace = H5FL_CALLOC(H5FS_t))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for free space free list"); /* Set immutable free list parameters */ H5_CHECKED_ASSIGN(fspace->nclasses, uint16_t, nclasses, size_t); if (nclasses > 0) { if (NULL == (fspace->sect_cls = H5FL_SEQ_MALLOC(H5FS_section_class_t, nclasses))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for free space section class array"); /* Initialize the section classes for this free space list */ for (u = 0; u < nclasses; u++) { /* Make certain that section class type can be used as an array index into this array */ assert(u == classes[u]->type); /* Copy the class information into the free space manager */ H5MM_memcpy(&fspace->sect_cls[u], classes[u], sizeof(H5FS_section_class_t)); /* Call the class initialization routine, if there is one */ if (fspace->sect_cls[u].init_cls) if ((fspace->sect_cls[u].init_cls)(&fspace->sect_cls[u], cls_init_udata) < 0) HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, NULL, "unable to initialize section class"); /* Determine maximum class-specific serialization size for each section */ if (fspace->sect_cls[u].serial_size > fspace->max_cls_serial_size) fspace->max_cls_serial_size = fspace->sect_cls[u].serial_size; } /* end for */ } /* end if */ /* Initialize non-zero information for new free space manager */ fspace->addr = HADDR_UNDEF; fspace->hdr_size = (size_t)H5FS_HEADER_SIZE(f); fspace->sect_addr = HADDR_UNDEF; /* Set return value */ ret_value = fspace; done: if (!ret_value) if (fspace) { /* Should probably call the class 'term' callback for all classes * that have had their 'init' callback called... -QAK */ if (fspace->sect_cls) fspace->sect_cls = (H5FS_section_class_t *)H5FL_SEQ_FREE(H5FS_section_class_t, fspace->sect_cls); fspace = H5FL_FREE(H5FS_t, fspace); } /* end if */ FUNC_LEAVE_NOAPI(ret_value) } /* H5FS__new() */ /*------------------------------------------------------------------------- * Function: H5FS_size * * Purpose: Collect meta storage info used by the free space manager * * Return: SUCCEED (Can't fail) * *------------------------------------------------------------------------- */ herr_t H5FS_size(const H5FS_t *fspace, hsize_t *meta_size) { FUNC_ENTER_NOAPI_NOINIT_NOERR /* * Check arguments. */ assert(fspace); assert(meta_size); /* Get the free space size info */ *meta_size += fspace->hdr_size + (fspace->sinfo ? fspace->sect_size : fspace->alloc_sect_size); FUNC_LEAVE_NOAPI(SUCCEED) } /* end H5FS_size() */ /*------------------------------------------------------------------------- * Function: H5FS__incr * * Purpose: Increment reference count on free space header * * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ herr_t H5FS__incr(H5FS_t *fspace) { herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_PACKAGE #ifdef H5FS_DEBUG fprintf(stderr, "%s: Entering, fpace->addr = %" PRIuHADDR ", fspace->rc = %u\n", __func__, fspace->addr, fspace->rc); #endif /* H5FS_DEBUG */ /* * Check arguments. */ assert(fspace); /* Check if we should pin the header in the cache */ if (fspace->rc == 0 && H5_addr_defined(fspace->addr)) if (H5AC_pin_protected_entry(fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTPIN, FAIL, "unable to pin free space header"); /* Increment reference count on header */ fspace->rc++; done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5FS__incr() */ /*------------------------------------------------------------------------- * Function: H5FS__decr * * Purpose: Decrement reference count on free space header * * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ herr_t H5FS__decr(H5FS_t *fspace) { herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_PACKAGE #ifdef H5FS_DEBUG fprintf(stderr, "%s: Entering, fpace->addr = %" PRIuHADDR ", fspace->rc = %u\n", __func__, fspace->addr, fspace->rc); #endif /* H5FS_DEBUG */ /* * Check arguments. */ assert(fspace); /* Decrement reference count on header */ fspace->rc--; /* Check if we should unpin the header in the cache */ if (fspace->rc == 0) { if (H5_addr_defined(fspace->addr)) { if (H5AC_unpin_entry(fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTUNPIN, FAIL, "unable to unpin free space header"); } /* end if */ else { if (H5FS__hdr_dest(fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTCLOSEOBJ, FAIL, "unable to destroy free space header"); } /* end else */ } /* end if */ done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5FS__decr() */ /*------------------------------------------------------------------------- * Function: H5FS__dirty * * Purpose: Mark free space header as dirty * * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ herr_t H5FS__dirty(H5FS_t *fspace) { herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_PACKAGE /* Sanity check */ assert(fspace); /* Check if the free space manager is persistent */ if (H5_addr_defined(fspace->addr)) /* Mark header as dirty in cache */ if (H5AC_mark_entry_dirty(fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTMARKDIRTY, FAIL, "unable to mark free space header as dirty"); done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5FS__dirty() */ /*------------------------------------------------------------------------- * Function: H5FS_alloc_hdr() * * Purpose: Allocate space for the free-space manager header * * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ herr_t H5FS_alloc_hdr(H5F_t *f, H5FS_t *fspace, haddr_t *fs_addr) { herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) /* Check arguments. */ assert(f); assert(fspace); if (!H5_addr_defined(fspace->addr)) { /* Allocate space for the free space header */ if (HADDR_UNDEF == (fspace->addr = H5MF_alloc(f, H5FD_MEM_FSPACE_HDR, (hsize_t)H5FS_HEADER_SIZE(f)))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "file allocation failed for free space header"); /* Cache the new free space header (pinned) */ if (H5AC_insert_entry(f, H5AC_FSPACE_HDR, fspace->addr, fspace, H5AC__PIN_ENTRY_FLAG) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTINIT, FAIL, "can't add free space header to cache"); } /* end if */ if (fs_addr) *fs_addr = fspace->addr; done: FUNC_LEAVE_NOAPI(ret_value) } /* H5FS_alloc_hdr() */ /*------------------------------------------------------------------------- * Function: H5FS_alloc_sect() * * Purpose: Allocate space for the free-space manager section info header * * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ herr_t H5FS_alloc_sect(H5F_t *f, H5FS_t *fspace) { herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) /* Check arguments. */ assert(f); assert(fspace); if (!H5_addr_defined(fspace->sect_addr) && fspace->sinfo && fspace->serial_sect_count > 0) { if (HADDR_UNDEF == (fspace->sect_addr = H5MF_alloc(f, H5FD_MEM_FSPACE_SINFO, fspace->sect_size))) HGOTO_ERROR(H5E_FSPACE, H5E_NOSPACE, FAIL, "file allocation failed for section info"); fspace->alloc_sect_size = fspace->sect_size; /* Mark free-space header as dirty */ if (H5FS__dirty(fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTMARKDIRTY, FAIL, "unable to mark free space header as dirty"); /* Cache the free-space section info */ if (H5AC_insert_entry(f, H5AC_FSPACE_SINFO, fspace->sect_addr, fspace->sinfo, H5AC__NO_FLAGS_SET) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTINIT, FAIL, "can't add free space sections to cache"); /* Since space has been allocated for the section info and the sinfo * has been inserted into the cache, relinquish ownership (i.e. float) * the section info. */ fspace->sinfo = NULL; } /* end if */ done: FUNC_LEAVE_NOAPI(ret_value) } /* H5FS_alloc_sect() */ /*------------------------------------------------------------------------- * Function: H5FS_free() * * Purpose: Free space for free-space manager header and section info header * * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ herr_t H5FS_free(H5F_t *f, H5FS_t *fspace, bool free_file_space) { haddr_t saved_addr; /* Previous address of item */ unsigned cache_flags; /* Flags for unprotecting cache entries */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) /* Check arguments. */ assert(f); assert(fspace); cache_flags = H5AC__DELETED_FLAG | H5AC__TAKE_OWNERSHIP_FLAG; /* Free space for section info */ if (H5_addr_defined(fspace->sect_addr)) { hsize_t saved_size; /* Size of previous section info */ unsigned sinfo_status = 0; /* Section info cache status */ /* Check whether free-space manager section info is in cache or not */ if (H5AC_get_entry_status(f, fspace->sect_addr, &sinfo_status) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTGET, FAIL, "unable to check metadata cache status for free-space section info"); /* Load free-space manager section info */ if (sinfo_status & H5AC_ES__IN_CACHE || !fspace->sinfo) { H5FS_sinfo_cache_ud_t cache_udata; /* User-data for cache callback */ /* Protect the free space sections */ cache_udata.f = f; cache_udata.fspace = fspace; if (NULL == (fspace->sinfo = (H5FS_sinfo_t *)H5AC_protect(f, H5AC_FSPACE_SINFO, fspace->sect_addr, &cache_udata, H5AC__READ_ONLY_FLAG))) HGOTO_ERROR(H5E_FSPACE, H5E_CANTPROTECT, FAIL, "unable to protect free space section info"); /* Unload and release ownership of the free-space manager section info */ if (H5AC_unprotect(f, H5AC_FSPACE_SINFO, fspace->sect_addr, fspace->sinfo, cache_flags) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTUNPROTECT, FAIL, "unable to release free space section info"); } /* end if */ saved_addr = fspace->sect_addr; saved_size = fspace->alloc_sect_size; fspace->sect_addr = HADDR_UNDEF; fspace->alloc_sect_size = 0; /* Free space for the free-space manager section info */ if (!H5F_IS_TMP_ADDR(f, saved_addr)) { if (free_file_space && H5MF_xfree(f, H5FD_MEM_FSPACE_SINFO, saved_addr, saved_size) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTFREE, FAIL, "unable to release free space sections"); } /* end if */ /* Mark free-space manager header as dirty */ if (H5FS__dirty(fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTMARKDIRTY, FAIL, "unable to mark free space header as dirty"); } /* end if */ /* Free space for header */ if (H5_addr_defined(fspace->addr)) { unsigned hdr_status = 0; /* Header entry status */ /* Check whether free-space manager header is in cache or not */ if (H5AC_get_entry_status(f, fspace->addr, &hdr_status) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTGET, FAIL, "unable to check metadata cache status for free-space section info"); if (hdr_status & H5AC_ES__IN_CACHE) { H5FS_hdr_cache_ud_t cache_udata; /* User-data for metadata cache callback */ /* Protect the free-space manager header */ /* (no class information necessary since it's in the cache) */ cache_udata.f = f; cache_udata.nclasses = 0; cache_udata.classes = NULL; cache_udata.cls_init_udata = NULL; if (NULL == (fspace = (H5FS_t *)H5AC_protect(f, H5AC_FSPACE_HDR, fspace->addr, &cache_udata, H5AC__READ_ONLY_FLAG))) HGOTO_ERROR(H5E_FSPACE, H5E_CANTPROTECT, FAIL, "unable to protect free space section info"); /* Unpin the free-space manager header */ if (H5AC_unpin_entry(fspace) < 0) HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPIN, FAIL, "unable to unpin fractal heap header"); /* Unload and release ownership of the free-space header */ if (H5AC_unprotect(f, H5AC_FSPACE_HDR, fspace->addr, fspace, cache_flags) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTUNPROTECT, FAIL, "unable to release free space section info"); } /* end if */ saved_addr = fspace->addr; fspace->addr = HADDR_UNDEF; /* Free space for the free-space manager header */ if (free_file_space && H5MF_xfree(f, H5FD_MEM_FSPACE_HDR, saved_addr, (hsize_t)H5FS_HEADER_SIZE(f)) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTFREE, FAIL, "unable to free free space header"); } /* end if */ done: FUNC_LEAVE_NOAPI(ret_value) } /* H5FS_free() */ /*------------------------------------------------------------------------- * Function: H5FS__hdr_dest * * Purpose: Destroys a free space header in memory. * * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ herr_t H5FS__hdr_dest(H5FS_t *fspace) { unsigned u; /* Local index variable */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_PACKAGE /* * Check arguments. */ assert(fspace); /* Terminate the section classes for this free space list */ for (u = 0; u < fspace->nclasses; u++) { /* Call the class termination routine, if there is one */ if (fspace->sect_cls[u].term_cls) if ((fspace->sect_cls[u].term_cls)(&fspace->sect_cls[u]) < 0) HGOTO_ERROR(H5E_RESOURCE, H5E_CANTRELEASE, FAIL, "unable to finalize section class"); } /* end for */ /* Release the memory for the free space section classes */ if (fspace->sect_cls) fspace->sect_cls = (H5FS_section_class_t *)H5FL_SEQ_FREE(H5FS_section_class_t, fspace->sect_cls); /* Free free space info */ fspace = H5FL_FREE(H5FS_t, fspace); done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5FS__hdr_dest() */ /*------------------------------------------------------------------------- * Function: H5FS__sinfo_free_sect_cb * * Purpose: Free a size-tracking node for a bin * * Return: SUCCEED (Can't fail) * *------------------------------------------------------------------------- */ static herr_t H5FS__sinfo_free_sect_cb(void *_sect, void H5_ATTR_UNUSED *key, void *op_data) { H5FS_section_info_t *sect = (H5FS_section_info_t *)_sect; /* Section to free */ const H5FS_sinfo_t *sinfo = (const H5FS_sinfo_t *)op_data; /* Free space manager for section */ FUNC_ENTER_PACKAGE_NOERR assert(sect); assert(sinfo); /* Call the section's class 'free' method on the section */ (*sinfo->fspace->sect_cls[sect->type].free)(sect); FUNC_LEAVE_NOAPI(SUCCEED) } /* H5FS__sinfo_free_sect_cb() */ /*------------------------------------------------------------------------- * Function: H5FS__sinfo_free_node_cb * * Purpose: Free a size-tracking node for a bin * * Return: SUCCEED (Can't fail) * *------------------------------------------------------------------------- */ static herr_t H5FS__sinfo_free_node_cb(void *item, void H5_ATTR_UNUSED *key, void *op_data) { H5FS_node_t *fspace_node = (H5FS_node_t *)item; /* Temporary pointer to free space list node */ FUNC_ENTER_PACKAGE_NOERR assert(fspace_node); assert(op_data); /* Release the skip list for sections of this size */ H5SL_destroy(fspace_node->sect_list, H5FS__sinfo_free_sect_cb, op_data); /* Release free space list node */ fspace_node = H5FL_FREE(H5FS_node_t, fspace_node); FUNC_LEAVE_NOAPI(SUCCEED) } /* H5FS__sinfo_free_node_cb() */ /*------------------------------------------------------------------------- * Function: H5FS__sinfo_dest * * Purpose: Destroys a free space section info in memory. * * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ herr_t H5FS__sinfo_dest(H5FS_sinfo_t *sinfo) { unsigned u; /* Local index variable */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_PACKAGE /* * Check arguments. */ assert(sinfo); assert(sinfo->fspace); assert(sinfo->bins); /* Clear out lists of nodes */ for (u = 0; u < sinfo->nbins; u++) if (sinfo->bins[u].bin_list) { H5SL_destroy(sinfo->bins[u].bin_list, H5FS__sinfo_free_node_cb, sinfo); sinfo->bins[u].bin_list = NULL; } /* end if */ /* Release bins for skip lists */ sinfo->bins = H5FL_SEQ_FREE(H5FS_bin_t, sinfo->bins); /* Release skip list for merging sections */ if (sinfo->merge_list) if (H5SL_close(sinfo->merge_list) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTCLOSEOBJ, FAIL, "can't destroy section merging skip list"); /* Decrement the reference count on free space header */ /* (make certain this is last action with section info, to allow for header * disappearing immediately) */ sinfo->fspace->sinfo = NULL; if (H5FS__decr(sinfo->fspace) < 0) HGOTO_ERROR(H5E_FSPACE, H5E_CANTDEC, FAIL, "unable to decrement ref. count on free space header"); sinfo->fspace = NULL; /* Release free space section info */ sinfo = H5FL_FREE(H5FS_sinfo_t, sinfo); done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5FS__sinfo_dest() */ herr_t H5FS_get_sect_count(const H5FS_t *frsp, hsize_t *tot_sect_count) { herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI_NOINIT_NOERR /* Check arguments. */ assert(frsp); assert(tot_sect_count); /* Report statistics for free space */ *tot_sect_count = frsp->serial_sect_count; FUNC_LEAVE_NOAPI(ret_value) } #ifdef H5FS_DEBUG_ASSERT /*------------------------------------------------------------------------- * Function: H5FS__assert * * Purpose: Verify that the free space manager is mostly sane * * Return: void * *------------------------------------------------------------------------- */ void H5FS__assert(const H5FS_t *fspace) { FUNC_ENTER_PACKAGE_NOERR /* Checks for section info, if it's available */ if (fspace->sinfo) { /* Sanity check sections */ H5FS__sect_assert(fspace); /* General assumptions about the section size counts */ assert(fspace->sinfo->tot_size_count >= fspace->sinfo->serial_size_count); assert(fspace->sinfo->tot_size_count >= fspace->sinfo->ghost_size_count); } /* end if */ /* General assumptions about the section counts */ assert(fspace->tot_sect_count >= fspace->serial_sect_count); assert(fspace->tot_sect_count >= fspace->ghost_sect_count); assert(fspace->tot_sect_count == (fspace->serial_sect_count + fspace->ghost_sect_count)); FUNC_LEAVE_NOAPI_VOID } /* end H5FS__assert() */ #endif /* H5FS_DEBUG_ASSERT */