diff options
author | jhendersonHDF <jhenderson@hdfgroup.org> | 2021-09-23 12:50:00 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-23 12:50:00 (GMT) |
commit | 464000505245195f57f0c5e29a909159bee303de (patch) | |
tree | be8a48f1f49a3b14614b4f98faf176e0828e6fb6 | |
parent | 534435271f63660a8234a52578b3e8f306541686 (diff) | |
download | hdf5-464000505245195f57f0c5e29a909159bee303de.zip hdf5-464000505245195f57f0c5e29a909159bee303de.tar.gz hdf5-464000505245195f57f0c5e29a909159bee303de.tar.bz2 |
VFD ctl feature (#981)
* Added "ctl" callback to the VFD interface, and the associated
H5FDctl() and H5FD_ctl() calls.
Modified the MPIO VFD accordingly -- specifically:
Added ctl() call with op-code support to expose rank, size,
and communicator.
Modified H5FD_mpi_get_rank(), H5FD_mpi_get_size(),
and H5FD_mpi_get_comm() to use the new ctl() callback. In passing
removed the const qualifier from the file parameter of these
functions, as the file parameter of the ctl callback is not
const.
Deleted the old H5FD__mpio_mpi_rank(), H5FD__mpio_mpi_size(), and
H5FD__mpio_communicator() calls from the MPIO VFD.
Deleted H5FD_class_mpi_t from H5FDprivate.h, and modified the
MPIO VFD accordingly. Note that all VFDs now use H5FD_class_t,
with no special class for VFDs that that support MPI.
Some minor touch ups to the Neil's selection I/O mods in passing.
Tested serial and parallel, debug and production on charis and
jelly.
* Reserve a range of VFD "ctl" opcodes for library and experimental usage
* Add "ctl" callbacks to passthrough VFDs
* Add RELEASE.txt entry for "ctl" callback
* Use H5FDopen with H5F_ACC_RDWR flag instead of H5F_ACC_TRUNC in vfd test
* Remove handling of passthrough "ctl" flag from multi VFD
* Move logic for testing H5FD_CTL__TEST_OPCODE into a testing VFD
Revise description of "ctl" callback in RELEASE.txt
Remove unused H5FD_CTL__NUM_OPCODES definition
Fix some warnings in multi VFD
Co-authored-by: mainzer <mainzer#hdfgroup.org>
-rw-r--r-- | release_docs/RELEASE.txt | 25 | ||||
-rw-r--r-- | src/H5FD.c | 105 | ||||
-rw-r--r-- | src/H5FDcore.c | 1 | ||||
-rw-r--r-- | src/H5FDdevelop.h | 2 | ||||
-rw-r--r-- | src/H5FDdirect.c | 1 | ||||
-rw-r--r-- | src/H5FDfamily.c | 1 | ||||
-rw-r--r-- | src/H5FDhdfs.c | 1 | ||||
-rw-r--r-- | src/H5FDlog.c | 1 | ||||
-rw-r--r-- | src/H5FDmirror.c | 1 | ||||
-rw-r--r-- | src/H5FDmpi.c | 90 | ||||
-rw-r--r-- | src/H5FDmpio.c | 223 | ||||
-rw-r--r-- | src/H5FDmulti.c | 51 | ||||
-rw-r--r-- | src/H5FDprivate.h | 14 | ||||
-rw-r--r-- | src/H5FDpublic.h | 71 | ||||
-rw-r--r-- | src/H5FDros3.c | 1 | ||||
-rw-r--r-- | src/H5FDsec2.c | 48 | ||||
-rw-r--r-- | src/H5FDsplitter.c | 60 | ||||
-rw-r--r-- | src/H5FDstdio.c | 1 | ||||
-rw-r--r-- | test/h5test.c | 1 | ||||
-rw-r--r-- | test/vfd.c | 544 |
20 files changed, 1082 insertions, 160 deletions
diff --git a/release_docs/RELEASE.txt b/release_docs/RELEASE.txt index ed12c5e..6043145 100644 --- a/release_docs/RELEASE.txt +++ b/release_docs/RELEASE.txt @@ -471,6 +471,31 @@ New Features Library: -------- + - Adds new "ctl" callback to VFD H5FD_class_t structure + with the following prototype: + + herr_t (*ctl)(H5FD_t *file, uint64_t op_code, + uint64_t flags, const void *input, + void **output); + + This newly-added "ctl" callback allows Virtual File + Drivers to intercept and handle arbitary operations + identified by an operation code. Its parameters are + as follows: + + `file` [in] - A pointer to the file to be operated on + `op_code` [in] - The operation code identifying the + operation to be performed + `flags` [in] - Flags governing the behavior of the + operation performed (see H5FDpublic.h + for a list of valid flags) + `input` [in] - A pointer to arguments passed to the + VFD performing the operation + `output` [out] - A pointer for the receiving VFD to + use for output from the operation + + (JRM - 2021/08/16) + - Change how the release part of version, in major.minor.release is checked for compatibility @@ -1688,6 +1688,111 @@ done: } /* end H5FD_unlock() */ /*------------------------------------------------------------------------- + * Function: H5FDctl + * + * Purpose: Perform a CTL operation. + * + * 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 + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: JRM -- 8/3/21 + * + *------------------------------------------------------------------------- + */ +herr_t +H5FDctl(H5FD_t *file, uint64_t op_code, uint64_t flags, const void *input, void **output) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_API(FAIL) + H5TRACE5("e", "*#ULUL*x**x", file, op_code, flags, input, output); + + /* Check arguments */ + if (!file) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "file pointer cannot be NULL") + + if (!file->cls) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "file class pointer cannot be NULL") + + /* Don't attempt to validate the op code. If appropriate, that will + * be done by the underlying VFD callback, along with the input and + * output parameters. + */ + + /* Call private function */ + if (H5FD_ctl(file, op_code, flags, input, output) < 0) + HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "VFD ctl request failed") + +done: + + FUNC_LEAVE_API(ret_value) + +} /* end H5FDctl() */ + +/*------------------------------------------------------------------------- + * Function: H5FD_ctl + * + * Purpose: Private version of H5FDctl() + * + * 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 + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: JRM -- 8/3/21 + * + *------------------------------------------------------------------------- + */ +herr_t +H5FD_ctl(H5FD_t *file, uint64_t op_code, uint64_t flags, const void *input, void **output) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + /* Sanity checks */ + HDassert(file); + HDassert(file->cls); + + /* Dispatch to driver if the ctl function exists. + * + * If it doesn't, fail if the H5FD_CTL__FAIL_IF_UNKNOWN_FLAG is set. + * + * Otherwise, report success. + */ + if (file->cls->ctl) { + + if ((file->cls->ctl)(file, op_code, flags, input, output) < 0) + + HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "VFD ctl request failed") + } + else if (flags & H5FD_CTL__FAIL_IF_UNKNOWN_FLAG) { + + HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, + "VFD ctl request failed (no ctl callback and fail if unknown flag is set)") + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* end H5FD_ctl() */ + +/*------------------------------------------------------------------------- * Function: H5FD_get_fileno * * Purpose: Quick and dirty routine to retrieve the file's 'fileno' value diff --git a/src/H5FDcore.c b/src/H5FDcore.c index 50288c4..820c74f 100644 --- a/src/H5FDcore.c +++ b/src/H5FDcore.c @@ -183,6 +183,7 @@ static const H5FD_class_t H5FD_core_g = { H5FD__core_lock, /* lock */ H5FD__core_unlock, /* unlock */ H5FD__core_delete, /* del */ + NULL, /* ctl */ H5FD_FLMAP_DICHOTOMY /* fl_map */ }; diff --git a/src/H5FDdevelop.h b/src/H5FDdevelop.h index 57f0a42..4895658 100644 --- a/src/H5FDdevelop.h +++ b/src/H5FDdevelop.h @@ -192,6 +192,7 @@ typedef struct H5FD_class_t { herr_t (*lock)(H5FD_t *file, hbool_t rw); herr_t (*unlock)(H5FD_t *file); herr_t (*del)(const char *name, hid_t fapl); + herr_t (*ctl)(H5FD_t *file, uint64_t op_code, uint64_t flags, const void *input, void **output); H5FD_mem_t fl_map[H5FD_MEM_NTYPES]; } H5FD_class_t; @@ -254,6 +255,7 @@ H5_DLL herr_t H5FDtruncate(H5FD_t *file, hid_t dxpl_id, hbool_t closing); H5_DLL herr_t H5FDlock(H5FD_t *file, hbool_t rw); H5_DLL herr_t H5FDunlock(H5FD_t *file); H5_DLL herr_t H5FDdelete(const char *name, hid_t fapl_id); +H5_DLL herr_t H5FDctl(H5FD_t *file, uint64_t op_code, uint64_t flags, const void *input, void **output); #ifdef __cplusplus } diff --git a/src/H5FDdirect.c b/src/H5FDdirect.c index a1b7b7e..7cca09f 100644 --- a/src/H5FDdirect.c +++ b/src/H5FDdirect.c @@ -172,6 +172,7 @@ static const H5FD_class_t H5FD_direct_g = { H5FD__direct_lock, /* lock */ H5FD__direct_unlock, /* unlock */ H5FD__direct_delete, /* del */ + NULL, /* ctl */ H5FD_FLMAP_DICHOTOMY /* fl_map */ }; diff --git a/src/H5FDfamily.c b/src/H5FDfamily.c index 8cf9f9e..af67c78 100644 --- a/src/H5FDfamily.c +++ b/src/H5FDfamily.c @@ -137,6 +137,7 @@ static const H5FD_class_t H5FD_family_g = { H5FD__family_lock, /* lock */ H5FD__family_unlock, /* unlock */ H5FD__family_delete, /* del */ + NULL, /* ctl */ H5FD_FLMAP_DICHOTOMY /* fl_map */ }; diff --git a/src/H5FDhdfs.c b/src/H5FDhdfs.c index 2c4bff6..ac48b42 100644 --- a/src/H5FDhdfs.c +++ b/src/H5FDhdfs.c @@ -310,6 +310,7 @@ static const H5FD_class_t H5FD_hdfs_g = { NULL, /* lock */ NULL, /* unlock */ NULL, /* del */ + NULL, /* ctl */ H5FD_FLMAP_DICHOTOMY /* fl_map */ }; diff --git a/src/H5FDlog.c b/src/H5FDlog.c index f996b9e..87871ab 100644 --- a/src/H5FDlog.c +++ b/src/H5FDlog.c @@ -212,6 +212,7 @@ static const H5FD_class_t H5FD_log_g = { H5FD__log_lock, /* lock */ H5FD__log_unlock, /* unlock */ H5FD__log_delete, /* del */ + NULL, /* ctl */ H5FD_FLMAP_DICHOTOMY /* fl_map */ }; diff --git a/src/H5FDmirror.c b/src/H5FDmirror.c index 8cbeff6..d539f4d 100644 --- a/src/H5FDmirror.c +++ b/src/H5FDmirror.c @@ -192,6 +192,7 @@ static const H5FD_class_t H5FD_mirror_g = { H5FD__mirror_lock, /* lock */ H5FD__mirror_unlock, /* unlock */ NULL, /* del */ + NULL, /* ctl */ H5FD_FLMAP_DICHOTOMY /* fl_map */ }; diff --git a/src/H5FDmpi.c b/src/H5FDmpi.c index 048b8f3..7eb1463 100644 --- a/src/H5FDmpi.c +++ b/src/H5FDmpi.c @@ -41,26 +41,42 @@ * Programmer: Quincey Koziol * Friday, January 30, 2004 * + * Changes: Reworked function to use the ctl callback so we can get + * rid of H5FD_class_mpi_t. Since there are no real limits + * on what the ctl callback can do, its file parameter can't + * be constant. Thus, I had to remove the const qualifier + * on this functions file parameter as well. Note also the + * circumlocution required to use the ctl callbacks output + * parameter to pass back the rank without introducing + * compiler warnings. + * JRM -- 8/13/21 + * *------------------------------------------------------------------------- */ int -H5FD_mpi_get_rank(const H5FD_t *file) +H5FD_mpi_get_rank(H5FD_t *file) { - const H5FD_class_mpi_t *cls; - - int ret_value; + const H5FD_class_t *cls; + uint64_t flags = H5FD_CTL__FAIL_IF_UNKNOWN_FLAG | H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG; + int rank = -1; + void * rank_ptr = (void *)(&rank); + int ret_value; FUNC_ENTER_NOAPI(FAIL) HDassert(file); - cls = (const H5FD_class_mpi_t *)(file->cls); + cls = (const H5FD_class_t *)(file->cls); HDassert(cls); - HDassert(cls->get_rank); /* All MPI drivers must implement this */ + HDassert(cls->ctl); /* All MPI drivers must implement this */ /* Dispatch to driver */ - if ((ret_value = (cls->get_rank)(file)) < 0) + if ((cls->ctl)(file, H5FD_CTL__GET_MPI_RANK_OPCODE, flags, NULL, &rank_ptr) < 0) HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "driver get_rank request failed") + HDassert(rank >= 0); + + ret_value = rank; + done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5FD_mpi_get_rank() */ @@ -77,25 +93,43 @@ done: * Programmer: Quincey Koziol * Friday, January 30, 2004 * + * Changes: Reworked function to use the ctl callback so we can get + * rid of H5FD_class_mpi_t. Since there are no real limits + * on what the ctl callback can do, its file parameter can't + * be constant. Thus, I had to remove the const qualifier + * on this functions file parameter as well. Note also the + * circumlocution required to use the ctl callbacks output + * parameter to pass back the rank without introducing + * compiler warnings. + * JRM -- 8/13/21 + * *------------------------------------------------------------------------- */ int -H5FD_mpi_get_size(const H5FD_t *file) +H5FD_mpi_get_size(H5FD_t *file) { - const H5FD_class_mpi_t *cls; - int ret_value; + const H5FD_class_t *cls; + uint64_t flags = H5FD_CTL__FAIL_IF_UNKNOWN_FLAG | H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG; + int size = 0; + void * size_ptr = (void *)(&size); + int ret_value; FUNC_ENTER_NOAPI(FAIL) HDassert(file); - cls = (const H5FD_class_mpi_t *)(file->cls); + cls = (const H5FD_class_t *)(file->cls); HDassert(cls); - HDassert(cls->get_size); /* All MPI drivers must implement this */ + HDassert(cls->ctl); /* All MPI drivers must implement this */ /* Dispatch to driver */ - if ((ret_value = (cls->get_size)(file)) < 0) + if ((cls->ctl)(file, H5FD_CTL__GET_MPI_SIZE_OPCODE, flags, NULL, &size_ptr) < 0) HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "driver get_size request failed") + if (0 >= size) + HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "driver get_size request returned bad value") + + ret_value = size; + done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5FD_mpi_get_size() */ @@ -112,25 +146,43 @@ done: * Programmer: Quincey Koziol * Friday, January 30, 2004 * + * Changes: Reworked function to use the ctl callback so we can get + * rid of H5FD_class_mpi_t. Since there are no real limits + * on what the ctl callback can do, its file parameter can't + * be constant. Thus, I had to remove the const qualifier + * on this functions file parameter as well. Note also the + * circumlocution required to use the ctl callbacks output + * parameter to pass back the rank without introducing + * compiler warnings. + * JRM -- 8/13/21 + * *------------------------------------------------------------------------- */ MPI_Comm -H5FD_mpi_get_comm(const H5FD_t *file) +H5FD_mpi_get_comm(H5FD_t *file) { - const H5FD_class_mpi_t *cls; - MPI_Comm ret_value; + const H5FD_class_t *cls; + uint64_t flags = H5FD_CTL__FAIL_IF_UNKNOWN_FLAG | H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG; + MPI_Comm comm = MPI_COMM_NULL; + void * comm_ptr = (void *)(&comm); + MPI_Comm ret_value; FUNC_ENTER_NOAPI(MPI_COMM_NULL) HDassert(file); - cls = (const H5FD_class_mpi_t *)(file->cls); + cls = (const H5FD_class_t *)(file->cls); HDassert(cls); - HDassert(cls->get_comm); /* All MPI drivers must implement this */ + HDassert(cls->ctl); /* All MPI drivers must implement this */ /* Dispatch to driver */ - if ((ret_value = (cls->get_comm)(file)) == MPI_COMM_NULL) + if ((cls->ctl)(file, H5FD_CTL__GET_MPI_COMMUNICATOR_OPCODE, flags, NULL, &comm_ptr) < 0) HGOTO_ERROR(H5E_VFL, H5E_CANTGET, MPI_COMM_NULL, "driver get_comm request failed") + if (comm == MPI_COMM_NULL) + HGOTO_ERROR(H5E_VFL, H5E_CANTGET, MPI_COMM_NULL, "driver get_comm request failed -- bad comm") + + ret_value = comm; + done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5FD_mpi_get_comm() */ diff --git a/src/H5FDmpio.c b/src/H5FDmpio.c index dd40399..7c85897 100644 --- a/src/H5FDmpio.c +++ b/src/H5FDmpio.c @@ -72,66 +72,60 @@ typedef struct H5FD_mpio_t { /* Private Prototypes */ /* Callbacks */ -static herr_t H5FD__mpio_term(void); -static H5FD_t * H5FD__mpio_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr); -static herr_t H5FD__mpio_close(H5FD_t *_file); -static herr_t H5FD__mpio_query(const H5FD_t *_f1, unsigned long *flags); -static haddr_t H5FD__mpio_get_eoa(const H5FD_t *_file, H5FD_mem_t type); -static herr_t H5FD__mpio_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr); -static haddr_t H5FD__mpio_get_eof(const H5FD_t *_file, H5FD_mem_t type); -static herr_t H5FD__mpio_get_handle(H5FD_t *_file, hid_t fapl, void **file_handle); -static herr_t H5FD__mpio_read(H5FD_t *_file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t size, - void *buf); -static herr_t H5FD__mpio_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__mpio_flush(H5FD_t *_file, hid_t dxpl_id, hbool_t closing); -static herr_t H5FD__mpio_truncate(H5FD_t *_file, hid_t dxpl_id, hbool_t closing); -static herr_t H5FD__mpio_delete(const char *filename, hid_t fapl_id); -static int H5FD__mpio_mpi_rank(const H5FD_t *_file); -static int H5FD__mpio_mpi_size(const H5FD_t *_file); -static MPI_Comm H5FD__mpio_communicator(const H5FD_t *_file); +static herr_t H5FD__mpio_term(void); +static H5FD_t *H5FD__mpio_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr); +static herr_t H5FD__mpio_close(H5FD_t *_file); +static herr_t H5FD__mpio_query(const H5FD_t *_f1, unsigned long *flags); +static haddr_t H5FD__mpio_get_eoa(const H5FD_t *_file, H5FD_mem_t type); +static herr_t H5FD__mpio_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr); +static haddr_t H5FD__mpio_get_eof(const H5FD_t *_file, H5FD_mem_t type); +static herr_t H5FD__mpio_get_handle(H5FD_t *_file, hid_t fapl, void **file_handle); +static herr_t H5FD__mpio_read(H5FD_t *_file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t size, + void *buf); +static herr_t H5FD__mpio_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__mpio_flush(H5FD_t *_file, hid_t dxpl_id, hbool_t closing); +static herr_t H5FD__mpio_truncate(H5FD_t *_file, hid_t dxpl_id, hbool_t closing); +static herr_t H5FD__mpio_delete(const char *filename, hid_t fapl_id); +static herr_t H5FD__mpio_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void *input, + void **output); /* The MPIO file driver information */ -static const H5FD_class_mpi_t H5FD_mpio_g = { - { - /* Start of superclass information */ - "mpio", /* name */ - HADDR_MAX, /* maxaddr */ - H5F_CLOSE_SEMI, /* fc_degree */ - H5FD__mpio_term, /* terminate */ - NULL, /* sb_size */ - NULL, /* sb_encode */ - NULL, /* sb_decode */ - 0, /* fapl_size */ - NULL, /* fapl_get */ - NULL, /* fapl_copy */ - NULL, /* fapl_free */ - 0, /* dxpl_size */ - NULL, /* dxpl_copy */ - NULL, /* dxpl_free */ - H5FD__mpio_open, /* open */ - H5FD__mpio_close, /* close */ - NULL, /* cmp */ - H5FD__mpio_query, /* query */ - NULL, /* get_type_map */ - NULL, /* alloc */ - NULL, /* free */ - H5FD__mpio_get_eoa, /* get_eoa */ - H5FD__mpio_set_eoa, /* set_eoa */ - H5FD__mpio_get_eof, /* get_eof */ - H5FD__mpio_get_handle, /* get_handle */ - H5FD__mpio_read, /* read */ - H5FD__mpio_write, /* write */ - H5FD__mpio_flush, /* flush */ - H5FD__mpio_truncate, /* truncate */ - NULL, /* lock */ - NULL, /* unlock */ - H5FD__mpio_delete, /* del */ - H5FD_FLMAP_DICHOTOMY /* fl_map */ - }, /* End of superclass information */ - H5FD__mpio_mpi_rank, /* get_rank */ - H5FD__mpio_mpi_size, /* get_size */ - H5FD__mpio_communicator /* get_comm */ +static const H5FD_class_t H5FD_mpio_g = { + "mpio", /* name */ + HADDR_MAX, /* maxaddr */ + H5F_CLOSE_SEMI, /* fc_degree */ + H5FD__mpio_term, /* terminate */ + NULL, /* sb_size */ + NULL, /* sb_encode */ + NULL, /* sb_decode */ + 0, /* fapl_size */ + NULL, /* fapl_get */ + NULL, /* fapl_copy */ + NULL, /* fapl_free */ + 0, /* dxpl_size */ + NULL, /* dxpl_copy */ + NULL, /* dxpl_free */ + H5FD__mpio_open, /* open */ + H5FD__mpio_close, /* close */ + NULL, /* cmp */ + H5FD__mpio_query, /* query */ + NULL, /* get_type_map */ + NULL, /* alloc */ + NULL, /* free */ + H5FD__mpio_get_eoa, /* get_eoa */ + H5FD__mpio_set_eoa, /* set_eoa */ + H5FD__mpio_get_eof, /* get_eof */ + H5FD__mpio_get_handle, /* get_handle */ + H5FD__mpio_read, /* read */ + H5FD__mpio_write, /* write */ + H5FD__mpio_flush, /* flush */ + H5FD__mpio_truncate, /* truncate */ + NULL, /* lock */ + NULL, /* unlock */ + H5FD__mpio_delete, /* del */ + H5FD__mpio_ctl, /* ctl */ + H5FD_FLMAP_DICHOTOMY /* fl_map */ }; #ifdef H5FDmpio_DEBUG @@ -245,7 +239,7 @@ H5FD_mpio_init(void) /* Register the MPI-IO VFD, if it isn't already */ if (H5I_VFL != H5I_get_type(H5FD_MPIO_g)) - H5FD_MPIO_g = H5FD_register((const H5FD_class_t *)&H5FD_mpio_g, sizeof(H5FD_class_mpi_t), FALSE); + H5FD_MPIO_g = H5FD_register((const H5FD_class_t *)&H5FD_mpio_g, sizeof(H5FD_class_t), FALSE); if (!H5FD_mpio_Debug_inited) { const char *s; /* String for environment variables */ @@ -1802,83 +1796,78 @@ done: } /* end H5FD__mpio_delete() */ /*------------------------------------------------------------------------- - * Function: H5FD__mpio_mpi_rank + * Function: H5FD__mpio_ctl * - * Purpose: Returns the MPI rank for a process + * Purpose: MPIO version of the ctl callback. * - * Return: Success: non-negative - * Failure: negative + * The desired operation is specified by the op_code + * parameter. * - * Programmer: Quincey Koziol - * Thursday, May 16, 2002 + * The flags parameter controls management of op_codes that + * are unknown to the callback * - *------------------------------------------------------------------------- - */ -static int -H5FD__mpio_mpi_rank(const H5FD_t *_file) -{ - const H5FD_mpio_t *file = (const H5FD_mpio_t *)_file; - - FUNC_ENTER_STATIC_NOERR - - /* Sanity checks */ - HDassert(file); - HDassert(H5FD_MPIO == file->pub.driver_id); - - FUNC_LEAVE_NOAPI(file->mpi_rank) -} /* end H5FD__mpio_mpi_rank() */ - -/*------------------------------------------------------------------------- - * Function: H5FD__mpio_mpi_size + * The input and output parameters allow op_code specific + * input and output * - * Purpose: Returns the number of MPI processes + * At present, the supported op codes are: * - * Return: Success: non-negative - * Failure: negative + * H5FD_CTL__GET_MPI_COMMUNICATOR_OPCODE + * H5FD_CTL__GET_MPI_RANK_OPCODE + * H5FD_CTL__GET_MPI_SIZE_OPCODE * - * Programmer: Quincey Koziol - * Thursday, May 16, 2002 + * Note that these opcodes must be supported by all VFDs that + * support MPI. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: JRM -- 8/3/21 * *------------------------------------------------------------------------- */ -static int -H5FD__mpio_mpi_size(const H5FD_t *_file) +static herr_t +H5FD__mpio_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void H5_ATTR_UNUSED *input, + void **output) { - const H5FD_mpio_t *file = (const H5FD_mpio_t *)_file; + H5FD_mpio_t *file = (H5FD_mpio_t *)_file; + herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_STATIC_NOERR + FUNC_ENTER_NOAPI(FAIL) /* Sanity checks */ HDassert(file); HDassert(H5FD_MPIO == file->pub.driver_id); - FUNC_LEAVE_NOAPI(file->mpi_size) -} /* end H5FD__mpio_mpi_size() */ + switch (op_code) { -/*------------------------------------------------------------------------- - * Function: H5FD__mpio_communicator - * - * Purpose: Returns the MPI communicator for the file. - * - * Return: Success: The communicator - * Failure: Can't fail - * - * Programmer: Robb Matzke - * Monday, August 9, 1999 - * - *------------------------------------------------------------------------- - */ -static MPI_Comm -H5FD__mpio_communicator(const H5FD_t *_file) -{ - const H5FD_mpio_t *file = (const H5FD_mpio_t *)_file; + case H5FD_CTL__GET_MPI_COMMUNICATOR_OPCODE: + HDassert(output); + HDassert(*output); + **((MPI_Comm **)output) = file->comm; + break; - FUNC_ENTER_STATIC_NOERR + case H5FD_CTL__GET_MPI_RANK_OPCODE: + HDassert(output); + HDassert(*output); + **((int **)output) = file->mpi_rank; + break; - /* Sanity checks */ - HDassert(file); - HDassert(H5FD_MPIO == file->pub.driver_id); + case H5FD_CTL__GET_MPI_SIZE_OPCODE: + HDassert(output); + HDassert(*output); + **((int **)output) = file->mpi_size; + break; + + default: /* unknown op code */ + if (flags & H5FD_CTL__FAIL_IF_UNKNOWN_FLAG) { + + HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "unknown op_code and fail if unknown") + } + break; + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) - FUNC_LEAVE_NOAPI(file->comm) -} /* end H5FD__mpio_communicator() */ +} /* end H5FD__mpio_ctl() */ #endif /* H5_HAVE_PARALLEL */ diff --git a/src/H5FDmulti.c b/src/H5FDmulti.c index cae4174..0d1967d 100644 --- a/src/H5FDmulti.c +++ b/src/H5FDmulti.c @@ -166,6 +166,8 @@ static herr_t H5FD_multi_truncate(H5FD_t *_file, hid_t dxpl_id, hbool_t closing static herr_t H5FD_multi_lock(H5FD_t *_file, hbool_t rw); static herr_t H5FD_multi_unlock(H5FD_t *_file); static herr_t H5FD_multi_delete(const char *filename, hid_t fapl_id); +static herr_t H5FD_multi_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void *input, + void **output); /* The class struct */ static const H5FD_class_t H5FD_multi_g = { @@ -201,6 +203,7 @@ static const H5FD_class_t H5FD_multi_g = { H5FD_multi_lock, /* lock */ H5FD_multi_unlock, /* unlock */ H5FD_multi_delete, /* del */ + H5FD_multi_ctl, /* ctl */ H5FD_FLMAP_DEFAULT /* fl_map */ }; @@ -2070,6 +2073,54 @@ H5FD_multi_delete(const char *filename, hid_t fapl_id) } /* end H5FD_multi_delete() */ H5_MULTI_GCC_DIAG_ON("format-nonliteral") +/*------------------------------------------------------------------------- + * Function: H5FD_multi_ctl + * + * Purpose: Multi VFD 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, this VFD supports no op codes of its own. + * + * Return: Non-negative on success/Negative on failure + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_multi_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void *input, void **output) +{ + H5FD_multi_t * file = (H5FD_multi_t *)_file; + static const char *func = "H5FD_multi_ctl"; /* Function Name for error reporting */ + herr_t ret_value = 0; + + /* Silence compiler */ + (void)file; + (void)input; + (void)output; + + /* Clear the error stack */ + H5Eclear2(H5E_DEFAULT); + + switch (op_code) { + /* Unknown op code */ + default: + if (flags & H5FD_CTL__FAIL_IF_UNKNOWN_FLAG) + H5Epush_ret(func, H5E_ERR_CLS, H5E_VFL, H5E_FCNTL, + "VFD ctl request failed (unknown op code and fail if unknown flag is set)", -1); + + break; + } + + return ret_value; +} /* end H5FD_multi_ctl() */ + #ifdef H5private_H /* * This is not related to the functionality of the driver code. diff --git a/src/H5FDprivate.h b/src/H5FDprivate.h index c00c123..6dbd483 100644 --- a/src/H5FDprivate.h +++ b/src/H5FDprivate.h @@ -45,13 +45,6 @@ /* Definitions for file MPI type property */ #define H5FD_MPI_XFER_FILE_MPI_TYPE_NAME "H5FD_mpi_file_mpi_type" -/* Sub-class the H5FD_class_t to add more specific functions for MPI-based VFDs */ -typedef struct H5FD_class_mpi_t { - H5FD_class_t super; /* Superclass information & methods */ - int (*get_rank)(const H5FD_t *file); /* Get the MPI rank of a process */ - int (*get_size)(const H5FD_t *file); /* Get the MPI size of a communicator */ - MPI_Comm (*get_comm)(const H5FD_t *file); /* Get the communicator for a file */ -} H5FD_class_mpi_t; #endif /****************************/ @@ -137,6 +130,7 @@ H5_DLL herr_t H5FD_truncate(H5FD_t *file, hbool_t closing); H5_DLL herr_t H5FD_lock(H5FD_t *file, hbool_t rw); H5_DLL herr_t H5FD_unlock(H5FD_t *file); H5_DLL herr_t H5FD_delete(const char *name, hid_t fapl_id); +H5_DLL herr_t H5FD_ctl(H5FD_t *file, uint64_t op_code, uint64_t flags, const void *input, void **output); H5_DLL herr_t H5FD_get_fileno(const H5FD_t *file, unsigned long *filenum); H5_DLL herr_t H5FD_get_vfd_handle(H5FD_t *file, hid_t fapl, void **file_handle); H5_DLL herr_t H5FD_set_base_addr(H5FD_t *file, haddr_t base_addr); @@ -156,9 +150,9 @@ H5_DLL herr_t H5FD_set_mpio_atomicity(H5FD_t *file, hbool_t flag); H5_DLL herr_t H5FD_get_mpio_atomicity(H5FD_t *file, hbool_t *flag); /* Driver specific methods */ -H5_DLL int H5FD_mpi_get_rank(const H5FD_t *file); -H5_DLL int H5FD_mpi_get_size(const H5FD_t *file); -H5_DLL MPI_Comm H5FD_mpi_get_comm(const H5FD_t *_file); +H5_DLL int H5FD_mpi_get_rank(H5FD_t *file); +H5_DLL int H5FD_mpi_get_size(H5FD_t *file); +H5_DLL MPI_Comm H5FD_mpi_get_comm(H5FD_t *file); #endif /* H5_HAVE_PARALLEL */ #endif /* H5FDprivate_H */ diff --git a/src/H5FDpublic.h b/src/H5FDpublic.h index 0cfb072..b8f4f12 100644 --- a/src/H5FDpublic.h +++ b/src/H5FDpublic.h @@ -138,6 +138,77 @@ */ #define H5FD_FEAT_DEFAULT_VFD_COMPATIBLE 0x00008000 +/* ctl function definitions: */ +#define H5FD_CTL_OPC_RESERVED 512 /* Opcodes below this value are reserved for library use */ +#define H5FD_CTL_OPC_EXPER_MIN \ + H5FD_CTL_OPC_RESERVED /* Minimum opcode value available for experimental use \ + */ +#define H5FD_CTL_OPC_EXPER_MAX \ + (H5FD_CTL_OPC_RESERVED + 511) /* Maximum opcode value available for experimental use */ + +/* ctl function op codes: */ +#define H5FD_CTL__INVALID_OPCODE 0 +#define H5FD_CTL__TEST_OPCODE 1 +#define H5FD_CTL__GET_MPI_COMMUNICATOR_OPCODE 2 +#define H5FD_CTL__GET_MPI_RANK_OPCODE 3 +#define H5FD_CTL__GET_MPI_SIZE_OPCODE 4 + +/* ctl function flags: */ + +/* Definitions: + * + * WARNING: While the following definitions of Terminal + * and Passthrough VFDs should be workable for now, they + * have to be adjusted as our use cases for VFDs expand. + * + * JRM -- 8/4/21 + * + * + * Terminal VFD: Lowest VFD in the VFD stack through + * which all VFD calls pass. Note that this definition + * is situational. For example, the sec2 VFD is typically + * terminal. However, in the context of the family file + * driver, it is not -- the family file driver is the + * bottom VFD through which all VFD calls pass, and thus + * it is terminal. + * + * Similarly, on the splitter VFD, a sec2 VFD on the + * R/W channel is terminal, but a sec2 VFD on the W/O + * channel is not. + * + * + * Pass through VFD: Any VFD that relays all VFD calls + * (with the possible exception of some non-I/O related + * calls) to underlying VFD(s). + */ + +/* Unknown op codes should be ignored silently unless the + * H5FD_CTL__FAIL_IF_UNKNOWN_FLAG is set. + * + * On terminal VFDs, unknown op codes should generate an + * error unconditionally if this flag is set. + * + * On pass through VFDs, unknown op codes should be routed + * to the underlying VFD(s) as indicated by any routing + * flags. In the absence of such flags, the VFD should + * generate an error. + */ +#define H5FD_CTL__FAIL_IF_UNKNOWN_FLAG 0x0001 + +/* The H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG is used only + * by non-ternminal VFDs, and only applies to unknown + * opcodes. (known op codes should be handled as + * appropriate.) + * + * If this flag is set for an uknown op code, that + * op code should be passed to the next VFD down + * the VFD stack en-route to the terminal VFD. + * If that VFD does not support the ctl call, the + * pass through VFD should fail or succeed as directed + * by the H5FD_CTL__FAIL_IF_UNKNOWN_FLAG. + */ +#define H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG 0x0002 + /*******************/ /* Public Typedefs */ /*******************/ diff --git a/src/H5FDros3.c b/src/H5FDros3.c index c0361f9..a32d65e 100644 --- a/src/H5FDros3.c +++ b/src/H5FDros3.c @@ -269,6 +269,7 @@ static const H5FD_class_t H5FD_ros3_g = { NULL, /* lock */ NULL, /* unlock */ NULL, /* del */ + NULL, /* ctl */ H5FD_FLMAP_DICHOTOMY /* fl_map */ }; diff --git a/src/H5FDsec2.c b/src/H5FDsec2.c index d823e3c..15103da 100644 --- a/src/H5FDsec2.c +++ b/src/H5FDsec2.c @@ -139,6 +139,8 @@ static herr_t H5FD__sec2_truncate(H5FD_t *_file, hid_t dxpl_id, hbool_t closing static herr_t H5FD__sec2_lock(H5FD_t *_file, hbool_t rw); static herr_t H5FD__sec2_unlock(H5FD_t *_file); static herr_t H5FD__sec2_delete(const char *filename, hid_t fapl_id); +static herr_t H5FD__sec2_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void *input, + void **output); static const H5FD_class_t H5FD_sec2_g = { "sec2", /* name */ @@ -173,6 +175,7 @@ static const H5FD_class_t H5FD_sec2_g = { H5FD__sec2_lock, /* lock */ H5FD__sec2_unlock, /* unlock */ H5FD__sec2_delete, /* del */ + H5FD__sec2_ctl, /* ctl */ H5FD_FLMAP_DICHOTOMY /* fl_map */ }; @@ -1068,3 +1071,48 @@ H5FD__sec2_delete(const char *filename, hid_t H5_ATTR_UNUSED fapl_id) done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5FD__sec2_delete() */ + +/*------------------------------------------------------------------------- + * Function: H5FD__sec2_ctl + * + * Purpose: Sec2 VFD 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, no op codes are supported by this VFD. + * + * Return: Non-negative on success/Negative on failure + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__sec2_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void H5_ATTR_UNUSED *input, + void H5_ATTR_UNUSED **output) +{ + H5FD_sec2_t *file = (H5FD_sec2_t *)_file; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(file); + HDassert(H5FD_SEC2 == file->pub.driver_id); + + switch (op_code) { + /* Unknown op code */ + default: + if (flags & H5FD_CTL__FAIL_IF_UNKNOWN_FLAG) + HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "unknown op_code and fail if unknown flag is set") + break; + } + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__sec2_ctl() */ diff --git a/src/H5FDsplitter.c b/src/H5FDsplitter.c index 73c898a..3113e8b 100644 --- a/src/H5FDsplitter.c +++ b/src/H5FDsplitter.c @@ -129,6 +129,8 @@ static herr_t H5FD__splitter_flush(H5FD_t *_file, hid_t dxpl_id, hbool_t closin static herr_t H5FD__splitter_truncate(H5FD_t *_file, hid_t dxpl_id, hbool_t closing); static herr_t H5FD__splitter_lock(H5FD_t *_file, hbool_t rw); static herr_t H5FD__splitter_unlock(H5FD_t *_file); +static herr_t H5FD__splitter_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void *input, + void **output); static const H5FD_class_t H5FD_splitter_g = { "splitter", /* name */ @@ -163,6 +165,7 @@ static const H5FD_class_t H5FD_splitter_g = { H5FD__splitter_lock, /* lock */ H5FD__splitter_unlock, /* unlock */ NULL, /* del */ + H5FD__splitter_ctl, /* ctl */ H5FD_FLMAP_DICHOTOMY /* fl_map */ }; @@ -1145,6 +1148,63 @@ done: } /* end H5FD__splitter_unlock */ /*------------------------------------------------------------------------- + * Function: H5FD__splitter_ctl + * + * Purpose: Splitter VFD 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, this VFD supports no op codes of its own and + * simply passes ctl calls on to the R/W channel VFD. + * + * Return: Non-negative on success/Negative on failure + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD__splitter_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void *input, void **output) +{ + H5FD_splitter_t *file = (H5FD_splitter_t *)_file; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(file); + HDassert(H5FD_SPLITTER == file->pub.driver_id); + + switch (op_code) { + /* Unknown op code */ + default: + if (flags & H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG) { + /* Pass ctl call down to R/W channel VFD */ + if (H5FDctl(file->rw_file, op_code, flags, input, output) < 0) + HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "VFD ctl request failed") + } + else { + /* If no valid VFD routing flag is specified, fail for unknown op code + * if H5FD_CTL__FAIL_IF_UNKNOWN_FLAG flag is set. + */ + if (flags & H5FD_CTL__FAIL_IF_UNKNOWN_FLAG) + HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, + "VFD ctl request failed (unknown op code and fail if unknown flag is set)") + } + + break; + } + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5FD__splitter_ctl() */ + +/*------------------------------------------------------------------------- * Function: H5FD__splitter_query * * Purpose: Set the flags that this VFL driver is capable of supporting. diff --git a/src/H5FDstdio.c b/src/H5FDstdio.c index 6631325..312263c 100644 --- a/src/H5FDstdio.c +++ b/src/H5FDstdio.c @@ -215,6 +215,7 @@ static const H5FD_class_t H5FD_stdio_g = { H5FD_stdio_lock, /* lock */ H5FD_stdio_unlock, /* unlock */ H5FD_stdio_delete, /* del */ + NULL, /* ctl */ H5FD_FLMAP_DICHOTOMY /* fl_map */ }; diff --git a/test/h5test.c b/test/h5test.c index 09fb5b5..ba0662a 100644 --- a/test/h5test.c +++ b/test/h5test.c @@ -1925,6 +1925,7 @@ static const H5FD_class_t H5FD_dummy_g = { NULL, /* lock */ NULL, /* unlock */ NULL, /* del */ + NULL, /* ctl */ H5FD_FLMAP_DICHOTOMY /* fl_map */ }; @@ -48,20 +48,22 @@ #define DSET2_DIM 4 #endif /* H5_HAVE_DIRECT */ -const char *FILENAME[] = {"sec2_file", /*0*/ - "core_file", /*1*/ - "family_file", /*2*/ - "new_family_v16_", /*3*/ - "multi_file", /*4*/ - "direct_file", /*5*/ - "log_file", /*6*/ - "stdio_file", /*7*/ - "windows_file", /*8*/ - "new_multi_file_v16", /*9*/ - "ro_s3_file", /*10*/ - "splitter_rw_file", /*11*/ - "splitter_wo_file", /*12*/ - "splitter.log", /*13*/ +const char *FILENAME[] = {"sec2_file", /*0*/ + "core_file", /*1*/ + "family_file", /*2*/ + "new_family_v16_", /*3*/ + "multi_file", /*4*/ + "direct_file", /*5*/ + "log_file", /*6*/ + "stdio_file", /*7*/ + "windows_file", /*8*/ + "new_multi_file_v16", /*9*/ + "ro_s3_file", /*10*/ + "splitter_rw_file", /*11*/ + "splitter_wo_file", /*12*/ + "splitter.log", /*13*/ + "ctl_file", /*14*/ + "ctl_splitter_wo_file", /*15*/ NULL}; #define LOG_FILENAME "log_vfd_out.log" @@ -94,6 +96,13 @@ struct splitter_dataset_def { int n_dims; /* rank */ }; +/* Op code type enum for ctl callback test */ +typedef enum { + CTL_OPC_KNOWN_PASSTHROUGH, /* op code known to passthrough VFD */ + CTL_OPC_KNOWN_TERMINAL, /* op code known to terminal VFD */ + CTL_OPC_UNKNOWN /* unknown op code */ +} ctl_test_opc_type; + static int splitter_prepare_file_paths(H5FD_splitter_vfd_config_t *vfd_config, char *filename_rw_out); static int splitter_create_single_file_at(const char *filename, hid_t fapl_id, const struct splitter_dataset_def *data); @@ -104,6 +113,19 @@ static int splitter_RO_test(const struct splitter_dataset_def *data, hid_t child static int splitter_tentative_open_test(hid_t child_fapl_id); static int file_exists(const char *filename, hid_t fapl_id); +static herr_t run_ctl_test(uint64_t op_code, uint64_t flags, ctl_test_opc_type opc_type, hid_t fapl_id); +static H5FD_t *H5FD__ctl_test_vfd_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr); +static herr_t H5FD__ctl_test_vfd_close(H5FD_t *_file); +static haddr_t H5FD__ctl_test_vfd_get_eoa(const H5FD_t *_file, H5FD_mem_t type); +static herr_t H5FD__ctl_test_vfd_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr); +static haddr_t H5FD__ctl_test_vfd_get_eof(const H5FD_t *_file, H5FD_mem_t type); +static herr_t H5FD__ctl_test_vfd_read(H5FD_t *_file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, + size_t size, void *buf); +static herr_t H5FD__ctl_test_vfd_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__ctl_test_vfd_ctl(H5FD_t *_file, uint64_t op_code, uint64_t flags, const void *input, + void **output); + /*------------------------------------------------------------------------- * Function: test_sec2 * @@ -3398,6 +3420,499 @@ error: #undef SPLITTER_TEST_FAULT +/* + * Callback implementations for ctl feature testing VFD + */ +static H5FD_t * +H5FD__ctl_test_vfd_open(const char H5_ATTR_UNUSED *name, unsigned H5_ATTR_UNUSED flags, + hid_t H5_ATTR_UNUSED fapl_id, haddr_t H5_ATTR_UNUSED maxaddr) +{ + return HDcalloc(1, sizeof(H5FD_t)); +} +static herr_t +H5FD__ctl_test_vfd_close(H5FD_t H5_ATTR_UNUSED *_file) +{ + HDfree(_file); + return SUCCEED; +} +static haddr_t +H5FD__ctl_test_vfd_get_eoa(const H5FD_t H5_ATTR_UNUSED *file, H5FD_mem_t H5_ATTR_UNUSED type) +{ + return HADDR_UNDEF; +} +static herr_t +H5FD__ctl_test_vfd_set_eoa(H5FD_t H5_ATTR_UNUSED *_file, H5FD_mem_t H5_ATTR_UNUSED type, + haddr_t H5_ATTR_UNUSED addr) +{ + return FAIL; +} +static haddr_t +H5FD__ctl_test_vfd_get_eof(const H5FD_t H5_ATTR_UNUSED *file, H5FD_mem_t H5_ATTR_UNUSED type) +{ + return HADDR_UNDEF; +} +static herr_t +H5FD__ctl_test_vfd_read(H5FD_t H5_ATTR_UNUSED *_file, H5FD_mem_t H5_ATTR_UNUSED type, + hid_t H5_ATTR_UNUSED fapl_id, haddr_t H5_ATTR_UNUSED addr, size_t H5_ATTR_UNUSED size, + void H5_ATTR_UNUSED *buf) +{ + return FAIL; +} +static herr_t +H5FD__ctl_test_vfd_write(H5FD_t H5_ATTR_UNUSED *_file, H5FD_mem_t H5_ATTR_UNUSED type, + hid_t H5_ATTR_UNUSED fapl_id, haddr_t H5_ATTR_UNUSED addr, + size_t H5_ATTR_UNUSED size, const void H5_ATTR_UNUSED *buf) +{ + return FAIL; +} +static herr_t +H5FD__ctl_test_vfd_ctl(H5FD_t H5_ATTR_UNUSED *_file, uint64_t op_code, uint64_t flags, + const void H5_ATTR_UNUSED *input, void H5_ATTR_UNUSED **output) +{ + herr_t ret_value = SUCCEED; + + switch (op_code) { + /* Op code for testing purposes */ + case H5FD_CTL__TEST_OPCODE: + break; + + /* Unknown op code */ + default: + if (flags & H5FD_CTL__FAIL_IF_UNKNOWN_FLAG) + ret_value = FAIL; + break; + } + + return ret_value; +} + +/* Minimal VFD for ctl feature tests */ +static const H5FD_class_t H5FD_ctl_test_vfd_g = { + "ctl_test_vfd", /* name */ + HADDR_MAX, /* maxaddr */ + H5F_CLOSE_SEMI, /* fc_degree */ + NULL, /* terminate */ + NULL, /* sb_size */ + NULL, /* sb_encode */ + NULL, /* sb_decode */ + 0, /* fapl_size */ + NULL, /* fapl_get */ + NULL, /* fapl_copy */ + NULL, /* fapl_free */ + 0, /* dxpl_size */ + NULL, /* dxpl_copy */ + NULL, /* dxpl_free */ + H5FD__ctl_test_vfd_open, /* open */ + H5FD__ctl_test_vfd_close, /* close */ + NULL, /* cmp */ + NULL, /* query */ + NULL, /* get_type_map */ + NULL, /* alloc */ + NULL, /* free */ + H5FD__ctl_test_vfd_get_eoa, /* get_eoa */ + H5FD__ctl_test_vfd_set_eoa, /* set_eoa */ + H5FD__ctl_test_vfd_get_eof, /* get_eof */ + NULL, /* get_handle */ + H5FD__ctl_test_vfd_read, /* read */ + H5FD__ctl_test_vfd_write, /* write */ + NULL, /* flush */ + NULL, /* truncate */ + NULL, /* lock */ + NULL, /* unlock */ + NULL, /* del */ + H5FD__ctl_test_vfd_ctl, /* ctl */ + H5FD_FLMAP_DICHOTOMY /* fl_map */ +}; + +/*------------------------------------------------------------------------- + * Function: run_ctl_test + * + * Purpose: Helper method for VFD "ctl" callback test + * + * Return: Non-negative on success/Negative on failure + * + *------------------------------------------------------------------------- + */ +static herr_t +run_ctl_test(uint64_t op_code, uint64_t flags, ctl_test_opc_type opc_type, hid_t fapl_id) +{ + hbool_t fail_if_unknown = FALSE; + hbool_t routing_flag_set = FALSE; + hbool_t is_passthrough_vfd = FALSE; + hbool_t expect_fail = FALSE; + H5FD_t *file_drv_ptr = NULL; + herr_t ctl_result = SUCCEED; + hid_t driver_id = H5I_INVALID_HID; + char filename[1024]; + + /* Check for a few ctl function flags */ + fail_if_unknown = (flags & H5FD_CTL__FAIL_IF_UNKNOWN_FLAG); + routing_flag_set = (flags & H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG); + + /* Determine if the top-level VFD is a passthrough VFD */ + if ((driver_id = H5Pget_driver(fapl_id)) < 0) + PUTS_ERROR("couldn't get VFD ID from FAPL"); + + is_passthrough_vfd = ((driver_id == H5FD_SPLITTER) || (driver_id == H5FD_MULTI)); + + /* + * "Open" testing file. Note that our VFD for testing the ctl + * feature doesn't actually create or open files, so we don't + * need to create the testing file; we just need the VFD to + * give us a pointer to a H5FD_t structure. + */ + h5_fixname(FILENAME[14], fapl_id, filename, sizeof(filename)); + if (NULL == (file_drv_ptr = H5FDopen(filename, H5F_ACC_RDWR, fapl_id, HADDR_UNDEF))) + PUTS_ERROR("couldn't get pointer to H5FD_t structure"); + + /* Determine whether the H5FDctl call is expected to fail */ + expect_fail = fail_if_unknown && (CTL_OPC_UNKNOWN == opc_type); + if (is_passthrough_vfd) { + /* Should fail if op code is unknown to passthrough VFD + * (but known to terminal VFD), no routing flag is specified + * and the "fail if unknown" flag is specified. + */ + expect_fail = + expect_fail || ((CTL_OPC_KNOWN_TERMINAL == opc_type) && !routing_flag_set && fail_if_unknown); + } + + /* Issue opcode to VFD */ + if (expect_fail) { + H5E_BEGIN_TRY + { + ctl_result = H5FDctl(file_drv_ptr, op_code, flags, NULL, NULL); + } + H5E_END_TRY; + } + else + ctl_result = H5FDctl(file_drv_ptr, op_code, flags, NULL, NULL); + + /* Verify result of H5FDctl call */ + if (expect_fail) { + if (ctl_result == SUCCEED) + PUTS_ERROR("H5FDctl call succeeded when it should have failed"); + } + else { + if (ctl_result != SUCCEED) + PUTS_ERROR("H5FDctl call failed when it should have succeeded"); + } + + /* Close H5FD_t structure pointer */ + if (H5FDclose(file_drv_ptr) < 0) + PUTS_ERROR("couldn't close H5FD_t structure pointer"); + file_drv_ptr = NULL; + + return 0; + +error: + H5E_BEGIN_TRY + { + H5FDclose(file_drv_ptr); + } + H5E_END_TRY; + + return -1; +} + +/*------------------------------------------------------------------------- + * Function: test_ctl + * + * Purpose: Tests the VFD "ctl" callback + * + * Return: Non-negative on success/Negative on failure + * + *------------------------------------------------------------------------- + */ +static herr_t +test_ctl(void) +{ + H5FD_splitter_vfd_config_t *splitter_config = NULL; + uint64_t op_code; + uint64_t flags; + hid_t driver_id = H5I_INVALID_HID; + hid_t fapl_id = H5I_INVALID_HID; + hid_t sub_fapl_id = H5I_INVALID_HID; + + TESTING("VFD ctl callback"); + HDputs(""); + + /* Register VFD for test */ + if ((driver_id = H5FDregister(&H5FD_ctl_test_vfd_g)) < 0) + PUTS_ERROR("couldn't register VFD for testing"); + + if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + PUTS_ERROR("couldn't create FAPL"); + if (H5Pset_driver(fapl_id, driver_id, NULL) < 0) + PUTS_ERROR("couldn't set testing VFD on FAPL"); + + TESTING_2("known op code to terminal VFD (without fail on unknown flag)") + + op_code = H5FD_CTL__TEST_OPCODE; + flags = 0; + + /* H5FDctl call should succeed normally */ + if (run_ctl_test(op_code, flags, CTL_OPC_KNOWN_TERMINAL, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("known op code to terminal VFD (with fail on unknown flag)") + + op_code = H5FD_CTL__TEST_OPCODE; + flags = H5FD_CTL__FAIL_IF_UNKNOWN_FLAG; + + /* H5FDctl call should succeed normally */ + if (run_ctl_test(op_code, flags, CTL_OPC_KNOWN_TERMINAL, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("known op code to terminal VFD (without fail on unknown flag/route to terminal VFD)") + + op_code = H5FD_CTL__TEST_OPCODE; + flags = H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG; + + /* H5FDctl call should succeed normally */ + if (run_ctl_test(op_code, flags, CTL_OPC_KNOWN_TERMINAL, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("known op code to terminal VFD (with fail on unknown flag/route to terminal VFD)") + + op_code = H5FD_CTL__TEST_OPCODE; + flags = H5FD_CTL__FAIL_IF_UNKNOWN_FLAG | H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG; + + /* H5FDctl call should succeed normally */ + if (run_ctl_test(op_code, flags, CTL_OPC_KNOWN_TERMINAL, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("unknown op code to terminal VFD (without fail on unknown flag)") + + op_code = H5FD_CTL_OPC_RESERVED; + flags = 0; + + /* H5FDctl call should silently ignore unknown op code and succeed */ + if (run_ctl_test(op_code, flags, CTL_OPC_UNKNOWN, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("unknown op code to terminal VFD (with fail on unknown flag)") + + op_code = H5FD_CTL_OPC_RESERVED; + flags = H5FD_CTL__FAIL_IF_UNKNOWN_FLAG; + + /* H5FDctl call should fail due to 'fail if unknown' flag being specified */ + if (run_ctl_test(op_code, flags, CTL_OPC_UNKNOWN, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("unknown op code to terminal VFD (without fail on unknown flag/route to terminal VFD)") + + op_code = H5FD_CTL_OPC_RESERVED; + flags = H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG; + + /* H5FDctl call should silently ignore unknown op code and succeed */ + if (run_ctl_test(op_code, flags, CTL_OPC_UNKNOWN, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("unknown op code to terminal VFD (with fail on unknown flag/route to terminal VFD)") + + op_code = H5FD_CTL_OPC_RESERVED; + flags = H5FD_CTL__FAIL_IF_UNKNOWN_FLAG | H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG; + + /* H5FDctl call should fail due to 'fail if unknown' flag being specified */ + if (run_ctl_test(op_code, flags, CTL_OPC_UNKNOWN, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + /* Set up splitter VFD config */ + if (NULL == (splitter_config = HDcalloc(1, sizeof(H5FD_splitter_vfd_config_t)))) + TEST_ERROR; + + splitter_config->magic = H5FD_SPLITTER_MAGIC; + splitter_config->version = H5FD_CURR_SPLITTER_VFD_CONFIG_VERSION; + splitter_config->ignore_wo_errs = TRUE; + splitter_config->rw_fapl_id = H5P_DEFAULT; + splitter_config->wo_fapl_id = H5P_DEFAULT; + h5_fixname(FILENAME[15], splitter_config->wo_fapl_id, splitter_config->wo_path, H5FD_SPLITTER_PATH_MAX); + + if ((sub_fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) + PUTS_ERROR("couldn't create FAPL"); + if (H5Pset_driver(sub_fapl_id, driver_id, NULL) < 0) + PUTS_ERROR("couldn't set testing VFD on FAPL"); + splitter_config->rw_fapl_id = sub_fapl_id; + + if (H5Pset_fapl_splitter(fapl_id, splitter_config) < 0) + PUTS_ERROR("couldn't set splitter VFD on FAPL"); + + TESTING_2("known op code through passthrough VFD to terminal VFD (without fail on unknown flag/no " + "routing flag)") + + op_code = H5FD_CTL__TEST_OPCODE; + flags = 0; + + /* + * H5FDctl call should silently ignore unknown op code in + * passthrough VFD since no routing flag is specified and + * 'fail if unknown' flag is not specified. + */ + if (run_ctl_test(op_code, flags, CTL_OPC_KNOWN_TERMINAL, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2( + "known op code through passthrough VFD to terminal VFD (with fail on unknown flag/no routing flag)") + + op_code = H5FD_CTL__TEST_OPCODE; + flags = H5FD_CTL__FAIL_IF_UNKNOWN_FLAG; + + /* + * H5FDctl call should fail since op code is unknown to + * passthrough VFD (though known to terminal VFD), no + * routing flag is specified and the 'fail if unknown' + * flag is specified. + */ + if (run_ctl_test(op_code, flags, CTL_OPC_KNOWN_TERMINAL, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("known op code through passthrough VFD to terminal VFD (without fail on unknown flag/route to " + "terminal VFD)") + + op_code = H5FD_CTL__TEST_OPCODE; + flags = H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG; + + /* + * H5Dctl call should succeed since the passthrough VFD + * doesn't recognize the op code, but has been instructed + * to route it down to the terminal VFD. + */ + if (run_ctl_test(op_code, flags, CTL_OPC_KNOWN_TERMINAL, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("known op code through passthrough VFD to terminal VFD (with fail on unknown flag/route to " + "terminal VFD)") + + op_code = H5FD_CTL__TEST_OPCODE; + flags = H5FD_CTL__FAIL_IF_UNKNOWN_FLAG | H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG; + + /* + * H5Dctl call should succeed since the passthrough VFD + * doesn't recognize the op code, but has been instructed + * to route it down to the terminal VFD. + */ + if (run_ctl_test(op_code, flags, CTL_OPC_KNOWN_TERMINAL, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("unknown op code to passthrough VFD (without fail on unknown flag)") + + op_code = H5FD_CTL_OPC_RESERVED; + flags = 0; + + /* + * H5FDctl call should silently ignore unknown op code in + * passthrough VFD since no routing flag is specified and + * 'fail if unknown' flag is not specified. + */ + if (run_ctl_test(op_code, flags, CTL_OPC_UNKNOWN, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("unknown op code to passthrough VFD (with fail on unknown flag)") + + op_code = H5FD_CTL_OPC_RESERVED; + flags = H5FD_CTL__FAIL_IF_UNKNOWN_FLAG; + + /* + * H5FDctl call should fail since op code is unknown to + * passthrough VFD, no routing flag is specified and the + * 'fail if unknown' flag is specified. + */ + if (run_ctl_test(op_code, flags, CTL_OPC_UNKNOWN, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("unknown op code to passthrough VFD (without fail on unknown flag/route to terminal VFD)") + + op_code = H5FD_CTL_OPC_RESERVED; + flags = H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG; + + /* + * H5Dctl call should succeed since the passthrough VFD + * doesn't recognize the op code, but has been instructed + * to route it down to the terminal VFD and the 'fail if + * unknown' flag has not been specified. Therefore, the + * terminal VFD should silently ignore the unknown op + * code. + */ + if (run_ctl_test(op_code, flags, CTL_OPC_UNKNOWN, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("unknown op code to passthrough VFD (with fail on unknown flag/route to terminal VFD)") + + op_code = H5FD_CTL_OPC_RESERVED; + flags = H5FD_CTL__FAIL_IF_UNKNOWN_FLAG | H5FD_CTL__ROUTE_TO_TERMINAL_VFD_FLAG; + + /* + * H5Dctl call should fail since the passthrough VFD + * doesn't recognize the op code, but has been instructed + * to route it down to the terminal VFD and the 'fail if + * unknown' flag has been specified. Therefore, the + * terminal VFD will throw an error for the unknown op + * code. + */ + if (run_ctl_test(op_code, flags, CTL_OPC_UNKNOWN, fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + TESTING_2("test cleanup") + + HDfree(splitter_config); + + if (H5FDunregister(driver_id) < 0) + TEST_ERROR; + if (H5Pclose(sub_fapl_id) < 0) + TEST_ERROR; + if (H5Pclose(fapl_id) < 0) + TEST_ERROR; + + PASSED(); + + return 0; + +error: + H5E_BEGIN_TRY + { + if (splitter_config) + HDfree(splitter_config); + H5FDunregister(driver_id); + H5Pclose(sub_fapl_id); + H5Pclose(fapl_id); + } + H5E_END_TRY; + + return -1; +} + /*------------------------------------------------------------------------- * Function: main * @@ -3429,6 +3944,7 @@ main(void) nerrors += test_windows() < 0 ? 1 : 0; nerrors += test_ros3() < 0 ? 1 : 0; nerrors += test_splitter() < 0 ? 1 : 0; + nerrors += test_ctl() < 0 ? 1 : 0; if (nerrors) { HDprintf("***** %d Virtual File Driver TEST%s FAILED! *****\n", nerrors, nerrors > 1 ? "S" : ""); |