mirror of
https://github.com/HDFGroup/hdf5.git
synced 2026-09-25 04:09:44 +03:00
3433 lines
139 KiB
C
3433 lines
139 KiB
C
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* 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: An initial implementation of a subfiling VFD which is
|
|
* derived from other "stacked" VFDs such as the splitter,
|
|
* mirror, and family VFDs.
|
|
*/
|
|
|
|
#include "H5FDdrvr_module.h" /* This source code file is part of the H5FD driver module */
|
|
|
|
#include "H5private.h" /* Generic Functions */
|
|
#include "H5CXprivate.h" /* API contexts, etc. */
|
|
#include "H5Dprivate.h" /* Dataset stuff */
|
|
#include "H5Eprivate.h" /* Error handling */
|
|
#include "H5FDprivate.h" /* File drivers */
|
|
#include "H5FDsubfiling.h" /* Subfiling file driver */
|
|
#include "H5FDsubfiling_priv.h" /* Subfiling file driver */
|
|
#include "H5FDsec2.h" /* Sec2 VFD */
|
|
#include "H5FLprivate.h" /* Free Lists */
|
|
#include "H5Fprivate.h" /* File access */
|
|
#include "H5Iprivate.h" /* IDs */
|
|
#include "H5MMprivate.h" /* Memory management */
|
|
#include "H5Pprivate.h" /* Property lists */
|
|
|
|
/* The driver identification number, initialized at runtime */
|
|
static hid_t H5FD_SUBFILING_g = H5I_INVALID_HID;
|
|
|
|
/* Whether the driver initialized MPI on its own */
|
|
static bool H5FD_mpi_self_initialized = false;
|
|
|
|
/* The description of a file belonging to this driver. The 'eoa' and 'eof'
|
|
* determine the amount of hdf5 address space in use and the high-water mark
|
|
* of the file (the current size of the underlying filesystem file). The
|
|
* 'pos' value is used to eliminate file position updates when they would be a
|
|
* no-op. Unfortunately we've found systems that use separate file position
|
|
* indicators for reading and writing so the lseek can only be eliminated if
|
|
* the current operation is the same as the previous operation. When opening
|
|
* a file the 'eof' will be set to the current file size, `eoa' will be set
|
|
* to zero, 'pos' will be set to H5F_ADDR_UNDEF (as it is when an error
|
|
* occurs), and 'op' will be set to H5F_OP_UNKNOWN.
|
|
*/
|
|
/***************************************************************************
|
|
*
|
|
* Structure: H5FD_subfiling_t
|
|
*
|
|
* Purpose:
|
|
*
|
|
* H5FD_subfiling_t is a structure used to store all information needed
|
|
* to setup, manage, and take down subfiling for a HDF5 file.
|
|
*
|
|
* This structure is created when such a file is "opened" and
|
|
* discarded when it is "closed".
|
|
*
|
|
* Presents a system of subfiles as a single file to the HDF5 library.
|
|
*
|
|
*
|
|
* `pub` (H5FD_t)
|
|
*
|
|
* Instance of H5FD_t which contains all fields common to all VFDs.
|
|
* It must be the first item in this structure, since at higher levels,
|
|
* this structure will be treated as an instance of H5FD_t.
|
|
*
|
|
* `fa` (H5FD_subfiling_config_t)
|
|
*
|
|
* Instance of `H5FD_subfiling_config_t` containing the subfiling
|
|
* configuration data needed to "open" the HDF5 file.
|
|
*
|
|
*
|
|
* Document additional subfiling fields here.
|
|
*
|
|
* Recall that the existing fields are inherited from the sec2 driver
|
|
* and should be kept or not as appropriate for the sub-filing VFD.
|
|
*
|
|
*
|
|
***************************************************************************/
|
|
|
|
typedef struct H5FD_subfiling_t {
|
|
H5FD_t pub; /* public stuff, must be first */
|
|
H5FD_subfiling_config_t fa; /* driver-specific file access properties */
|
|
|
|
/* MPI Info */
|
|
MPI_Comm comm;
|
|
MPI_Comm ext_comm;
|
|
MPI_Info info;
|
|
int mpi_rank;
|
|
int mpi_size;
|
|
|
|
H5FD_t *sf_file;
|
|
H5FD_t *stub_file;
|
|
|
|
uint64_t file_id;
|
|
int64_t context_id; /* The value used to lookup a subfiling context for the file */
|
|
|
|
bool fail_to_encode; /* Used to check for failures from sb_get_size routine */
|
|
|
|
char *file_dir; /* Directory where we find files */
|
|
char *file_path; /* The user defined filename */
|
|
|
|
/*
|
|
* The element layouts above this point are identical with the
|
|
* H5FD_ioc_t structure. As a result,
|
|
*
|
|
* Everything which follows is unique to the H5FD_subfiling_t
|
|
*/
|
|
haddr_t eoa; /* end of allocated region */
|
|
haddr_t eof; /* end of file; current file size */
|
|
haddr_t last_eoa; /* Last known end-of-address marker */
|
|
haddr_t local_eof; /* Local end-of-file address for each process */
|
|
haddr_t pos; /* current file I/O position */
|
|
H5FD_file_op_t op; /* last operation */
|
|
char filename[H5FD_MAX_FILENAME_LEN]; /* Copy of file name from open operation */
|
|
} H5FD_subfiling_t;
|
|
|
|
/*
|
|
* These macros check for overflow of various quantities. These macros
|
|
* assume that HDoff_t is signed and haddr_t and size_t are unsigned.
|
|
*
|
|
* ADDR_OVERFLOW: Checks whether a file address of type `haddr_t'
|
|
* is too large to be represented by the second argument
|
|
* of the file seek function.
|
|
*
|
|
* SIZE_OVERFLOW: Checks whether a buffer size of type `hsize_t' is too
|
|
* large to be represented by the `size_t' type.
|
|
*
|
|
* REGION_OVERFLOW: Checks whether an address and size pair describe data
|
|
* which can be addressed entirely by the second
|
|
* argument of the file seek function.
|
|
*/
|
|
#define MAXADDR (((haddr_t)1 << (8 * sizeof(HDoff_t) - 1)) - 1)
|
|
#define ADDR_OVERFLOW(A) (HADDR_UNDEF == (A) || ((A) & ~(haddr_t)MAXADDR))
|
|
#define SIZE_OVERFLOW(Z) ((Z) & ~(hsize_t)MAXADDR)
|
|
#define REGION_OVERFLOW(A, Z) \
|
|
(ADDR_OVERFLOW(A) || SIZE_OVERFLOW(Z) || HADDR_UNDEF == (A) + (Z) || (HDoff_t)((A) + (Z)) < (HDoff_t)(A))
|
|
|
|
/*
|
|
* NOTE: Must be kept in sync with the private
|
|
* H5F_MAX_DRVINFOBLOCK_SIZE macro value for now
|
|
*/
|
|
#define H5FD_SUBFILING_MAX_DRV_INFO_SIZE 1024
|
|
|
|
/* Prototypes */
|
|
static herr_t H5FD__subfiling_term(void);
|
|
static hsize_t H5FD__subfiling_sb_size(H5FD_t *_file);
|
|
static herr_t H5FD__subfiling_sb_encode(H5FD_t *_file, char *name, unsigned char *buf);
|
|
static herr_t H5FD__subfiling_sb_decode(H5FD_t *_file, const char *name, const unsigned char *buf);
|
|
static void *H5FD__subfiling_fapl_get(H5FD_t *_file);
|
|
static void *H5FD__subfiling_fapl_copy(const void *_old_fa);
|
|
static herr_t H5FD__subfiling_fapl_free(void *_fa);
|
|
static H5FD_t *H5FD__subfiling_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr);
|
|
static herr_t H5FD__subfiling_close(H5FD_t *_file);
|
|
static int H5FD__subfiling_cmp(const H5FD_t *_f1, const H5FD_t *_f2);
|
|
static herr_t H5FD__subfiling_query(const H5FD_t *_f1, unsigned long *flags);
|
|
static haddr_t H5FD__subfiling_get_eoa(const H5FD_t *_file, H5FD_mem_t type);
|
|
static herr_t H5FD__subfiling_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr);
|
|
static haddr_t H5FD__subfiling_get_eof(const H5FD_t *_file, H5FD_mem_t type);
|
|
static herr_t H5FD__subfiling_get_handle(H5FD_t *_file, hid_t fapl, void **file_handle);
|
|
static herr_t H5FD__subfiling_read(H5FD_t *_file, H5FD_mem_t type, hid_t fapl_id, haddr_t addr, size_t size,
|
|
void *buf);
|
|
static herr_t H5FD__subfiling_write(H5FD_t *_file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t size,
|
|
const void *buf);
|
|
static herr_t H5FD__subfiling_read_vector(H5FD_t *file, hid_t dxpl_id, uint32_t count, H5FD_mem_t types[],
|
|
haddr_t addrs[], size_t sizes[], void *bufs[] /* out */);
|
|
static herr_t H5FD__subfiling_write_vector(H5FD_t *file, hid_t dxpl_id, uint32_t count, H5FD_mem_t types[],
|
|
haddr_t addrs[], size_t sizes[], const void *bufs[] /* in */);
|
|
static herr_t H5FD__subfiling_truncate(H5FD_t *_file, hid_t dxpl_id, bool closing);
|
|
#if 0
|
|
static herr_t H5FD__subfiling_lock(H5FD_t *_file, bool rw);
|
|
static herr_t H5FD__subfiling_unlock(H5FD_t *_file);
|
|
#endif
|
|
static herr_t H5FD__subfiling_del(const char *name, hid_t fapl);
|
|
static herr_t H5FD__subfiling_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void *input,
|
|
void **output);
|
|
|
|
static herr_t H5FD__subfiling_get_default_config(hid_t fapl_id, H5FD_subfiling_config_t *config_out);
|
|
static herr_t H5FD__subfiling_validate_config(const H5FD_subfiling_config_t *fa);
|
|
static int H5FD__copy_plist(hid_t fapl_id, hid_t *id_out_ptr);
|
|
|
|
static herr_t H5FD__subfiling_close_int(H5FD_subfiling_t *file_ptr);
|
|
|
|
static herr_t init_indep_io(subfiling_context_t *sf_context, int64_t file_offset, size_t io_nelemts,
|
|
size_t dtype_extent, size_t max_iovec_len, int64_t *mem_buf_offset,
|
|
int64_t *target_file_offset, int64_t *io_block_len, int *first_subfile_index,
|
|
int *n_subfiles_used, int64_t *max_io_req_per_subfile);
|
|
static herr_t iovec_fill_first(subfiling_context_t *sf_context, int64_t iovec_depth, int64_t target_datasize,
|
|
int64_t start_mem_offset, int64_t start_file_offset, int64_t first_io_len,
|
|
int64_t *mem_offset_out, int64_t *target_file_offset_out,
|
|
int64_t *io_block_len_out);
|
|
static herr_t iovec_fill_last(subfiling_context_t *sf_context, int64_t iovec_depth, int64_t target_datasize,
|
|
int64_t start_mem_offset, int64_t start_file_offset, int64_t last_io_len,
|
|
int64_t *mem_offset_out, int64_t *target_file_offset_out,
|
|
int64_t *io_block_len_out);
|
|
static herr_t iovec_fill_first_last(subfiling_context_t *sf_context, int64_t iovec_depth,
|
|
int64_t target_datasize, int64_t start_mem_offset,
|
|
int64_t start_file_offset, int64_t first_io_len, int64_t last_io_len,
|
|
int64_t *mem_offset_out, int64_t *target_file_offset_out,
|
|
int64_t *io_block_len_out);
|
|
static herr_t iovec_fill_uniform(subfiling_context_t *sf_context, int64_t iovec_depth,
|
|
int64_t target_datasize, int64_t start_mem_offset, int64_t start_file_offset,
|
|
int64_t *mem_offset_out, int64_t *target_file_offset_out,
|
|
int64_t *io_block_len_out);
|
|
|
|
void H5FD__subfiling_mpi_finalize(void);
|
|
|
|
static const H5FD_class_t H5FD_subfiling_g = {
|
|
H5FD_CLASS_VERSION, /* VFD interface version */
|
|
H5_VFD_SUBFILING, /* value */
|
|
H5FD_SUBFILING_NAME, /* name */
|
|
MAXADDR, /* maxaddr */
|
|
H5F_CLOSE_WEAK, /* fc_degree */
|
|
H5FD__subfiling_term, /* terminate */
|
|
H5FD__subfiling_sb_size, /* sb_size */
|
|
H5FD__subfiling_sb_encode, /* sb_encode */
|
|
H5FD__subfiling_sb_decode, /* sb_decode */
|
|
sizeof(H5FD_subfiling_config_t), /* fapl_size */
|
|
H5FD__subfiling_fapl_get, /* fapl_get */
|
|
H5FD__subfiling_fapl_copy, /* fapl_copy */
|
|
H5FD__subfiling_fapl_free, /* fapl_free */
|
|
0, /* dxpl_size */
|
|
NULL, /* dxpl_copy */
|
|
NULL, /* dxpl_free */
|
|
H5FD__subfiling_open, /* open */
|
|
H5FD__subfiling_close, /* close */
|
|
H5FD__subfiling_cmp, /* cmp */
|
|
H5FD__subfiling_query, /* query */
|
|
NULL, /* get_type_map */
|
|
NULL, /* alloc */
|
|
NULL, /* free */
|
|
H5FD__subfiling_get_eoa, /* get_eoa */
|
|
H5FD__subfiling_set_eoa, /* set_eoa */
|
|
H5FD__subfiling_get_eof, /* get_eof */
|
|
H5FD__subfiling_get_handle, /* get_handle */
|
|
H5FD__subfiling_read, /* read */
|
|
H5FD__subfiling_write, /* write */
|
|
H5FD__subfiling_read_vector, /* read_vector */
|
|
H5FD__subfiling_write_vector, /* write_vector */
|
|
NULL, /* read_selection */
|
|
NULL, /* write_selection */
|
|
NULL, /* flush */
|
|
H5FD__subfiling_truncate, /* truncate */
|
|
NULL /* H5FD__subfiling_lock */, /* lock */
|
|
NULL /* H5FD__subfiling_unlock */, /* unlock */
|
|
H5FD__subfiling_del, /* del */
|
|
H5FD__subfiling_ctl, /* ctl */
|
|
H5FD_FLMAP_DICHOTOMY /* fl_map */
|
|
};
|
|
|
|
/* Declare a free list to manage the H5FD_subfiling_t struct */
|
|
H5FL_DEFINE_STATIC(H5FD_subfiling_t);
|
|
|
|
/*
|
|
* If this VFD initialized MPI, this routine will be registered
|
|
* as an atexit handler in order to finalize MPI before the
|
|
* application exits.
|
|
*/
|
|
void
|
|
H5FD__subfiling_mpi_finalize(void)
|
|
{
|
|
H5close();
|
|
MPI_Finalize();
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD_subfiling_init
|
|
*
|
|
* Purpose: Initialize this driver by registering the driver with the
|
|
* library.
|
|
*
|
|
* Return: Success: The driver ID for the subfiling driver
|
|
* Failure: H5I_INVALID_HID
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
hid_t
|
|
H5FD_subfiling_init(void)
|
|
{
|
|
hid_t ret_value = H5I_INVALID_HID; /* Return value */
|
|
|
|
/* Register the Subfiling VFD, if it isn't already registered */
|
|
if (H5I_VFL != H5I_get_type(H5FD_SUBFILING_g)) {
|
|
int mpi_initialized = 0;
|
|
int provided = 0;
|
|
int mpi_code;
|
|
|
|
if ((H5FD_SUBFILING_g = H5FD_register(&H5FD_subfiling_g, sizeof(H5FD_class_t), false)) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ID, H5E_CANTREGISTER, H5I_INVALID_HID,
|
|
"can't register subfiling VFD");
|
|
|
|
/* Initialize error reporting */
|
|
if ((H5subfiling_err_stack_g = H5Ecreate_stack()) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTINIT, H5I_INVALID_HID, "can't create HDF5 error stack");
|
|
if ((H5subfiling_err_class_g = H5Eregister_class(H5SUBFILING_ERR_CLS_NAME, H5SUBFILING_ERR_LIB_NAME,
|
|
H5SUBFILING_ERR_VER)) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTINIT, H5I_INVALID_HID,
|
|
"can't register error class with HDF5 error API");
|
|
|
|
/* Initialize MPI if not already initialized */
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Initialized(&mpi_initialized)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(H5I_INVALID_HID, "MPI_Initialized failed", mpi_code);
|
|
if (mpi_initialized) {
|
|
/* If MPI is initialized, validate that it was initialized with MPI_THREAD_MULTIPLE */
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Query_thread(&provided)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(H5I_INVALID_HID, "MPI_Query_thread failed", mpi_code);
|
|
if (provided != MPI_THREAD_MULTIPLE)
|
|
H5_SUBFILING_GOTO_ERROR(
|
|
H5E_VFL, H5E_CANTINIT, H5I_INVALID_HID,
|
|
"Subfiling VFD requires the use of MPI_Init_thread with MPI_THREAD_MULTIPLE");
|
|
}
|
|
else {
|
|
int required = MPI_THREAD_MULTIPLE;
|
|
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Init_thread(NULL, NULL, required, &provided)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(H5I_INVALID_HID, "MPI_Init_thread failed", mpi_code);
|
|
|
|
H5FD_mpi_self_initialized = true;
|
|
|
|
if (provided != required)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTINIT, H5I_INVALID_HID,
|
|
"MPI doesn't support MPI_Init_thread with MPI_THREAD_MULTIPLE");
|
|
|
|
if (atexit(H5FD__subfiling_mpi_finalize) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTINIT, H5I_INVALID_HID,
|
|
"can't register atexit handler for MPI_Finalize");
|
|
}
|
|
|
|
/*
|
|
* Create the MPI Datatype that will be used
|
|
* for sending/receiving RPC messages
|
|
*/
|
|
HDcompile_assert(sizeof(((sf_work_request_t *)NULL)->header) == 3 * sizeof(int64_t));
|
|
if (H5_subfiling_rpc_msg_type == MPI_DATATYPE_NULL) {
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Type_contiguous(3, MPI_INT64_T, &H5_subfiling_rpc_msg_type)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(H5I_INVALID_HID, "MPI_Type_contiguous failed", mpi_code);
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Type_commit(&H5_subfiling_rpc_msg_type)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(H5I_INVALID_HID, "MPI_Type_commit failed", mpi_code);
|
|
}
|
|
}
|
|
|
|
/* Set return value */
|
|
ret_value = H5FD_SUBFILING_g;
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD_subfiling_init() */
|
|
|
|
/*---------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_term
|
|
*
|
|
* Purpose: Shut down the VFD
|
|
*
|
|
* Returns: SUCCEED (Can't fail)
|
|
*
|
|
*---------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_term(void)
|
|
{
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
if (H5FD_SUBFILING_g >= 0) {
|
|
int mpi_finalized;
|
|
int mpi_code;
|
|
|
|
/*
|
|
* Retrieve status of whether MPI has already been terminated.
|
|
* This can happen if an HDF5 ID is left unclosed and HDF5
|
|
* shuts down after MPI_Finalize() is called in an application.
|
|
*/
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Finalized(&mpi_finalized)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(FAIL, "MPI_Finalized failed", mpi_code);
|
|
|
|
/* Free RPC message MPI Datatype */
|
|
if (H5_subfiling_rpc_msg_type != MPI_DATATYPE_NULL) {
|
|
if (!mpi_finalized) {
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Type_free(&H5_subfiling_rpc_msg_type)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(FAIL, "MPI_Type_free failed", mpi_code);
|
|
}
|
|
#ifdef H5FD_SUBFILING_DEBUG
|
|
else
|
|
printf("** WARNING **: HDF5 is terminating the Subfiling VFD after MPI_Finalize() was "
|
|
"called - an HDF5 ID was probably left unclosed\n");
|
|
#endif
|
|
}
|
|
|
|
/* Clean up resources */
|
|
if (H5_subfiling_terminate() < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTFREE, FAIL,
|
|
"can't cleanup internal subfiling resources");
|
|
|
|
/* Unregister from HDF5 error API */
|
|
if (H5subfiling_err_class_g >= 0) {
|
|
if (H5Eunregister_class(H5subfiling_err_class_g) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CLOSEERROR, FAIL,
|
|
"can't unregister error class from HDF5 error API");
|
|
}
|
|
if (H5subfiling_err_stack_g >= 0) {
|
|
/* Print the current error stack before destroying it */
|
|
PRINT_ERROR_STACK;
|
|
|
|
/* Destroy the error stack */
|
|
if (H5Eclose_stack(H5subfiling_err_stack_g) < 0) {
|
|
H5_SUBFILING_DONE_ERROR(H5E_VFL, H5E_CLOSEERROR, FAIL, "can't close HDF5 error stack");
|
|
PRINT_ERROR_STACK;
|
|
} /* end if */
|
|
|
|
H5subfiling_err_stack_g = H5I_INVALID_HID;
|
|
H5subfiling_err_class_g = H5I_INVALID_HID;
|
|
}
|
|
}
|
|
|
|
done:
|
|
/* Reset VFL ID */
|
|
H5FD_SUBFILING_g = H5I_INVALID_HID;
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_term() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5Pset_fapl_subfiling
|
|
*
|
|
* Purpose: Modify the file access property list to use the
|
|
* H5FD_SUBFILING driver defined in this source file. All
|
|
* driver specific properties are passed in as a pointer to
|
|
* a suitably initialized instance of H5FD_subfiling_config_t.
|
|
* If NULL is passed for the H5FD_subfiling_config_t
|
|
* structure, a default structure will be used instead.
|
|
*
|
|
* Return: SUCCEED/FAIL
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
herr_t
|
|
H5Pset_fapl_subfiling(hid_t fapl_id, const H5FD_subfiling_config_t *vfd_config)
|
|
{
|
|
H5FD_subfiling_config_t *subfiling_conf = NULL;
|
|
H5P_genplist_t *plist = NULL;
|
|
H5P_genplist_t *ioc_plist = NULL;
|
|
MPI_Comm comm = MPI_COMM_NULL;
|
|
MPI_Info info = MPI_INFO_NULL;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
/*NO TRACE*/
|
|
|
|
/* Ensure Subfiling (and therefore MPI) is initialized before doing anything */
|
|
if (H5FD_subfiling_init() < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "can't initialize subfiling VFD");
|
|
|
|
if (NULL == (plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file access property list");
|
|
|
|
if (vfd_config == NULL) {
|
|
if (NULL == (subfiling_conf = calloc(1, sizeof(*subfiling_conf))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfiling VFD configuration");
|
|
subfiling_conf->ioc_fapl_id = H5I_INVALID_HID;
|
|
|
|
/* Get subfiling VFD defaults */
|
|
if (H5FD__subfiling_get_default_config(fapl_id, subfiling_conf) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL,
|
|
"can't get default subfiling VFD configuration");
|
|
|
|
vfd_config = subfiling_conf;
|
|
}
|
|
|
|
/* Check if any MPI parameters were set on the FAPL */
|
|
if (H5P_get(plist, H5F_ACS_MPI_PARAMS_COMM_NAME, &comm) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get MPI communicator from plist");
|
|
if (H5P_get(plist, H5F_ACS_MPI_PARAMS_INFO_NAME, &info) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get MPI info from plist");
|
|
if (comm == MPI_COMM_NULL)
|
|
comm = MPI_COMM_WORLD;
|
|
|
|
/* Set MPI parameters on IOC FAPL */
|
|
if (NULL == (ioc_plist = H5P_object_verify(vfd_config->ioc_fapl_id, H5P_FILE_ACCESS)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file access property list");
|
|
if (H5P_set(ioc_plist, H5F_ACS_MPI_PARAMS_COMM_NAME, &comm) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set MPI communicator on plist");
|
|
if (H5P_set(ioc_plist, H5F_ACS_MPI_PARAMS_INFO_NAME, &info) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set MPI info on plist");
|
|
|
|
if (H5FD__subfiling_validate_config(vfd_config) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid subfiling VFD configuration");
|
|
|
|
/* Set Subfiling configuration on IOC FAPL */
|
|
if (H5_subfiling_set_config_prop(ioc_plist, &vfd_config->shared_cfg) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL,
|
|
"can't set subfiling configuration on IOC FAPL");
|
|
|
|
ret_value = H5P_set_driver(plist, H5FD_SUBFILING, vfd_config, NULL);
|
|
|
|
done:
|
|
if (H5_mpi_comm_free(&comm) < 0)
|
|
H5_SUBFILING_DONE_ERROR(H5E_VFL, H5E_CANTFREE, FAIL, "can't free MPI Communicator");
|
|
if (H5_mpi_info_free(&info) < 0)
|
|
H5_SUBFILING_DONE_ERROR(H5E_VFL, H5E_CANTFREE, FAIL, "can't free MPI Info object");
|
|
|
|
if (subfiling_conf) {
|
|
if (subfiling_conf->ioc_fapl_id >= 0 && H5I_dec_ref(subfiling_conf->ioc_fapl_id) < 0)
|
|
H5_SUBFILING_DONE_ERROR(H5E_PLIST, H5E_CANTDEC, FAIL, "can't close IOC FAPL");
|
|
free(subfiling_conf);
|
|
}
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5Pset_fapl_subfiling() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5Pget_fapl_subfiling
|
|
*
|
|
* Purpose: Returns information about the subfiling file access
|
|
* property list though the function arguments.
|
|
*
|
|
* Return: Non-negative on success/Negative on failure
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
herr_t
|
|
H5Pget_fapl_subfiling(hid_t fapl_id, H5FD_subfiling_config_t *config_out)
|
|
{
|
|
const H5FD_subfiling_config_t *config_ptr = NULL;
|
|
H5P_genplist_t *plist = NULL;
|
|
bool use_default_config = false;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
/*NO TRACE*/
|
|
|
|
if (config_out == NULL)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "config_out is NULL");
|
|
|
|
if (NULL == (plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file access property list");
|
|
|
|
if (H5FD_SUBFILING != H5P_peek_driver(plist))
|
|
use_default_config = true;
|
|
else {
|
|
config_ptr = H5P_peek_driver_info(plist);
|
|
if (NULL == config_ptr)
|
|
use_default_config = true;
|
|
}
|
|
|
|
if (use_default_config) {
|
|
if (H5FD__subfiling_get_default_config(fapl_id, config_out) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL,
|
|
"can't get default Subfiling VFD configuration");
|
|
}
|
|
else {
|
|
/* Copy the subfiling fapl data out */
|
|
H5MM_memcpy(config_out, config_ptr, sizeof(H5FD_subfiling_config_t));
|
|
|
|
/* Copy the driver info value */
|
|
if (H5FD__copy_plist(config_ptr->ioc_fapl_id, &(config_out->ioc_fapl_id)) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "can't copy IOC FAPL");
|
|
}
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5Pget_fapl_subfiling() */
|
|
|
|
static herr_t
|
|
H5FD__subfiling_get_default_config(hid_t fapl_id, H5FD_subfiling_config_t *config_out)
|
|
{
|
|
MPI_Comm comm = MPI_COMM_NULL;
|
|
MPI_Info info = MPI_INFO_NULL;
|
|
char *h5_require_ioc;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
assert(config_out);
|
|
|
|
memset(config_out, 0, sizeof(*config_out));
|
|
|
|
config_out->magic = H5FD_SUBFILING_FAPL_MAGIC;
|
|
config_out->version = H5FD_SUBFILING_CURR_FAPL_VERSION;
|
|
config_out->ioc_fapl_id = H5I_INVALID_HID;
|
|
config_out->require_ioc = true;
|
|
|
|
config_out->shared_cfg.ioc_selection = SELECT_IOC_ONE_PER_NODE;
|
|
config_out->shared_cfg.stripe_size = H5FD_SUBFILING_DEFAULT_STRIPE_SIZE;
|
|
config_out->shared_cfg.stripe_count = H5FD_SUBFILING_DEFAULT_STRIPE_COUNT;
|
|
|
|
if ((h5_require_ioc = getenv("H5_REQUIRE_IOC")) != NULL) {
|
|
int value_check = atoi(h5_require_ioc);
|
|
if (value_check == 0)
|
|
config_out->require_ioc = false;
|
|
}
|
|
|
|
/* Check if any MPI parameters were set on the FAPL */
|
|
if (H5Pget_mpi_params(fapl_id, &comm, &info) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get MPI Comm/Info");
|
|
if (comm == MPI_COMM_NULL) {
|
|
comm = MPI_COMM_WORLD;
|
|
|
|
/* Set MPI_COMM_WORLD on FAPL if no MPI parameters were set */
|
|
if (H5Pset_mpi_params(fapl_id, comm, info) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set MPI Comm/Info");
|
|
}
|
|
|
|
/* Create a default FAPL and choose an appropriate underlying driver */
|
|
if ((config_out->ioc_fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTCREATE, FAIL, "can't create default FAPL");
|
|
|
|
if (config_out->require_ioc) {
|
|
if (H5Pset_mpi_params(config_out->ioc_fapl_id, comm, info) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't get MPI Comm/Info on IOC FAPL");
|
|
|
|
if (H5Pset_fapl_ioc(config_out->ioc_fapl_id, NULL) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set IOC VFD on IOC FAPL");
|
|
}
|
|
else {
|
|
if (H5Pset_fapl_sec2(config_out->ioc_fapl_id) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set Sec2 VFD on IOC FAPL");
|
|
}
|
|
|
|
done:
|
|
if (H5_mpi_comm_free(&comm) < 0)
|
|
H5_SUBFILING_DONE_ERROR(H5E_VFL, H5E_CANTFREE, FAIL, "can't free MPI Communicator");
|
|
if (H5_mpi_info_free(&info) < 0)
|
|
H5_SUBFILING_DONE_ERROR(H5E_VFL, H5E_CANTFREE, FAIL, "can't free MPI Info object");
|
|
|
|
if (ret_value < 0) {
|
|
if (config_out->ioc_fapl_id >= 0 && H5Pclose(config_out->ioc_fapl_id) < 0)
|
|
H5_SUBFILING_DONE_ERROR(H5E_PLIST, H5E_CANTCLOSEOBJ, FAIL, "can't close FAPL");
|
|
config_out->ioc_fapl_id = H5I_INVALID_HID;
|
|
}
|
|
|
|
H5_SUBFILING_FUNC_LEAVE;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_validate_config()
|
|
*
|
|
* Purpose: Test to see if the supplied instance of
|
|
* H5FD_subfiling_config_t contains internally consistent data.
|
|
* Return SUCCEED if so, and FAIL otherwise.
|
|
*
|
|
* Note the difference between internally consistent and
|
|
* correct. As we will have to try to setup subfiling to
|
|
* determine whether the supplied data is correct,
|
|
* we will settle for internal consistency at this point
|
|
*
|
|
* Return: SUCCEED if instance of H5FD_subfiling_config_t contains
|
|
* internally consistent data, FAIL otherwise.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_validate_config(const H5FD_subfiling_config_t *fa)
|
|
{
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
assert(fa != NULL);
|
|
|
|
if (fa->version != H5FD_SUBFILING_CURR_FAPL_VERSION)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unknown H5FD_subfiling_config_t version");
|
|
|
|
if (fa->magic != H5FD_SUBFILING_FAPL_MAGIC)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid H5FD_subfiling_config_t magic value");
|
|
|
|
if (fa->ioc_fapl_id < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid IOC FAPL ID");
|
|
|
|
if (!fa->require_ioc)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
|
|
"Subfiling VFD currently always requires IOC VFD to be used");
|
|
|
|
if (H5_subfiling_validate_config(&fa->shared_cfg) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid subfiling configuration parameters");
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE;
|
|
} /* end H5FD__subfiling_validate_config() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_sb_size
|
|
*
|
|
* Purpose: Returns the size of the subfiling configuration information
|
|
* to be stored in the superblock.
|
|
*
|
|
* Return: Size of subfiling configuration information (never fails)
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static hsize_t
|
|
H5FD__subfiling_sb_size(H5FD_t *_file)
|
|
{
|
|
subfiling_context_t *sf_context = NULL;
|
|
H5FD_subfiling_t *file = (H5FD_subfiling_t *)_file;
|
|
hsize_t ret_value = 0;
|
|
|
|
assert(file);
|
|
|
|
/* Configuration structure magic number */
|
|
ret_value += sizeof(uint32_t);
|
|
|
|
/* Configuration structure version number */
|
|
ret_value += sizeof(uint32_t);
|
|
|
|
/* "Require IOC" field */
|
|
ret_value += sizeof(int32_t);
|
|
|
|
/* Subfiling stripe size */
|
|
ret_value += sizeof(int64_t);
|
|
|
|
/* Subfiling stripe count (encoded as int64_t for future) */
|
|
ret_value += sizeof(int64_t);
|
|
|
|
/* Subfiling config file prefix string length */
|
|
ret_value += sizeof(uint64_t);
|
|
|
|
/*
|
|
* Since this callback currently can't return any errors, we
|
|
* will set the "fail to encode" flag on the file if we fail
|
|
* to retrieve the context object here so we can check for
|
|
* errors later.
|
|
*/
|
|
if (NULL == (sf_context = H5_get_subfiling_object(file->context_id))) {
|
|
file->fail_to_encode = true;
|
|
}
|
|
else {
|
|
if (sf_context->config_file_prefix) {
|
|
ret_value += strlen(sf_context->config_file_prefix) + 1;
|
|
}
|
|
}
|
|
|
|
/* Add superblock information from IOC file if necessary */
|
|
if (file->sf_file) {
|
|
/* Encode the IOC's name into the subfiling information */
|
|
ret_value += 9;
|
|
|
|
ret_value += H5FD_sb_size(file->sf_file);
|
|
}
|
|
|
|
/*
|
|
* Since the library doesn't currently properly check this,
|
|
* set the "fail to encode" flag if the message size is
|
|
* larger than the library's currently accepted max message
|
|
* size so that we don't try to encode the message and overrun
|
|
* a buffer.
|
|
*/
|
|
if (ret_value > H5FD_SUBFILING_MAX_DRV_INFO_SIZE)
|
|
file->fail_to_encode = true;
|
|
|
|
H5_SUBFILING_FUNC_LEAVE;
|
|
} /* end H5FD__subfiling_sb_size() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_sb_encode
|
|
*
|
|
* Purpose: Encodes the subfiling configuration information into the
|
|
* specified buffer.
|
|
*
|
|
* Return: Non-negative on success/Negative on failure
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_sb_encode(H5FD_t *_file, char *name, unsigned char *buf)
|
|
{
|
|
subfiling_context_t *sf_context = NULL;
|
|
H5FD_subfiling_t *file = (H5FD_subfiling_t *)_file;
|
|
uint8_t *p = (uint8_t *)buf;
|
|
uint64_t tmpu64;
|
|
int64_t tmp64;
|
|
int32_t tmp32;
|
|
size_t prefix_len = 0;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
/* Check if the "fail to encode flag" is set */
|
|
if (file->fail_to_encode)
|
|
H5_SUBFILING_GOTO_ERROR(
|
|
H5E_VFL, H5E_CANTENCODE, FAIL,
|
|
"can't encode subfiling driver info message - message was too large or internal error occurred");
|
|
|
|
if (NULL == (sf_context = H5_get_subfiling_object(file->context_id)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "can't get subfiling context object");
|
|
|
|
/* Encode driver name */
|
|
strncpy(name, "Subfilin", 9);
|
|
name[8] = '\0';
|
|
|
|
/* Encode configuration structure magic number */
|
|
UINT32ENCODE(p, file->fa.magic);
|
|
|
|
/* Encode configuration structure version number */
|
|
UINT32ENCODE(p, file->fa.version);
|
|
|
|
/* Encode "require IOC" field */
|
|
tmp32 = (int32_t)file->fa.require_ioc;
|
|
INT32ENCODE(p, tmp32);
|
|
|
|
/* Encode subfiling stripe size */
|
|
INT64ENCODE(p, sf_context->sf_stripe_size);
|
|
|
|
/* Encode subfiling stripe count (number of subfiles) */
|
|
tmp64 = sf_context->sf_num_subfiles;
|
|
INT64ENCODE(p, tmp64);
|
|
|
|
/* Encode config file prefix string length */
|
|
if (sf_context->config_file_prefix) {
|
|
prefix_len = strlen(sf_context->config_file_prefix) + 1;
|
|
H5_CHECKED_ASSIGN(tmpu64, uint64_t, prefix_len, size_t);
|
|
}
|
|
else
|
|
tmpu64 = 0;
|
|
UINT64ENCODE(p, tmpu64);
|
|
|
|
/* Encode config file prefix string */
|
|
if (sf_context->config_file_prefix) {
|
|
H5MM_memcpy(p, sf_context->config_file_prefix, prefix_len);
|
|
p += prefix_len;
|
|
}
|
|
|
|
/* Encode IOC VFD configuration information if necessary */
|
|
if (file->sf_file) {
|
|
char ioc_name[9];
|
|
|
|
memset(ioc_name, 0, sizeof(ioc_name));
|
|
|
|
if (H5FD_sb_encode(file->sf_file, ioc_name, p + 9) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTENCODE, FAIL,
|
|
"unable to encode IOC VFD's superblock information");
|
|
|
|
/* Copy the IOC VFD's name into our buffer */
|
|
H5MM_memcpy(p, ioc_name, 9);
|
|
}
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE;
|
|
} /* end H5FD__subfiling_sb_encode() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_sb_decode
|
|
*
|
|
* Purpose: Decodes the subfiling configuration information from the
|
|
* specified buffer.
|
|
*
|
|
* Return: Non-negative on success/Negative on failure
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_sb_decode(H5FD_t *_file, const char *name, const unsigned char *buf)
|
|
{
|
|
subfiling_context_t *sf_context = NULL;
|
|
H5FD_subfiling_t *file = (H5FD_subfiling_t *)_file;
|
|
const uint8_t *p = (const uint8_t *)buf;
|
|
uint64_t tmpu64;
|
|
int64_t tmp64;
|
|
int32_t tmp32;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
/* Check if we previously failed to encode the info */
|
|
if (file->fail_to_encode)
|
|
H5_SUBFILING_GOTO_ERROR(
|
|
H5E_VFL, H5E_CANTDECODE, FAIL,
|
|
"can't decode subfiling driver info message - message wasn't encoded (or encoded improperly)");
|
|
|
|
if (NULL == (sf_context = H5_get_subfiling_object(file->context_id)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "can't get subfiling context object");
|
|
|
|
if (strncmp(name, "Subfilin", 9))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "invalid driver name in superblock");
|
|
|
|
/* Decode configuration structure magic number */
|
|
UINT32DECODE(p, file->fa.magic);
|
|
|
|
/* Decode configuration structure version number */
|
|
UINT32DECODE(p, file->fa.version);
|
|
|
|
/* Decode "require IOC" field */
|
|
INT32DECODE(p, tmp32);
|
|
file->fa.require_ioc = (bool)tmp32;
|
|
|
|
/* Decode subfiling stripe size */
|
|
INT64DECODE(p, file->fa.shared_cfg.stripe_size);
|
|
|
|
/* Decode subfiling stripe count */
|
|
INT64DECODE(p, tmp64);
|
|
H5_CHECK_OVERFLOW(tmp64, int64_t, int32_t);
|
|
file->fa.shared_cfg.stripe_count = (int32_t)tmp64;
|
|
|
|
/* Decode config file prefix string length */
|
|
UINT64DECODE(p, tmpu64);
|
|
|
|
/* Decode config file prefix string */
|
|
if (tmpu64 > 0) {
|
|
if (!sf_context->config_file_prefix) {
|
|
if (NULL == (sf_context->config_file_prefix = malloc(tmpu64)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate space for config file prefix string");
|
|
|
|
H5MM_memcpy(sf_context->config_file_prefix, p, tmpu64);
|
|
|
|
/* Just in case... */
|
|
sf_context->config_file_prefix[tmpu64 - 1] = '\0';
|
|
}
|
|
|
|
p += tmpu64;
|
|
}
|
|
|
|
if (file->sf_file) {
|
|
char ioc_name[9];
|
|
|
|
H5MM_memcpy(ioc_name, p, 9);
|
|
p += 9;
|
|
|
|
if (H5FD_sb_load(file->sf_file, ioc_name, p) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTDECODE, FAIL,
|
|
"unable to decode IOC VFD's superblock information");
|
|
}
|
|
|
|
/* Validate the decoded configuration */
|
|
if (H5FD__subfiling_validate_config(&file->fa) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL,
|
|
"decoded subfiling configuration info is invalid");
|
|
|
|
if (file->fa.shared_cfg.stripe_size != sf_context->sf_stripe_size)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL,
|
|
"specified subfiling stripe size (%" PRId64
|
|
") doesn't match value stored in file (%" PRId64 ")",
|
|
sf_context->sf_stripe_size, file->fa.shared_cfg.stripe_size);
|
|
|
|
if (file->fa.shared_cfg.stripe_count != sf_context->sf_num_subfiles)
|
|
H5_SUBFILING_GOTO_ERROR(
|
|
H5E_VFL, H5E_BADVALUE, FAIL,
|
|
"specified subfiling stripe count (%d) doesn't match value stored in file (%" PRId32 ")",
|
|
sf_context->sf_num_subfiles, file->fa.shared_cfg.stripe_count);
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE;
|
|
} /* end H5FD__subfiling_sb_decode() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_fapl_get
|
|
*
|
|
* Purpose: Gets a file access property list which could be used to
|
|
* create an identical file.
|
|
*
|
|
* Return: Success: Ptr to new file access property list value.
|
|
*
|
|
* Failure: NULL
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static void *
|
|
H5FD__subfiling_fapl_get(H5FD_t *_file)
|
|
{
|
|
H5FD_subfiling_t *file = (H5FD_subfiling_t *)_file;
|
|
H5FD_subfiling_config_t *fa = NULL;
|
|
void *ret_value = NULL;
|
|
|
|
fa = (H5FD_subfiling_config_t *)H5MM_calloc(sizeof(H5FD_subfiling_config_t));
|
|
|
|
if (fa == NULL) {
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
|
|
}
|
|
|
|
/* Copy the fields of the structure */
|
|
H5MM_memcpy(fa, &(file->fa), sizeof(H5FD_subfiling_config_t));
|
|
|
|
/* Copy the driver info value */
|
|
if (H5FD__copy_plist(file->fa.ioc_fapl_id, &(fa->ioc_fapl_id)) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't copy IOC FAPL");
|
|
|
|
/* Set return value */
|
|
ret_value = fa;
|
|
|
|
done:
|
|
if (ret_value == NULL) {
|
|
|
|
if (fa != NULL) {
|
|
H5MM_xfree(fa);
|
|
}
|
|
}
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_fapl_get() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__copy_plist
|
|
*
|
|
* Purpose: Sanity-wrapped H5P_copy_plist() for each channel.
|
|
* Utility function for operation in multiple locations.
|
|
*
|
|
* Return: 0 on success, -1 on error.
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
/* TODO: no need for this function */
|
|
static int
|
|
H5FD__copy_plist(hid_t fapl_id, hid_t *id_out_ptr)
|
|
{
|
|
int ret_value = 0;
|
|
H5P_genplist_t *plist_ptr = NULL;
|
|
|
|
assert(id_out_ptr != NULL);
|
|
|
|
if (false == H5P_isa_class(fapl_id, H5P_FILE_ACCESS))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADTYPE, -1, "not a file access property list");
|
|
|
|
plist_ptr = (H5P_genplist_t *)H5I_object(fapl_id);
|
|
if (NULL == plist_ptr)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADTYPE, -1, "unable to get property list");
|
|
|
|
*id_out_ptr = H5P_copy_plist(plist_ptr, false);
|
|
if (H5I_INVALID_HID == *id_out_ptr)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_BADTYPE, -1, "unable to copy file access property list");
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE;
|
|
} /* end H5FD__copy_plist() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_fapl_copy
|
|
*
|
|
* Purpose: Copies the subfiling-specific file access properties.
|
|
*
|
|
* Return: Success: Ptr to a new property list
|
|
*
|
|
* Failure: NULL
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static void *
|
|
H5FD__subfiling_fapl_copy(const void *_old_fa)
|
|
{
|
|
const H5FD_subfiling_config_t *old_fa = (const H5FD_subfiling_config_t *)_old_fa;
|
|
H5FD_subfiling_config_t *new_fa = NULL;
|
|
void *ret_value = NULL;
|
|
|
|
new_fa = (H5FD_subfiling_config_t *)H5MM_malloc(sizeof(H5FD_subfiling_config_t));
|
|
if (new_fa == NULL) {
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
|
|
}
|
|
|
|
H5MM_memcpy(new_fa, old_fa, sizeof(H5FD_subfiling_config_t));
|
|
|
|
if (H5FD__copy_plist(old_fa->ioc_fapl_id, &(new_fa->ioc_fapl_id)) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't copy the IOC FAPL");
|
|
|
|
ret_value = new_fa;
|
|
|
|
done:
|
|
if (ret_value == NULL) {
|
|
|
|
if (new_fa != NULL) {
|
|
H5MM_xfree(new_fa);
|
|
}
|
|
}
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_fapl_copy() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_fapl_free
|
|
*
|
|
* Purpose: Frees the subfiling-specific file access properties.
|
|
*
|
|
* Return: SUCCEED (cannot fail)
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_fapl_free(void *_fa)
|
|
{
|
|
H5FD_subfiling_config_t *fa = (H5FD_subfiling_config_t *)_fa;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
assert(fa != NULL); /* sanity check */
|
|
|
|
if (fa->ioc_fapl_id >= 0 && H5I_dec_ref(fa->ioc_fapl_id) < 0)
|
|
H5_SUBFILING_DONE_ERROR(H5E_PLIST, H5E_CANTDEC, FAIL, "can't close IOC FAPL");
|
|
fa->ioc_fapl_id = H5I_INVALID_HID;
|
|
|
|
H5MM_xfree(fa);
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_fapl_free() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_open
|
|
*
|
|
* Purpose: Create and/or opens a file as an HDF5 file.
|
|
*
|
|
* Return: Success: A pointer to a new file data structure. The
|
|
* public fields will be initialized by the
|
|
* caller, which is always H5FD_open().
|
|
* Failure: NULL
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static H5FD_t *
|
|
H5FD__subfiling_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr)
|
|
{
|
|
H5FD_subfiling_t *file_ptr = NULL; /* Subfiling VFD info */
|
|
const H5FD_subfiling_config_t *config_ptr = NULL; /* Driver-specific property list */
|
|
H5FD_subfiling_config_t default_config;
|
|
H5FD_class_t *driver = NULL; /* VFD for file */
|
|
H5P_genplist_t *plist_ptr = NULL;
|
|
H5FD_driver_prop_t driver_prop; /* Property for driver ID & info */
|
|
bool bcasted_eof = false;
|
|
int64_t sf_eof = -1;
|
|
int mpi_code; /* MPI return code */
|
|
H5FD_t *ret_value = NULL;
|
|
|
|
/* Check arguments */
|
|
if (!name || !*name)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "invalid file name");
|
|
if (0 == maxaddr || HADDR_UNDEF == maxaddr)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADRANGE, NULL, "bogus maxaddr");
|
|
if (ADDR_OVERFLOW(maxaddr))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_OVERFLOW, NULL, "bogus maxaddr");
|
|
|
|
if (NULL == (file_ptr = (H5FD_subfiling_t *)H5FL_CALLOC(H5FD_subfiling_t)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTALLOC, NULL, "unable to allocate file struct");
|
|
file_ptr->comm = MPI_COMM_NULL;
|
|
file_ptr->info = MPI_INFO_NULL;
|
|
file_ptr->file_id = UINT64_MAX;
|
|
file_ptr->context_id = -1;
|
|
file_ptr->fa.ioc_fapl_id = H5I_INVALID_HID;
|
|
file_ptr->ext_comm = MPI_COMM_NULL;
|
|
file_ptr->fail_to_encode = false;
|
|
|
|
/* Get the driver-specific file access properties */
|
|
if (NULL == (plist_ptr = (H5P_genplist_t *)H5I_object(fapl_id)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a file access property list");
|
|
|
|
if (H5FD_mpi_self_initialized) {
|
|
file_ptr->comm = MPI_COMM_WORLD;
|
|
file_ptr->info = MPI_INFO_NULL;
|
|
}
|
|
else {
|
|
/* Get the MPI communicator and info object from the property list */
|
|
if (H5P_get(plist_ptr, H5F_ACS_MPI_PARAMS_COMM_NAME, &file_ptr->comm) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTGET, NULL, "can't get MPI communicator");
|
|
if (H5P_get(plist_ptr, H5F_ACS_MPI_PARAMS_INFO_NAME, &file_ptr->info) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTGET, NULL, "can't get MPI info object");
|
|
|
|
if (file_ptr->comm == MPI_COMM_NULL)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "invalid or unset MPI communicator in FAPL");
|
|
}
|
|
|
|
/* Get the MPI rank of this process and the total number of processes */
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Comm_rank(file_ptr->comm, &file_ptr->mpi_rank)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(NULL, "MPI_Comm_rank failed", mpi_code);
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Comm_size(file_ptr->comm, &file_ptr->mpi_size)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(NULL, "MPI_Comm_size failed", mpi_code);
|
|
|
|
/* Work around an HDF5 metadata cache bug with distributed metadata writes when MPI size == 1 */
|
|
if (file_ptr->mpi_size == 1) {
|
|
H5AC_cache_config_t mdc_config;
|
|
|
|
/* Get the current initial metadata cache resize configuration */
|
|
if (H5P_get(plist_ptr, H5F_ACS_META_CACHE_INIT_CONFIG_NAME, &mdc_config) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get metadata cache initial config");
|
|
mdc_config.metadata_write_strategy = H5AC_METADATA_WRITE_STRATEGY__PROCESS_0_ONLY;
|
|
if (H5P_set(plist_ptr, H5F_ACS_META_CACHE_INIT_CONFIG_NAME, &mdc_config) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTSET, NULL, "can't set metadata cache initial config");
|
|
}
|
|
|
|
config_ptr = H5P_peek_driver_info(plist_ptr);
|
|
if (!config_ptr || (H5P_FILE_ACCESS_DEFAULT == fapl_id)) {
|
|
if (H5FD__subfiling_get_default_config(fapl_id, &default_config) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL,
|
|
"can't get default subfiling VFD configuration");
|
|
config_ptr = &default_config;
|
|
}
|
|
|
|
H5MM_memcpy(&file_ptr->fa, config_ptr, sizeof(H5FD_subfiling_config_t));
|
|
if (H5FD__copy_plist(config_ptr->ioc_fapl_id, &(file_ptr->fa.ioc_fapl_id)) < 0) {
|
|
file_ptr->fa.ioc_fapl_id = H5I_INVALID_HID;
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL, "can't copy FAPL");
|
|
}
|
|
|
|
/* Check the "native" driver (IOC/sec2/etc.) */
|
|
if (NULL == (plist_ptr = H5I_object(file_ptr->fa.ioc_fapl_id)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_BADVALUE, NULL, "invalid IOC FAPL");
|
|
|
|
if (H5P_peek(plist_ptr, H5F_ACS_FILE_DRV_NAME, &driver_prop) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get driver ID & info");
|
|
if (NULL == (driver = (H5FD_class_t *)H5I_object(driver_prop.driver_id)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_BADVALUE, NULL,
|
|
"invalid driver ID in file access property list");
|
|
|
|
if (driver->value != H5_VFD_IOC)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL,
|
|
"unable to open file '%s' - only IOC VFD is currently supported for subfiles",
|
|
name);
|
|
|
|
/* Fully resolve the given filepath and get its dirname */
|
|
if (H5_resolve_pathname(name, file_ptr->comm, &file_ptr->file_path) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTGET, NULL, "can't resolve filepath");
|
|
if (H5_dirname(file_ptr->file_path, &file_ptr->file_dir) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTGET, NULL, "can't get filepath dirname");
|
|
|
|
/*
|
|
* Create/open the HDF5 stub file and get its inode value for
|
|
* the internal mapping from file inode to subfiling context.
|
|
*/
|
|
if (H5_open_subfiling_stub_file(file_ptr->file_path, flags, file_ptr->comm, &file_ptr->stub_file,
|
|
&file_ptr->file_id) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "can't open HDF5 stub file");
|
|
|
|
/* Set stub file ID on IOC fapl so it can reuse on open */
|
|
if (H5_subfiling_set_file_id_prop(plist_ptr, file_ptr->file_id) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTSET, NULL, "can't set stub file ID on FAPL");
|
|
|
|
/* Open the HDF5 file's subfiles */
|
|
if (NULL == (file_ptr->sf_file = H5FD_open(name, flags, file_ptr->fa.ioc_fapl_id, HADDR_UNDEF)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "unable to open IOC file");
|
|
|
|
if (driver->value == H5_VFD_IOC) {
|
|
/* Get a copy of the context ID for later use */
|
|
file_ptr->context_id = H5_subfile_fid_to_context(file_ptr->file_id);
|
|
file_ptr->fa.require_ioc = true;
|
|
}
|
|
else if (driver->value == H5_VFD_SEC2) {
|
|
int ioc_flags;
|
|
|
|
/* Translate the HDF5 file open flags into standard POSIX open flags */
|
|
ioc_flags = (H5F_ACC_RDWR & flags) ? O_RDWR : O_RDONLY;
|
|
if (H5F_ACC_TRUNC & flags)
|
|
ioc_flags |= O_TRUNC;
|
|
if (H5F_ACC_CREAT & flags)
|
|
ioc_flags |= O_CREAT;
|
|
if (H5F_ACC_EXCL & flags)
|
|
ioc_flags |= O_EXCL;
|
|
|
|
/*
|
|
* Open the subfiles for this HDF5 file. A subfiling
|
|
* context ID will be returned, which is used for
|
|
* further interactions with this file's subfiles.
|
|
*/
|
|
if (H5_open_subfiles(file_ptr->file_path, file_ptr->file_id, &file_ptr->fa.shared_cfg, ioc_flags,
|
|
file_ptr->comm, &file_ptr->context_id) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to open subfiling files = %s\n",
|
|
name);
|
|
}
|
|
|
|
if (file_ptr->mpi_rank == 0) {
|
|
if (H5FD__subfiling__get_real_eof(file_ptr->context_id, &sf_eof) < 0)
|
|
sf_eof = -1;
|
|
}
|
|
|
|
if (file_ptr->mpi_size > 1) {
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Bcast(&sf_eof, 1, MPI_INT64_T, 0, file_ptr->comm)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(NULL, "MPI_Bcast", mpi_code);
|
|
}
|
|
|
|
bcasted_eof = true;
|
|
|
|
if (sf_eof < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "lead MPI process failed to get file EOF");
|
|
|
|
file_ptr->eof = (haddr_t)sf_eof;
|
|
file_ptr->local_eof = file_ptr->eof;
|
|
|
|
ret_value = (H5FD_t *)file_ptr;
|
|
|
|
done:
|
|
if (config_ptr == &default_config)
|
|
if (H5I_dec_ref(config_ptr->ioc_fapl_id) < 0)
|
|
H5_SUBFILING_DONE_ERROR(H5E_PLIST, H5E_CANTCLOSEOBJ, NULL, "can't close IOC FAPL");
|
|
|
|
if (NULL == ret_value) {
|
|
if (file_ptr) {
|
|
/* Participate in possible MPI collectives on failure */
|
|
if (file_ptr->comm != MPI_COMM_NULL) {
|
|
if (!bcasted_eof) {
|
|
sf_eof = -1;
|
|
|
|
if (file_ptr->mpi_size > 1) {
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Bcast(&sf_eof, 1, MPI_INT64_T, 0, file_ptr->comm)))
|
|
H5_SUBFILING_MPI_DONE_ERROR(NULL, "MPI_Bcast failed", mpi_code);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (H5FD__subfiling_close_int(file_ptr) < 0)
|
|
H5_SUBFILING_DONE_ERROR(H5E_FILE, H5E_CLOSEERROR, NULL, "couldn't close file");
|
|
}
|
|
}
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_open() */
|
|
|
|
static herr_t
|
|
H5FD__subfiling_close_int(H5FD_subfiling_t *file_ptr)
|
|
{
|
|
int mpi_finalized;
|
|
int mpi_code;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
assert(file_ptr);
|
|
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Finalized(&mpi_finalized)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(FAIL, "MPI_Finalized failed", mpi_code);
|
|
|
|
if (file_ptr->sf_file && H5FD_close(file_ptr->sf_file) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTCLOSEFILE, FAIL, "unable to close subfile");
|
|
if (file_ptr->stub_file && H5FD_close(file_ptr->stub_file) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTCLOSEFILE, FAIL, "unable to close HDF5 stub file");
|
|
|
|
/* if set, close the copy of the plist for the underlying VFD. */
|
|
if ((file_ptr->fa.ioc_fapl_id >= 0) && (H5I_dec_ref(file_ptr->fa.ioc_fapl_id) < 0))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_ARGS, FAIL, "can't close IOC FAPL");
|
|
file_ptr->fa.ioc_fapl_id = H5I_INVALID_HID;
|
|
|
|
if (!mpi_finalized) {
|
|
if (H5_mpi_comm_free(&file_ptr->comm) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTFREE, FAIL, "unable to free MPI Communicator");
|
|
if (H5_mpi_info_free(&file_ptr->info) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTFREE, FAIL, "unable to free MPI Info object");
|
|
|
|
if (H5_mpi_comm_free(&file_ptr->ext_comm) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTFREE, FAIL, "can't free MPI communicator");
|
|
}
|
|
|
|
file_ptr->fail_to_encode = false;
|
|
|
|
done:
|
|
free(file_ptr->file_path);
|
|
file_ptr->file_path = NULL;
|
|
|
|
H5MM_free(file_ptr->file_dir);
|
|
file_ptr->file_dir = NULL;
|
|
|
|
/* Release the file info */
|
|
file_ptr = H5FL_FREE(H5FD_subfiling_t, file_ptr);
|
|
|
|
H5_SUBFILING_FUNC_LEAVE;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_close
|
|
*
|
|
* Purpose: Closes an HDF5 file.
|
|
*
|
|
* Return: Success: SUCCEED
|
|
* Failure: FAIL, file not closed.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_close(H5FD_t *_file)
|
|
{
|
|
H5FD_subfiling_t *file_ptr = (H5FD_subfiling_t *)_file;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
if (H5FD__subfiling_close_int(file_ptr) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "unable to close file");
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_close() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_cmp
|
|
*
|
|
* Purpose: Compares two files belonging to this driver using an
|
|
* arbitrary (but consistent) ordering.
|
|
*
|
|
* Return: Success: A value like strcmp()
|
|
* Failure: never fails (arguments were checked by the
|
|
* caller).
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static int
|
|
H5FD__subfiling_cmp(const H5FD_t *_f1, const H5FD_t *_f2)
|
|
{
|
|
const H5FD_subfiling_t *f1 = (const H5FD_subfiling_t *)_f1;
|
|
const H5FD_subfiling_t *f2 = (const H5FD_subfiling_t *)_f2;
|
|
int ret_value = 0;
|
|
|
|
assert(f1);
|
|
assert(f2);
|
|
|
|
ret_value = H5FD_cmp(f1->sf_file, f2->sf_file);
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_cmp() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_query
|
|
*
|
|
* Purpose: Set the flags that this VFL driver is capable of supporting.
|
|
* (listed in H5FDpublic.h)
|
|
*
|
|
* For now, duplicate the flags used for the MPIO VFD.
|
|
* Revisit this when we have a version of the subfiling VFD
|
|
* that is usable in serial builds.
|
|
*
|
|
* Return: SUCCEED (Can't fail)
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_query(const H5FD_t H5_ATTR_UNUSED *_file, unsigned long *flags /* out */)
|
|
{
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
/* Set the VFL feature flags that this driver supports */
|
|
if (flags) {
|
|
*flags = 0;
|
|
*flags |= H5FD_FEAT_AGGREGATE_METADATA; /* OK to aggregate metadata allocations */
|
|
*flags |= H5FD_FEAT_AGGREGATE_SMALLDATA; /* OK to aggregate "small" raw data allocations */
|
|
*flags |= H5FD_FEAT_HAS_MPI; /* This driver uses MPI */
|
|
}
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_query() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_get_eoa
|
|
*
|
|
* Purpose: Gets the end-of-address marker for the file. The EOA marker
|
|
* is the first address past the last byte allocated in the
|
|
* format address space.
|
|
*
|
|
* Return: The end-of-address marker.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static haddr_t
|
|
H5FD__subfiling_get_eoa(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type)
|
|
{
|
|
const H5FD_subfiling_t *file = (const H5FD_subfiling_t *)_file;
|
|
haddr_t ret_value = HADDR_UNDEF;
|
|
|
|
ret_value = file->eoa;
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_get_eoa() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_set_eoa
|
|
*
|
|
* Purpose: Set the end-of-address marker for the file. This function is
|
|
* called shortly after an existing HDF5 file is opened in order
|
|
* to tell the driver where the end of the HDF5 data is located.
|
|
*
|
|
* Return: SUCCEED (Can't fail)
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr)
|
|
{
|
|
H5FD_subfiling_t *file_ptr = (H5FD_subfiling_t *)_file;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
file_ptr->eoa = addr;
|
|
|
|
/* Set EOA for HDF5 stub file */
|
|
if (file_ptr->mpi_rank == 0) {
|
|
if (H5FD_set_eoa(file_ptr->stub_file, type, addr) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "can't set HDF5 stub file EOA");
|
|
}
|
|
|
|
ret_value = H5FD_set_eoa(file_ptr->sf_file, type, addr);
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_set_eoa() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_get_eof
|
|
*
|
|
* Purpose: Returns the end-of-file marker from the filesystem
|
|
* perspective.
|
|
*
|
|
* Return: End of file address, the first address past the end of the
|
|
* "file", either the filesystem file or the HDF5 file.
|
|
*
|
|
* NOTE: This VFD mimics the MPI I/O VFD and so does not try
|
|
* to keep the EOF updated. The EOF is mostly just needed
|
|
* right after the file is opened so the library can determine
|
|
* if the file is empty, truncated or okay.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static haddr_t
|
|
H5FD__subfiling_get_eof(const H5FD_t *_file, H5FD_mem_t H5_ATTR_UNUSED type)
|
|
{
|
|
const H5FD_subfiling_t *file = (const H5FD_subfiling_t *)_file;
|
|
haddr_t ret_value = HADDR_UNDEF;
|
|
|
|
ret_value = file->eof;
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_get_eof() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_get_handle
|
|
*
|
|
* Purpose: Returns the file handle of subfiling file driver.
|
|
*
|
|
* Returns: SUCCEED/FAIL
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_get_handle(H5FD_t *_file, hid_t H5_ATTR_UNUSED fapl, void **file_handle)
|
|
{
|
|
H5FD_subfiling_t *file = (H5FD_subfiling_t *)_file;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
if (!file_handle)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "file handle not valid");
|
|
|
|
H5FD_get_vfd_handle(file->sf_file, file->fa.ioc_fapl_id, file_handle);
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_get_handle() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_read
|
|
*
|
|
* Purpose: Reads SIZE bytes of data from FILE beginning at address ADDR
|
|
* into buffer BUF according to data transfer properties in
|
|
* DXPL_ID.
|
|
*
|
|
* Return: Success: SUCCEED. Result is stored in caller-supplied
|
|
* buffer BUF.
|
|
* Failure: FAIL, Contents of buffer BUF are undefined.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_read(H5FD_t *_file, H5FD_mem_t type, hid_t H5_ATTR_UNUSED dxpl_id, haddr_t addr, size_t size,
|
|
void *buf /*out*/)
|
|
{
|
|
subfiling_context_t *sf_context = NULL;
|
|
H5FD_subfiling_t *file_ptr = (H5FD_subfiling_t *)_file;
|
|
H5FD_mem_t *io_types = NULL;
|
|
haddr_t *io_addrs = NULL;
|
|
size_t *io_sizes = NULL;
|
|
void **io_bufs = NULL;
|
|
int64_t *source_data_offset = NULL;
|
|
int64_t *sf_data_size = NULL;
|
|
int64_t *sf_offset = NULL;
|
|
bool rank0_bcast = false;
|
|
int num_subfiles;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
assert(file_ptr && file_ptr->pub.cls);
|
|
assert(buf);
|
|
|
|
/* Check for overflow conditions */
|
|
if (!H5_addr_defined(addr))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "addr undefined, addr = %" PRIuHADDR, addr);
|
|
if (REGION_OVERFLOW(addr, size))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_OVERFLOW, FAIL,
|
|
"addr overflow, addr = %" PRIuHADDR ", size = %zu", addr, size);
|
|
|
|
/* Temporarily reject collective I/O until support is implemented (unless types are simple MPI_BYTE) */
|
|
{
|
|
H5FD_mpio_xfer_t xfer_mode;
|
|
|
|
if (H5CX_get_io_xfer_mode(&xfer_mode) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_CONTEXT, H5E_CANTGET, FAIL,
|
|
"can't determine I/O collectivity setting");
|
|
|
|
if (xfer_mode == H5FD_MPIO_COLLECTIVE) {
|
|
MPI_Datatype btype, ftype;
|
|
|
|
if (H5CX_get_mpi_coll_datatypes(&btype, &ftype) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "can't get MPI-I/O datatypes");
|
|
if (MPI_BYTE != btype || MPI_BYTE != ftype)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_UNSUPPORTED, FAIL,
|
|
"collective I/O is currently unsupported");
|
|
}
|
|
|
|
/* Determine whether a rank 0 bcast approach has been requested */
|
|
rank0_bcast = H5CX_get_mpio_rank0_bcast();
|
|
|
|
/*
|
|
* If we reached here, we're still doing independent I/O regardless
|
|
* of collectivity setting, so set that.
|
|
*/
|
|
H5CX_set_io_xfer_mode(H5FD_MPIO_INDEPENDENT);
|
|
}
|
|
|
|
/*
|
|
* Retrieve the subfiling context object and the number
|
|
* of subfiles.
|
|
*
|
|
* Given the current I/O and the I/O concentrator info,
|
|
* we can determine some I/O transaction parameters.
|
|
* In particular, for large I/O operations, each IOC
|
|
* may require multiple I/Os to fulfill the user I/O
|
|
* request. The block size and number of IOCs are used
|
|
* to size the vectors that will be used to invoke the
|
|
* underlying I/O operations.
|
|
*/
|
|
sf_context = (subfiling_context_t *)H5_get_subfiling_object(file_ptr->context_id);
|
|
assert(sf_context);
|
|
assert(sf_context->topology);
|
|
|
|
num_subfiles = sf_context->sf_num_subfiles;
|
|
|
|
if (num_subfiles <= 0) {
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid number of subfiles (%d)",
|
|
num_subfiles);
|
|
}
|
|
else if (num_subfiles == 1) {
|
|
/***************************************
|
|
* No striping - just a single subfile *
|
|
***************************************/
|
|
|
|
/* Make vector read call to subfile */
|
|
if (H5FD_read_vector(file_ptr->sf_file, 1, &type, &addr, &size, &buf) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "read from subfile failed");
|
|
}
|
|
else {
|
|
int64_t max_io_req_per_subfile;
|
|
int64_t file_offset;
|
|
int64_t block_size;
|
|
size_t max_depth;
|
|
herr_t status;
|
|
int num_subfiles_used = 0;
|
|
int first_subfile_idx = -1;
|
|
|
|
/*************************************
|
|
* Striping across multiple subfiles *
|
|
*************************************/
|
|
|
|
block_size = sf_context->sf_blocksize_per_stripe;
|
|
max_depth = (size / (size_t)block_size) + 2;
|
|
|
|
/*
|
|
* Given the number of subfiles, allocate vectors (one per subfile)
|
|
* to contain the translation of the I/O request into a collection of
|
|
* I/O requests.
|
|
*/
|
|
if (NULL ==
|
|
(source_data_offset = calloc(1, (size_t)num_subfiles * max_depth * sizeof(*source_data_offset))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate source data offset I/O vector");
|
|
if (NULL == (sf_data_size = calloc(1, (size_t)num_subfiles * max_depth * sizeof(*sf_data_size))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfile data size I/O vector");
|
|
if (NULL == (sf_offset = calloc(1, (size_t)num_subfiles * max_depth * sizeof(*sf_offset))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfile offset I/O vector");
|
|
|
|
H5_CHECKED_ASSIGN(file_offset, int64_t, addr, haddr_t);
|
|
|
|
/*
|
|
* Get the potential set of IOC transactions; e.g., data sizes,
|
|
* offsets and datatypes.
|
|
*/
|
|
status = init_indep_io(sf_context, /* IN: Context used to look up config info */
|
|
file_offset, /* IN: Starting file offset */
|
|
size, /* IN: I/O size */
|
|
1, /* IN: Data extent of the 'type' assumes byte */
|
|
max_depth, /* IN: Maximum stripe depth */
|
|
source_data_offset, /* OUT: Memory offset */
|
|
sf_offset, /* OUT: File offset */
|
|
sf_data_size, /* OUT: Length of this contiguous block */
|
|
&first_subfile_idx, /* OUT: Subfile index corresponding to starting offset */
|
|
&num_subfiles_used, /* OUT: Number of actual subfiles used */
|
|
&max_io_req_per_subfile); /* OUT: Maximum number of requests to any subfile */
|
|
|
|
if (status < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTINIT, FAIL, "can't initialize IOC transactions");
|
|
|
|
if (max_io_req_per_subfile > 0) {
|
|
uint32_t vector_len;
|
|
|
|
H5_CHECKED_ASSIGN(vector_len, uint32_t, num_subfiles_used, int);
|
|
|
|
/* Allocate I/O vectors */
|
|
if (NULL == (io_types = malloc(vector_len * sizeof(*io_types))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfile I/O types vector");
|
|
if (NULL == (io_addrs = malloc(vector_len * sizeof(*io_addrs))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfile I/O addresses vector");
|
|
if (NULL == (io_sizes = malloc(vector_len * sizeof(*io_sizes))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfile I/O sizes vector");
|
|
if (NULL == (io_bufs = malloc(vector_len * sizeof(*io_bufs))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfile I/O buffers vector");
|
|
|
|
for (int64_t i = 0; i < max_io_req_per_subfile; i++) {
|
|
uint32_t final_vec_len = vector_len;
|
|
int next_subfile_idx = first_subfile_idx;
|
|
|
|
/* Fill in I/O types, offsets, sizes and buffers vectors */
|
|
for (uint32_t k = 0, vec_idx = 0; k < vector_len; k++) {
|
|
size_t idx = (size_t)next_subfile_idx * max_depth + (size_t)i;
|
|
|
|
io_types[vec_idx] = type;
|
|
H5_CHECKED_ASSIGN(io_addrs[vec_idx], haddr_t, sf_offset[idx], int64_t);
|
|
H5_CHECKED_ASSIGN(io_sizes[vec_idx], size_t, sf_data_size[idx], int64_t);
|
|
io_bufs[vec_idx] = ((char *)buf + source_data_offset[idx]);
|
|
|
|
next_subfile_idx = (next_subfile_idx + 1) % num_subfiles;
|
|
|
|
/* Skip 0-sized I/Os */
|
|
if (io_sizes[vec_idx] == 0) {
|
|
final_vec_len--;
|
|
continue;
|
|
}
|
|
|
|
vec_idx++;
|
|
}
|
|
|
|
if (!rank0_bcast || (file_ptr->mpi_rank == 0)) {
|
|
/* Make vector read call to subfile */
|
|
if (H5FD_read_vector(file_ptr->sf_file, final_vec_len, io_types, io_addrs, io_sizes,
|
|
io_bufs) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "read from subfile failed");
|
|
}
|
|
}
|
|
|
|
if (rank0_bcast && (file_ptr->mpi_size > 1)) {
|
|
H5_CHECK_OVERFLOW(size, size_t, int);
|
|
if (MPI_SUCCESS != MPI_Bcast(buf, (int)size, MPI_BYTE, 0, file_ptr->comm))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "can't broadcast data from rank 0");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Point to the end of the current I/O */
|
|
addr += (haddr_t)size;
|
|
|
|
/* Update current file position and EOF */
|
|
file_ptr->pos = addr;
|
|
file_ptr->op = OP_READ;
|
|
|
|
done:
|
|
free(io_bufs);
|
|
free(io_sizes);
|
|
free(io_addrs);
|
|
free(io_types);
|
|
free(sf_offset);
|
|
free(sf_data_size);
|
|
free(source_data_offset);
|
|
|
|
if (ret_value < 0) {
|
|
/* Reset last file I/O information */
|
|
file_ptr->pos = HADDR_UNDEF;
|
|
file_ptr->op = OP_UNKNOWN;
|
|
} /* end if */
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_read() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_write
|
|
*
|
|
* Purpose: Writes SIZE bytes of data to FILE beginning at address ADDR
|
|
* from buffer BUF according to data transfer properties in
|
|
* DXPL_ID.
|
|
*
|
|
* Return: SUCCEED/FAIL
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_write(H5FD_t *_file, H5FD_mem_t type, hid_t H5_ATTR_UNUSED dxpl_id, haddr_t addr, size_t size,
|
|
const void *buf /*in*/)
|
|
{
|
|
subfiling_context_t *sf_context = NULL;
|
|
H5FD_subfiling_t *file_ptr = (H5FD_subfiling_t *)_file;
|
|
const void **io_bufs = NULL;
|
|
H5FD_mem_t *io_types = NULL;
|
|
haddr_t *io_addrs = NULL;
|
|
size_t *io_sizes = NULL;
|
|
int64_t *source_data_offset = NULL;
|
|
int64_t *sf_data_size = NULL;
|
|
int64_t *sf_offset = NULL;
|
|
int num_subfiles;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
assert(file_ptr && file_ptr->pub.cls);
|
|
assert(buf);
|
|
|
|
/* Check for overflow conditions */
|
|
if (!H5_addr_defined(addr))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "addr undefined, addr = %" PRIuHADDR, addr);
|
|
if (REGION_OVERFLOW(addr, size))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_OVERFLOW, FAIL,
|
|
"addr overflow, addr = %" PRIuHADDR ", size = %zu", addr, size);
|
|
|
|
/* Temporarily reject collective I/O until support is implemented (unless types are simple MPI_BYTE) */
|
|
{
|
|
H5FD_mpio_xfer_t xfer_mode;
|
|
|
|
if (H5CX_get_io_xfer_mode(&xfer_mode) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_CONTEXT, H5E_CANTGET, FAIL,
|
|
"can't determine I/O collectivity setting");
|
|
|
|
if (xfer_mode == H5FD_MPIO_COLLECTIVE) {
|
|
MPI_Datatype btype, ftype;
|
|
|
|
if (H5CX_get_mpi_coll_datatypes(&btype, &ftype) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "can't get MPI-I/O datatypes");
|
|
if (MPI_BYTE != btype || MPI_BYTE != ftype)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_UNSUPPORTED, FAIL,
|
|
"collective I/O is currently unsupported");
|
|
}
|
|
|
|
/*
|
|
* If we reached here, we're still doing independent I/O regardless
|
|
* of collectivity setting, so set that.
|
|
*/
|
|
H5CX_set_io_xfer_mode(H5FD_MPIO_INDEPENDENT);
|
|
}
|
|
|
|
/*
|
|
* Retrieve the subfiling context object and the number
|
|
* of subfiles.
|
|
*
|
|
* Given the current I/O and the I/O concentrator info,
|
|
* we can determine some I/O transaction parameters.
|
|
* In particular, for large I/O operations, each IOC
|
|
* may require multiple I/Os to fulfill the user I/O
|
|
* request. The block size and number of IOCs are used
|
|
* to size the vectors that will be used to invoke the
|
|
* underlying I/O operations.
|
|
*/
|
|
sf_context = (subfiling_context_t *)H5_get_subfiling_object(file_ptr->context_id);
|
|
assert(sf_context);
|
|
assert(sf_context->topology);
|
|
|
|
num_subfiles = sf_context->sf_num_subfiles;
|
|
|
|
if (num_subfiles <= 0) {
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid number of subfiles (%d)",
|
|
num_subfiles);
|
|
}
|
|
else if (num_subfiles == 1) {
|
|
/***************************************
|
|
* No striping - just a single subfile *
|
|
***************************************/
|
|
|
|
/* Make vector write call to subfile */
|
|
if (H5FD_write_vector(file_ptr->sf_file, 1, &type, &addr, &size, &buf) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "write to subfile failed");
|
|
|
|
/*
|
|
* Mirror superblock writes to the stub file so that
|
|
* legacy HDF5 applications can check what type of
|
|
* file they are reading
|
|
*/
|
|
if ((type == H5FD_MEM_SUPER) && (file_ptr->mpi_rank == 0)) {
|
|
if (H5FD_write_vector(file_ptr->stub_file, 1, &type, &addr, &size, &buf) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL,
|
|
"couldn't write superblock information to stub file");
|
|
}
|
|
}
|
|
else {
|
|
int64_t max_io_req_per_subfile;
|
|
int64_t file_offset;
|
|
int64_t block_size;
|
|
size_t max_depth;
|
|
herr_t status;
|
|
int num_subfiles_used = 0;
|
|
int first_subfile_idx = -1;
|
|
|
|
/*************************************
|
|
* Striping across multiple subfiles *
|
|
*************************************/
|
|
|
|
block_size = sf_context->sf_blocksize_per_stripe;
|
|
max_depth = (size / (size_t)block_size) + 2;
|
|
|
|
/*
|
|
* Given the number of subfiles, allocate vectors (one per subfile)
|
|
* to contain the translation of the I/O request into a collection of
|
|
* I/O requests.
|
|
*/
|
|
if (NULL ==
|
|
(source_data_offset = calloc(1, (size_t)num_subfiles * max_depth * sizeof(*source_data_offset))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate source data offset I/O vector");
|
|
if (NULL == (sf_data_size = calloc(1, (size_t)num_subfiles * max_depth * sizeof(*sf_data_size))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfile data size I/O vector");
|
|
if (NULL == (sf_offset = calloc(1, (size_t)num_subfiles * max_depth * sizeof(*sf_offset))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfile offset I/O vector");
|
|
|
|
H5_CHECKED_ASSIGN(file_offset, int64_t, addr, haddr_t);
|
|
|
|
/*
|
|
* Get the potential set of IOC transactions; e.g., data sizes,
|
|
* offsets and datatypes.
|
|
*/
|
|
status = init_indep_io(sf_context, /* IN: Context used to look up config info */
|
|
file_offset, /* IN: Starting file offset */
|
|
size, /* IN: I/O size */
|
|
1, /* IN: Data extent of the 'type' assumes byte */
|
|
max_depth, /* IN: Maximum stripe depth */
|
|
source_data_offset, /* OUT: Memory offset */
|
|
sf_offset, /* OUT: File offset */
|
|
sf_data_size, /* OUT: Length of this contiguous block */
|
|
&first_subfile_idx, /* OUT: Subfile index corresponding to starting offset */
|
|
&num_subfiles_used, /* OUT: Number of actual subfiles used */
|
|
&max_io_req_per_subfile); /* OUT: Maximum number of requests to any subfile */
|
|
|
|
if (status < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTINIT, FAIL, "can't initialize IOC transactions");
|
|
|
|
if (max_io_req_per_subfile > 0) {
|
|
uint32_t vector_len;
|
|
|
|
H5_CHECKED_ASSIGN(vector_len, uint32_t, num_subfiles_used, int);
|
|
|
|
/* Allocate I/O vectors */
|
|
if (NULL == (io_types = malloc(vector_len * sizeof(*io_types))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfile I/O types vector");
|
|
if (NULL == (io_addrs = malloc(vector_len * sizeof(*io_addrs))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfile I/O addresses vector");
|
|
if (NULL == (io_sizes = malloc(vector_len * sizeof(*io_sizes))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfile I/O sizes vector");
|
|
if (NULL == (io_bufs = malloc(vector_len * sizeof(*io_bufs))))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL,
|
|
"can't allocate subfile I/O buffers vector");
|
|
|
|
for (int64_t i = 0; i < max_io_req_per_subfile; i++) {
|
|
uint32_t final_vec_len = vector_len;
|
|
int next_subfile_idx = first_subfile_idx;
|
|
|
|
/* Fill in I/O types, offsets, sizes and buffers vectors */
|
|
for (uint32_t k = 0, vec_idx = 0; k < vector_len; k++) {
|
|
size_t idx = (size_t)next_subfile_idx * max_depth + (size_t)i;
|
|
|
|
io_types[vec_idx] = type;
|
|
H5_CHECKED_ASSIGN(io_addrs[vec_idx], haddr_t, sf_offset[idx], int64_t);
|
|
H5_CHECKED_ASSIGN(io_sizes[vec_idx], size_t, sf_data_size[idx], int64_t);
|
|
io_bufs[vec_idx] = ((const char *)buf + source_data_offset[idx]);
|
|
|
|
next_subfile_idx = (next_subfile_idx + 1) % num_subfiles;
|
|
|
|
/* Skip 0-sized I/Os */
|
|
if (io_sizes[vec_idx] == 0) {
|
|
final_vec_len--;
|
|
continue;
|
|
}
|
|
|
|
vec_idx++;
|
|
}
|
|
|
|
/* Make vector write call to subfile */
|
|
if (H5FD_write_vector(file_ptr->sf_file, final_vec_len, io_types, io_addrs, io_sizes,
|
|
io_bufs) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "write to subfile failed");
|
|
|
|
/*
|
|
* Mirror superblock writes to the stub file so that
|
|
* legacy HDF5 applications can check what type of
|
|
* file they are reading
|
|
*/
|
|
if (file_ptr->mpi_rank == 0) {
|
|
for (size_t count_idx = 0; count_idx < (size_t)final_vec_len; count_idx++) {
|
|
if (io_types[count_idx] == H5FD_MEM_SUPER) {
|
|
if (H5FD_write(file_ptr->stub_file, H5FD_MEM_SUPER, io_addrs[count_idx],
|
|
io_sizes[count_idx], io_bufs[count_idx]) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL,
|
|
"couldn't write superblock information to stub file");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Point to the end of the current I/O */
|
|
addr += (haddr_t)size;
|
|
|
|
/* Update current file position and EOF */
|
|
file_ptr->pos = addr;
|
|
file_ptr->op = OP_WRITE;
|
|
|
|
/* Mimic the MPI I/O VFD */
|
|
file_ptr->eof = HADDR_UNDEF;
|
|
|
|
if (file_ptr->pos > file_ptr->local_eof)
|
|
file_ptr->local_eof = file_ptr->pos;
|
|
|
|
done:
|
|
free(io_bufs);
|
|
free(io_sizes);
|
|
free(io_addrs);
|
|
free(io_types);
|
|
free(sf_offset);
|
|
free(sf_data_size);
|
|
free(source_data_offset);
|
|
|
|
if (ret_value < 0) {
|
|
/* Reset last file I/O information */
|
|
file_ptr->pos = HADDR_UNDEF;
|
|
file_ptr->op = OP_UNKNOWN;
|
|
} /* end if */
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_write() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfile_read_vector (internal function)
|
|
*
|
|
* Purpose: Vector Read function for the sub-filing VFD.
|
|
*
|
|
* Perform count reads from the specified file at the offsets
|
|
* provided in the addrs array, with the lengths and memory
|
|
* types provided in the sizes and types arrays. Data read
|
|
* is returned in the buffers provided in the bufs array.
|
|
*
|
|
* All reads are done according to the data transfer property
|
|
* list dxpl_id (which may be the constant H5P_DEFAULT).
|
|
*
|
|
* Return: Success: SUCCEED
|
|
* All reads have completed successfully, and
|
|
* the results havce been into the supplied
|
|
* buffers.
|
|
*
|
|
* Failure: FAIL
|
|
* The contents of supplied buffers are undefined.
|
|
*
|
|
* Notes: Thus function doesn't actually implement vector read.
|
|
* Instead, it converts the vector read call into a series
|
|
* of scalar read calls. Fix this when time permits.
|
|
*
|
|
* Also, it didn't support the sizes and types optimization.
|
|
* I implemented a version of this which is more generous
|
|
* than that currently defined in the RFC. This is good
|
|
* enough for now, but the final version should follow
|
|
* the RFC.
|
|
* JRM -- 10/5/21
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_read_vector(H5FD_t *_file, hid_t dxpl_id, uint32_t count, H5FD_mem_t types[], haddr_t addrs[],
|
|
size_t sizes[], void *bufs[] /* out */)
|
|
{
|
|
H5FD_subfiling_t *file_ptr = (H5FD_subfiling_t *)_file;
|
|
H5FD_mpio_xfer_t xfer_mode = H5FD_MPIO_INDEPENDENT;
|
|
herr_t ret_value = SUCCEED; /* Return value */
|
|
|
|
/* Check arguments
|
|
* RAW - Do we really need to check arguments once again?
|
|
* These have already been checked in H5FD__subfiling_read_vector (see below)!
|
|
*/
|
|
if (!file_ptr)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "file pointer cannot be NULL");
|
|
|
|
if ((!types) && (count > 0))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
|
|
"types parameter can't be NULL if count is positive");
|
|
|
|
if ((!addrs) && (count > 0))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
|
|
"addrs parameter can't be NULL if count is positive");
|
|
|
|
if ((!sizes) && (count > 0))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
|
|
"sizes parameter can't be NULL if count is positive");
|
|
|
|
if ((!bufs) && (count > 0))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
|
|
"bufs parameter can't be NULL if count is positive");
|
|
|
|
/* Get the default dataset transfer property list if the user didn't provide one */
|
|
if (H5P_DEFAULT == dxpl_id) {
|
|
dxpl_id = H5P_DATASET_XFER_DEFAULT;
|
|
}
|
|
else {
|
|
if (true != H5P_isa_class(dxpl_id, H5P_DATASET_XFER))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a data transfer property list");
|
|
}
|
|
|
|
/* Set DXPL for operation */
|
|
H5CX_set_dxpl(dxpl_id);
|
|
|
|
/* TODO: setup real support for vector I/O */
|
|
if (file_ptr->fa.require_ioc) {
|
|
|
|
bool extend_sizes = false;
|
|
bool extend_types = false;
|
|
int k;
|
|
size_t size;
|
|
H5FD_mem_t type;
|
|
haddr_t eoa;
|
|
|
|
assert((count == 0) || (sizes[0] != 0));
|
|
assert((count == 0) || (types[0] != H5FD_MEM_NOLIST));
|
|
|
|
if (H5CX_get_io_xfer_mode(&xfer_mode) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_CONTEXT, H5E_CANTGET, FAIL,
|
|
"can't determine I/O collectivity setting");
|
|
|
|
/* Currently, treat collective calls as independent */
|
|
if (xfer_mode != H5FD_MPIO_INDEPENDENT)
|
|
if (H5CX_set_io_xfer_mode(H5FD_MPIO_INDEPENDENT) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_CONTEXT, H5E_CANTSET, FAIL, "can't set I/O collectivity setting");
|
|
|
|
/* Note that the following code does not let the sub-filing VFD participate
|
|
* in collective calls when there is no data to write. This is not an issue
|
|
* now, as we don't do anything special with collective operations. However
|
|
* this needs to be fixed.
|
|
*/
|
|
for (k = 0; k < (int)count; k++) {
|
|
|
|
if (!extend_sizes) {
|
|
|
|
if (sizes[k] == 0) {
|
|
|
|
extend_sizes = true;
|
|
size = sizes[k - 1];
|
|
}
|
|
else {
|
|
|
|
size = sizes[k];
|
|
}
|
|
}
|
|
|
|
if (!extend_types) {
|
|
|
|
if (types[k] == H5FD_MEM_NOLIST) {
|
|
|
|
extend_types = true;
|
|
type = types[k - 1];
|
|
}
|
|
else {
|
|
|
|
type = types[k];
|
|
}
|
|
}
|
|
|
|
if (HADDR_UNDEF == (eoa = H5FD__subfiling_get_eoa(_file, type)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "driver get_eoa request failed");
|
|
|
|
if ((addrs[k] + size) > eoa)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_OVERFLOW, FAIL,
|
|
"addr overflow, addrs[%d] = %llu, sizes[%d] = %llu, eoa = %llu",
|
|
(int)k, (unsigned long long)(addrs[k]), (int)k,
|
|
(unsigned long long)size, (unsigned long long)eoa);
|
|
|
|
if (H5FD__subfiling_read(_file, type, dxpl_id, addrs[k], size, bufs[k]) != SUCCEED)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "file vector read request failed");
|
|
}
|
|
}
|
|
else {
|
|
/* sec2 driver..
|
|
* Call the subfiling 'direct write' version
|
|
* of subfiling.
|
|
*/
|
|
if (H5FD_read_vector(_file, count, types, addrs, sizes, bufs) != SUCCEED)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "file vector read request failed");
|
|
}
|
|
|
|
done:
|
|
if (xfer_mode != H5FD_MPIO_INDEPENDENT)
|
|
if (H5CX_set_io_xfer_mode(xfer_mode) < 0)
|
|
H5_SUBFILING_DONE_ERROR(H5E_CONTEXT, H5E_CANTSET, FAIL, "can't set I/O collectivity setting");
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_read_vector() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfile_write_vector (internal function)
|
|
*
|
|
* Purpose: Perform count writes to the specified file at the offsets
|
|
* provided in the addrs array. Lengths and memory
|
|
* types provided in the sizes and types arrays. Data to be
|
|
* written is referenced by the bufs array.
|
|
*
|
|
* All writes are done according to the data transfer property
|
|
* list dxpl_id (which may be the constant H5P_DEFAULT).
|
|
*
|
|
* Return: Success: SUCCEED
|
|
* All writes have completed successfully.
|
|
*
|
|
* Failure: FAIL
|
|
* An internal error was encountered, e.g the
|
|
* input arguments are not valid, or the actual
|
|
* subfiling writes have failed for some reason.
|
|
*
|
|
* Notes: Thus function doesn't actually implement vector write.
|
|
* Instead, it converts the vector write call into a series
|
|
* of scalar read calls. Fix this when time permits.
|
|
*
|
|
* Also, it didn't support the sizes and types optimization.
|
|
* I implemented a version of this which is more generous
|
|
* than that currently defined in the RFC. This is good
|
|
* enough for now, but the final version should follow
|
|
* the RFC.
|
|
* JRM -- 10/5/21
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_write_vector(H5FD_t *_file, hid_t dxpl_id, uint32_t count, H5FD_mem_t types[],
|
|
haddr_t addrs[], size_t sizes[], const void *bufs[] /* in */)
|
|
{
|
|
H5FD_subfiling_t *file_ptr = (H5FD_subfiling_t *)_file;
|
|
H5FD_mpio_xfer_t xfer_mode = H5FD_MPIO_INDEPENDENT;
|
|
herr_t ret_value = SUCCEED; /* Return value */
|
|
|
|
assert(file_ptr != NULL); /* sanity check */
|
|
|
|
/* Check arguments
|
|
* RAW - Do we really need to check arguments once again?
|
|
* These have already been checked in H5FD__subfiling_write_vector (see below)!
|
|
*/
|
|
if (!file_ptr)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "file pointer cannot be NULL");
|
|
|
|
if ((!types) && (count > 0))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
|
|
"types parameter can't be NULL if count is positive");
|
|
|
|
if ((!addrs) && (count > 0))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
|
|
"addrs parameter can't be NULL if count is positive");
|
|
|
|
if ((!sizes) && (count > 0))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
|
|
"sizes parameter can't be NULL if count is positive");
|
|
|
|
if ((!bufs) && (count > 0))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
|
|
"bufs parameter can't be NULL if count is positive");
|
|
|
|
/* Get the default dataset transfer property list if the user didn't provide one */
|
|
if (H5P_DEFAULT == dxpl_id) {
|
|
dxpl_id = H5P_DATASET_XFER_DEFAULT;
|
|
}
|
|
else {
|
|
if (true != H5P_isa_class(dxpl_id, H5P_DATASET_XFER))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a data transfer property list");
|
|
}
|
|
/* Call the subfiling IOC write*/
|
|
if (file_ptr->fa.require_ioc) {
|
|
|
|
bool extend_sizes = false;
|
|
bool extend_types = false;
|
|
int k;
|
|
size_t size;
|
|
H5FD_mem_t type;
|
|
haddr_t eoa;
|
|
|
|
assert((count == 0) || (sizes[0] != 0));
|
|
assert((count == 0) || (types[0] != H5FD_MEM_NOLIST));
|
|
|
|
if (H5CX_get_io_xfer_mode(&xfer_mode) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_CONTEXT, H5E_CANTGET, FAIL,
|
|
"can't determine I/O collectivity setting");
|
|
|
|
/* Currently, treat collective calls as independent */
|
|
if (xfer_mode != H5FD_MPIO_INDEPENDENT)
|
|
if (H5CX_set_io_xfer_mode(H5FD_MPIO_INDEPENDENT) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_CONTEXT, H5E_CANTSET, FAIL, "can't set I/O collectivity setting");
|
|
|
|
/* Note that the following code does not let the sub-filing VFD participate
|
|
* in collective calls when there is no data to write. This is not an issue
|
|
* now, as we don't do anything special with collective operations. However
|
|
* this needs to be fixed.
|
|
*/
|
|
for (k = 0; k < (int)count; k++) {
|
|
|
|
if (!extend_sizes) {
|
|
|
|
if (sizes[k] == 0) {
|
|
|
|
extend_sizes = true;
|
|
size = sizes[k - 1];
|
|
}
|
|
else {
|
|
|
|
size = sizes[k];
|
|
}
|
|
}
|
|
|
|
if (!extend_types) {
|
|
|
|
if (types[k] == H5FD_MEM_NOLIST) {
|
|
|
|
extend_types = true;
|
|
type = types[k - 1];
|
|
}
|
|
else {
|
|
|
|
type = types[k];
|
|
}
|
|
}
|
|
|
|
if (HADDR_UNDEF == (eoa = H5FD__subfiling_get_eoa(_file, type)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "driver get_eoa request failed");
|
|
|
|
if ((addrs[k] + size) > eoa)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_OVERFLOW, FAIL,
|
|
"addr overflow, addrs[%d] = %llu, sizes[%d] = %llu, eoa = %llu",
|
|
(int)k, (unsigned long long)(addrs[k]), (int)k,
|
|
(unsigned long long)size, (unsigned long long)eoa);
|
|
|
|
if (H5FD__subfiling_write(_file, type, dxpl_id, addrs[k], size, bufs[k]) != SUCCEED)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "file vector write request failed");
|
|
}
|
|
}
|
|
else {
|
|
/* sec2 driver..
|
|
* Call the subfiling 'direct write' version
|
|
* of subfiling.
|
|
*/
|
|
if (H5FD_write_vector(_file, count, types, addrs, sizes, bufs) != SUCCEED)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "file vector write request failed");
|
|
}
|
|
|
|
done:
|
|
if (xfer_mode != H5FD_MPIO_INDEPENDENT)
|
|
if (H5CX_set_io_xfer_mode(xfer_mode) < 0)
|
|
H5_SUBFILING_DONE_ERROR(H5E_CONTEXT, H5E_CANTSET, FAIL, "can't set I/O collectivity setting");
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FDsubfile__write_vector() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_truncate
|
|
*
|
|
* Purpose: Makes sure that the true file size is the same as
|
|
* the end-of-allocation.
|
|
*
|
|
* Return: SUCCEED/FAIL
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_truncate(H5FD_t *_file, hid_t H5_ATTR_UNUSED dxpl_id, bool H5_ATTR_UNUSED closing)
|
|
{
|
|
H5FD_subfiling_t *file = (H5FD_subfiling_t *)_file;
|
|
herr_t ret_value = SUCCEED; /* Return value */
|
|
|
|
assert(file);
|
|
|
|
/* Extend the file to make sure it's large enough */
|
|
if (!H5_addr_eq(file->eoa, file->last_eoa)) {
|
|
int64_t sf_eof;
|
|
int64_t eoa;
|
|
int mpi_code;
|
|
|
|
if (!H5CX_get_mpi_file_flushing()) {
|
|
if (file->mpi_size > 1)
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Barrier(file->comm)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(FAIL, "MPI_Barrier failed", mpi_code);
|
|
}
|
|
|
|
if (0 == file->mpi_rank) {
|
|
if (H5FD__subfiling__get_real_eof(file->context_id, &sf_eof) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "can't get EOF");
|
|
}
|
|
|
|
if (file->mpi_size > 1) {
|
|
if (MPI_SUCCESS != (mpi_code = MPI_Bcast(&sf_eof, 1, MPI_INT64_T, 0, file->comm)))
|
|
H5_SUBFILING_MPI_GOTO_ERROR(FAIL, "MPI_Bcast failed", mpi_code);
|
|
}
|
|
|
|
if (sf_eof < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "invalid EOF");
|
|
|
|
H5_CHECKED_ASSIGN(eoa, int64_t, file->eoa, haddr_t);
|
|
|
|
/* truncate subfiles */
|
|
/* This is a hack. We should be doing the truncate of the subfiles via calls to
|
|
* H5FD_truncate() with the IOC. However, that system is messed up at present.
|
|
* thus the following hack.
|
|
* JRM -- 12/18/21
|
|
*/
|
|
if (H5FD__subfiling__truncate_sub_files(file->context_id, eoa, file->comm) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTUPDATE, FAIL, "subfile truncate request failed");
|
|
|
|
#if 0 /* TODO: Should be truncated only to size of superblock metadata */
|
|
/* Truncate the HDF5 stub file */
|
|
if (file->mpi_rank == 0) {
|
|
if (H5FD_truncate(file->stub_file, closing) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTUPDATE, FAIL, "stub file truncate request failed");
|
|
}
|
|
#endif
|
|
|
|
/* Reset last file I/O information */
|
|
file->pos = HADDR_UNDEF;
|
|
file->op = OP_UNKNOWN;
|
|
|
|
/* Update the 'last' eoa value */
|
|
file->last_eoa = file->eoa;
|
|
}
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_truncate() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_lock
|
|
*
|
|
* Purpose: To place an advisory lock on a file.
|
|
* The lock type to apply depends on the parameter "rw":
|
|
* true--opens for write: an exclusive lock
|
|
* false--opens for read: a shared lock
|
|
*
|
|
* Return: SUCCEED/FAIL
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#if 0
|
|
static herr_t
|
|
H5FD__subfiling_lock(H5FD_t *_file, bool rw)
|
|
{
|
|
H5FD_subfiling_t *file = (H5FD_subfiling_t *)_file; /* VFD file struct */
|
|
herr_t ret_value = SUCCEED; /* Return value */
|
|
|
|
assert(file);
|
|
|
|
if (file->fa.require_ioc) {
|
|
#ifdef VERBOSE
|
|
puts("Subfiling driver doesn't support file locking");
|
|
#endif
|
|
}
|
|
else {
|
|
if (H5FD_lock(file->sf_file, rw) < 0)
|
|
H5_SUBFILING_SYS_GOTO_ERROR(H5E_FILE, H5E_BADFILE, FAIL, "unable to lock file");
|
|
} /* end if */
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_lock() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_unlock
|
|
*
|
|
* Purpose: To remove the existing lock on the file
|
|
*
|
|
* Return: SUCCEED/FAIL
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_unlock(H5FD_t *_file)
|
|
{
|
|
H5FD_subfiling_t *file = (H5FD_subfiling_t *)_file; /* VFD file struct */
|
|
herr_t ret_value = SUCCEED; /* Return value */
|
|
|
|
assert(file);
|
|
|
|
if (H5FD_unlock(file->sf_file) < 0)
|
|
H5_SUBFILING_SYS_GOTO_ERROR(H5E_FILE, H5E_BADFILE, FAIL, "unable to lock file");
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_unlock() */
|
|
#endif
|
|
|
|
static herr_t
|
|
H5FD__subfiling_del(const char *name, hid_t fapl)
|
|
{
|
|
const H5FD_subfiling_config_t *subfiling_config = NULL;
|
|
H5FD_subfiling_config_t default_config;
|
|
H5P_genplist_t *plist = NULL;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
if (NULL == (plist = H5P_object_verify(fapl, H5P_FILE_ACCESS)))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file access property list");
|
|
|
|
if (H5FD_SUBFILING != H5P_peek_driver(plist))
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "incorrect driver set on FAPL");
|
|
|
|
if (NULL == (subfiling_config = H5P_peek_driver_info(plist))) {
|
|
if (H5FD__subfiling_get_default_config(fapl, &default_config) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL,
|
|
"can't get default Subfiling VFD configuration");
|
|
subfiling_config = &default_config;
|
|
}
|
|
|
|
if (H5FD_delete(name, subfiling_config->ioc_fapl_id) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_FILE, H5E_CANTDELETE, FAIL, "unable to delete file");
|
|
|
|
done:
|
|
if (subfiling_config == &default_config)
|
|
if (H5I_dec_ref(subfiling_config->ioc_fapl_id) < 0)
|
|
H5_SUBFILING_DONE_ERROR(H5E_PLIST, H5E_CANTCLOSEOBJ, FAIL, "unable to close IOC FAPL");
|
|
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: H5FD__subfiling_ctl
|
|
*
|
|
* Purpose: Subfiling version of the ctl callback.
|
|
*
|
|
* The desired operation is specified by the op_code
|
|
* parameter.
|
|
*
|
|
* The flags parameter controls management of op_codes that
|
|
* are unknown to the callback
|
|
*
|
|
* The input and output parameters allow op_code specific
|
|
* input and output
|
|
*
|
|
* At present, the supported op codes are:
|
|
*
|
|
* H5FD_CTL_GET_MPI_COMMUNICATOR_OPCODE
|
|
* H5FD_CTL_GET_MPI_RANK_OPCODE
|
|
* H5FD_CTL_GET_MPI_SIZE_OPCODE
|
|
*
|
|
* Note that these opcodes must be supported by all VFDs that
|
|
* support MPI.
|
|
*
|
|
* Return: Non-negative on success/Negative on failure
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
H5FD__subfiling_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void H5_ATTR_UNUSED *input,
|
|
void **output)
|
|
{
|
|
H5FD_subfiling_t *file = (H5FD_subfiling_t *)_file;
|
|
herr_t ret_value = SUCCEED; /* Return value */
|
|
|
|
/* Sanity checks */
|
|
assert(file);
|
|
assert(H5FD_SUBFILING == file->pub.driver_id);
|
|
|
|
switch (op_code) {
|
|
|
|
case H5FD_CTL_GET_MPI_COMMUNICATOR_OPCODE:
|
|
assert(output);
|
|
assert(*output);
|
|
|
|
/*
|
|
* Return a separate MPI communicator to the caller so
|
|
* that our own MPI calls won't have a chance to conflict
|
|
*/
|
|
if (file->ext_comm == MPI_COMM_NULL) {
|
|
if (H5_mpi_comm_dup(file->comm, &file->ext_comm) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "can't duplicate MPI communicator");
|
|
}
|
|
|
|
**((MPI_Comm **)output) = file->ext_comm;
|
|
break;
|
|
|
|
case H5FD_CTL_GET_MPI_INFO_OPCODE:
|
|
assert(output);
|
|
assert(*output);
|
|
**((MPI_Info **)output) = file->info;
|
|
break;
|
|
|
|
case H5FD_CTL_GET_MPI_RANK_OPCODE:
|
|
assert(output);
|
|
assert(*output);
|
|
**((int **)output) = file->mpi_rank;
|
|
break;
|
|
|
|
case H5FD_CTL_GET_MPI_SIZE_OPCODE:
|
|
assert(output);
|
|
assert(*output);
|
|
**((int **)output) = file->mpi_size;
|
|
break;
|
|
|
|
default: /* unknown op code */
|
|
if (flags & H5FD_CTL_FAIL_IF_UNKNOWN_FLAG) {
|
|
H5_SUBFILING_GOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "unknown op_code and fail if unknown");
|
|
}
|
|
break;
|
|
}
|
|
|
|
done:
|
|
H5_SUBFILING_FUNC_LEAVE_API;
|
|
} /* end H5FD__subfiling_ctl() */
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: init_indep_io
|
|
*
|
|
* Purpose: Utility function to initialize the set of I/O transactions
|
|
* used to communicate with I/O concentrators for read and
|
|
* write I/O operations.
|
|
*
|
|
* Fills the I/O vectors contained in the output arrays
|
|
* `mem_buf_offset`, `target_file_offset` and `io_block_len`.
|
|
* As a consequence of not allowing use of MPI derived
|
|
* datatypes in the VFD layer, we need to accommodate the
|
|
* possibility that large I/O transactions will be required to
|
|
* use multiple I/Os per subfile.
|
|
*
|
|
* Example: Using 4 subfiles, each with 1M stripe-depth; when
|
|
* presented an I/O request for 8MB then at a minimum each
|
|
* subfile will require 2 I/Os of 1MB each. Depending on the
|
|
* starting file offset, the 2 I/Os can instead be 3...
|
|
*
|
|
* To fully describe the I/O transactions for reads and writes
|
|
* the output arrays are therefore arrays of I/O vectors,
|
|
* where each vector has a length of which corresponds to the
|
|
* max number of I/O transactions per subfile. In the example
|
|
* above, these vector lengths can be 2 or 3. The actual
|
|
* length is determined by the 'container_depth' variable.
|
|
*
|
|
* For I/O operations which involve a subset of subfiles, the
|
|
* vector entries for the unused subfiles will have lengths of
|
|
* zero and be empty. The 'container_depth' in this case will
|
|
* always be 1.
|
|
*
|
|
* sf_context (IN)
|
|
* - the subfiling context for the file
|
|
*
|
|
* file_offset (IN)
|
|
* - the starting file offset for I/O
|
|
*
|
|
* io_nelemts (IN)
|
|
* - the number of data elements for the I/O operation
|
|
*
|
|
* dtype_extent (IN)
|
|
* - the extent of the datatype of each data element for
|
|
* the I/O operation
|
|
*
|
|
* max_iovec_len (IN)
|
|
* - the maximum size for a single I/O vector in each of
|
|
* the output arrays `mem_buf_offset`, `io_block_len`
|
|
* and `sf_offset`. NOTE that this routine expects each
|
|
* of these output arrays to have enough space allocated
|
|
* for one I/O vector PER subfile. Therefore, the total
|
|
* size of each output array should be at least
|
|
* `max_iovec_len * num_subfiles`.
|
|
*
|
|
* mem_buf_offset (OUT)
|
|
* - output array of vectors (one vector for each subfile)
|
|
* containing the set of offsets into the memory buffer
|
|
* for I/O
|
|
*
|
|
* target_file_offset (OUT)
|
|
* - output array of vectors (one vector for each subfile)
|
|
* containing the set of offsets into the target file
|
|
*
|
|
* io_block_len (OUT)
|
|
* - output array of vectors (one vector for each subfile)
|
|
* containing the set of block lengths for each source
|
|
* buffer/target file offset.
|
|
*
|
|
* first_subfile_index (OUT)
|
|
* - the index of the first subfile that this I/O operation
|
|
* begins at
|
|
*
|
|
* n_subfiles_used (OUT)
|
|
* - the number of subfiles actually used for this I/O
|
|
* operation, which may be different from the total
|
|
* number of subfiles for the file
|
|
*
|
|
* max_io_req_per_subfile (OUT)
|
|
* - the maximum number of I/O requests to any particular
|
|
* subfile, or the maximum "depth" of each I/O vector
|
|
* in the output arrays.
|
|
*
|
|
* Return: Non-negative on success/Negative on failure
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
init_indep_io(subfiling_context_t *sf_context, int64_t file_offset, size_t io_nelemts, size_t dtype_extent,
|
|
size_t max_iovec_len, int64_t *mem_buf_offset, int64_t *target_file_offset,
|
|
int64_t *io_block_len, int *first_subfile_index, int *n_subfiles_used,
|
|
int64_t *max_io_req_per_subfile)
|
|
{
|
|
int64_t stripe_size = 0;
|
|
int64_t block_size = 0;
|
|
int64_t data_size = 0;
|
|
int64_t stripe_idx = 0;
|
|
int64_t final_stripe_idx = 0;
|
|
int64_t curr_stripe_idx = 0;
|
|
int64_t offset_in_stripe = 0;
|
|
int64_t offset_in_block = 0;
|
|
int64_t final_offset = 0;
|
|
int64_t start_length = 0;
|
|
int64_t final_length = 0;
|
|
int64_t first_subfile = 0;
|
|
int64_t last_subfile = 0;
|
|
int64_t start_row = 0;
|
|
int64_t row_offset = 0;
|
|
int64_t row_stripe_idx_start = 0;
|
|
int64_t row_stripe_idx_final = 0;
|
|
int64_t max_iovec_depth = 0;
|
|
int64_t curr_max_iovec_depth = 0;
|
|
int64_t total_bytes = 0;
|
|
int64_t mem_offset = 0;
|
|
int num_subfiles = 0;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
assert(sf_context);
|
|
assert(sf_context->sf_stripe_size > 0);
|
|
assert(sf_context->sf_blocksize_per_stripe > 0);
|
|
assert(sf_context->sf_num_subfiles > 0);
|
|
assert(sf_context->topology);
|
|
assert(mem_buf_offset);
|
|
assert(target_file_offset);
|
|
assert(io_block_len);
|
|
assert(first_subfile_index);
|
|
assert(n_subfiles_used);
|
|
assert(max_io_req_per_subfile);
|
|
|
|
*first_subfile_index = 0;
|
|
*n_subfiles_used = 0;
|
|
*max_io_req_per_subfile = 0;
|
|
|
|
/*
|
|
* Retrieve the needed fields from the subfiling context.
|
|
*
|
|
* stripe_size
|
|
* - the size of the data striping across the file's subfiles
|
|
* block_size
|
|
* - the size of a "block" across the IOCs, as calculated
|
|
* by the stripe size multiplied by the number of
|
|
* subfiles
|
|
* num_subfiles
|
|
* - the total number of subfiles for the logical
|
|
* HDF5 file
|
|
* num_io_concentrators
|
|
* - the number of I/O concentrators currently being
|
|
* used
|
|
*/
|
|
stripe_size = sf_context->sf_stripe_size;
|
|
block_size = sf_context->sf_blocksize_per_stripe;
|
|
num_subfiles = sf_context->sf_num_subfiles;
|
|
|
|
H5_CHECKED_ASSIGN(data_size, int64_t, (io_nelemts * dtype_extent), size_t);
|
|
|
|
/*
|
|
* Calculate the following from the starting file offset:
|
|
*
|
|
* stripe_idx
|
|
* - a stripe "index" given by the file offset divided by the
|
|
* stripe size. Note that when the file offset equals or exceeds
|
|
* the block size, we simply wrap around. So, for example, if 4
|
|
* subfiles are being used with a stripe size of 1MiB, the block
|
|
* size would be 4MiB and file offset 4096 would have a stripe
|
|
* index of 4 and reside in the same subfile as stripe index 0
|
|
* (offsets 0-1023)
|
|
* offset_in_stripe
|
|
* - the relative offset in the stripe that the starting file
|
|
* offset resides in
|
|
* offset_in_block
|
|
* - the relative offset in the "block" of stripes across the
|
|
* subfiles
|
|
* final_offset
|
|
* - the last offset in the virtual file covered by this I/O
|
|
* operation. Simply the I/O size added to the starting file
|
|
* offset.
|
|
*/
|
|
stripe_idx = file_offset / stripe_size;
|
|
offset_in_stripe = file_offset % stripe_size;
|
|
offset_in_block = file_offset % block_size;
|
|
final_offset = file_offset + data_size;
|
|
|
|
/* Determine the size of data written to the first and last stripes */
|
|
start_length = MIN(data_size, (stripe_size - offset_in_stripe));
|
|
final_length = (start_length == data_size ? 0 : final_offset % stripe_size);
|
|
assert(start_length <= stripe_size);
|
|
assert(final_length <= stripe_size);
|
|
|
|
/*
|
|
* Determine which subfile the I/O request begins in and which
|
|
* "row" the I/O request begins in within the "block" of stripes
|
|
* across the subfiles. Note that "row" here is just a conceptual
|
|
* way to think of how a block of data stripes is laid out across
|
|
* the subfiles. A block's "column" size in bytes is equal to the
|
|
* stripe size multiplied by the number of subfiles. Therefore,
|
|
* file offsets that are multiples of the block size begin a new
|
|
* "row".
|
|
*/
|
|
start_row = stripe_idx / num_subfiles;
|
|
first_subfile = stripe_idx % num_subfiles;
|
|
H5_CHECK_OVERFLOW(first_subfile, int64_t, int);
|
|
|
|
/*
|
|
* Set initial file offset for starting "row"
|
|
* based on the start row index
|
|
*/
|
|
row_offset = start_row * block_size;
|
|
|
|
/*
|
|
* Determine the stripe "index" of the last offset in the
|
|
* virtual file and, from that, determine the subfile that
|
|
* the I/O request ends in.
|
|
*/
|
|
final_stripe_idx = final_offset / stripe_size;
|
|
last_subfile = final_stripe_idx % num_subfiles;
|
|
|
|
/*
|
|
* Determine how "deep" the resulting I/O vectors are at
|
|
* most by calculating the maximum number of "rows" spanned
|
|
* for any particular subfile; e.g. the maximum number of
|
|
* I/O requests for any particular subfile
|
|
*/
|
|
row_stripe_idx_start = stripe_idx - first_subfile;
|
|
row_stripe_idx_final = final_stripe_idx - last_subfile;
|
|
max_iovec_depth = ((row_stripe_idx_final - row_stripe_idx_start) / num_subfiles) + 1;
|
|
|
|
if (last_subfile < first_subfile)
|
|
max_iovec_depth--;
|
|
|
|
/* Set returned parameters early */
|
|
*first_subfile_index = (int)first_subfile;
|
|
*n_subfiles_used = num_subfiles;
|
|
*max_io_req_per_subfile = max_iovec_depth;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: FILE OFFSET = %" PRId64 ", DATA SIZE = %zu, STRIPE SIZE = %" PRId64, __func__,
|
|
file_offset, io_nelemts, stripe_size);
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: FIRST SUBFILE = %" PRId64 ", LAST SUBFILE = %" PRId64 ", "
|
|
"MAX IOVEC DEPTH = %" PRId64 ", START LENGTH = %" PRId64 ", FINAL LENGTH = %" PRId64,
|
|
__func__, first_subfile, last_subfile, max_iovec_depth, start_length, final_length);
|
|
#endif
|
|
|
|
/*
|
|
* Loop through the set of subfiles to determine the various
|
|
* vector components for each. Subfiles whose data size is
|
|
* zero will not have I/O requests passed to them.
|
|
*/
|
|
curr_stripe_idx = stripe_idx;
|
|
curr_max_iovec_depth = max_iovec_depth;
|
|
for (int i = 0, k = (int)first_subfile; i < num_subfiles; i++) {
|
|
int64_t *_mem_buf_offset;
|
|
int64_t *_target_file_offset;
|
|
int64_t *_io_block_len;
|
|
int64_t subfile_bytes = 0;
|
|
int64_t iovec_depth;
|
|
bool is_first = false;
|
|
bool is_last = false;
|
|
size_t output_offset;
|
|
|
|
iovec_depth = curr_max_iovec_depth;
|
|
|
|
/*
|
|
* Setup the pointers to the next set of I/O vectors in
|
|
* the output arrays and clear those vectors
|
|
*/
|
|
output_offset = (size_t)(k)*max_iovec_len;
|
|
_mem_buf_offset = mem_buf_offset + output_offset;
|
|
_target_file_offset = target_file_offset + output_offset;
|
|
_io_block_len = io_block_len + output_offset;
|
|
|
|
memset(_mem_buf_offset, 0, (max_iovec_len * sizeof(*_mem_buf_offset)));
|
|
memset(_target_file_offset, 0, (max_iovec_len * sizeof(*_target_file_offset)));
|
|
memset(_io_block_len, 0, (max_iovec_len * sizeof(*_io_block_len)));
|
|
|
|
if (total_bytes == data_size) {
|
|
*n_subfiles_used = i;
|
|
goto done;
|
|
}
|
|
|
|
if (total_bytes < data_size) {
|
|
int64_t num_full_stripes = iovec_depth;
|
|
|
|
if (k == first_subfile) {
|
|
is_first = true;
|
|
|
|
/*
|
|
* Add partial segment length if not
|
|
* starting on a stripe boundary
|
|
*/
|
|
if (start_length < stripe_size) {
|
|
subfile_bytes += start_length;
|
|
num_full_stripes--;
|
|
}
|
|
}
|
|
|
|
if (k == last_subfile) {
|
|
is_last = true;
|
|
|
|
/*
|
|
* Add partial segment length if not
|
|
* ending on a stripe boundary
|
|
*/
|
|
if (final_length < stripe_size) {
|
|
subfile_bytes += final_length;
|
|
if (num_full_stripes)
|
|
num_full_stripes--;
|
|
}
|
|
}
|
|
|
|
/* Account for subfiles with uniform segments */
|
|
if (!is_first && !is_last) {
|
|
bool thin_uniform_section = false;
|
|
|
|
if (last_subfile >= first_subfile) {
|
|
/*
|
|
* When a subfile has an index value that is greater
|
|
* than both the starting subfile and ending subfile
|
|
* indices, it is a "thinner" section with a smaller
|
|
* I/O vector depth.
|
|
*/
|
|
thin_uniform_section = (k > first_subfile) && (k > last_subfile);
|
|
}
|
|
|
|
if (last_subfile < first_subfile) {
|
|
/*
|
|
* This can also happen when the subfile with the final
|
|
* data segment has a smaller subfile index than the
|
|
* subfile with the first data segment and the current
|
|
* subfile index falls between the two.
|
|
*/
|
|
thin_uniform_section =
|
|
thin_uniform_section || ((last_subfile < k) && (k < first_subfile));
|
|
}
|
|
|
|
if (thin_uniform_section) {
|
|
assert(iovec_depth > 1);
|
|
assert(num_full_stripes > 1);
|
|
|
|
iovec_depth--;
|
|
num_full_stripes--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* After accounting for the length of the initial
|
|
* and/or final data segments, add the combined
|
|
* size of the fully selected I/O stripes to the
|
|
* running bytes total
|
|
*/
|
|
subfile_bytes += num_full_stripes * stripe_size;
|
|
total_bytes += subfile_bytes;
|
|
}
|
|
|
|
_mem_buf_offset[0] = mem_offset;
|
|
_target_file_offset[0] = row_offset + offset_in_block;
|
|
_io_block_len[0] = subfile_bytes;
|
|
|
|
if (num_subfiles > 1) {
|
|
int64_t curr_file_offset = row_offset + offset_in_block;
|
|
|
|
/* Fill the I/O vectors */
|
|
if (is_first) {
|
|
if (is_last) { /* First + Last */
|
|
if (iovec_fill_first_last(sf_context, iovec_depth, subfile_bytes, mem_offset,
|
|
curr_file_offset, start_length, final_length, _mem_buf_offset,
|
|
_target_file_offset, _io_block_len) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTINIT, FAIL, "can't fill I/O vectors");
|
|
}
|
|
else { /* First ONLY */
|
|
if (iovec_fill_first(sf_context, iovec_depth, subfile_bytes, mem_offset, curr_file_offset,
|
|
start_length, _mem_buf_offset, _target_file_offset,
|
|
_io_block_len) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTINIT, FAIL, "can't fill I/O vectors");
|
|
}
|
|
/* Move the memory pointer to the starting location
|
|
* for next subfile I/O request.
|
|
*/
|
|
mem_offset += start_length;
|
|
}
|
|
else if (is_last) { /* Last ONLY */
|
|
if (iovec_fill_last(sf_context, iovec_depth, subfile_bytes, mem_offset, curr_file_offset,
|
|
final_length, _mem_buf_offset, _target_file_offset, _io_block_len) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTINIT, FAIL, "can't fill I/O vectors");
|
|
|
|
mem_offset += stripe_size;
|
|
}
|
|
else { /* Everything else (uniform) */
|
|
if (iovec_fill_uniform(sf_context, iovec_depth, subfile_bytes, mem_offset, curr_file_offset,
|
|
_mem_buf_offset, _target_file_offset, _io_block_len) < 0)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTINIT, FAIL, "can't fill I/O vectors");
|
|
|
|
mem_offset += stripe_size;
|
|
}
|
|
}
|
|
|
|
offset_in_block += _io_block_len[0];
|
|
|
|
k++;
|
|
curr_stripe_idx++;
|
|
|
|
if (k == num_subfiles) {
|
|
k = 0;
|
|
offset_in_block = 0;
|
|
curr_max_iovec_depth = ((final_stripe_idx - curr_stripe_idx) / num_subfiles) + 1;
|
|
|
|
row_offset += block_size;
|
|
}
|
|
|
|
assert(offset_in_block <= block_size);
|
|
}
|
|
|
|
if (total_bytes != data_size)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTINIT, FAIL,
|
|
"total bytes (%" PRId64 ") didn't match data size (%" PRId64 ")!",
|
|
total_bytes, data_size);
|
|
|
|
done:
|
|
return ret_value;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: iovec_fill_first
|
|
*
|
|
* Purpose: Fills I/O vectors for the case where the IOC has the first
|
|
* data segment of the I/O operation.
|
|
*
|
|
* If the 'first_io_len' is sufficient to complete the I/O to
|
|
* the IOC, then the first entry in the I/O vectors is simply
|
|
* filled out with the given starting memory/file offsets and
|
|
* the first I/O size. Otherwise, the remaining entries in the
|
|
* I/O vectors are filled out as data segments with size equal
|
|
* to the stripe size. Each data segment is separated from a
|
|
* previous or following segment by 'sf_blocksize_per_stripe'
|
|
* bytes of data.
|
|
*
|
|
* Return: Non-negative on success/Negative on failure
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
iovec_fill_first(subfiling_context_t *sf_context, int64_t iovec_depth, int64_t target_datasize,
|
|
int64_t start_mem_offset, int64_t start_file_offset, int64_t first_io_len,
|
|
int64_t *mem_offset_out, int64_t *target_file_offset_out, int64_t *io_block_len_out)
|
|
{
|
|
int64_t stripe_size;
|
|
int64_t block_size;
|
|
int64_t total_bytes = 0;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
assert(sf_context);
|
|
assert(mem_offset_out);
|
|
assert(target_file_offset_out);
|
|
assert(io_block_len_out);
|
|
assert(iovec_depth > 0);
|
|
|
|
stripe_size = sf_context->sf_stripe_size;
|
|
block_size = sf_context->sf_blocksize_per_stripe;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: start_mem_offset = %" PRId64 ", start_file_offset = %" PRId64
|
|
", first_io_len = %" PRId64,
|
|
__func__, start_mem_offset, start_file_offset, first_io_len);
|
|
#endif
|
|
|
|
mem_offset_out[0] = start_mem_offset;
|
|
target_file_offset_out[0] = start_file_offset;
|
|
io_block_len_out[0] = first_io_len;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: mem_offset[0] = %" PRId64 ", file_offset[0] = %" PRId64
|
|
", io_block_len[0] = %" PRId64,
|
|
__func__, mem_offset_out[0], target_file_offset_out[0], io_block_len_out[0]);
|
|
#endif
|
|
|
|
if (first_io_len == target_datasize)
|
|
H5_SUBFILING_GOTO_DONE(SUCCEED);
|
|
|
|
if (first_io_len > 0) {
|
|
int64_t offset_in_stripe = start_file_offset % stripe_size;
|
|
int64_t next_mem_offset = block_size - offset_in_stripe;
|
|
int64_t next_file_offset = start_file_offset + (block_size - offset_in_stripe);
|
|
|
|
total_bytes = first_io_len;
|
|
|
|
for (int64_t i = 1; i < iovec_depth; i++) {
|
|
mem_offset_out[i] = next_mem_offset;
|
|
target_file_offset_out[i] = next_file_offset;
|
|
io_block_len_out[i] = stripe_size;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: mem_offset[%" PRId64 "] = %" PRId64 ", file_offset[%" PRId64 "] = %" PRId64
|
|
", io_block_len[%" PRId64 "] = %" PRId64,
|
|
__func__, i, mem_offset_out[i], i, target_file_offset_out[i], i,
|
|
io_block_len_out[i]);
|
|
#endif
|
|
|
|
next_mem_offset += block_size;
|
|
next_file_offset += block_size;
|
|
total_bytes += stripe_size;
|
|
}
|
|
|
|
if (total_bytes != target_datasize)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTINIT, FAIL,
|
|
"total bytes (%" PRId64 ") didn't match target data size (%" PRId64 ")!",
|
|
total_bytes, target_datasize);
|
|
}
|
|
|
|
done:
|
|
return ret_value;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: iovec_fill_last
|
|
*
|
|
* Purpose: Fills I/O vectors for the case where the IOC has the last
|
|
* data segment of the I/O operation.
|
|
*
|
|
* If the 'last_io_len' is sufficient to complete the I/O to
|
|
* the IOC, then the first entry in the I/O vectors is simply
|
|
* filled out with the given starting memory/file offsets and
|
|
* the last I/O size. Otherwise, all entries in the I/O
|
|
* vectors except the last entry are filled out as data
|
|
* segments with size equal to the stripe size. Each data
|
|
* segment is separated from a previous or following segment
|
|
* by 'sf_blocksize_per_stripe' bytes of data. Then, the last
|
|
* entry in the I/O vectors is filled out with the final
|
|
* memory/file offsets and the last I/O size.
|
|
*
|
|
* Return: Non-negative on success/Negative on failure
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
iovec_fill_last(subfiling_context_t *sf_context, int64_t iovec_depth, int64_t target_datasize,
|
|
int64_t start_mem_offset, int64_t start_file_offset, int64_t last_io_len,
|
|
int64_t *mem_offset_out, int64_t *target_file_offset_out, int64_t *io_block_len_out)
|
|
{
|
|
int64_t stripe_size;
|
|
int64_t block_size;
|
|
int64_t total_bytes = 0;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
assert(sf_context);
|
|
assert(mem_offset_out);
|
|
assert(target_file_offset_out);
|
|
assert(io_block_len_out);
|
|
assert(iovec_depth > 0);
|
|
|
|
stripe_size = sf_context->sf_stripe_size;
|
|
block_size = sf_context->sf_blocksize_per_stripe;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: start_mem_offset = %" PRId64 ", start_file_offset = %" PRId64
|
|
", last_io_len = %" PRId64,
|
|
__func__, start_mem_offset, start_file_offset, last_io_len);
|
|
#endif
|
|
|
|
mem_offset_out[0] = start_mem_offset;
|
|
target_file_offset_out[0] = start_file_offset;
|
|
io_block_len_out[0] = last_io_len;
|
|
|
|
if (last_io_len == target_datasize) {
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: mem_offset[0] = %" PRId64 ", file_offset[0] = %" PRId64
|
|
", io_block_len[0] = %" PRId64,
|
|
__func__, mem_offset_out[0], target_file_offset_out[0], io_block_len_out[0]);
|
|
#endif
|
|
|
|
H5_SUBFILING_GOTO_DONE(SUCCEED);
|
|
}
|
|
else {
|
|
int64_t next_mem_offset = start_mem_offset + block_size;
|
|
int64_t next_file_offset = start_file_offset + block_size;
|
|
int64_t i;
|
|
|
|
/*
|
|
* If the last I/O size doesn't cover the target data
|
|
* size, there is at least one full stripe preceding
|
|
* the last I/O block
|
|
*/
|
|
io_block_len_out[0] = stripe_size;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: mem_offset[0] = %" PRId64 ", file_offset[0] = %" PRId64
|
|
", io_block_len[0] = %" PRId64,
|
|
__func__, mem_offset_out[0], target_file_offset_out[0], io_block_len_out[0]);
|
|
#endif
|
|
|
|
total_bytes = stripe_size;
|
|
|
|
for (i = 1; i < iovec_depth - 1;) {
|
|
mem_offset_out[i] = next_mem_offset;
|
|
target_file_offset_out[i] = next_file_offset;
|
|
io_block_len_out[i] = stripe_size;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: mem_offset[%" PRId64 "] = %" PRId64 ", file_offset[%" PRId64 "] = %" PRId64
|
|
", io_block_len[%" PRId64 "] = %" PRId64,
|
|
__func__, i, mem_offset_out[i], i, target_file_offset_out[i], i,
|
|
io_block_len_out[i]);
|
|
#endif
|
|
|
|
next_mem_offset += block_size;
|
|
next_file_offset += block_size;
|
|
total_bytes += stripe_size;
|
|
|
|
i++;
|
|
}
|
|
|
|
mem_offset_out[i] = next_mem_offset;
|
|
target_file_offset_out[i] = next_file_offset;
|
|
io_block_len_out[i] = last_io_len;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: mem_offset[%" PRId64 "] = %" PRId64 ", file_offset[%" PRId64 "] = %" PRId64
|
|
", io_block_len[%" PRId64 "] = %" PRId64,
|
|
__func__, i, mem_offset_out[i], i, target_file_offset_out[i], i,
|
|
io_block_len_out[i]);
|
|
#endif
|
|
|
|
total_bytes += last_io_len;
|
|
|
|
if (total_bytes != target_datasize)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTINIT, FAIL,
|
|
"total bytes (%" PRId64 ") didn't match target data size (%" PRId64 ")!",
|
|
total_bytes, target_datasize);
|
|
}
|
|
|
|
done:
|
|
return ret_value;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: iovec_fill_first_last
|
|
*
|
|
* Purpose: Fills I/O vectors for the case where the IOC has the first
|
|
* and last data segments of the I/O operation. This function
|
|
* is essentially a merge of the iovec_fill_first and
|
|
* iovec_fill_last functions.
|
|
*
|
|
* If the 'first_io_len' is sufficient to complete the I/O to
|
|
* the IOC, then the first entry in the I/O vectors is simply
|
|
* filled out with the given starting memory/file offsets and
|
|
* the first I/O size. Otherwise, the remaining entries in the
|
|
* I/O vectors except the last are filled out as data segments
|
|
* with size equal to the stripe size. Each data segment is
|
|
* separated from a previous or following segment by
|
|
* 'sf_blocksize_per_stripe' bytes of data. Then, the last
|
|
* entry in the I/O vectors is filled out with the final
|
|
* memory/file offsets and the last I/O size.
|
|
*
|
|
* Return: Non-negative on success/Negative on failure
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
iovec_fill_first_last(subfiling_context_t *sf_context, int64_t iovec_depth, int64_t target_datasize,
|
|
int64_t start_mem_offset, int64_t start_file_offset, int64_t first_io_len,
|
|
int64_t last_io_len, int64_t *mem_offset_out, int64_t *target_file_offset_out,
|
|
int64_t *io_block_len_out)
|
|
{
|
|
int64_t stripe_size;
|
|
int64_t block_size;
|
|
int64_t total_bytes = 0;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
assert(sf_context);
|
|
assert(mem_offset_out);
|
|
assert(target_file_offset_out);
|
|
assert(io_block_len_out);
|
|
assert(iovec_depth > 0);
|
|
|
|
stripe_size = sf_context->sf_stripe_size;
|
|
block_size = sf_context->sf_blocksize_per_stripe;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: start_mem_offset = %" PRId64 ", start_file_offset = %" PRId64
|
|
", first_io_len = %" PRId64 ", last_io_len = %" PRId64,
|
|
__func__, start_mem_offset, start_file_offset, first_io_len, last_io_len);
|
|
#endif
|
|
|
|
mem_offset_out[0] = start_mem_offset;
|
|
target_file_offset_out[0] = start_file_offset;
|
|
io_block_len_out[0] = first_io_len;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: mem_offset[0] = %" PRId64 ", file_offset[0] = %" PRId64
|
|
", io_block_len[0] = %" PRId64,
|
|
__func__, mem_offset_out[0], target_file_offset_out[0], io_block_len_out[0]);
|
|
#endif
|
|
|
|
if (first_io_len == target_datasize)
|
|
H5_SUBFILING_GOTO_DONE(SUCCEED);
|
|
|
|
if (first_io_len > 0) {
|
|
int64_t offset_in_stripe = start_file_offset % stripe_size;
|
|
int64_t next_mem_offset = block_size - offset_in_stripe;
|
|
int64_t next_file_offset = start_file_offset + (block_size - offset_in_stripe);
|
|
int64_t i;
|
|
|
|
total_bytes = first_io_len;
|
|
|
|
for (i = 1; i < iovec_depth - 1;) {
|
|
mem_offset_out[i] = next_mem_offset;
|
|
target_file_offset_out[i] = next_file_offset;
|
|
io_block_len_out[i] = stripe_size;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: mem_offset[%" PRId64 "] = %" PRId64 ", file_offset[%" PRId64 "] = %" PRId64
|
|
", io_block_len[%" PRId64 "] = %" PRId64,
|
|
__func__, i, mem_offset_out[i], i, target_file_offset_out[i], i,
|
|
io_block_len_out[i]);
|
|
#endif
|
|
|
|
next_mem_offset += block_size;
|
|
next_file_offset += block_size;
|
|
total_bytes += stripe_size;
|
|
|
|
i++;
|
|
}
|
|
|
|
mem_offset_out[i] = next_mem_offset;
|
|
target_file_offset_out[i] = next_file_offset;
|
|
io_block_len_out[i] = last_io_len;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: mem_offset[%" PRId64 "] = %" PRId64 ", file_offset[%" PRId64 "] = %" PRId64
|
|
", io_block_len[%" PRId64 "] = %" PRId64,
|
|
__func__, i, mem_offset_out[i], i, target_file_offset_out[i], i,
|
|
io_block_len_out[i]);
|
|
#endif
|
|
|
|
total_bytes += last_io_len;
|
|
|
|
if (total_bytes != target_datasize)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTINIT, FAIL,
|
|
"total bytes (%" PRId64 ") didn't match target data size (%" PRId64 ")!",
|
|
total_bytes, target_datasize);
|
|
}
|
|
|
|
done:
|
|
return ret_value;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------
|
|
* Function: iovec_fill_uniform
|
|
*
|
|
* Purpose: Fills I/O vectors for the typical I/O operation when
|
|
* reading data from or writing data to an I/O Concentrator
|
|
* (IOC).
|
|
*
|
|
* Each data segment is of 'stripe_size' length and will be
|
|
* separated from a previous or following segment by
|
|
* 'sf_blocksize_per_stripe' bytes of data.
|
|
*
|
|
* Return: Non-negative on success/Negative on failure
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
static herr_t
|
|
iovec_fill_uniform(subfiling_context_t *sf_context, int64_t iovec_depth, int64_t target_datasize,
|
|
int64_t start_mem_offset, int64_t start_file_offset, int64_t *mem_offset_out,
|
|
int64_t *target_file_offset_out, int64_t *io_block_len_out)
|
|
{
|
|
int64_t stripe_size;
|
|
int64_t block_size;
|
|
int64_t total_bytes = 0;
|
|
herr_t ret_value = SUCCEED;
|
|
|
|
assert(sf_context);
|
|
assert(mem_offset_out);
|
|
assert(target_file_offset_out);
|
|
assert(io_block_len_out);
|
|
assert((iovec_depth > 0) || (target_datasize == 0));
|
|
|
|
stripe_size = sf_context->sf_stripe_size;
|
|
block_size = sf_context->sf_blocksize_per_stripe;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: start_mem_offset = %" PRId64 ", start_file_offset = %" PRId64
|
|
", segment size = %" PRId64,
|
|
__func__, start_mem_offset, start_file_offset, stripe_size);
|
|
#endif
|
|
|
|
mem_offset_out[0] = start_mem_offset;
|
|
target_file_offset_out[0] = start_file_offset;
|
|
io_block_len_out[0] = stripe_size;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: mem_offset[0] = %" PRId64 ", file_offset[0] = %" PRId64
|
|
", io_block_len[0] = %" PRId64,
|
|
__func__, mem_offset_out[0], target_file_offset_out[0], io_block_len_out[0]);
|
|
#endif
|
|
|
|
if (target_datasize == 0) {
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id, "%s: target_datasize = 0", __func__);
|
|
#endif
|
|
|
|
io_block_len_out[0] = 0;
|
|
H5_SUBFILING_GOTO_DONE(SUCCEED);
|
|
}
|
|
|
|
if (target_datasize > stripe_size) {
|
|
int64_t next_mem_offset = start_mem_offset + block_size;
|
|
int64_t next_file_offset = start_file_offset + block_size;
|
|
|
|
total_bytes = stripe_size;
|
|
|
|
for (int64_t i = 1; i < iovec_depth; i++) {
|
|
mem_offset_out[i] = next_mem_offset;
|
|
target_file_offset_out[i] = next_file_offset;
|
|
io_block_len_out[i] = stripe_size;
|
|
|
|
#ifdef H5_SUBFILING_DEBUG
|
|
H5_subfiling_log(sf_context->sf_context_id,
|
|
"%s: mem_offset[%" PRId64 "] = %" PRId64 ", file_offset[%" PRId64 "] = %" PRId64
|
|
", io_block_len[%" PRId64 "] = %" PRId64,
|
|
__func__, i, mem_offset_out[i], i, target_file_offset_out[i], i,
|
|
io_block_len_out[i]);
|
|
#endif
|
|
|
|
next_mem_offset += block_size;
|
|
next_file_offset += block_size;
|
|
total_bytes += stripe_size;
|
|
}
|
|
|
|
if (total_bytes != target_datasize)
|
|
H5_SUBFILING_GOTO_ERROR(H5E_IO, H5E_CANTINIT, FAIL,
|
|
"total bytes (%" PRId64 ") didn't match target data size (%" PRId64 ")!",
|
|
total_bytes, target_datasize);
|
|
}
|
|
|
|
done:
|
|
return ret_value;
|
|
}
|