From 63b63d48c3ce807639e787723b73eeb28cf80738 Mon Sep 17 00:00:00 2001 From: Jeff Squyres Date: Sat, 26 Jul 2008 12:23:30 +0000 Subject: [PATCH] Fixes trac:1370, #1147 Update the version of ROMIO to that which was contained in MPICH2-1.0.7, plus a few patches from the upstream ROMIO maintainers (because OMPI uses a few code paths in ROMIO that MPICH2 does not; there were a few compile bugs in the ROMIO from MPICH2-1.0.7). Added an info MCA param to be able to tell which version of ROMIO is contained in OMPI: io_romio_version. Many, many thanks to romio-maint@mcs.anl.gov for all their help in integrating this new version of ROMIO into Open MPI. This commit was SVN r19045. The following Trac tickets were found above: Ticket 1370 --> https://svn.open-mpi.org/trac/ompi/ticket/1370 --- NEWS | 2 +- ompi/mca/io/base/io_base_request.c | 8 +- ompi/mca/io/base/io_base_request.h | 8 +- ompi/mca/io/romio/configure.m4 | 9 +- ompi/mca/io/romio/romio/Makefile.am | 5 +- ompi/mca/io/romio/romio/README | 2 +- ompi/mca/io/romio/romio/adio/Makefile.am | 68 +- .../io/romio/romio/adio/ad_bgl/.state-cache | 58 + .../io/romio/romio/adio/ad_bgl/Makefile.in | 47 + ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl.c | 57 + ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl.h | 94 ++ .../io/romio/romio/adio/ad_bgl/ad_bgl_aggrs.c | 970 +++++++++++ .../io/romio/romio/adio/ad_bgl/ad_bgl_aggrs.h | 98 ++ .../io/romio/romio/adio/ad_bgl/ad_bgl_close.c | 52 + .../io/romio/romio/adio/ad_bgl/ad_bgl_fcntl.c | 57 + .../io/romio/romio/adio/ad_bgl/ad_bgl_getsh.c | 84 + .../io/romio/romio/adio/ad_bgl/ad_bgl_hints.c | 338 ++++ .../io/romio/romio/adio/ad_bgl/ad_bgl_open.c | 114 ++ .../io/romio/romio/adio/ad_bgl/ad_bgl_pset.c | 109 ++ .../io/romio/romio/adio/ad_bgl/ad_bgl_pset.h | 80 + .../romio/romio/adio/ad_bgl/ad_bgl_rdcoll.c | 1309 +++++++++++++++ .../io/romio/romio/adio/ad_bgl/ad_bgl_read.c | 496 ++++++ .../io/romio/romio/adio/ad_bgl/ad_bgl_setsh.c | 68 + .../romio/romio/adio/ad_bgl/ad_bgl_tuning.c | 104 ++ .../romio/romio/adio/ad_bgl/ad_bgl_tuning.h | 94 ++ .../romio/romio/adio/ad_bgl/ad_bgl_wrcoll.c | 1451 +++++++++++++++++ .../io/romio/romio/adio/ad_bgl/ad_bgl_write.c | 546 +++++++ .../romio/adio/ad_bglockless/.state-cache | 7 + .../romio/adio/ad_bglockless/Makefile.am | 24 + .../romio/adio/ad_bglockless/Makefile.in | 49 + .../romio/adio/ad_bglockless/ad_bglockless.c | 41 + .../romio/romio/adio/ad_lustre/.state-cache | 22 + .../io/romio/romio/adio/ad_lustre/Makefile.am | 31 + .../io/romio/romio/adio/ad_lustre/Makefile.in | 47 + ompi/mca/io/romio/romio/adio/ad_lustre/README | 40 + .../io/romio/romio/adio/ad_lustre/ad_lustre.c | 39 + .../io/romio/romio/adio/ad_lustre/ad_lustre.h | 64 + .../romio/adio/ad_lustre/ad_lustre_fcntl.c | 97 ++ .../romio/adio/ad_lustre/ad_lustre_hints.c | 140 ++ .../romio/adio/ad_lustre/ad_lustre_open.c | 134 ++ .../romio/adio/ad_lustre/ad_lustre_rwcontig.c | 187 +++ ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs.c | 6 +- ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs.h | 5 +- .../io/romio/romio/adio/ad_nfs/ad_nfs_done.c | 110 -- .../io/romio/romio/adio/ad_nfs/ad_nfs_fcntl.c | 12 + .../io/romio/romio/adio/ad_nfs/ad_nfs_getsh.c | 30 + .../io/romio/romio/adio/ad_nfs/ad_nfs_iread.c | 36 +- .../romio/romio/adio/ad_nfs/ad_nfs_iwrite.c | 105 +- .../io/romio/romio/adio/ad_nfs/ad_nfs_open.c | 15 +- .../io/romio/romio/adio/ad_nfs/ad_nfs_read.c | 95 +- .../io/romio/romio/adio/ad_nfs/ad_nfs_setsh.c | 12 + .../io/romio/romio/adio/ad_nfs/ad_nfs_wait.c | 3 + .../io/romio/romio/adio/ad_nfs/ad_nfs_write.c | 170 +- .../mca/io/romio/romio/adio/ad_ntfs/ad_ntfs.h | 4 +- .../romio/romio/adio/ad_ntfs/ad_ntfs_close.c | 4 +- .../romio/romio/adio/ad_ntfs/ad_ntfs_done.c | 84 +- .../romio/romio/adio/ad_ntfs/ad_ntfs_fcntl.c | 8 +- .../romio/romio/adio/ad_ntfs/ad_ntfs_flush.c | 4 +- .../romio/romio/adio/ad_ntfs/ad_ntfs_iread.c | 18 +- .../romio/romio/adio/ad_ntfs/ad_ntfs_iwrite.c | 282 +++- .../romio/romio/adio/ad_ntfs/ad_ntfs_open.c | 8 +- .../romio/romio/adio/ad_ntfs/ad_ntfs_read.c | 40 +- .../romio/romio/adio/ad_ntfs/ad_ntfs_resize.c | 8 +- .../romio/romio/adio/ad_ntfs/ad_ntfs_wait.c | 72 +- .../romio/romio/adio/ad_ntfs/ad_ntfs_write.c | 40 +- .../io/romio/romio/adio/ad_panfs/Makefile.am | 6 +- .../io/romio/romio/adio/ad_panfs/ad_panfs.c | 11 +- .../io/romio/romio/adio/ad_panfs/ad_panfs.h | 50 +- .../romio/adio/ad_panfs/ad_panfs_hints.c | 3 +- .../romio/romio/adio/ad_panfs/ad_panfs_open.c | 214 ++- .../romio/romio/adio/ad_panfs/ad_panfs_read.c | 68 + .../romio/adio/ad_panfs/ad_panfs_resize.c | 49 + .../romio/adio/ad_panfs/ad_panfs_write.c | 68 + .../io/romio/romio/adio/ad_pfs/ad_pfs_iread.c | 2 +- .../io/romio/romio/adio/ad_pfs/ad_pfs_open.c | 9 - .../io/romio/romio/adio/ad_pfs/ad_pfs_read.c | 27 - .../io/romio/romio/adio/ad_pfs/ad_pfs_write.c | 27 - .../romio/romio/adio/ad_piofs/ad_piofs_open.c | 10 - .../romio/romio/adio/ad_piofs/ad_piofs_read.c | 27 - .../romio/adio/ad_piofs/ad_piofs_write.c | 47 - .../romio/romio/adio/ad_pvfs/ad_pvfs_close.c | 6 + .../romio/romio/adio/ad_pvfs/ad_pvfs_fcntl.c | 15 +- .../romio/romio/adio/ad_pvfs/ad_pvfs_open.c | 15 +- .../romio/romio/adio/ad_pvfs/ad_pvfs_read.c | 30 +- .../romio/romio/adio/ad_pvfs/ad_pvfs_write.c | 112 +- .../io/romio/romio/adio/ad_pvfs2/Makefile.am | 1 + .../io/romio/romio/adio/ad_pvfs2/ad_pvfs2.c | 5 + .../io/romio/romio/adio/ad_pvfs2/ad_pvfs2.h | 12 + .../romio/romio/adio/ad_pvfs2/ad_pvfs2_aio.c | 220 +++ .../romio/adio/ad_pvfs2/ad_pvfs2_common.c | 1 + .../romio/romio/adio/ad_pvfs2/ad_pvfs2_open.c | 14 +- .../romio/romio/adio/ad_pvfs2/ad_pvfs2_read.c | 67 +- .../romio/adio/ad_pvfs2/ad_pvfs2_write.c | 75 +- .../romio/adio/ad_testfs/ad_testfs_done.c | 47 +- .../romio/adio/ad_testfs/ad_testfs_iread.c | 38 +- .../romio/adio/ad_testfs/ad_testfs_iwrite.c | 35 +- .../romio/adio/ad_testfs/ad_testfs_wait.c | 40 +- ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs.c | 5 + ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs.h | 3 + .../io/romio/romio/adio/ad_ufs/ad_ufs_open.c | 16 +- .../romio/romio/adio/ad_xfs/ad_xfs_iwrite.c | 2 +- .../io/romio/romio/adio/common/Makefile.am | 12 +- .../io/romio/romio/adio/common/ad_aggregate.c | 40 +- .../mca/io/romio/romio/adio/common/ad_close.c | 10 +- .../io/romio/romio/adio/common/ad_darray.c | 8 +- ompi/mca/io/romio/romio/adio/common/ad_done.c | 116 +- .../io/romio/romio/adio/common/ad_done_fake.c | 14 +- ompi/mca/io/romio/romio/adio/common/ad_end.c | 36 +- .../mca/io/romio/romio/adio/common/ad_fcntl.c | 15 +- .../io/romio/romio/adio/common/ad_fstype.c | 171 +- .../io/romio/romio/adio/common/ad_get_sh_fp.c | 8 + .../mca/io/romio/romio/adio/common/ad_hints.c | 2 +- ompi/mca/io/romio/romio/adio/common/ad_init.c | 61 +- .../mca/io/romio/romio/adio/common/ad_iread.c | 60 +- .../romio/romio/adio/common/ad_iread_fake.c | 39 +- .../io/romio/romio/adio/common/ad_iwrite.c | 278 +++- .../romio/romio/adio/common/ad_iwrite_fake.c | 38 +- ompi/mca/io/romio/romio/adio/common/ad_open.c | 162 +- ompi/mca/io/romio/romio/adio/common/ad_read.c | 12 + .../io/romio/romio/adio/common/ad_read_coll.c | 29 - ompi/mca/io/romio/romio/adio/common/ad_seek.c | 3 - .../io/romio/romio/adio/common/ad_set_sh_fp.c | 8 + ompi/mca/io/romio/romio/adio/common/ad_wait.c | 114 +- .../io/romio/romio/adio/common/ad_wait_fake.c | 14 +- .../mca/io/romio/romio/adio/common/ad_write.c | 12 + .../romio/romio/adio/common/ad_write_coll.c | 78 +- .../romio/romio/adio/common/ad_write_nolock.c | 396 +++++ .../io/romio/romio/adio/common/adi_close.c | 25 +- ompi/mca/io/romio/romio/adio/common/flatten.c | 90 +- .../mca/io/romio/romio/adio/common/greq_fns.c | 31 + ompi/mca/io/romio/romio/adio/common/lock.c | 38 +- .../io/romio/romio/adio/common/system_hints.c | 149 ++ .../io/romio/romio/adio/include/Makefile.am | 2 + ompi/mca/io/romio/romio/adio/include/adio.h | 22 +- .../io/romio/romio/adio/include/adio_extern.h | 14 +- ompi/mca/io/romio/romio/adio/include/adioi.h | 122 +- .../romio/romio/adio/include/adioi_errmsg.h | 1 + .../romio/romio/adio/include/adioi_fs_proto.h | 15 + .../io/romio/romio/adio/include/mpio_error.h | 2 + ompi/mca/io/romio/romio/adio/include/mpipr.h | 17 +- .../{mpi-io => adio/include}/mpiu_greq.h | 1 - .../romio/romio/common/dataloop/.state-cache | 64 + .../romio/romio/common/dataloop/Makefile.in | 121 ++ .../romio/common/dataloop/darray_support.c | 301 ++++ .../io/romio/romio/common/dataloop/dataloop.c | 731 +++++++++ .../io/romio/romio/common/dataloop/dataloop.h | 21 + .../romio/common/dataloop/dataloop_create.c | 387 +++++ .../romio/common/dataloop/dataloop_create.h | 95 ++ .../dataloop/dataloop_create_blockindexed.c | 325 ++++ .../common/dataloop/dataloop_create_contig.c | 160 ++ .../common/dataloop/dataloop_create_indexed.c | 429 +++++ .../dataloop/dataloop_create_pairtype.c | 82 + .../common/dataloop/dataloop_create_struct.c | 672 ++++++++ .../common/dataloop/dataloop_create_vector.c | 161 ++ .../romio/common/dataloop/dataloop_parts.h | 466 ++++++ .../romio/common/dataloop/romio_dataloop.c | 575 +++++++ .../romio/common/dataloop/romio_dataloop.h | 144 ++ .../romio/common/dataloop/romio_segment_ops.c | 482 ++++++ .../io/romio/romio/common/dataloop/segment.c | 988 +++++++++++ .../romio/romio/common/dataloop/segment_ops.c | 816 +++++++++ .../romio/common/dataloop/subarray_support.c | 99 ++ .../romio/common/dataloop/typesize_support.c | 927 +++++++++++ .../romio/common/dataloop/typesize_support.h | 28 + ompi/mca/io/romio/romio/configure.in | 354 ++-- ompi/mca/io/romio/romio/doc/Makefile.am | 3 +- ompi/mca/io/romio/romio/doc/romio.bib | 92 ++ ompi/mca/io/romio/romio/doc/users-guide.bbl | 41 - ompi/mca/io/romio/romio/doc/users-guide.pdf | Bin 0 -> 157904 bytes ompi/mca/io/romio/romio/doc/users-guide.ps.gz | Bin 126177 -> 0 bytes ompi/mca/io/romio/romio/doc/users-guide.tex | 75 +- .../io/romio/romio/include/io_romio_conv.h | 13 - ompi/mca/io/romio/romio/mpi-io/Makefile.am | 11 +- ompi/mca/io/romio/romio/mpi-io/close.c | 2 + ompi/mca/io/romio/romio/mpi-io/get_amode.c | 4 +- .../romio/mpi-io/glue/default/mpio_file.c | 4 + ompi/mca/io/romio/romio/mpi-io/iread.c | 117 +- ompi/mca/io/romio/romio/mpi-io/iread_at.c | 95 -- ompi/mca/io/romio/romio/mpi-io/iread_sh.c | 115 +- ompi/mca/io/romio/romio/mpi-io/iwrite.c | 123 +- ompi/mca/io/romio/romio/mpi-io/iwrite_at.c | 97 -- ompi/mca/io/romio/romio/mpi-io/iwrite_sh.c | 100 +- ompi/mca/io/romio/romio/mpi-io/mpioprof.h | 5 + ompi/mca/io/romio/romio/mpi-io/mpiu_greq.c | 6 +- ompi/mca/io/romio/romio/test/Makefile.in | 3 +- ompi/mca/io/romio/romio/test/aggregation1.c | 251 +++ ompi/mca/io/romio/romio/test/aggregation2.c | 82 + ompi/mca/io/romio/romio/test/async-multiple.c | 139 ++ ompi/mca/io/romio/romio/test/async.c | 36 +- ompi/mca/io/romio/romio/test/file_info.c | 11 +- ompi/mca/io/romio/romio/test/ordered_fp.c | 144 ++ ompi/mca/io/romio/romio/test/runtests.in | 34 + ompi/mca/io/romio/romio/test/simple.c | 36 +- ompi/mca/io/romio/src/io_romio.h | 2 +- ompi/mca/io/romio/src/io_romio_component.c | 28 +- 194 files changed, 19589 insertions(+), 2571 deletions(-) create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/.state-cache create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/Makefile.in create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl.h create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_aggrs.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_aggrs.h create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_close.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_fcntl.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_getsh.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_hints.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_open.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_pset.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_pset.h create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_rdcoll.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_read.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_setsh.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_tuning.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_tuning.h create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_wrcoll.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_write.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_bglockless/.state-cache create mode 100644 ompi/mca/io/romio/romio/adio/ad_bglockless/Makefile.am create mode 100644 ompi/mca/io/romio/romio/adio/ad_bglockless/Makefile.in create mode 100644 ompi/mca/io/romio/romio/adio/ad_bglockless/ad_bglockless.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_lustre/.state-cache create mode 100644 ompi/mca/io/romio/romio/adio/ad_lustre/Makefile.am create mode 100644 ompi/mca/io/romio/romio/adio/ad_lustre/Makefile.in create mode 100644 ompi/mca/io/romio/romio/adio/ad_lustre/README create mode 100644 ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre.h create mode 100644 ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_fcntl.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_hints.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_open.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_rwcontig.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_read.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_resize.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_write.c create mode 100644 ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_aio.c create mode 100644 ompi/mca/io/romio/romio/adio/common/ad_write_nolock.c create mode 100644 ompi/mca/io/romio/romio/adio/common/greq_fns.c create mode 100644 ompi/mca/io/romio/romio/adio/common/system_hints.c rename ompi/mca/io/romio/romio/{mpi-io => adio/include}/mpiu_greq.h (94%) create mode 100644 ompi/mca/io/romio/romio/common/dataloop/.state-cache create mode 100644 ompi/mca/io/romio/romio/common/dataloop/Makefile.in create mode 100644 ompi/mca/io/romio/romio/common/dataloop/darray_support.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/dataloop.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/dataloop.h create mode 100644 ompi/mca/io/romio/romio/common/dataloop/dataloop_create.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/dataloop_create.h create mode 100644 ompi/mca/io/romio/romio/common/dataloop/dataloop_create_blockindexed.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/dataloop_create_contig.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/dataloop_create_indexed.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/dataloop_create_pairtype.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/dataloop_create_struct.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/dataloop_create_vector.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/dataloop_parts.h create mode 100644 ompi/mca/io/romio/romio/common/dataloop/romio_dataloop.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/romio_dataloop.h create mode 100644 ompi/mca/io/romio/romio/common/dataloop/romio_segment_ops.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/segment.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/segment_ops.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/subarray_support.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/typesize_support.c create mode 100644 ompi/mca/io/romio/romio/common/dataloop/typesize_support.h create mode 100644 ompi/mca/io/romio/romio/doc/romio.bib delete mode 100644 ompi/mca/io/romio/romio/doc/users-guide.bbl create mode 100644 ompi/mca/io/romio/romio/doc/users-guide.pdf delete mode 100644 ompi/mca/io/romio/romio/doc/users-guide.ps.gz create mode 100644 ompi/mca/io/romio/romio/test/aggregation1.c create mode 100644 ompi/mca/io/romio/romio/test/aggregation2.c create mode 100644 ompi/mca/io/romio/romio/test/async-multiple.c create mode 100644 ompi/mca/io/romio/romio/test/ordered_fp.c diff --git a/NEWS b/NEWS index 7cff84da90..0f775ede72 100644 --- a/NEWS +++ b/NEWS @@ -60,7 +60,7 @@ Trunk (not on release branches yet) - Remove use of STL from C++ bindings. - Added support for Platform/LSF job launchers. Must be Platform LSF v7.0.2 or later. -- Updated ROMIO with the version from MPICH2 1.0.5p4 +- Updated ROMIO with the version from MPICH2 1.0.7 - Added RDMA capable one-sided component (called rdma), which can be used with BTL components that expose a full one-sided interface. diff --git a/ompi/mca/io/base/io_base_request.c b/ompi/mca/io/base/io_base_request.c index a8c3a89f97..095f4e3650 100644 --- a/ompi/mca/io/base/io_base_request.c +++ b/ompi/mca/io/base/io_base_request.c @@ -220,8 +220,8 @@ int mca_io_base_request_alloc(ompi_file_t *file, /* * Free a module-specific IO MPI_Request */ -void mca_io_base_request_free(ompi_file_t *file, - mca_io_base_request_t *req) +OMPI_DECLSPEC void mca_io_base_request_free(ompi_file_t *file, + mca_io_base_request_t *req) { /* Put the request back on the per-module freelist, since it's been initialized for that module */ @@ -295,7 +295,7 @@ mca_io_base_request_progress_init(void) } -void +OMPI_DECLSPEC void mca_io_base_request_progress_add(void) { #if OMPI_ENABLE_PROGRESS_THREADS @@ -319,7 +319,7 @@ mca_io_base_request_progress_add(void) } -void +OMPI_DECLSPEC void mca_io_base_request_progress_del(void) { OPAL_THREAD_ADD32(&mca_io_base_request_num_pending, -1); diff --git a/ompi/mca/io/base/io_base_request.h b/ompi/mca/io/base/io_base_request.h index be0e2f462e..2fa6ab9e66 100644 --- a/ompi/mca/io/base/io_base_request.h +++ b/ompi/mca/io/base/io_base_request.h @@ -114,8 +114,8 @@ extern "C" { * * For optimization reasons, \em no error checking is performed. */ - void mca_io_base_request_free(ompi_file_t *file, - mca_io_base_request_t *req); + OMPI_DECLSPEC void mca_io_base_request_free(ompi_file_t *file, + mca_io_base_request_t *req); /* @@ -135,12 +135,12 @@ extern "C" { /** * */ - void mca_io_base_request_progress_add(void); + OMPI_DECLSPEC void mca_io_base_request_progress_add(void); /** * */ - void mca_io_base_request_progress_del(void); + OMPI_DECLSPEC void mca_io_base_request_progress_del(void); /** * Finalize the request progress code diff --git a/ompi/mca/io/romio/configure.m4 b/ompi/mca/io/romio/configure.m4 index 420eaf505b..7befdd4dc7 100644 --- a/ompi/mca/io/romio/configure.m4 +++ b/ompi/mca/io/romio/configure.m4 @@ -10,6 +10,7 @@ # University of Stuttgart. All rights reserved. # Copyright (c) 2004-2005 The Regents of the University of California. # All rights reserved. +# Copyright (c) 2008 Cisco Systems, Inc. All rights reserved. # $COPYRIGHT$ # # Additional copyrights may follow @@ -28,12 +29,14 @@ AC_DEFUN([MCA_io_romio_POST_CONFIG], [ # MCA_io_romio_CONFIG([action-if-found], [action-if-not-found]) # ----------------------------------------------------------- AC_DEFUN([MCA_io_romio_CONFIG],[ + OMPI_VAR_SCOPE_PUSH([io_romio_flags io_romio_flags_define io_romio_happy io_romio_save_LIBS]) AC_ARG_ENABLE([io-romio], [AC_HELP_STRING([--disable-io-romio], [Disable the ROMIO MPI-IO component])]) AC_ARG_WITH([io-romio-flags], [AC_HELP_STRING([--with-io-romio-flags=FLAGS], [Pass FLAGS to the ROMIO distribution configuration script])]) + AC_DEFINE_UNQUOTED([MCA_io_romio_USER_CONFIGURE_FLAGS], ["$with_io_romio_flags"], [Set of user-defined configure flags given to ROMIOs configure script via --with-io-romio-flags]) AC_MSG_CHECKING([if want ROMIO component]) AS_IF([test "$enable_io_romio" = "no"], [AC_MSG_RESULT([no]) @@ -73,7 +76,10 @@ AC_DEFUN([MCA_io_romio_CONFIG],[ [AS_IF([test ! -z $build], [io_romio_flags="$io_romio_flags --build=$build"]) AS_IF([test ! -z $host], [io_romio_flags="$io_romio_flags --host=$host"]) AS_IF([test ! -z $target], [io_romio_flags="$io_romio_flags --target=$target"])]) - io_romio_flags="$io_romio_flags CFLAGS="'"'"$CFLAGS"'"'" CPPFLAGS="'"'"$CPPFLAGS"'"'" FFLAGS="'"'"$FFLAGS"'"'" LDFLAGS="'"'"$LDFLAGS"'"'" --$io_romio_shared-shared --$io_romio_static-static $io_romio_flags $io_romio_prefix_arg --with-mpi=open_mpi" + io_romio_flags_define="$io_romio_flags CFLAGS='$CFLAGS' CPPFLAGS='$CPPFLAGS' FFLAGS='$FFLAGS' LDFLAGS='$LDFLAGS' --$io_romio_shared-shared --$io_romio_static-static $io_romio_flags $io_romio_prefix_arg --with-mpi=open_mpi --disable-aio" + AC_DEFINE_UNQUOTED([MCA_io_romio_COMPLETE_CONFIGURE_FLAGS], ["$io_romio_flags_define"], [Complete set of command line arguments given to ROMIOs configure script]) + + io_romio_flags="$io_romio_flags CFLAGS="'"'"$CFLAGS"'"'" CPPFLAGS="'"'"$CPPFLAGS"'"'" FFLAGS="'"'"$FFLAGS"'"'" LDFLAGS="'"'"$LDFLAGS"'"'" --$io_romio_shared-shared --$io_romio_static-static $io_romio_flags $io_romio_prefix_arg --with-mpi=open_mpi --disable-aio" ompi_show_subtitle "Configuring ROMIO distribution" OMPI_CONFIG_SUBDIR([ompi/mca/io/romio/romio], @@ -98,4 +104,5 @@ AC_DEFUN([MCA_io_romio_CONFIG],[ [AC_MSG_ERROR([ROMIO distribution did not configure successfully])], [AC_MSG_WARN([ROMIO distribution did not configure successfully])]) $2])])]) + OMPI_VAR_SCOPE_POP ]) diff --git a/ompi/mca/io/romio/romio/Makefile.am b/ompi/mca/io/romio/romio/Makefile.am index 38a1199944..21e4c5e631 100644 --- a/ompi/mca/io/romio/romio/Makefile.am +++ b/ompi/mca/io/romio/romio/Makefile.am @@ -9,6 +9,7 @@ # University of Stuttgart. All rights reserved. # Copyright (c) 2004-2005 The Regents of the University of California. # All rights reserved. +# Copyright (c) 2008 Cisco Systems Inc. All rights reserved. # $COPYRIGHT$ # # Additional copyrights may follow @@ -18,7 +19,9 @@ include $(top_srcdir)/Makefile.options -SUBDIRS = include adio mpi-io +# Left out common/dataloop -- it's not enabled in MPICH2-1.0.7. + +SUBDIRS = include adio mpi-io DIST_SUBDIRS = $(SUBDIRS) doc util EXTRA_DIST = README COPYRIGHT README_OMPI diff --git a/ompi/mca/io/romio/romio/README b/ompi/mca/io/romio/romio/README index 026d905593..42fe329a9f 100644 --- a/ompi/mca/io/romio/romio/README +++ b/ompi/mca/io/romio/romio/README @@ -1,6 +1,6 @@ ROMIO: A High-Performance, Portable MPI-IO Implementation - Version 2005-06-09 + Version 2008-03-09 Major Changes in this version: ------------------------------ diff --git a/ompi/mca/io/romio/romio/adio/Makefile.am b/ompi/mca/io/romio/romio/adio/Makefile.am index 09080df92e..83ef83e065 100644 --- a/ompi/mca/io/romio/romio/adio/Makefile.am +++ b/ompi/mca/io/romio/romio/adio/Makefile.am @@ -9,6 +9,7 @@ # University of Stuttgart. All rights reserved. # Copyright (c) 2004-2005 The Regents of the University of California. # All rights reserved. +# Copyright (c) 2008 Cisco Systems, Inc. All rights reserved. # $COPYRIGHT$ # # Additional copyrights may follow @@ -20,6 +21,22 @@ include $(top_srcdir)/Makefile.options # Conditionals whether to build each subdir or not +if BUILD_BGL +BGL_DIR = ad_bgl +BGL_LIB = ad_bgl/libadio_bgl.la +else +BGL_DIR = +BGL_LIB = +endif + +if BUILD_BGLOCKLESS +BGLOCKLESS_DIR = ad_bglockless +BGLOCKLESS_LIB = ad_bglockless/libadio_bglockless.la +else +BGLOCKLESS_DIR = +BGLOCKLESS_LIB = +endif + if BUILD_GRIDFTP GRIDFTP_DIR = ad_gridftp GRIDFTP_LIB = ad_gridftp/libadio_gridftp.la @@ -28,12 +45,21 @@ GRIDFTP_DIR = GRIDFTP_LIB = endif -if BUILD_HFS -HFS_DIR = ad_hfs -HFS_LIB = ad_hfs/libadio_hfs.la +# 8 July 2008: romio-maint@mcs.anl.gov says that this is deprecated +#if BUILD_HFS +#HFS_DIR = ad_hfs +#HFS_LIB = ad_hfs/libadio_hfs.la +#else +#HFS_DIR = +#HFS_LIB = +#endif + +if BUILD_LUSTRE +LUSTRE_DIR = ad_lustre +LUSTRE_LIB = ad_lustre/libadio_lustre.la else -HFS_DIR = -HFS_LIB = +LUSTRE_DIR = +LUSTRE_LIB = endif if BUILD_NFS @@ -69,13 +95,14 @@ PFS_DIR = PFS_LIB = endif -if BUILD_PIOFS -PIOFS_DIR = ad_piofs -PIOFS_LIB = ad_piofs/libadio_piofs.la -else -PIOFS_DIR = -PIOFS_LIB = -endif +# 8 July 2008: romio-maint@mcs.anl.gov says that this is deprecated +#if BUILD_PIOFS +#PIOFS_DIR = ad_piofs +#PIOFS_LIB = ad_piofs/libadio_piofs.la +#else +#PIOFS_DIR = +#PIOFS_LIB = +#endif if BUILD_PVFS PVFS_DIR = ad_pvfs @@ -126,21 +153,20 @@ XFS_LIB = endif SUBDIRS = common include \ - $(GRIDFTP_DIR) $(HFS_DIR) $(NFS_DIR) $(NTFS_DIR) $(PANFS_DIR) \ - $(PFS_DIR) $(PIOFS_DIR) $(PVFS_DIR) $(PVFS2_DIR) $(SFS_DIR) \ + $(BG_DIR) $(BGLOCKLESS_DIR) \ + $(GRIDFTP_DIR) $(LUSTRE_DIR) $(NFS_DIR) $(NTFS_DIR) $(PANFS_DIR) \ + $(PFS_DIR) $(PVFS_DIR) $(PVFS2_DIR) $(SFS_DIR) \ $(TESTFS_DIR) $(UFS_DIR) $(XFS_DIR) DIST_SUBDIRS = common include \ - ad_gridftp ad_hfs ad_nfs ad_ntfs ad_panfs ad_pfs ad_piofs ad_pvfs \ - ad_pvfs2 ad_sfs ad_testfs ad_ufs ad_xfs + ad_bgl ad_bglockless ad_gridftp ad_lustre ad_nfs ad_ntfs \ + ad_panfs ad_pfs ad_pvfs ad_pvfs2 ad_sfs ad_testfs ad_ufs ad_xfs # Library noinst_LTLIBRARIES = libadio.la libadio_la_SOURCES = libadio_la_LIBADD = \ common/libadio_common.la \ - $(GRIDFTP_LIB) $(HFS_LIB) $(NFS_LIB) $(NTFS_LIB) $(PANFS_LIB) \ - $(PFS_LIB) $(PIOFS_LIB) $(PVFS_LIB) $(PVFS2_LIB) $(SFS_LIB) \ + $(BG_LIB) $(BGLOCKLESS_LIB) \ + $(GRIDFTP_LIB) $(LUSTRE_LIB) $(NFS_LIB) $(NTFS_LIB) $(PANFS_LIB) \ + $(PFS_LIB) $(PVFS_LIB) $(PVFS2_LIB) $(SFS_LIB) \ $(TESTFS_LIB) $(UFS_LIB) $(XFS_LIB) -libadio_la_DEPENDENCIES = \ - $(libadio_la_LIBADD) - diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/.state-cache b/ompi/mca/io/romio/romio/adio/ad_bgl/.state-cache new file mode 100644 index 0000000000..10ea8a7bf6 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/.state-cache @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/Makefile.in b/ompi/mca/io/romio/romio/adio/ad_bgl/Makefile.in new file mode 100644 index 0000000000..a9b53216ec --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/Makefile.in @@ -0,0 +1,47 @@ +CC = @CC@ +AR = @AR@ +LIBNAME = @LIBNAME@ +srcdir = @srcdir@ +CC_SHL = @CC_SHL@ +SHLIBNAME = @SHLIBNAME@ + +INCLUDE_DIR = -I@MPI_INCLUDE_DIR@ -I${srcdir}/../include -I../include -I../../include -I${srcdir}/../../../../include -I../../../../include @CPPFLAGS@ +CFLAGS = @CFLAGS@ $(INCLUDE_DIR) -DBGL_OPTIM_STEP1_2=1 -DBGL_OPTIM_STEP1_1=1 + +C_COMPILE_SHL = $(CC_SHL) @CFLAGS@ $(INCLUDE_DIR) + +@VPATH@ + +AD_BGL_OBJECTS = \ + ad_bgl_open.o ad_bgl_close.o \ + ad_bgl_fcntl.o \ + ad_bgl_read.o ad_bgl_write.o ad_bgl_getsh.o ad_bgl_setsh.o \ + ad_bgl.o ad_bgl_aggrs.o ad_bgl_pset.o ad_bgl_hints.o \ + ad_bgl_rdcoll.o ad_bgl_wrcoll.o ad_bgl_tuning.o + +default: $(LIBNAME) + @if [ "@ENABLE_SHLIB@" != "none" ] ; then \ + $(MAKE) $(SHLIBNAME).la ;\ + fi + +.SUFFIXES: $(SUFFIXES) .p .lo + +.c.o: + $(CC) $(CFLAGS) -c $< +.c.lo: + $(C_COMPILE_SHL) -c $< + @mv -f $*.o $*.lo + +$(LIBNAME): $(AD_BGL_OBJECTS) + $(AR) $(LIBNAME) $(AD_BGL_OBJECTS) + +AD_BGL_LOOBJECTS=$(AD_BGL_OBJECTS:.o=.lo) +$(SHLIBNAME).la: $(AD_BGL_LOOBJECTS) + $(AR) $(SHLIBNAME).la $(AD_BGL_LOOBJECTS) + +coverage: + -@for file in ${AD_BGL_OBJECTS:.o=.c} ; do \ + gcov -b -f $$file ; done + +clean: + @rm -f *.o *.lo diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl.c new file mode 100644 index 0000000000..2911543cd9 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl.c @@ -0,0 +1,57 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/** + * \file ad_bgl.c + * \brief ??? + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 2001 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "ad_bgl.h" + +/* adioi.h has the ADIOI_Fns_struct define */ +#include "adioi.h" + +struct ADIOI_Fns_struct ADIO_BGL_operations = { + ADIOI_BGL_Open, /* Open */ + ADIOI_BGL_ReadContig, /* ReadContig */ + ADIOI_BGL_WriteContig, /* WriteContig */ +#if BGL_OPTIM_STEP1_2 + ADIOI_BGL_ReadStridedColl, /* ReadStridedColl */ + ADIOI_BGL_WriteStridedColl, /* WriteStridedColl */ +#else + ADIOI_GEN_ReadStridedColl, /* ReadStridedColl */ + ADIOI_GEN_WriteStridedColl, /* WriteStridedColl */ +#endif + ADIOI_GEN_SeekIndividual, /* SeekIndividual */ + ADIOI_BGL_Fcntl, /* Fcntl */ +#if BGL_OPTIM_STEP1_1 + ADIOI_BGL_SetInfo, /* SetInfo */ +#else + ADIOI_GEN_SetInfo, /* SetInfo */ +#endif + ADIOI_BGL_ReadStrided, /* ReadStrided */ + ADIOI_BGL_WriteStrided, /* WriteStrided */ + ADIOI_BGL_Close, /* Close */ +#ifdef ROMIO_HAVE_WORKING_AIO +#warning Consider BG support for NFS before enabling this. + ADIOI_GEN_IreadContig, /* IreadContig */ + ADIOI_GEN_IwriteContig, /* IwriteContig */ +#else + ADIOI_FAKE_IreadContig, /* IreadContig */ + ADIOI_FAKE_IwriteContig, /* IwriteContig */ +#endif + ADIOI_GEN_IODone, /* ReadDone */ + ADIOI_GEN_IODone, /* WriteDone */ + ADIOI_GEN_IOComplete, /* ReadComplete */ + ADIOI_GEN_IOComplete, /* WriteComplete */ + ADIOI_GEN_IreadStrided, /* IreadStrided */ + ADIOI_GEN_IwriteStrided, /* IwriteStrided */ + ADIOI_GEN_Flush, /* Flush */ + ADIOI_GEN_Resize, /* Resize */ + ADIOI_GEN_Delete, /* Delete */ +}; diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl.h b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl.h new file mode 100644 index 0000000000..1209785be1 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl.h @@ -0,0 +1,94 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/* ---------------------------------------------------------------- */ +/** + * \file ad_bgl.h + * \brief ??? + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#ifndef AD_BGL_INCLUDE +#define AD_BGL_INCLUDE + +#include +#include +#include +#include +#include "adio.h" + +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_AIO_H +#include +#endif + +int ADIOI_BGL_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, + int wr, void *handle); + +void ADIOI_BGL_Open(ADIO_File fd, int *error_code); + +void ADIOI_BGL_Close(ADIO_File fd, int *error_code); + +void ADIOI_BGL_ReadContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code); +void ADIOI_BGL_WriteContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code); +#if 0 +void ADIOI_BGL_IwriteContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Request *request, int + *error_code); +void ADIOI_BGL_IreadContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Request *request, int + *error_code); +int ADIOI_BGL_ReadDone(ADIO_Request *request, ADIO_Status *status, int + *error_code); +int ADIOI_BGL_WriteDone(ADIO_Request *request, ADIO_Status *status, int + *error_code); +void ADIOI_BGL_ReadComplete(ADIO_Request *request, ADIO_Status *status, int + *error_code); +void ADIOI_BGL_WriteComplete(ADIO_Request *request, ADIO_Status *status, + int *error_code); +#endif +void ADIOI_BGL_Fcntl(ADIO_File fd, int flag, ADIO_Fcntl_t *fcntl_struct, int + *error_code); +void ADIOI_BGL_SetInfo(ADIO_File fd, MPI_Info users_info, int *error_code); + +void ADIOI_BGL_WriteStrided(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code); +void ADIOI_BGL_ReadStrided(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code); + +void ADIOI_BGL_ReadStridedColl(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code); + +void ADIOI_BGL_WriteStridedColl(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code); + +void ADIOI_BGL_Get_shared_fp(ADIO_File fd, int size, ADIO_Offset *shared_fp, int *error_code); +void ADIOI_BGL_Set_shared_fp(ADIO_File fd, ADIO_Offset offset, int *error_code); + + +#include "ad_bgl_tuning.h" + + +#endif diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_aggrs.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_aggrs.c new file mode 100644 index 0000000000..5e14af114c --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_aggrs.c @@ -0,0 +1,970 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/** + * \file ad_bgl_aggrs.c + * \brief The externally used function from this file is is declared in ad_bgl_aggrs.h + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "adio.h" +#include "adio_cb_config_list.h" +#include "ad_bgl.h" +#include "ad_bgl_pset.h" +#include "ad_bgl_aggrs.h" + + +int aggrsInPsetSize=0; +int *aggrsInPset=NULL; + +/* forward declaration */ +static void +ADIOI_BGL_compute_agg_ranklist_serial ( ADIO_File fd, + const ADIOI_BGL_ConfInfo_t *confInfo, + ADIOI_BGL_ProcInfo_t *all_procInfo, + int *aggrsInPset ); + +/* + * Compute the aggregator-related parameters that are required in 2-phase collective IO of ADIO. + * The parameters are + * . the number of aggregators (proxies) : fd->hints->cb_nodes + * . the ranks of the aggregators : fd->hints->ranklist + * By compute these two parameters in a BGL-PSET-aware way, the default 2-phase collective IO of + * ADIO can work more efficiently. + */ +int +ADIOI_BGL_gen_agg_ranklist(ADIO_File fd, int n_aggrs_per_pset) +{ + int r, s; + ADIOI_BGL_ProcInfo_t *procInfo, *all_procInfo; + ADIOI_BGL_ConfInfo_t *confInfo; + + MPI_Comm_size( fd->comm, &s ); + MPI_Comm_rank( fd->comm, &r ); + + /* Collect individual BGL personality information */ + confInfo = ADIOI_BGL_ConfInfo_new (); + procInfo = ADIOI_BGL_ProcInfo_new (); + ADIOI_BGL_persInfo_init( confInfo, procInfo, s, r, n_aggrs_per_pset ); + + /* Gather BGL personality infomation onto process 0 */ + // if (r == 0) + all_procInfo = ADIOI_BGL_ProcInfo_new_n (s); + if(s > aggrsInPsetSize) + { + if(aggrsInPset) ADIOI_Free(aggrsInPset); + aggrsInPset = (int *) ADIOI_Malloc (s *sizeof(int)); + aggrsInPsetSize = s; + } + + + MPI_Gather( (void *)procInfo, sizeof(ADIOI_BGL_ProcInfo_t), MPI_BYTE, + (void *)all_procInfo, sizeof(ADIOI_BGL_ProcInfo_t), MPI_BYTE, + 0, + fd->comm ); + + /* Compute a list of the ranks of chosen IO proxy CN on process 0 */ + if (r == 0) { + ADIOI_BGL_compute_agg_ranklist_serial (fd, confInfo, all_procInfo, aggrsInPset); + // ADIOI_BGL_ProcInfo_free (all_procInfo); + } + ADIOI_BGL_ProcInfo_free (all_procInfo); + + /* Send the info of IO proxy CN to all processes and keep the info in fd->hints struct. + Declared in adio_cb_config_list.h */ + ADIOI_cb_bcast_rank_map(fd); + + /* Broadcast the BGL-GPFS related file domain info */ + MPI_Bcast( (void *)aggrsInPset, + fd->hints->cb_nodes * sizeof(int), MPI_BYTE, + 0, + fd->comm ); + + ADIOI_BGL_persInfo_free( confInfo, procInfo ); + return 0; +} + +/* + * the purpose of abstracting out this routine is to make it easy for trying different proxy-selection criteria. + */ +static int +ADIOI_BGL_select_agg_in_pset (const ADIOI_BGL_ConfInfo_t *confInfo, + ADIOI_BGL_ProcInfo_t *pset_procInfo, + int nCN_in_pset, + int *tmp_ranklist) +{ +/* first implementation, based on their rank order. */ + + int i, j, k; + + /* The number of aggregators in the PSET is proportional to the CNs in the PSET */ + int nAggrs = nCN_in_pset * confInfo->aggRatio; + if (nAggrs < ADIOI_BGL_NAGG_PSET_MIN) nAggrs = ADIOI_BGL_NAGG_PSET_MIN; + + /* for not virtual-node-mode, pick aggregators in this PSET based on the order of the global rank */ + if (!confInfo->isVNM) + { + for (i=0; icpuidSize; k++){ + for (i=0; i< nCN_in_pset ; i++) { + if (pset_procInfo[i].cpuid == k) + tmp_ranklist[j++] = pset_procInfo[i].rank; + if ( j >= nAggrs) break; + } + if ( j >= nAggrs) break; + } + } + + return nAggrs; +} + +/* + * Pick IO aggregators based on the under PSET organization and stores the ranks of the proxy CNs in tmp_ranklist. + * The first order of tmp_ranklist is : PSET number + * The secondary order of the list is determined in ADIOI_BGL_select_agg_in_pset() and thus adjustable. + */ +static int +ADIOI_BGL_compute_agg_ranklist_serial_do (const ADIOI_BGL_ConfInfo_t *confInfo, + ADIOI_BGL_ProcInfo_t *all_procInfo, + int *aggrsInPset, + int *tmp_ranklist) +{ + int i, j; + + /* a list of the numbers of all the PSETS */ + int *psetNumList = (int *) ADIOI_Malloc ( confInfo->nProcs * sizeof(int) ); + + /* sweep through all processes' records, collect the numbers of all the PSETS. + * The reason for not doing MIN, MAX is that the owned PSETs may not have contiguous numbers */ + int n_psets=0; + for (i=0; inProcs; i++) { + + ADIOI_BGL_ProcInfo_t *info_p = all_procInfo+i; + + int exist = 0; + for (j=n_psets-1; j>=0; j--) + if (info_p->psetNum == psetNumList[j]) { exist=1; break; } + + if (!exist) { + psetNumList [n_psets] = info_p->psetNum; + n_psets ++; + } + } + + /* bucket sort: put the CN nodes into ordered buckets, each of which represents a PSET */ + + /* bucket space for bucket sort */ + ADIOI_BGL_ProcInfo_t *sorted_procInfo = ADIOI_BGL_ProcInfo_new_n ( n_psets * confInfo->virtualPsetSize ); + int *PsetIdx = (int *) ADIOI_Malloc ( n_psets * sizeof(int) ); + AD_BGL_assert ( (PsetIdx != NULL) ); + + /* initialize bucket pointer */ + for (i=0; ivirtualPsetSize; + } + + /* sort */ + for (i=0; inProcs; i++) { + int pset_id = all_procInfo[i].psetNum; + + for (j=n_psets-1; j>=0; j--) if (pset_id == psetNumList[j]) break; + AD_BGL_assert ( (j >= 0) ); /* got to find a PSET bucket */ + + sorted_procInfo[ PsetIdx[j] ++ ] = all_procInfo[i]; + } + + ADIOI_Free(psetNumList); + + /* select a number of CN aggregators from each Pset */ + int naggs = 0; + for (i=0; ivirtualPsetSize; + + /* select aggregators and put them into tmp_ranklist contiguously. */ + int local_naggs = ADIOI_BGL_select_agg_in_pset( confInfo, + sorted_procInfo + i*confInfo->virtualPsetSize, + nCN_in_pset, + tmp_ranklist + naggs); + aggrsInPset[i+1] = local_naggs; + + naggs += local_naggs; + } + aggrsInPset[0] = n_psets; + + /* leave */ + ADIOI_Free ( PsetIdx ); + ADIOI_BGL_ProcInfo_free ( sorted_procInfo ); + return naggs; +} + +/* + * compute aggregators ranklist and put it into fd->hints struct + */ +static void +ADIOI_BGL_compute_agg_ranklist_serial ( ADIO_File fd, + const ADIOI_BGL_ConfInfo_t *confInfo, + ADIOI_BGL_ProcInfo_t *all_procInfo, + int *aggrsInPset ) +{ +# define DEBUG 0 +# if DEBUG + int i; +# endif + int naggs; + int *tmp_ranklist; + + /* compute the ranklist of IO aggregators and put into tmp_ranklist */ + tmp_ranklist = (int *) ADIOI_Malloc (confInfo->nProcs * sizeof(int)); + +# if DEBUG + for (i=0; inProcs; i++) + printf( "\tcpuid %1d, rank = %6d\n", all_procInfo[i].cpuid, all_procInfo[i].rank ); +# endif + + naggs = + ADIOI_BGL_compute_agg_ranklist_serial_do (confInfo, all_procInfo, aggrsInPset, tmp_ranklist); + +# define VERIFY 0 +# if VERIFY + printf( "\tconfInfo = %3d,%3d,%3d,%3d,%3d,%3d,%.4f; naggs = %d\n", + confInfo->PsetSize , + confInfo->numPsets , + confInfo->isVNM , + confInfo->virtualPsetSize , + confInfo->nProcs , + confInfo->nAggrs , + confInfo->aggRatio , + naggs ); +# endif + +# if DEBUG + for (i=0; ihints */ + if(fd->hints->ranklist != NULL) ADIOI_Free (fd->hints->ranklist); + + fd->hints->cb_nodes = naggs; + fd->hints->ranklist = (int *) ADIOI_Malloc (naggs * sizeof(int)); + memcpy( fd->hints->ranklist, tmp_ranklist, naggs*sizeof(int) ); + + /* */ + ADIOI_Free( tmp_ranklist ); + return; +} + + + +/* + * Compute a dynamic access range based file domain partition among I/O aggregators, + * which align to the GPFS block size + * Divide the I/O workload among "nprocs_for_coll" processes. This is + * done by (logically) dividing the file into file domains (FDs); each + * process may directly access only its own file domain. + * Additional effort is to make sure that each I/O aggregator get + * a file domain that aligns to the GPFS block size. So, there will + * not be any false sharing of GPFS file blocks among multiple I/O nodes. + */ +void ADIOI_BGL_GPFS_Calc_file_domains(ADIO_Offset *st_offsets, + ADIO_Offset *end_offsets, + int nprocs, + int nprocs_for_coll, + ADIO_Offset *min_st_offset_ptr, + ADIO_Offset **fd_start_ptr, + ADIO_Offset **fd_end_ptr, + ADIO_Offset *fd_size_ptr, + void *fs_ptr) +{ + ADIO_Offset min_st_offset, max_end_offset, *fd_start, *fd_end, *fd_size; + int i, aggr; + static char myname[] = "ADIOI_BGL_GPFS_Calc_file_domains"; + __blksize_t blksize = 1048576; /* default to 1M */ + if(fs_ptr && ((ADIOI_BGL_fs*)fs_ptr)->blksize) /* ignore null ptr or 0 blksize */ + blksize = ((ADIOI_BGL_fs*)fs_ptr)->blksize; +/* FPRINTF(stderr,"%s(%d): Blocksize=%ld\n",myname,__LINE__,blksize);*/ + + /* find the range of all the requests */ + min_st_offset = st_offsets [0]; + max_end_offset = end_offsets[0]; + for (i=1; ihints->cb_nodes; i++) + if (fd->hints->ranklist[i] == myrank) return i; + return -1; +} + +/* + * This is more general aggregator search function which does not base on the assumption + * that each aggregator hosts the file domain with the same size + */ +int ADIOI_BGL_Calc_aggregator(ADIO_File fd, + ADIO_Offset off, + ADIO_Offset min_off, + ADIO_Offset *len, + ADIO_Offset fd_size, + ADIO_Offset *fd_start, + ADIO_Offset *fd_end) +{ + int rank_index, rank; + ADIO_Offset avail_bytes; + + AD_BGL_assert ( (off <= fd_end[fd->hints->cb_nodes-1] && off >= min_off && fd_start[0] >= min_off ) ); + + /* binary search --> rank_index is returned */ + int ub = fd->hints->cb_nodes; + int lb = 0; + rank_index = fd->hints->cb_nodes / 2; + while ( off < fd_start[rank_index] || off > fd_end[rank_index] ) { + if ( off > fd_end [rank_index] ) { + lb = rank_index; + rank_index = (rank_index + ub) / 2; + } + else + if ( off < fd_start[rank_index] ) { + ub = rank_index; + rank_index = (rank_index + lb) / 2; + } + } + + // printf ("ADIOI_BGL_Calc_aggregator: rank_index = %d\n", rank_index ); + + /* + * remember here that even in Rajeev's original code it was the case that + * different aggregators could end up with different amounts of data to + * aggregate. here we use fd_end[] to make sure that we know how much + * data this aggregator is working with. + * + * the +1 is to take into account the end vs. length issue. + */ + avail_bytes = fd_end[rank_index] + 1 - off; + if (avail_bytes < *len && avail_bytes > 0) { + /* this file domain only has part of the requested contig. region */ + + *len = avail_bytes; + } + + /* map our index to a rank */ + /* NOTE: FOR NOW WE DON'T HAVE A MAPPING...JUST DO 0..NPROCS_FOR_COLL */ + rank = fd->hints->ranklist[rank_index]; + + return rank; +} + + +/* + * ADIOI_BGL_Calc_my_req() overrides ADIOI_Calc_my_req for the default implementation + * is specific for static file domain partitioning. + * + * ADIOI_Calc_my_req() calculate what portions of the access requests + * of this process are located in the file domains of various processes + * (including this one) + */ +void ADIOI_BGL_Calc_my_req(ADIO_File fd, ADIO_Offset *offset_list, int *len_list, + int contig_access_count, ADIO_Offset + min_st_offset, ADIO_Offset *fd_start, + ADIO_Offset *fd_end, ADIO_Offset fd_size, + int nprocs, + int *count_my_req_procs_ptr, + int **count_my_req_per_proc_ptr, + ADIOI_Access **my_req_ptr, + int **buf_idx_ptr) +{ + int *count_my_req_per_proc, count_my_req_procs, *buf_idx; + int i, l, proc; + ADIO_Offset fd_len, rem_len, curr_idx, off; + ADIOI_Access *my_req; + + + *count_my_req_per_proc_ptr = (int *) ADIOI_Calloc(nprocs,sizeof(int)); + count_my_req_per_proc = *count_my_req_per_proc_ptr; +/* count_my_req_per_proc[i] gives the no. of contig. requests of this + process in process i's file domain. calloc initializes to zero. + I'm allocating memory of size nprocs, so that I can do an + MPI_Alltoall later on.*/ + + buf_idx = (int *) ADIOI_Malloc(nprocs*sizeof(int)); +/* buf_idx is relevant only if buftype_is_contig. + buf_idx[i] gives the index into user_buf where data received + from proc. i should be placed. This allows receives to be done + without extra buffer. This can't be done if buftype is not contig. */ + + /* initialize buf_idx to -1 */ + for (i=0; i < nprocs; i++) buf_idx[i] = -1; + + /* one pass just to calculate how much space to allocate for my_req; + * contig_access_count was calculated way back in ADIOI_Calc_my_off_len() + */ + for (i=0; i < contig_access_count; i++) { + + /* When there is no data being processed, bypass this loop */ + if (len_list[i] == 0) continue; + + off = offset_list[i]; + fd_len = len_list[i]; + /* note: we set fd_len to be the total size of the access. then + * ADIOI_Calc_aggregator() will modify the value to return the + * amount that was available from the file domain that holds the + * first part of the access. + */ + proc = ADIOI_BGL_Calc_aggregator(fd, off, min_st_offset, &fd_len, fd_size, + fd_start, fd_end); + count_my_req_per_proc[proc]++; + + /* figure out how much data is remaining in the access (i.e. wasn't + * part of the file domain that had the starting byte); we'll take + * care of this data (if there is any) in the while loop below. + */ + rem_len = len_list[i] - fd_len; + + while (rem_len > 0) { + off += fd_len; /* point to first remaining byte */ + fd_len = rem_len; /* save remaining size, pass to calc */ + proc = ADIOI_BGL_Calc_aggregator(fd, off, min_st_offset, &fd_len, + fd_size, fd_start, fd_end); + + count_my_req_per_proc[proc]++; + rem_len -= fd_len; /* reduce remaining length by amount from fd */ + } + } + +/* now allocate space for my_req, offset, and len */ + + *my_req_ptr = (ADIOI_Access *) + ADIOI_Malloc(nprocs*sizeof(ADIOI_Access)); + my_req = *my_req_ptr; + + count_my_req_procs = 0; + for (i=0; i < nprocs; i++) { + if (count_my_req_per_proc[i]) { + my_req[i].offsets = (ADIO_Offset *) + ADIOI_Malloc(count_my_req_per_proc[i] * sizeof(ADIO_Offset)); + my_req[i].lens = (int *) + ADIOI_Malloc(count_my_req_per_proc[i] * sizeof(int)); + count_my_req_procs++; + } + my_req[i].count = 0; /* will be incremented where needed + later */ + } + +/* now fill in my_req */ + curr_idx = 0; + for (i=0; i 0) { + off += fd_len; + fd_len = rem_len; + proc = ADIOI_BGL_Calc_aggregator(fd, off, min_st_offset, &fd_len, + fd_size, fd_start, fd_end); + + if (buf_idx[proc] == -1) buf_idx[proc] = (int) curr_idx; + + l = my_req[proc].count; + curr_idx += fd_len; + rem_len -= fd_len; + + my_req[proc].offsets[l] = off; + my_req[proc].lens[l] = (int) fd_len; + my_req[proc].count++; + } + } + +#ifdef AGG_DEBUG + for (i=0; i 0) { + FPRINTF(stdout, "data needed from %d (count = %d):\n", i, + my_req[i].count); + for (l=0; l < my_req[i].count; l++) { + FPRINTF(stdout, " off[%d] = %Ld, len[%d] = %d\n", l, + my_req[i].offsets[l], l, my_req[i].lens[l]); + } + } + } +#if 0 + for (i=0; icomm); +/* total_cora2a+=timebase()-cora2a1; */ + + /* Allocate storage for an array of other nodes' accesses of our + * node's file domain. Also allocate storage for the alltoallv + * parameters. + */ + *others_req_ptr = (ADIOI_Access *) + ADIOI_Malloc(nprocs*sizeof(ADIOI_Access)); + others_req = *others_req_ptr; + + scounts = ADIOI_Malloc(nprocs*sizeof(int)); + sdispls = ADIOI_Malloc(nprocs*sizeof(int)); + rcounts = ADIOI_Malloc(nprocs*sizeof(int)); + rdispls = ADIOI_Malloc(nprocs*sizeof(int)); + + /* If process[i] has any requests in my file domain, + * initialize an ADIOI_Access structure that will describe each request + * from process[i]. The offsets, lengths, and buffer pointers still need + * to be obtained to complete the setting of this structure. + */ + count_others_req_procs = 0; + for (i=0; icomm); + + /************************/ + /* Exchange the lengths */ + /************************/ + + for (i=0; icomm); + + /* Clean up */ + ADIOI_Free(count_others_req_per_proc); + ADIOI_Free (scounts); + ADIOI_Free (sdispls); + ADIOI_Free (rcounts); + ADIOI_Free (rdispls); + + *count_others_req_procs_ptr = count_others_req_procs; +} diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_aggrs.h b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_aggrs.h new file mode 100644 index 0000000000..6f0a9e2e94 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_aggrs.h @@ -0,0 +1,98 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/** + * \file ad_bgl_aggrs.h + * \brief ??? + */ + +/* + * File: ad_bgl_aggrs.h + * + * Declares functions specific for BG/L - GPFS parallel I/O solution. The implemented optimizations are: + * . Aligned file-domain partitioning, integrated in 7/28/2005 + * + * In addition, following optimizations are planned: + * . Integrating multiple file-domain partitioning schemes + * (corresponding to Alok Chouhdary's persistent file domain work). + */ + +#ifndef AD_BGL_AGGRS_H_ +#define AD_BGL_AGGRS_H_ + +#include "adio.h" +#include + + extern int *aggrsInPset; /* defined in ad_bgl_aggrs.c */ + + + /* File system (BGL) specific information - + hung off of ADIOI_FileD file descriptor (fd->fs_ptr) at open */ + typedef struct ADIOI_BGL_fs_s { + __blksize_t blksize; + } ADIOI_BGL_fs; + + /* generate a list of I/O aggregators that utilizes BGL-PSET orginization. */ + int ADIOI_BGL_gen_agg_ranklist(ADIO_File fd, int n_aggrs_per_pset); + + /* overriding ADIOI_Calc_file_domains() to apply 'aligned file domain partitioning'. */ + void ADIOI_BGL_GPFS_Calc_file_domains(ADIO_Offset *st_offsets, + ADIO_Offset *end_offsets, + int nprocs, + int nprocs_for_coll, + ADIO_Offset *min_st_offset_ptr, + ADIO_Offset **fd_start_ptr, + ADIO_Offset **fd_end_ptr, + ADIO_Offset *fd_size_ptr, + void *fs_ptr); + + /* a utilitiy function for debugging */ + int ADIOI_BGL_Aggrs_index(ADIO_File fd, int myrank ); + + /* overriding ADIOI_Calc_aggregator() for the default implementation is specific for + static file domain partitioning */ + int ADIOI_BGL_Calc_aggregator(ADIO_File fd, + ADIO_Offset off, + ADIO_Offset min_off, + ADIO_Offset *len, + ADIO_Offset fd_size, + ADIO_Offset *fd_start, + ADIO_Offset *fd_end); + + /* overriding ADIOI_Calc_my_req for the default implementation is specific for + static file domain partitioning */ + void ADIOI_BGL_Calc_my_req ( ADIO_File fd, ADIO_Offset *offset_list, int *len_list, + int contig_access_count, ADIO_Offset + min_st_offset, ADIO_Offset *fd_start, + ADIO_Offset *fd_end, ADIO_Offset fd_size, + int nprocs, + int *count_my_req_procs_ptr, + int **count_my_req_per_proc_ptr, + ADIOI_Access **my_req_ptr, + int **buf_idx_ptr); + + /* + * ADIOI_Calc_others_req + * + * param[in] count_my_req_procs Number of processes whose file domain my + * request touches. + * param[in] count_my_req_per_proc count_my_req_per_proc[i] gives the no. of + * contig. requests of this process in + * process i's file domain. + * param[in] my_req A structure defining my request + * param[in] nprocs Number of nodes in the block + * param[in] myrank Rank of this node + * param[out] count_others_req_proc_ptr Number of processes whose requests lie in + * my process's file domain (including my + * process itself) + * param[out] others_req_ptr Array of other process' requests that lie + * in my process's file domain + */ + void ADIOI_BGL_Calc_others_req(ADIO_File fd, int count_my_req_procs, + int *count_my_req_per_proc, + ADIOI_Access *my_req, + int nprocs, int myrank, + int *count_others_req_procs_ptr, + ADIOI_Access **others_req_ptr); + + +#endif /* AD_BGL_AGGRS_H_ */ diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_close.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_close.c new file mode 100644 index 0000000000..63f620446f --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_close.c @@ -0,0 +1,52 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/** + * \file ad_bgl_open.c + * \brief ??? + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "ad_bgl.h" +#include "ad_bgl_aggrs.h" + +void ADIOI_BGL_Close(ADIO_File fd, int *error_code) +{ + int err, derr=0; + static char myname[] = "ADIOI_BGL_CLOSE"; + +#ifdef PROFILE + MPE_Log_event(9, 0, "start close"); +#endif + + err = close(fd->fd_sys); + if (fd->fd_direct >= 0) + { + derr = close(fd->fd_direct); + } + +#ifdef PROFILE + MPE_Log_event(10, 0, "end close"); +#endif + +/* FPRINTF(stderr,"%s(%d):'%s'. Free %#X\n",myname,__LINE__,fd->filename,(int)fd->fs_ptr);*/ + if (fd->fs_ptr != NULL) { + ADIOI_Free(fd->fs_ptr); + fd->fs_ptr = NULL; + } + fd->fd_sys = -1; + fd->fd_direct = -1; + + if (err == -1 || derr == -1) + { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, + myname, __LINE__, MPI_ERR_IO, + "**io", + "**io %s", strerror(errno)); + } + else *error_code = MPI_SUCCESS; +} diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_fcntl.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_fcntl.c new file mode 100644 index 0000000000..c47c2aa5b5 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_fcntl.c @@ -0,0 +1,57 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/** + * \file ad_bgl_fcntl.c + * \brief ??? + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "ad_bgl.h" +#include "adio_extern.h" +/* #ifdef MPISGI +#include "mpisgi2.h" +#endif */ + +void ADIOI_BGL_Fcntl(ADIO_File fd, int flag, ADIO_Fcntl_t *fcntl_struct, + int *error_code) +{ + static char myname[] = "ADIOI_BGL_FCNTL"; + + switch(flag) { + case ADIO_FCNTL_GET_FSIZE: + fcntl_struct->fsize = lseek(fd->fd_sys, 0, SEEK_END); + if (fd->fp_sys_posn != -1) + lseek(fd->fd_sys, fd->fp_sys_posn, SEEK_SET); + if (fcntl_struct->fsize == -1) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + } + else *error_code = MPI_SUCCESS; + break; + + case ADIO_FCNTL_SET_DISKSPACE: + ADIOI_GEN_Prealloc(fd, fcntl_struct->diskspace, error_code); + break; + + case ADIO_FCNTL_SET_ATOMICITY: + fd->atomicity = (fcntl_struct->atomicity == 0) ? 0 : 1; + *error_code = MPI_SUCCESS; + break; + + /* --BEGIN ERROR HANDLING-- */ + default: + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + myname, __LINE__, + MPI_ERR_ARG, + "**flag", "**flag %d", flag); + /* --END ERROR HANDLING-- */ + } +} diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_getsh.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_getsh.c new file mode 100644 index 0000000000..2c51b1f357 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_getsh.c @@ -0,0 +1,84 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/* ---------------------------------------------------------------- */ +/** + * \file ad_bgl_getsh.c + * \brief ??? + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "ad_bgl.h" + +/* returns the current location of the shared_fp in terms of the + no. of etypes relative to the current view, and also increments the + shared_fp by the number of etypes to be accessed (incr) in the read + or write following this function. */ + +void ADIOI_BGL_Get_shared_fp(ADIO_File fd, int incr, ADIO_Offset *shared_fp, + int *error_code) +{ + ADIO_Offset new_fp; + int err; + MPI_Comm dupcommself; + static char myname[] = "ADIOI_BGL_GET_SHARED_FP"; + + if (fd->shared_fp_fd == ADIO_FILE_NULL) { + MPI_Comm_dup(MPI_COMM_SELF, &dupcommself); + fd->shared_fp_fd = ADIO_Open(MPI_COMM_SELF, + dupcommself, + fd->shared_fp_fname, + fd->file_system, + fd->fns, + ADIO_CREATE | ADIO_RDWR | ADIO_DELETE_ON_CLOSE, + 0, + MPI_BYTE, + MPI_BYTE, + MPI_INFO_NULL, + ADIO_PERM_NULL, + error_code); + if (*error_code != MPI_SUCCESS) return; + *shared_fp = 0; + ADIOI_WRITE_LOCK(fd->shared_fp_fd, 0, SEEK_SET, sizeof(ADIO_Offset)); + err = read(fd->shared_fp_fd->fd_sys, shared_fp, sizeof(ADIO_Offset)); + /* if the file is empty, the above read may return error + (reading beyond end of file). In that case, shared_fp = 0, + set above, is the correct value. */ + } + else { + ADIOI_WRITE_LOCK(fd->shared_fp_fd, 0, SEEK_SET, sizeof(ADIO_Offset)); + + err = lseek(fd->shared_fp_fd->fd_sys, 0, SEEK_SET); + if (err == 0) { + err = read(fd->shared_fp_fd->fd_sys, shared_fp, + sizeof(ADIO_Offset)); + } + if (err == -1) { + ADIOI_UNLOCK(fd->shared_fp_fd, 0, SEEK_SET, sizeof(ADIO_Offset)); + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + return; + } + } + + new_fp = *shared_fp + incr; + + err = lseek(fd->shared_fp_fd->fd_sys, 0, SEEK_SET); + if (err == 0) { + err = write(fd->shared_fp_fd->fd_sys, &new_fp, sizeof(ADIO_Offset)); + } + ADIOI_UNLOCK(fd->shared_fp_fd, 0, SEEK_SET, sizeof(ADIO_Offset)); + if (err == -1) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, + myname, __LINE__, MPI_ERR_IO, + "**io", + "**io %s", strerror(errno)); + } + else *error_code = MPI_SUCCESS; +} diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_hints.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_hints.c new file mode 100644 index 0000000000..8a233b2783 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_hints.c @@ -0,0 +1,338 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/* ---------------------------------------------------------------- */ +/** + * \file ad_bgl_hints.c + * \brief ??? + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "adio.h" +#include "adio_extern.h" + +#include "ad_bgl.h" +#include "ad_bgl_pset.h" +#include "ad_bgl_aggrs.h" + +#define ADIOI_BGL_CB_BUFFER_SIZE_DFLT "16777216" +#define ADIOI_BGL_IND_RD_BUFFER_SIZE_DFLT "4194304" +#define ADIOI_BGL_IND_WR_BUFFER_SIZE_DFLT "4194304" +#define ADIOI_BGL_NAGG_IN_PSET_HINT_NAME "bgl_nodes_pset" + +/* Compute the aggregator-related parameters that are required in 2-phase collective IO of ADIO. */ +extern int +ADIOI_BGL_gen_agg_ranklist(ADIO_File fd, int n_proxy_per_pset); + +void ADIOI_BGL_SetInfo(ADIO_File fd, MPI_Info users_info, int *error_code) +{ +/* if fd->info is null, create a new info object. + Initialize fd->info to default values. + Initialize fd->hints to default values. + Examine the info object passed by the user. If it contains values that + ROMIO understands, override the default. */ + + MPI_Info info; + char *value; + int flag, intval, tmp_val, nprocs, nprocs_is_valid = 0; + static char myname[] = "ADIOI_GEN_SETINFO"; + + int did_anything = 0; + + if (fd->info == MPI_INFO_NULL) MPI_Info_create(&(fd->info)); + info = fd->info; + + /* Note that fd->hints is allocated at file open time; thus it is + * not necessary to allocate it, or check for allocation, here. + */ + + value = (char *) ADIOI_Malloc((MPI_MAX_INFO_VAL+1)*sizeof(char)); + AD_BGL_assert ((value != NULL)); + + /* initialize info and hints to default values if they haven't been + * previously initialized + */ + if (!fd->hints->initialized) { + + did_anything = 1; + + /* buffer size for collective I/O */ + MPI_Info_set(info, "cb_buffer_size", ADIOI_BGL_CB_BUFFER_SIZE_DFLT); + fd->hints->cb_buffer_size = atoi(ADIOI_BGL_CB_BUFFER_SIZE_DFLT); + + /* default is to let romio automatically decide when to use + * collective buffering + */ + MPI_Info_set(info, "romio_cb_read", "enable"); + fd->hints->cb_read = ADIOI_HINT_ENABLE; + MPI_Info_set(info, "romio_cb_write", "enable"); + fd->hints->cb_write = ADIOI_HINT_ENABLE; + + if ( fd->hints->cb_config_list != NULL ) ADIOI_Free (fd->hints->cb_config_list); + fd->hints->cb_config_list = NULL; + + /* number of processes that perform I/O in collective I/O */ + MPI_Comm_size(fd->comm, &nprocs); + nprocs_is_valid = 1; + sprintf(value, "%d", nprocs); + MPI_Info_set(info, "cb_nodes", value); + fd->hints->cb_nodes = -1; + + /* hint indicating that no indep. I/O will be performed on this file */ + MPI_Info_set(info, "romio_no_indep_rw", "false"); + fd->hints->no_indep_rw = 0; + /* deferred_open derrived from no_indep_rw and cb_{read,write} */ + fd->hints->deferred_open = 0; + + /* buffer size for data sieving in independent reads */ + MPI_Info_set(info, "ind_rd_buffer_size", ADIOI_BGL_IND_RD_BUFFER_SIZE_DFLT); + fd->hints->ind_rd_buffer_size = atoi(ADIOI_BGL_IND_RD_BUFFER_SIZE_DFLT); + + /* buffer size for data sieving in independent writes */ + MPI_Info_set(info, "ind_wr_buffer_size", ADIOI_BGL_IND_WR_BUFFER_SIZE_DFLT); + fd->hints->ind_wr_buffer_size = atoi(ADIOI_BGL_IND_WR_BUFFER_SIZE_DFLT); + + if(fd->file_system == ADIO_UFS) + { + /* default for ufs/pvfs is to disable data sieving */ + MPI_Info_set(info, "romio_ds_read", "disable"); + fd->hints->ds_read = ADIOI_HINT_DISABLE; + MPI_Info_set(info, "romio_ds_write", "disable"); + fd->hints->ds_write = ADIOI_HINT_DISABLE; + } + else + { + /* default is to let romio automatically decide when to use data + * sieving + */ + MPI_Info_set(info, "romio_ds_read", "automatic"); + fd->hints->ds_read = ADIOI_HINT_AUTO; + MPI_Info_set(info, "romio_ds_write", "automatic"); + fd->hints->ds_write = ADIOI_HINT_AUTO; + } + + fd->hints->initialized = 1; + } + + /* add in user's info if supplied */ + if (users_info != MPI_INFO_NULL) { + MPI_Info_get(users_info, "cb_buffer_size", MPI_MAX_INFO_VAL, + value, &flag); + if (flag && ((intval=atoi(value)) > 0)) { + tmp_val = intval; + + MPI_Bcast(&tmp_val, 1, MPI_INT, 0, fd->comm); + /* --BEGIN ERROR HANDLING-- */ + if (tmp_val != intval) { + MPIO_ERR_CREATE_CODE_INFO_NOT_SAME(myname, + "cb_buffer_size", + error_code); + return; + } + /* --END ERROR HANDLING-- */ + + MPI_Info_set(info, "cb_buffer_size", value); + fd->hints->cb_buffer_size = intval; + + } + + /* new hints for enabling/disabling coll. buffering on + * reads/writes + */ + MPI_Info_get(users_info, "romio_cb_read", MPI_MAX_INFO_VAL, value, &flag); + if (flag) { + if (!strcmp(value, "enable") || !strcmp(value, "ENABLE")) { + MPI_Info_set(info, "romio_cb_read", value); + fd->hints->cb_read = ADIOI_HINT_ENABLE; + } + else if (!strcmp(value, "disable") || !strcmp(value, "DISABLE")) { + /* romio_cb_read overrides no_indep_rw */ + MPI_Info_set(info, "romio_cb_read", value); + MPI_Info_set(info, "romio_no_indep_rw", "false"); + fd->hints->cb_read = ADIOI_HINT_DISABLE; + fd->hints->no_indep_rw = ADIOI_HINT_DISABLE; + } + else if (!strcmp(value, "automatic") || !strcmp(value, "AUTOMATIC")) + { + MPI_Info_set(info, "romio_cb_read", value); + fd->hints->cb_read = ADIOI_HINT_AUTO; + } + + tmp_val = fd->hints->cb_read; + + MPI_Bcast(&tmp_val, 1, MPI_INT, 0, fd->comm); + /* --BEGIN ERROR HANDLING-- */ + if (tmp_val != fd->hints->cb_read) { + MPIO_ERR_CREATE_CODE_INFO_NOT_SAME(myname, + "romio_cb_read", + error_code); + return; + } + /* --END ERROR HANDLING-- */ + } + MPI_Info_get(users_info, "romio_cb_write", MPI_MAX_INFO_VAL, value, &flag); + if (flag) { + if (!strcmp(value, "enable") || !strcmp(value, "ENABLE")) { + MPI_Info_set(info, "romio_cb_write", value); + fd->hints->cb_write = ADIOI_HINT_ENABLE; + } + else if (!strcmp(value, "disable") || !strcmp(value, "DISABLE")) + { + /* romio_cb_write overrides no_indep_rw, too */ + MPI_Info_set(info, "romio_cb_write", value); + MPI_Info_set(info, "romio_no_indep_rw", "false"); + fd->hints->cb_write = ADIOI_HINT_DISABLE; + fd->hints->no_indep_rw = ADIOI_HINT_DISABLE; + } + else if (!strcmp(value, "automatic") || + !strcmp(value, "AUTOMATIC")) + { + MPI_Info_set(info, "romio_cb_write", value); + fd->hints->cb_write = ADIOI_HINT_AUTO; + } + + tmp_val = fd->hints->cb_write; + + MPI_Bcast(&tmp_val, 1, MPI_INT, 0, fd->comm); + /* --BEGIN ERROR HANDLING-- */ + if (tmp_val != fd->hints->cb_write) { + MPIO_ERR_CREATE_CODE_INFO_NOT_SAME(myname, + "romio_cb_write", + error_code); + return; + } + /* --END ERROR HANDLING-- */ + } + + /* new hint for specifying no indep. read/write will be performed */ + MPI_Info_get(users_info, "romio_no_indep_rw", MPI_MAX_INFO_VAL, value, &flag); + if (flag) { + if (!strcmp(value, "true") || !strcmp(value, "TRUE")) { + /* if 'no_indep_rw' set, also hint that we will do + * collective buffering: if we aren't doing independent io, + * then we have to do collective */ + MPI_Info_set(info, "romio_no_indep_rw", value); + MPI_Info_set(info, "romio_cb_write", "enable"); + MPI_Info_set(info, "romio_cb_read", "enable"); + fd->hints->no_indep_rw = 1; + fd->hints->cb_read = 1; + fd->hints->cb_write = 1; + tmp_val = 1; + } + else if (!strcmp(value, "false") || !strcmp(value, "FALSE")) { + MPI_Info_set(info, "romio_no_indep_rw", value); + fd->hints->no_indep_rw = 0; + tmp_val = 0; + } + else { + /* default is above */ + tmp_val = 0; + } + + MPI_Bcast(&tmp_val, 1, MPI_INT, 0, fd->comm); + /* --BEGIN ERROR HANDLING-- */ + if (tmp_val != fd->hints->no_indep_rw) { + MPIO_ERR_CREATE_CODE_INFO_NOT_SAME(myname, + "romio_no_indep_rw", + error_code); + return; + } + /* --END ERROR HANDLING-- */ + } + /* new hints for enabling/disabling data sieving on + * reads/writes + */ + MPI_Info_get(users_info, "romio_ds_read", MPI_MAX_INFO_VAL, value, + &flag); + if (flag) { + if (!strcmp(value, "enable") || !strcmp(value, "ENABLE")) { + MPI_Info_set(info, "romio_ds_read", value); + fd->hints->ds_read = ADIOI_HINT_ENABLE; + } + else if (!strcmp(value, "disable") || !strcmp(value, "DISABLE")) { + MPI_Info_set(info, "romio_ds_read", value); + fd->hints->ds_read = ADIOI_HINT_DISABLE; + } + else if (!strcmp(value, "automatic") || !strcmp(value, "AUTOMATIC")) + { + MPI_Info_set(info, "romio_ds_read", value); + fd->hints->ds_read = ADIOI_HINT_AUTO; + } + /* otherwise ignore */ + } + MPI_Info_get(users_info, "romio_ds_write", MPI_MAX_INFO_VAL, value, + &flag); + if (flag) { + if (!strcmp(value, "enable") || !strcmp(value, "ENABLE")) { + MPI_Info_set(info, "romio_ds_write", value); + fd->hints->ds_write = ADIOI_HINT_ENABLE; + } + else if (!strcmp(value, "disable") || !strcmp(value, "DISABLE")) { + MPI_Info_set(info, "romio_ds_write", value); + fd->hints->ds_write = ADIOI_HINT_DISABLE; + } + else if (!strcmp(value, "automatic") || !strcmp(value, "AUTOMATIC")) + { + MPI_Info_set(info, "romio_ds_write", value); + fd->hints->ds_write = ADIOI_HINT_AUTO; + } + /* otherwise ignore */ + } + + MPI_Info_get(users_info, "ind_wr_buffer_size", MPI_MAX_INFO_VAL, + value, &flag); + if (flag && ((intval = atoi(value)) > 0)) { + MPI_Info_set(info, "ind_wr_buffer_size", value); + fd->hints->ind_wr_buffer_size = intval; + } + + MPI_Info_get(users_info, "ind_rd_buffer_size", MPI_MAX_INFO_VAL, + value, &flag); + if (flag && ((intval = atoi(value)) > 0)) { + MPI_Info_set(info, "ind_rd_buffer_size", value); + fd->hints->ind_rd_buffer_size = intval; + } + + memset( value, 0, MPI_MAX_INFO_VAL+1 ); + MPI_Info_get(users_info, ADIOI_BGL_NAGG_IN_PSET_HINT_NAME, MPI_MAX_INFO_VAL, + value, &flag); + if (flag && ((intval = atoi(value)) > 0)) { + + did_anything = 1; + MPI_Info_set(info, ADIOI_BGL_NAGG_IN_PSET_HINT_NAME, value); + fd->hints->cb_nodes = intval; + } + } + + /* associate CB aggregators to certain CNs in every involved PSET */ + if (did_anything) { + ADIOI_BGL_gen_agg_ranklist(fd, fd->hints->cb_nodes); + } + + /* deferred_open won't be set by callers, but if the user doesn't + * explicitly disable collecitve buffering (two-phase) and does hint that + * io w/o independent io is going on, we'll set this internal hint as a + * convenience */ + if ( ( (fd->hints->cb_read != ADIOI_HINT_DISABLE) + && (fd->hints->cb_write != ADIOI_HINT_DISABLE) + && fd->hints->no_indep_rw ) ) + { + fd->hints->deferred_open = 1; + } else { + /* setting romio_no_indep_rw enable and romio_cb_{read,write} + * disable at the same time doesn't make sense. honor + * romio_cb_{read,write} and force the no_indep_rw hint to + * 'disable' */ + MPI_Info_set(info, "romio_no_indep_rw", "false"); + fd->hints->no_indep_rw = 0; + fd->hints->deferred_open = 0; + } + + ADIOI_Free(value); + + *error_code = MPI_SUCCESS; +} diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_open.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_open.c new file mode 100644 index 0000000000..3c7119291c --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_open.c @@ -0,0 +1,114 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/* ---------------------------------------------------------------- */ +/** + * \file ad_bgl_open.c + * \brief ??? + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "ad_bgl.h" +#include "ad_bgl_aggrs.h" + +void ADIOI_BGL_Open(ADIO_File fd, int *error_code) +{ + int perm, old_mask, amode; + static char myname[] = "ADIOI_BGL_OPEN"; + + /* set internal variables for tuning environment variables */ + ad_bgl_get_env_vars(); + + if (fd->perm == ADIO_PERM_NULL) { + old_mask = umask(022); + umask(old_mask); + perm = old_mask ^ 0666; + } + else perm = fd->perm; + + amode = 0; + if (fd->access_mode & ADIO_CREATE) + amode = amode | O_CREAT; + if (fd->access_mode & ADIO_RDONLY) + amode = amode | O_RDONLY; + if (fd->access_mode & ADIO_WRONLY) + amode = amode | O_WRONLY; + if (fd->access_mode & ADIO_RDWR) + amode = amode | O_RDWR; + if (fd->access_mode & ADIO_EXCL) + amode = amode | O_EXCL; + + fd->fd_sys = open(fd->filename, amode, perm); + fd->fd_direct = -1; + + if ((fd->fd_sys != -1) && (fd->access_mode & ADIO_APPEND)) + fd->fp_ind = fd->fp_sys_posn = lseek(fd->fd_sys, 0, SEEK_END); + + if(fd->fd_sys != -1) + { + struct stat64 bgl_stat; + int rc = stat64(fd->filename,&bgl_stat); + if (rc >= 0) + { + /* store the blksize in the file system specific storage */ + AD_BGL_assert(fd->fs_ptr == NULL); + fd->fs_ptr = (ADIOI_BGL_fs*) ADIOI_Malloc(sizeof(ADIOI_BGL_fs)); + ((ADIOI_BGL_fs*)fd->fs_ptr)->blksize = bgl_stat.st_blksize; +/* FPRINTF(stderr,"%s(%d):Successful stat '%s'. Blocksize=%ld\n",myname,__LINE__,fd->filename,bgl_stat.st_blksize);*/ + } +/* else + FPRINTF(stderr,"%s(%d):Stat '%s' failed with rc=%d, errno=%d\n",myname,__LINE__,fd->filename,rc,errno);*/ + } + + if (fd->fd_sys == -1) { + if (errno == ENAMETOOLONG) + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_BAD_FILE, + "**filenamelong", + "**filenamelong %s %d", + fd->filename, + strlen(fd->filename)); + else if (errno == ENOENT) + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_NO_SUCH_FILE, + "**filenoexist", + "**filenoexist %s", + fd->filename); + else if (errno == ENOTDIR || errno == ELOOP) + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + myname, __LINE__, + MPI_ERR_BAD_FILE, + "**filenamedir", + "**filenamedir %s", + fd->filename); + else if (errno == EACCES) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_ACCESS, + "**fileaccess", + "**fileaccess %s", + fd->filename ); + } + else if (errno == EROFS) { + /* Read only file or file system and write access requested */ + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_READ_ONLY, + "**ioneedrd", 0 ); + } + else { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + } + } + else *error_code = MPI_SUCCESS; +} diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_pset.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_pset.c new file mode 100644 index 0000000000..370b7315c8 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_pset.c @@ -0,0 +1,109 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/* ---------------------------------------------------------------- */ +/** + * \file ad_bgl_pset.c + * \brief Definition of functions associated to structs ADIOI_BGL_ProcInfo_t and ADIOI_BGL_ConfInfo_t + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include +#include "ad_bgl.h" +#include "ad_bgl_pset.h" +#include "mpidimpl.h" + +ADIOI_BGL_ProcInfo_t * +ADIOI_BGL_ProcInfo_new() +{ + ADIOI_BGL_ProcInfo_t *p = (ADIOI_BGL_ProcInfo_t *) ADIOI_Malloc (sizeof(ADIOI_BGL_ProcInfo_t)); + AD_BGL_assert ((p != NULL)); + return p; +} + +ADIOI_BGL_ProcInfo_t * +ADIOI_BGL_ProcInfo_new_n( int n ) +{ + ADIOI_BGL_ProcInfo_t *p = (ADIOI_BGL_ProcInfo_t *) ADIOI_Malloc (n * sizeof(ADIOI_BGL_ProcInfo_t)); + AD_BGL_assert ((p != NULL)); + return p; +} + +void +ADIOI_BGL_ProcInfo_free( ADIOI_BGL_ProcInfo_t *info ) +{ + if (info != NULL) ADIOI_Free (info); +} + +static +void +ADIOI_BGL_ProcInfo_set(ADIOI_BGL_ProcInfo_t *info, const DCMF_Hardware_t *hw, int r) +{ + info->psetNum = hw->idOfPset; + info->xInPset = hw->xCoord; + info->yInPset = hw->yCoord; + info->zInPset = hw->zCoord; + info->cpuid = hw->tCoord; + info->rank = r; + info->rankInPset = hw->rankInPset; +} + + +ADIOI_BGL_ConfInfo_t * +ADIOI_BGL_ConfInfo_new () +{ + ADIOI_BGL_ConfInfo_t *p = (ADIOI_BGL_ConfInfo_t *) ADIOI_Malloc (sizeof(ADIOI_BGL_ConfInfo_t)); + AD_BGL_assert ((p != NULL)); + return p; +} + +static +void +ADIOI_BGL_ConfInfo_set(ADIOI_BGL_ConfInfo_t *info, const DCMF_Hardware_t *hw, int s, int n_aggrs) +{ + info->PsetSize = hw->sizeOfPset; + info->numPsets = (hw->xSize * hw->ySize * + hw->zSize) / hw->sizeOfPset; + info->isVNM = (hw->tSize != 1); + info->cpuidSize = hw->tSize; + info->virtualPsetSize = hw->sizeOfPset * hw->tSize; + info->nProcs = s; + + /* More complicated logic maybe needed for nAggrs specification */ + info->nAggrs = n_aggrs; + if ( info->nAggrs <=0 || MIN(info->nProcs, info->virtualPsetSize) < info->nAggrs ) + info->nAggrs = ADIOI_BGL_NAGG_PSET_DFLT; + if ( info->nAggrs > info->virtualPsetSize ) info->nAggrs = info->virtualPsetSize; + + info->aggRatio = 1. * info->nAggrs / info->virtualPsetSize; + if (info->aggRatio > 1) info->aggRatio = 1.; +} + +void +ADIOI_BGL_ConfInfo_free( ADIOI_BGL_ConfInfo_t *info ) +{ + if (info != NULL) ADIOI_Free (info); +} + +void +ADIOI_BGL_persInfo_init(ADIOI_BGL_ConfInfo_t *conf, + ADIOI_BGL_ProcInfo_t *proc, + int s, int r, int n_aggrs) +{ + DCMF_Hardware_t hw; + DCMF_Hardware(&hw); + + ADIOI_BGL_ConfInfo_set (conf, &hw, s, n_aggrs); + ADIOI_BGL_ProcInfo_set (proc, &hw, r); +} + +void +ADIOI_BGL_persInfo_free( ADIOI_BGL_ConfInfo_t *conf, ADIOI_BGL_ProcInfo_t *proc ) +{ + ADIOI_BGL_ConfInfo_free( conf ); + ADIOI_BGL_ProcInfo_free( proc ); +} diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_pset.h b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_pset.h new file mode 100644 index 0000000000..a5e45f6d2d --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_pset.h @@ -0,0 +1,80 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/* ---------------------------------------------------------------- */ +/** + * \file ad_bgl_pset.h + * \brief ??? + */ + +/* File: ad_bgl_pset.h + * + * Defines two structures that keep BG/L PSET specific information and their public interfaces: + * . ADIOI_BGL_ProcInfo_t object keeps specific information to each process + * . ADIOI_BGL_ConfInfo_t object keeps general information for the whole communicator, only kept + * on process 0. + */ + +#ifndef AD_BGL_PSET_H_ +#define AD_BGL_PSET_H_ + +/* Keeps specific information to each process, will be exchanged among processes */ +typedef struct { + + int psetNum; /* which PSET I am in */ + int rank; /* my rank */ + int xInPset; /* my relative coordinates in my PSET */ + int yInPset; + int zInPset; + int cpuid; /* my CPU id -- for virtual node mode (t coord)*/ + int rankInPset; /* my relative rank in my PSET */ + +} ADIOI_BGL_ProcInfo_t __attribute__((aligned(16))); + + +/* Keeps general information for the whole communicator, only on process 0 */ +typedef struct { + + int PsetSize; + int nAggrs; + int numPsets; + int isVNM; + int virtualPsetSize; + int nProcs; + float aggRatio; + int cpuidSize; /* how many cpu ids? (t size) */ + +} ADIOI_BGL_ConfInfo_t __attribute__((aligned(16))); + + +#undef MIN +#define MIN(a,b) ((acomm, &nprocs); + MPI_Comm_rank(fd->comm, &myrank); + + /* number of aggregators, cb_nodes, is stored in the hints */ + nprocs_for_coll = fd->hints->cb_nodes; + orig_fp = fd->fp_ind; + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, r, 0, 1, 0, BGLMPIO_CIO_LCOMP, BGLMPIO_CIO_LAST ) +#endif + + /* only check for interleaving if cb_read isn't disabled */ + if (fd->hints->cb_read != ADIOI_HINT_DISABLE) { + /* For this process's request, calculate the list of offsets and + lengths in the file and determine the start and end offsets. */ + + /* Note: end_offset points to the last byte-offset that will be accessed. + e.g., if start_offset=0 and 100 bytes to be read, end_offset=99*/ + + ADIOI_Calc_my_off_len(fd, count, datatype, file_ptr_type, offset, + &offset_list, &len_list, &start_offset, + &end_offset, &contig_access_count); + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, r, 1, 1, 1, BGLMPIO_CIO_GATHER, BGLMPIO_CIO_LCOMP ) +#endif + + /* for (i=0; icomm ); + + for (ii=0; iicomm); + MPI_Allgather(&end_offset, 1, ADIO_OFFSET, end_offsets, 1, + ADIO_OFFSET, fd->comm); + } + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, r, 0, 1, 1, BGLMPIO_CIO_PATANA, BGLMPIO_CIO_GATHER ) +#endif + + /* are the accesses of different processes interleaved? */ + for (i=1; ihints->cb_read == ADIOI_HINT_DISABLE + || (!interleave_count && (fd->hints->cb_read == ADIOI_HINT_AUTO))) + { + /* don't do aggregation */ + if (fd->hints->cb_read != ADIOI_HINT_DISABLE) { + ADIOI_Free(offset_list); + ADIOI_Free(len_list); + ADIOI_Free(st_offsets); + ADIOI_Free(end_offsets); + } + + fd->fp_ind = orig_fp; + ADIOI_Datatype_iscontig(fd->filetype, &filetype_is_contig); + + if (buftype_is_contig && filetype_is_contig) { + if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { + off = fd->disp + (fd->etype_size) * offset; + ADIO_ReadContig(fd, buf, count, datatype, ADIO_EXPLICIT_OFFSET, + off, status, error_code); + } + else ADIO_ReadContig(fd, buf, count, datatype, ADIO_INDIVIDUAL, + 0, status, error_code); + } + else ADIO_ReadStrided(fd, buf, count, datatype, file_ptr_type, + offset, status, error_code); + + return; + } + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, r, 1, 1, 1, BGLMPIO_CIO_FD_PART, BGLMPIO_CIO_PATANA ) +#endif + + /* We're going to perform aggregation of I/O. Here we call + * ADIOI_Calc_file_domains() to determine what processes will handle I/O + * to what regions. We pass nprocs_for_coll into this function; it is + * used to determine how many processes will perform I/O, which is also + * the number of regions into which the range of bytes must be divided. + * These regions are called "file domains", or FDs. + * + * When this function returns, fd_start, fd_end, fd_size, and + * min_st_offset will be filled in. fd_start holds the starting byte + * location for each file domain. fd_end holds the ending byte location. + * min_st_offset holds the minimum byte location that will be accessed. + * + * Both fd_start[] and fd_end[] are indexed by an aggregator number; this + * needs to be mapped to an actual rank in the communicator later. + * + */ + if (bglmpio_tuneblocking) + ADIOI_BGL_GPFS_Calc_file_domains(st_offsets, end_offsets, nprocs, + nprocs_for_coll, &min_st_offset, + &fd_start, &fd_end, &fd_size, fd->fs_ptr); + else + ADIOI_Calc_file_domains(st_offsets, end_offsets, nprocs, + nprocs_for_coll, &min_st_offset, + &fd_start, &fd_end, &fd_size); + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, r, 0, 1, 1, BGLMPIO_CIO_MYREQ, BGLMPIO_CIO_FD_PART ) +#endif + + /* calculate where the portions of the access requests of this process + * are located in terms of the file domains. this could be on the same + * process or on other processes. this function fills in: + * count_my_req_procs - number of processes (including this one) for which + * this process has requests in their file domain + * count_my_req_per_proc - count of requests for each process, indexed + * by rank of the process + * my_req[] - array of data structures describing the requests to be + * performed by each process (including self). indexed by rank. + * buf_idx[] - array of locations into which data can be directly moved; + * this is only valid for contiguous buffer case + */ + if (bglmpio_tuneblocking) + ADIOI_BGL_Calc_my_req(fd, offset_list, len_list, contig_access_count, + min_st_offset, fd_start, fd_end, fd_size, + nprocs, &count_my_req_procs, + &count_my_req_per_proc, &my_req, + &buf_idx); + else + ADIOI_Calc_my_req(fd, offset_list, len_list, contig_access_count, + min_st_offset, fd_start, fd_end, fd_size, + nprocs, &count_my_req_procs, + &count_my_req_per_proc, &my_req, + &buf_idx); + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, r, 1, 1, 1, BGLMPIO_CIO_OTHREQ, BGLMPIO_CIO_MYREQ ) +#endif + + /* perform a collective communication in order to distribute the + * data calculated above. fills in the following: + * count_others_req_procs - number of processes (including this + * one) which have requests in this process's file domain. + * count_others_req_per_proc[] - number of separate contiguous + * requests from proc i lie in this process's file domain. + */ + if (bglmpio_tuneblocking) + ADIOI_BGL_Calc_others_req(fd, count_my_req_procs, + count_my_req_per_proc, my_req, + nprocs, myrank, &count_others_req_procs, + &others_req); + + else + ADIOI_Calc_others_req(fd, count_my_req_procs, + count_my_req_per_proc, my_req, + nprocs, myrank, &count_others_req_procs, + &others_req); + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, r, 1, 1, 1, BGLMPIO_CIO_DEXCH, BGLMPIO_CIO_OTHREQ ) +#endif + + /* my_req[] and count_my_req_per_proc aren't needed at this point, so + * let's free the memory + */ + ADIOI_Free(count_my_req_per_proc); + for (i=0; ifp_sys_posn = -1; /* set it to null. */ +} + +#if 0 +void ADIOI_Calc_my_off_len(ADIO_File fd, int bufcount, MPI_Datatype + datatype, int file_ptr_type, ADIO_Offset + offset, ADIO_Offset **offset_list_ptr, int + **len_list_ptr, ADIO_Offset *start_offset_ptr, + ADIO_Offset *end_offset_ptr, int + *contig_access_count_ptr) +{ + int filetype_size, buftype_size, etype_size; + int i, j, k, frd_size=0, old_frd_size=0, st_index=0; + int n_filetypes, etype_in_filetype; + ADIO_Offset abs_off_in_filetype=0; + int bufsize, sum, n_etypes_in_filetype, size_in_filetype; + int contig_access_count, *len_list, flag, filetype_is_contig; + MPI_Aint filetype_extent, filetype_lb; + ADIOI_Flatlist_node *flat_file; + ADIO_Offset *offset_list, off, end_offset=0, disp; + +/* For this process's request, calculate the list of offsets and + lengths in the file and determine the start and end offsets. */ + + ADIOI_Datatype_iscontig(fd->filetype, &filetype_is_contig); + + MPI_Type_size(fd->filetype, &filetype_size); + MPI_Type_extent(fd->filetype, &filetype_extent); + MPI_Type_lb(fd->filetype, &filetype_lb); + MPI_Type_size(datatype, &buftype_size); + etype_size = fd->etype_size; + + if ( ! filetype_size ) { + *contig_access_count_ptr = 0; + *offset_list_ptr = (ADIO_Offset *) ADIOI_Malloc(2*sizeof(ADIO_Offset)); + *len_list_ptr = (int *) ADIOI_Malloc(2*sizeof(int)); + /* 2 is for consistency. everywhere I malloc one more than needed */ + + offset_list = *offset_list_ptr; + len_list = *len_list_ptr; + offset_list[0] = (file_ptr_type == ADIO_INDIVIDUAL) ? fd->fp_ind : + fd->disp + etype_size * offset; + len_list[0] = 0; + *start_offset_ptr = offset_list[0]; + *end_offset_ptr = offset_list[0] + len_list[0] - 1; + + return; + } + + if (filetype_is_contig) { + *contig_access_count_ptr = 1; + *offset_list_ptr = (ADIO_Offset *) ADIOI_Malloc(2*sizeof(ADIO_Offset)); + *len_list_ptr = (int *) ADIOI_Malloc(2*sizeof(int)); + /* 2 is for consistency. everywhere I malloc one more than needed */ + + offset_list = *offset_list_ptr; + len_list = *len_list_ptr; + offset_list[0] = (file_ptr_type == ADIO_INDIVIDUAL) ? fd->fp_ind : + fd->disp + etype_size * offset; + len_list[0] = bufcount * buftype_size; + *start_offset_ptr = offset_list[0]; + *end_offset_ptr = offset_list[0] + len_list[0] - 1; + + /* update file pointer */ + if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind = *end_offset_ptr + 1; + } + + else { + + /* First calculate what size of offset_list and len_list to allocate */ + + /* filetype already flattened in ADIO_Open or ADIO_Fcntl */ + flat_file = ADIOI_Flatlist; + while (flat_file->type != fd->filetype) flat_file = flat_file->next; + disp = fd->disp; + + if (file_ptr_type == ADIO_INDIVIDUAL) { + offset = fd->fp_ind; /* in bytes */ + n_filetypes = -1; + flag = 0; + while (!flag) { + n_filetypes++; + for (i=0; icount; i++) { + if (disp + flat_file->indices[i] + + (ADIO_Offset) n_filetypes*filetype_extent + + flat_file->blocklens[i] >= offset) + { + st_index = i; + frd_size = (int) (disp + flat_file->indices[i] + + (ADIO_Offset) n_filetypes*filetype_extent + + flat_file->blocklens[i] - offset); + flag = 1; + break; + } + } + } + } + else { + n_etypes_in_filetype = filetype_size/etype_size; + n_filetypes = (int) (offset / n_etypes_in_filetype); + etype_in_filetype = (int) (offset % n_etypes_in_filetype); + size_in_filetype = etype_in_filetype * etype_size; + + sum = 0; + for (i=0; icount; i++) { + sum += flat_file->blocklens[i]; + if (sum > size_in_filetype) { + st_index = i; + frd_size = sum - size_in_filetype; + abs_off_in_filetype = flat_file->indices[i] + + size_in_filetype - (sum - flat_file->blocklens[i]); + break; + } + } + + /* abs. offset in bytes in the file */ + offset = disp + (ADIO_Offset) n_filetypes*filetype_extent + + abs_off_in_filetype; + } + + /* calculate how much space to allocate for offset_list, len_list */ + + old_frd_size = frd_size; + contig_access_count = i = 0; + j = st_index; + bufsize = buftype_size * bufcount; + frd_size = ADIOI_MIN(frd_size, bufsize); + while (i < bufsize) { + if (frd_size) contig_access_count++; + i += frd_size; + j = (j + 1) % flat_file->count; + frd_size = ADIOI_MIN(flat_file->blocklens[j], bufsize-i); + } + + /* allocate space for offset_list and len_list */ + + *offset_list_ptr = (ADIO_Offset *) + ADIOI_Malloc((contig_access_count+1)*sizeof(ADIO_Offset)); + *len_list_ptr = (int *) ADIOI_Malloc((contig_access_count+1)*sizeof(int)); + /* +1 to avoid a 0-size malloc */ + + offset_list = *offset_list_ptr; + len_list = *len_list_ptr; + + /* find start offset, end offset, and fill in offset_list and len_list */ + + *start_offset_ptr = offset; /* calculated above */ + + i = k = 0; + j = st_index; + off = offset; + frd_size = ADIOI_MIN(old_frd_size, bufsize); + while (i < bufsize) { + if (frd_size) { + offset_list[k] = off; + len_list[k] = frd_size; + k++; + } + i += frd_size; + end_offset = off + frd_size - 1; + + /* Note: end_offset points to the last byte-offset that will be accessed. + e.g., if start_offset=0 and 100 bytes to be read, end_offset=99*/ + + if (off + frd_size < disp + flat_file->indices[j] + + flat_file->blocklens[j] + + (ADIO_Offset) n_filetypes*filetype_extent) + { + off += frd_size; + /* did not reach end of contiguous block in filetype. + * no more I/O needed. off is incremented by frd_size. + */ + } + else { + if (j < (flat_file->count - 1)) j++; + else { + /* hit end of flattened filetype; + * start at beginning again + */ + j = 0; + n_filetypes++; + } + off = disp + flat_file->indices[j] + + (ADIO_Offset) n_filetypes*filetype_extent; + frd_size = ADIOI_MIN(flat_file->blocklens[j], bufsize-i); + } + } + + /* update file pointer */ + if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind = off; + + *contig_access_count_ptr = contig_access_count; + *end_offset_ptr = end_offset; + } +} +#endif + +static void ADIOI_Read_and_exch(ADIO_File fd, void *buf, MPI_Datatype + datatype, int nprocs, + int myrank, ADIOI_Access + *others_req, ADIO_Offset *offset_list, + int *len_list, int contig_access_count, ADIO_Offset + min_st_offset, ADIO_Offset fd_size, + ADIO_Offset *fd_start, ADIO_Offset *fd_end, + int *buf_idx, int *error_code) +{ +/* Read in sizes of no more than coll_bufsize, an info parameter. + Send data to appropriate processes. + Place recd. data in user buf. + The idea is to reduce the amount of extra memory required for + collective I/O. If all data were read all at once, which is much + easier, it would require temp space more than the size of user_buf, + which is often unacceptable. For example, to read a distributed + array from a file, where each local array is 8Mbytes, requiring + at least another 8Mbytes of temp space is unacceptable. */ + + int i, j, m, size, ntimes, max_ntimes, buftype_is_contig; + ADIO_Offset st_loc=-1, end_loc=-1, off, done, real_off, req_off; + char *read_buf = NULL, *tmp_buf; + int *curr_offlen_ptr, *count, *send_size, *recv_size; + int *partial_send, *recd_from_proc, *start_pos, for_next_iter; + int real_size, req_len, flag, for_curr_iter, rank; + MPI_Status status; + ADIOI_Flatlist_node *flat_buf=NULL; + MPI_Aint buftype_extent; + int coll_bufsize; + + int iii; + + *error_code = MPI_SUCCESS; /* changed below if error */ + /* only I/O errors are currently reported */ + +/* calculate the number of reads of size coll_bufsize + to be done by each process and the max among all processes. + That gives the no. of communication phases as well. + coll_bufsize is obtained from the hints object. */ + + coll_bufsize = fd->hints->cb_buffer_size; + + /* grab some initial values for st_loc and end_loc */ + for (i=0; i < nprocs; i++) { + if (others_req[i].count) { + st_loc = others_req[i].offsets[0]; + end_loc = others_req[i].offsets[0]; + break; + } + } + + /* now find the real values */ + for (i=0; i < nprocs; i++) + for (j=0; jcomm); + + if (ntimes) read_buf = (char *) ADIOI_Malloc(coll_bufsize); + + curr_offlen_ptr = (int *) ADIOI_Calloc(nprocs, sizeof(int)); + /* its use is explained below. calloc initializes to 0. */ + + count = (int *) ADIOI_Malloc(nprocs * sizeof(int)); + /* to store count of how many off-len pairs per proc are satisfied + in an iteration. */ + + partial_send = (int *) ADIOI_Calloc(nprocs, sizeof(int)); + /* if only a portion of the last off-len pair is sent to a process + in a particular iteration, the length sent is stored here. + calloc initializes to 0. */ + + send_size = (int *) ADIOI_Malloc(nprocs * sizeof(int)); + /* total size of data to be sent to each proc. in an iteration */ + + recv_size = (int *) ADIOI_Malloc(nprocs * sizeof(int)); + /* total size of data to be recd. from each proc. in an iteration. + Of size nprocs so that I can use MPI_Alltoall later. */ + + recd_from_proc = (int *) ADIOI_Calloc(nprocs, sizeof(int)); + /* amount of data recd. so far from each proc. Used in + ADIOI_Fill_user_buffer. initialized to 0 here. */ + + start_pos = (int *) ADIOI_Malloc(nprocs*sizeof(int)); + /* used to store the starting value of curr_offlen_ptr[i] in + this iteration */ + + ADIOI_Datatype_iscontig(datatype, &buftype_is_contig); + if (!buftype_is_contig) { + ADIOI_Flatten_datatype(datatype); + flat_buf = ADIOI_Flatlist; + while (flat_buf->type != datatype) flat_buf = flat_buf->next; + } + MPI_Type_extent(datatype, &buftype_extent); + + done = 0; + off = st_loc; + for_curr_iter = for_next_iter = 0; + + MPI_Comm_rank(fd->comm, &rank); + +#ifdef PROFILE + MPE_Log_event(14, 0, "end computation"); +#endif + + for (m=0; mcomm); + + nprocs_recv = 0; + for (i=0; i < nprocs; i++) if (recv_size[i]) nprocs_recv++; + + nprocs_send = 0; + for (i=0; icomm, requests+j); + j++; + buf_idx[i] += recv_size[i]; + } + } + else { +/* allocate memory for recv_buf and post receives */ + recv_buf = (char **) ADIOI_Malloc(nprocs * sizeof(char*)); + for (i=0; i < nprocs; i++) + if (recv_size[i]) recv_buf[i] = + (char *) ADIOI_Malloc(recv_size[i]); + + j = 0; + for (i=0; i < nprocs; i++) + if (recv_size[i]) { + MPI_Irecv(recv_buf[i], recv_size[i], MPI_BYTE, i, + myrank+i+100*iter, fd->comm, requests+j); + j++; + /* FPRINTF(stderr, "node %d, recv_size %d, tag %d \n", + myrank, recv_size[i], myrank+i+100*iter); */ + } + } + +/* create derived datatypes and send data */ + + j = 0; + for (i=0; icomm, requests+nprocs_recv+j); + MPI_Type_free(&send_type); + if (partial_send[i]) others_req[i].lens[k] = tmp; + j++; + } + } + + statuses = (MPI_Status *) ADIOI_Malloc((nprocs_send+nprocs_recv+1) * \ + sizeof(MPI_Status)); + /* +1 to avoid a 0-size malloc */ + + /* wait on the receives */ + if (nprocs_recv) { +#ifdef NEEDS_MPI_TEST + j = 0; + while (!j) MPI_Testall(nprocs_recv, requests, &j, statuses); +#else + MPI_Waitall(nprocs_recv, requests, statuses); +#endif + + /* if noncontiguous, to the copies from the recv buffers */ + if (!buftype_is_contig) + ADIOI_Fill_user_buffer(fd, buf, flat_buf, recv_buf, + offset_list, len_list, recv_size, + requests, statuses, recd_from_proc, + nprocs, contig_access_count, + min_st_offset, fd_size, fd_start, fd_end, + buftype_extent); + } + + /* wait on the sends*/ + MPI_Waitall(nprocs_send, requests+nprocs_recv, statuses+nprocs_recv); + + ADIOI_Free(statuses); + ADIOI_Free(requests); + + if (!buftype_is_contig) { + for (i=0; i < nprocs; i++) + if (recv_size[i]) ADIOI_Free(recv_buf[i]); + ADIOI_Free(recv_buf); + } +} + + +#define ADIOI_BUF_INCR \ +{ \ + while (buf_incr) { \ + size_in_buf = ADIOI_MIN(buf_incr, flat_buf_sz); \ + user_buf_idx += size_in_buf; \ + flat_buf_sz -= size_in_buf; \ + if (!flat_buf_sz) { \ + if (flat_buf_idx < (flat_buf->count - 1)) flat_buf_idx++; \ + else { \ + flat_buf_idx = 0; \ + n_buftypes++; \ + } \ + user_buf_idx = flat_buf->indices[flat_buf_idx] + \ + n_buftypes*buftype_extent; \ + flat_buf_sz = flat_buf->blocklens[flat_buf_idx]; \ + } \ + buf_incr -= size_in_buf; \ + } \ +} + + +#define ADIOI_BUF_COPY \ +{ \ + while (size) { \ + size_in_buf = ADIOI_MIN(size, flat_buf_sz); \ + memcpy(((char *) buf) + user_buf_idx, \ + &(recv_buf[p][recv_buf_idx[p]]), size_in_buf); \ + recv_buf_idx[p] += size_in_buf; \ + user_buf_idx += size_in_buf; \ + flat_buf_sz -= size_in_buf; \ + if (!flat_buf_sz) { \ + if (flat_buf_idx < (flat_buf->count - 1)) flat_buf_idx++; \ + else { \ + flat_buf_idx = 0; \ + n_buftypes++; \ + } \ + user_buf_idx = flat_buf->indices[flat_buf_idx] + \ + n_buftypes*buftype_extent; \ + flat_buf_sz = flat_buf->blocklens[flat_buf_idx]; \ + } \ + size -= size_in_buf; \ + buf_incr -= size_in_buf; \ + } \ + ADIOI_BUF_INCR \ +} + + +static void ADIOI_Fill_user_buffer(ADIO_File fd, void *buf, ADIOI_Flatlist_node + *flat_buf, char **recv_buf, ADIO_Offset + *offset_list, int *len_list, + int *recv_size, + MPI_Request *requests, MPI_Status *statuses, + int *recd_from_proc, int nprocs, + int contig_access_count, + ADIO_Offset min_st_offset, + ADIO_Offset fd_size, ADIO_Offset *fd_start, + ADIO_Offset *fd_end, + MPI_Aint buftype_extent) +{ +/* this function is only called if buftype is not contig */ + + int i, p, flat_buf_idx, size, buf_incr; + int flat_buf_sz, size_in_buf, n_buftypes; + ADIO_Offset off, len, rem_len, user_buf_idx; + + int *curr_from_proc, *done_from_proc, *recv_buf_idx; + +/* curr_from_proc[p] = amount of data recd from proc. p that has already + been accounted for so far + done_from_proc[p] = amount of data already recd from proc. p and + filled into user buffer in previous iterations + user_buf_idx = current location in user buffer + recv_buf_idx[p] = current location in recv_buf of proc. p */ + curr_from_proc = (int *) ADIOI_Malloc(nprocs * sizeof(int)); + done_from_proc = (int *) ADIOI_Malloc(nprocs * sizeof(int)); + recv_buf_idx = (int *) ADIOI_Malloc(nprocs * sizeof(int)); + + for (i=0; i < nprocs; i++) { + recv_buf_idx[i] = curr_from_proc[i] = 0; + done_from_proc[i] = recd_from_proc[i]; + } + + user_buf_idx = flat_buf->indices[0]; + flat_buf_idx = 0; + n_buftypes = 0; + flat_buf_sz = flat_buf->blocklens[0]; + + /* flat_buf_idx = current index into flattened buftype + flat_buf_sz = size of current contiguous component in + flattened buf */ + + for (i=0; i 0) { + len = rem_len; + /* NOTE: len value is modified by ADIOI_Calc_aggregator() to be no + * longer than the single region that processor "p" is responsible + * for. + */ + p = ADIOI_BGL_Calc_aggregator(fd, + off, + min_st_offset, + &len, + fd_size, + fd_start, + fd_end); + + if (recv_buf_idx[p] < recv_size[p]) { + if (curr_from_proc[p]+len > done_from_proc[p]) { + if (done_from_proc[p] > curr_from_proc[p]) { + size = (int)ADIOI_MIN(curr_from_proc[p] + len - + done_from_proc[p], recv_size[p]-recv_buf_idx[p]); + buf_incr = done_from_proc[p] - curr_from_proc[p]; + ADIOI_BUF_INCR + buf_incr = (int)(curr_from_proc[p]+len-done_from_proc[p]); + curr_from_proc[p] = done_from_proc[p] + size; + ADIOI_BUF_COPY + } + else { + size = (int)ADIOI_MIN(len,recv_size[p]-recv_buf_idx[p]); + buf_incr = (int)len; + curr_from_proc[p] += size; + ADIOI_BUF_COPY + } + } + else { + curr_from_proc[p] += (int)len; + buf_incr = (int)len; + ADIOI_BUF_INCR + } + } + else { + buf_incr = (int)len; + ADIOI_BUF_INCR + } + off += len; + rem_len -= len; + } + } + for (i=0; i < nprocs; i++) + if (recv_size[i]) recd_from_proc[i] = curr_from_proc[i]; + + ADIOI_Free(curr_from_proc); + ADIOI_Free(done_from_proc); + ADIOI_Free(recv_buf_idx); +} + +static void ADIOI_R_Exchange_data_alltoallv( + ADIO_File fd, void *buf, ADIOI_Flatlist_node + *flat_buf, ADIO_Offset *offset_list, int + *len_list, int *send_size, int *recv_size, + int *count, int *start_pos, int *partial_send, + int *recd_from_proc, int nprocs, + int myrank, int + buftype_is_contig, int contig_access_count, + ADIO_Offset min_st_offset, ADIO_Offset fd_size, + ADIO_Offset *fd_start, ADIO_Offset *fd_end, + ADIOI_Access *others_req, + int iter, MPI_Aint buftype_extent, int *buf_idx) +{ + int i, j, k=0, tmp=0, nprocs_recv, nprocs_send; + char **recv_buf = NULL; + MPI_Request *requests; + MPI_Datatype send_type; + MPI_Status *statuses; + int rtail, stail; + char *sbuf_ptr, *from_ptr; + int len; + int *sdispls, *rdispls; + char *all_recv_buf, *all_send_buf; + + /* exchange send_size info so that each process knows how much to + receive from whom and how much memory to allocate. */ + MPI_Alltoall(send_size, 1, MPI_INT, recv_size, 1, MPI_INT, fd->comm); + + nprocs_recv = 0; + for (i=0; icomm ); + +#if 0 + printf( "\tall_recv_buf = " ); + for (i=131072; i<131073; i++) { printf( "%2d,", all_recv_buf [i] ); } + printf( "\n" ); +#endif + + /* unpack at the receiver side */ + if (nprocs_recv) { + if (!buftype_is_contig) + ADIOI_Fill_user_buffer(fd, buf, flat_buf, recv_buf, + offset_list, len_list, recv_size, + requests, statuses, /* never used inside */ + recd_from_proc, + nprocs, contig_access_count, + min_st_offset, fd_size, fd_start, fd_end, + buftype_extent); + else { + rtail = 0; + for (i=0; i < nprocs; i++) + if (recv_size[i]) { + memcpy( (char *)buf + buf_idx[i], all_recv_buf + rtail, recv_size[i] ); + buf_idx[i] += recv_size[i]; + rtail += recv_size[i]; + } + } + } + + ADIOI_Free( all_send_buf ); + ADIOI_Free( all_recv_buf ); + return; +} diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_read.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_read.c new file mode 100644 index 0000000000..363d8c3436 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_read.c @@ -0,0 +1,496 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/* ---------------------------------------------------------------- */ +/** + * \file ad_bgl_read.c + * \brief ??? + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "ad_bgl.h" +#include "adio_extern.h" + +#include "ad_bgl_tuning.h" + +void ADIOI_BGL_ReadContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int *error_code) +{ + int err=-1, datatype_size, len; + static char myname[] = "ADIOI_BGL_READCONTIG"; + +#if BGL_PROFILE + /* timing */ + double io_time, io_time2; + + if (bglmpio_timing) { + io_time = MPI_Wtime(); + bglmpio_prof_cr[ BGLMPIO_CIO_DATA_SIZE ] += len; + } +#endif + + MPI_Type_size(datatype, &datatype_size); + len = datatype_size * count; + +#if BGL_PROFILE + + if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { + if (bglmpio_timing2) io_time2 = MPI_Wtime(); + if (fd->fp_sys_posn != offset) + lseek(fd->fd_sys, offset, SEEK_SET); + if (bglmpio_timing2) bglmpio_prof_cr[ BGLMPIO_CIO_T_SEEK ] += (MPI_Wtime() - io_time2); + if (fd->atomicity) + ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); + else ADIOI_READ_LOCK(fd, offset, SEEK_SET, len); + if (bglmpio_timing2) io_time2 = MPI_Wtime(); + err = read(fd->fd_sys, buf, len); + if (bglmpio_timing2) bglmpio_prof_cr[ BGLMPIO_CIO_T_POSI_RW ] += (MPI_Wtime() - io_time2); + ADIOI_UNLOCK(fd, offset, SEEK_SET, len); + fd->fp_sys_posn = offset + err; + /* individual file pointer not updated */ + } + else { /* read from curr. location of ind. file pointer */ + offset = fd->fp_ind; + if (bglmpio_timing2) io_time2 = MPI_Wtime(); + if (fd->fp_sys_posn != fd->fp_ind) + lseek(fd->fd_sys, fd->fp_ind, SEEK_SET); + if (bglmpio_timing2) bglmpio_prof_cr[ BGLMPIO_CIO_T_SEEK ] += (MPI_Wtime() - io_time2); + if (fd->atomicity) + ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); + else ADIOI_READ_LOCK(fd, offset, SEEK_SET, len); + if (bglmpio_timing2) io_time2 = MPI_Wtime(); + err = read(fd->fd_sys, buf, len); + if (bglmpio_timing2) bglmpio_prof_cr[ BGLMPIO_CIO_T_POSI_RW ] += (MPI_Wtime() - io_time2); + ADIOI_UNLOCK(fd, offset, SEEK_SET, len); + fd->fp_ind += err; + fd->fp_sys_posn = fd->fp_ind; + } + +#else /* BGL_PROFILE */ + + if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { + if (fd->fp_sys_posn != offset) + lseek(fd->fd_sys, offset, SEEK_SET); + if (fd->atomicity) + ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); + else ADIOI_READ_LOCK(fd, offset, SEEK_SET, len); + err = read(fd->fd_sys, buf, len); + ADIOI_UNLOCK(fd, offset, SEEK_SET, len); + fd->fp_sys_posn = offset + err; + /* individual file pointer not updated */ + } + else { /* read from curr. location of ind. file pointer */ + offset = fd->fp_ind; + if (fd->fp_sys_posn != fd->fp_ind) + lseek(fd->fd_sys, fd->fp_ind, SEEK_SET); + if (fd->atomicity) + ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); + else ADIOI_READ_LOCK(fd, offset, SEEK_SET, len); + err = read(fd->fd_sys, buf, len); + ADIOI_UNLOCK(fd, offset, SEEK_SET, len); + fd->fp_ind += err; + fd->fp_sys_posn = fd->fp_ind; + } + +#endif /* BGL_PROFILE */ + +#if BGL_PROFILE + if (bglmpio_timing) bglmpio_prof_cr[ BGLMPIO_CIO_T_MPIO_RW ] += (MPI_Wtime() - io_time); +#endif + + /* --BEGIN ERROR HANDLING-- */ + if (err == -1) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, + myname, __LINE__, MPI_ERR_IO, + "**io", "**io %s", strerror(errno)); + return; + } + /* --END ERROR HANDLING-- */ + +#ifdef HAVE_STATUS_SET_BYTES + MPIR_Status_set_bytes(status, datatype, err); +#endif + + *error_code = MPI_SUCCESS; +} + + + +#define ADIOI_BUFFERED_READ \ +{ \ + if (req_off >= readbuf_off + readbuf_len) { \ + readbuf_off = req_off; \ + readbuf_len = (int) (ADIOI_MIN(max_bufsize, end_offset-readbuf_off+1));\ + lseek(fd->fd_sys, readbuf_off, SEEK_SET);\ + if (!(fd->atomicity)) ADIOI_READ_LOCK(fd, readbuf_off, SEEK_SET, readbuf_len);\ + err = read(fd->fd_sys, readbuf, readbuf_len);\ + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, readbuf_off, SEEK_SET, readbuf_len);\ + if (err == -1) err_flag = 1; \ + } \ + while (req_len > readbuf_off + readbuf_len - req_off) { \ + partial_read = (int) (readbuf_off + readbuf_len - req_off); \ + tmp_buf = (char *) ADIOI_Malloc(partial_read); \ + memcpy(tmp_buf, readbuf+readbuf_len-partial_read, partial_read); \ + ADIOI_Free(readbuf); \ + readbuf = (char *) ADIOI_Malloc(partial_read + max_bufsize); \ + memcpy(readbuf, tmp_buf, partial_read); \ + ADIOI_Free(tmp_buf); \ + readbuf_off += readbuf_len-partial_read; \ + readbuf_len = (int) (partial_read + ADIOI_MIN(max_bufsize, \ + end_offset-readbuf_off+1)); \ + lseek(fd->fd_sys, readbuf_off+partial_read, SEEK_SET);\ + if (!(fd->atomicity)) ADIOI_READ_LOCK(fd, readbuf_off+partial_read, SEEK_SET, readbuf_len-partial_read);\ + err = read(fd->fd_sys, readbuf+partial_read, readbuf_len-partial_read);\ + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, readbuf_off+partial_read, SEEK_SET, readbuf_len-partial_read);\ + if (err == -1) err_flag = 1; \ + } \ + memcpy((char *)buf + userbuf_off, readbuf+req_off-readbuf_off, req_len); \ +} + + +void ADIOI_BGL_ReadStrided(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code) +{ +/* offset is in units of etype relative to the filetype. */ + + ADIOI_Flatlist_node *flat_buf, *flat_file; + int i, j, k, err=-1, brd_size, frd_size=0, st_index=0; + int bufsize, num, size, sum, n_etypes_in_filetype, size_in_filetype; + int n_filetypes, etype_in_filetype; + ADIO_Offset abs_off_in_filetype=0; + int filetype_size, etype_size, buftype_size, req_len, partial_read; + MPI_Aint filetype_extent, buftype_extent; + int buf_count, buftype_is_contig, filetype_is_contig; + ADIO_Offset userbuf_off; + ADIO_Offset off, req_off, disp, end_offset=0, readbuf_off, start_off; + char *readbuf, *tmp_buf, *value; + int flag, st_frd_size, st_n_filetypes, readbuf_len; + int new_brd_size, new_frd_size, err_flag=0, info_flag, max_bufsize; + + static char myname[] = "ADIOI_BGL_READSTRIDED"; + + if (fd->hints->ds_read == ADIOI_HINT_DISABLE) { + /* if user has disabled data sieving on reads, use naive + * approach instead. + */ + /*FPRINTF(stderr, "ADIOI_GEN_ReadStrided_naive(%d):\n", __LINE__);*/ + ADIOI_GEN_ReadStrided_naive(fd, + buf, + count, + datatype, + file_ptr_type, + offset, + status, + error_code); + return; + } + /*FPRINTF(stderr, "%s(%d):\n",myname, __LINE__);*/ + + ADIOI_Datatype_iscontig(datatype, &buftype_is_contig); + ADIOI_Datatype_iscontig(fd->filetype, &filetype_is_contig); + + MPI_Type_size(fd->filetype, &filetype_size); + if ( ! filetype_size ) { + *error_code = MPI_SUCCESS; + return; + } + + MPI_Type_extent(fd->filetype, &filetype_extent); + MPI_Type_size(datatype, &buftype_size); + MPI_Type_extent(datatype, &buftype_extent); + etype_size = fd->etype_size; + + bufsize = buftype_size * count; + +/* get max_bufsize from the info object. */ + + value = (char *) ADIOI_Malloc((MPI_MAX_INFO_VAL+1)*sizeof(char)); + MPI_Info_get(fd->info, "ind_rd_buffer_size", MPI_MAX_INFO_VAL, value, + &info_flag); + max_bufsize = atoi(value); + ADIOI_Free(value); + + if (!buftype_is_contig && filetype_is_contig) { + +/* noncontiguous in memory, contiguous in file. */ + + ADIOI_Flatten_datatype(datatype); + flat_buf = ADIOI_Flatlist; + while (flat_buf->type != datatype) flat_buf = flat_buf->next; + + off = (file_ptr_type == ADIO_INDIVIDUAL) ? fd->fp_ind : + fd->disp + etype_size * offset; + + start_off = off; + end_offset = off + bufsize - 1; + readbuf_off = off; + readbuf = (char *) ADIOI_Malloc(max_bufsize); + readbuf_len = (int) (ADIOI_MIN(max_bufsize, end_offset-readbuf_off+1)); + +/* if atomicity is true, lock (exclusive) the region to be accessed */ + if (fd->atomicity) + ADIOI_WRITE_LOCK(fd, start_off, SEEK_SET, end_offset-start_off+1); + + lseek(fd->fd_sys, readbuf_off, SEEK_SET); + if (!(fd->atomicity)) ADIOI_READ_LOCK(fd, readbuf_off, SEEK_SET, readbuf_len); + err = read(fd->fd_sys, readbuf, readbuf_len); + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, readbuf_off, SEEK_SET, readbuf_len); + if (err == -1) err_flag = 1; + + for (j=0; jcount; i++) { + userbuf_off = j*buftype_extent + flat_buf->indices[i]; + req_off = off; + req_len = flat_buf->blocklens[i]; + ADIOI_BUFFERED_READ + off += flat_buf->blocklens[i]; + } + + if (fd->atomicity) + ADIOI_UNLOCK(fd, start_off, SEEK_SET, end_offset-start_off+1); + + if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind = off; + + ADIOI_Free(readbuf); /* malloced in the buffered_read macro */ + + if (err_flag) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + } + else *error_code = MPI_SUCCESS; + } + + else { /* noncontiguous in file */ + +/* filetype already flattened in ADIO_Open */ + flat_file = ADIOI_Flatlist; + while (flat_file->type != fd->filetype) flat_file = flat_file->next; + disp = fd->disp; + + if (file_ptr_type == ADIO_INDIVIDUAL) { + offset = fd->fp_ind; /* in bytes */ + n_filetypes = -1; + flag = 0; + while (!flag) { + n_filetypes++; + for (i=0; icount; i++) { + if (disp + flat_file->indices[i] + + (ADIO_Offset) n_filetypes*filetype_extent + flat_file->blocklens[i] + >= offset) { + st_index = i; + frd_size = (int) (disp + flat_file->indices[i] + + (ADIO_Offset) n_filetypes*filetype_extent + + flat_file->blocklens[i] - offset); + flag = 1; + break; + } + } + } + } + else { + n_etypes_in_filetype = filetype_size/etype_size; + n_filetypes = (int) (offset / n_etypes_in_filetype); + etype_in_filetype = (int) (offset % n_etypes_in_filetype); + size_in_filetype = etype_in_filetype * etype_size; + + sum = 0; + for (i=0; icount; i++) { + sum += flat_file->blocklens[i]; + if (sum > size_in_filetype) { + st_index = i; + frd_size = sum - size_in_filetype; + abs_off_in_filetype = flat_file->indices[i] + + size_in_filetype - (sum - flat_file->blocklens[i]); + break; + } + } + + /* abs. offset in bytes in the file */ + offset = disp + (ADIO_Offset) n_filetypes*filetype_extent + abs_off_in_filetype; + } + + start_off = offset; + + /* Calculate end_offset, the last byte-offset that will be accessed. + e.g., if start_offset=0 and 100 bytes to be read, end_offset=99*/ + + st_frd_size = frd_size; + st_n_filetypes = n_filetypes; + i = 0; + j = st_index; + off = offset; + frd_size = ADIOI_MIN(st_frd_size, bufsize); + while (i < bufsize) { + i += frd_size; + end_offset = off + frd_size - 1; + + if (j < (flat_file->count - 1)) j++; + else { + j = 0; + n_filetypes++; + } + + off = disp + flat_file->indices[j] + (ADIO_Offset) n_filetypes*filetype_extent; + frd_size = ADIOI_MIN(flat_file->blocklens[j], bufsize-i); + } + +/* if atomicity is true, lock (exclusive) the region to be accessed */ + if (fd->atomicity) + ADIOI_WRITE_LOCK(fd, start_off, SEEK_SET, end_offset-start_off+1); + + /* initial read into readbuf */ + readbuf_off = offset; + readbuf = (char *) ADIOI_Malloc(max_bufsize); + readbuf_len = (int) (ADIOI_MIN(max_bufsize, end_offset-readbuf_off+1)); + + lseek(fd->fd_sys, offset, SEEK_SET); + if (!(fd->atomicity)) ADIOI_READ_LOCK(fd, offset, SEEK_SET, readbuf_len); + err = read(fd->fd_sys, readbuf, readbuf_len); + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, offset, SEEK_SET, readbuf_len); + + if (err == -1) err_flag = 1; + + if (buftype_is_contig && !filetype_is_contig) { + +/* contiguous in memory, noncontiguous in file. should be the most + common case. */ + + i = 0; + j = st_index; + off = offset; + n_filetypes = st_n_filetypes; + frd_size = ADIOI_MIN(st_frd_size, bufsize); + while (i < bufsize) { + if (frd_size) { + /* TYPE_UB and TYPE_LB can result in + frd_size = 0. save system call in such cases */ + /* lseek(fd->fd_sys, off, SEEK_SET); + err = read(fd->fd_sys, ((char *) buf) + i, frd_size);*/ + + req_off = off; + req_len = frd_size; + userbuf_off = i; + ADIOI_BUFFERED_READ + } + i += frd_size; + + if (off + frd_size < disp + flat_file->indices[j] + + flat_file->blocklens[j] + (ADIO_Offset) n_filetypes*filetype_extent) + off += frd_size; + /* did not reach end of contiguous block in filetype. + no more I/O needed. off is incremented by frd_size. */ + else { + if (j < (flat_file->count - 1)) j++; + else { + j = 0; + n_filetypes++; + } + off = disp + flat_file->indices[j] + + (ADIO_Offset) n_filetypes*filetype_extent; + frd_size = ADIOI_MIN(flat_file->blocklens[j], bufsize-i); + } + } + } + else { +/* noncontiguous in memory as well as in file */ + + ADIOI_Flatten_datatype(datatype); + flat_buf = ADIOI_Flatlist; + while (flat_buf->type != datatype) flat_buf = flat_buf->next; + + k = num = buf_count = 0; + i = (int) (flat_buf->indices[0]); + j = st_index; + off = offset; + n_filetypes = st_n_filetypes; + frd_size = st_frd_size; + brd_size = flat_buf->blocklens[0]; + + while (num < bufsize) { + size = ADIOI_MIN(frd_size, brd_size); + if (size) { + /* lseek(fd->fd_sys, off, SEEK_SET); + err = read(fd->fd_sys, ((char *) buf) + i, size); */ + + req_off = off; + req_len = size; + userbuf_off = i; + ADIOI_BUFFERED_READ + } + + new_frd_size = frd_size; + new_brd_size = brd_size; + + if (size == frd_size) { +/* reached end of contiguous block in file */ + if (j < (flat_file->count - 1)) j++; + else { + j = 0; + n_filetypes++; + } + + off = disp + flat_file->indices[j] + + (ADIO_Offset) n_filetypes*filetype_extent; + + new_frd_size = flat_file->blocklens[j]; + if (size != brd_size) { + i += size; + new_brd_size -= size; + } + } + + if (size == brd_size) { +/* reached end of contiguous block in memory */ + + k = (k + 1)%flat_buf->count; + buf_count++; + i = (int) (buftype_extent*(buf_count/flat_buf->count) + + flat_buf->indices[k]); + new_brd_size = flat_buf->blocklens[k]; + if (size != frd_size) { + off += size; + new_frd_size -= size; + } + } + num += size; + frd_size = new_frd_size; + brd_size = new_brd_size; + } + } + + if (fd->atomicity) + ADIOI_UNLOCK(fd, start_off, SEEK_SET, end_offset-start_off+1); + + if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind = off; + + ADIOI_Free(readbuf); /* malloced in the buffered_read macro */ + + if (err_flag) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + } + else *error_code = MPI_SUCCESS; + } + + fd->fp_sys_posn = -1; /* set it to null. */ + +#ifdef HAVE_STATUS_SET_BYTES + MPIR_Status_set_bytes(status, datatype, bufsize); +/* This is a temporary way of filling in status. The right way is to + keep track of how much data was actually read and placed in buf + by ADIOI_BUFFERED_READ. */ +#endif + + if (!buftype_is_contig) ADIOI_Delete_flattened(datatype); +} diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_setsh.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_setsh.c new file mode 100644 index 0000000000..b7a8bce998 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_setsh.c @@ -0,0 +1,68 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/* ---------------------------------------------------------------- */ +/** + * \file ad_bgl_setsh.c + * \brief ??? + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "ad_bgl.h" + +/* set the shared file pointer to "offset" etypes relative to the current + view */ + +/* +This looks very similar to ADIOI_GEN_Set_shared_fp, except this +function avoids locking the file twice. The generic version does + +Write lock +ADIO_WriteContig +Unlock + +For BGL, ADIOI_BGL_WriteContig does a lock before writing to disable +caching. To avoid the lock being called twice, this version for BGL does + +Write lock +Lseek +Write +Unlock + +*/ + +void ADIOI_BGL_Set_shared_fp(ADIO_File fd, ADIO_Offset offset, int *error_code) +{ + int err; + MPI_Comm dupcommself; + static char myname[] = "ADIOI_BGL_SET_SHARED_FP"; + + if (fd->shared_fp_fd == ADIO_FILE_NULL) { + MPI_Comm_dup(MPI_COMM_SELF, &dupcommself); + fd->shared_fp_fd = ADIO_Open(MPI_COMM_SELF, dupcommself, + fd->shared_fp_fname, + fd->file_system, fd->fns, + ADIO_CREATE | ADIO_RDWR | ADIO_DELETE_ON_CLOSE, + 0, MPI_BYTE, MPI_BYTE, MPI_INFO_NULL, + ADIO_PERM_NULL, error_code); + } + + if (*error_code != MPI_SUCCESS) return; + + ADIOI_WRITE_LOCK(fd->shared_fp_fd, 0, SEEK_SET, sizeof(ADIO_Offset)); + lseek(fd->shared_fp_fd->fd_sys, 0, SEEK_SET); + err = write(fd->shared_fp_fd->fd_sys, &offset, sizeof(ADIO_Offset)); + ADIOI_UNLOCK(fd->shared_fp_fd, 0, SEEK_SET, sizeof(ADIO_Offset)); + + if (err == -1) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, + myname, __LINE__, MPI_ERR_IO, + "**io", + "**io %s", strerror(errno)); + } + else *error_code = MPI_SUCCESS; +} diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_tuning.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_tuning.c new file mode 100644 index 0000000000..4959fa3eeb --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_tuning.c @@ -0,0 +1,104 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/* ---------------------------------------------------------------- */ +/** + * \file ad_bgl_tuning.c + * \brief ??? + */ + +/*--------------------------------------------------------------------- + * ad_bgl_tuning.c + * + * defines global variables and functions for performance tuning and + * functional debugging. + *---------------------------------------------------------------------*/ + +#include "ad_bgl_tuning.h" +#include "mpi.h" + +int bglmpio_timing; +int bglmpio_timing2; +int bglmpio_comm; +int bglmpio_tunegather; +int bglmpio_tuneblocking; + +double bglmpio_prof_cw [BGLMPIO_CIO_LAST]; +double bglmpio_prof_cr [BGLMPIO_CIO_LAST]; + +/* set internal variables for tuning environment variables */ +void ad_bgl_get_env_vars() { + char *x; + + bglmpio_comm = 0; + x = getenv( "BGLMPIO_COMM" ); + if (x) bglmpio_comm = atoi(x); + bglmpio_timing = 0; + x = getenv( "BGLMPIO_TIMING" ); + if (x) bglmpio_timing = atoi(x); + bglmpio_timing2 = 0; + x = getenv( "BGLMPIO_TIMING2" ); + if (x) bglmpio_timing2 = atoi(x); + bglmpio_tunegather = 1; + x = getenv( "BGLMPIO_TUNEGATHER" ); + if (x) bglmpio_tunegather = atoi(x); + bglmpio_tuneblocking = 1; + x = getenv( "BGLMPIO_TUNEBLOCKING" ); + if (x) bglmpio_tuneblocking = atoi(x); +} + +/* report timing breakdown for MPI I/O collective call */ +void ad_bgl_wr_timing_report( int rw, ADIO_File fd, int myrank, int nprocs ) +{ + int i; + + if (bglmpio_timing) { + + double *bglmpio_prof_org = bglmpio_prof_cr; + if (rw) bglmpio_prof_org = bglmpio_prof_cw; + + double bglmpio_prof_avg[ BGLMPIO_CIO_LAST ]; + double bglmpio_prof_max[ BGLMPIO_CIO_LAST ]; + + MPI_Reduce( bglmpio_prof_org, bglmpio_prof_avg, BGLMPIO_CIO_LAST, MPI_DOUBLE, MPI_SUM, 0, fd->comm ); + MPI_Reduce( bglmpio_prof_org, bglmpio_prof_max, BGLMPIO_CIO_LAST, MPI_DOUBLE, MPI_MAX, 0, fd->comm ); + + if (myrank == 0) { + + for (i=0; i= LEVEL) { \ + int i; \ + for ( i = 0; i < BGLMPIO_T_LAST; i ++ ) \ + bglmpio_prof_c##RW [ i ] = 0; \ + } + +#define BGLMPIO_T_CIO_REPORT( LEVEL, RW, FD, MYRANK, NPROCS ) \ + if (bglmpio_timing_cw_level >= LEVEL) { \ + ad_bgl_timing_crw_report ( RW, FD, MYRANK, NPROCS ); \ + } + +#define BGLMPIO_T_CIO_SET_GET( LEVEL, RW, DOBAR, ISSET, ISGET, VAR1, VAR2 ) \ + if (bglmpio_timing_cw_level >= LEVEL) { \ + if ( DOBAR ) MPI_Barrier( fd->comm ); \ + double temp = MPI_Wtime(); \ + if ( ISSET ) bglmpio_prof_c##RW [ VAR1 ] = temp; \ + if ( ISGET ) bglmpio_prof_c##RW [ VAR2 ] = temp - bglmpio_prof_c##RW [ VAR2 ] ; \ + } + +#endif /* AD_BGL_TUNING_H_ */ diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_wrcoll.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_wrcoll.c new file mode 100644 index 0000000000..c8fabc1818 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_wrcoll.c @@ -0,0 +1,1451 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/* ---------------------------------------------------------------- */ +/** + * \file ad_bgl_wrcoll.c + * \brief ??? + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "adio.h" +#include "adio_extern.h" +#include "ad_bgl.h" +#include "ad_bgl_pset.h" +#include "ad_bgl_aggrs.h" + +#ifdef PROFILE +#include "mpe.h" +#endif + +/* prototypes of functions used for collective writes only. */ +static void ADIOI_Exch_and_write(ADIO_File fd, void *buf, MPI_Datatype + datatype, int nprocs, int myrank, ADIOI_Access + *others_req, ADIO_Offset *offset_list, + int *len_list, int contig_access_count, ADIO_Offset + min_st_offset, ADIO_Offset fd_size, + ADIO_Offset *fd_start, ADIO_Offset *fd_end, + int *buf_idx, int *error_code); +static void ADIOI_W_Exchange_data(ADIO_File fd, void *buf, char *write_buf, + ADIOI_Flatlist_node *flat_buf, ADIO_Offset + *offset_list, int *len_list, int *send_size, + int *recv_size, ADIO_Offset off, int size, + int *count, int *start_pos, int *partial_recv, + int *sent_to_proc, int nprocs, + int myrank, int + buftype_is_contig, int contig_access_count, + ADIO_Offset min_st_offset, ADIO_Offset fd_size, + ADIO_Offset *fd_start, ADIO_Offset *fd_end, + ADIOI_Access *others_req, + int *send_buf_idx, int *curr_to_proc, + int *done_to_proc, int *hole, int iter, + MPI_Aint buftype_extent, int *buf_idx, int *error_code); +static void ADIOI_W_Exchange_data_alltoallv( + ADIO_File fd, void *buf, + char *write_buf, /* 1 */ + ADIOI_Flatlist_node *flat_buf, + ADIO_Offset *offset_list, + int *len_list, int *send_size, int *recv_size, + ADIO_Offset off, int size, /* 2 */ + int *count, int *start_pos, int *partial_recv, + int *sent_to_proc, int nprocs, int myrank, + int buftype_is_contig, int contig_access_count, + ADIO_Offset min_st_offset, + ADIO_Offset fd_size, + ADIO_Offset *fd_start, + ADIO_Offset *fd_end, + ADIOI_Access *others_req, + int *send_buf_idx, int *curr_to_proc, /* 3 */ + int *done_to_proc, int *hole, /* 4 */ + int iter, MPI_Aint buftype_extent, int *buf_idx, + int *error_code); +static void ADIOI_Fill_send_buffer(ADIO_File fd, void *buf, ADIOI_Flatlist_node + *flat_buf, char **send_buf, ADIO_Offset + *offset_list, int *len_list, int *send_size, + MPI_Request *requests, int *sent_to_proc, + int nprocs, int myrank, + int contig_access_count, ADIO_Offset + min_st_offset, ADIO_Offset fd_size, + ADIO_Offset *fd_start, ADIO_Offset *fd_end, + int *send_buf_idx, int *curr_to_proc, + int *done_to_proc, int iter, + MPI_Aint buftype_extent); +static void ADIOI_Fill_send_buffer_nosend(ADIO_File fd, void *buf, ADIOI_Flatlist_node + *flat_buf, char **send_buf, ADIO_Offset + *offset_list, int *len_list, int *send_size, + MPI_Request *requests, int *sent_to_proc, + int nprocs, int myrank, + int contig_access_count, ADIO_Offset + min_st_offset, ADIO_Offset fd_size, + ADIO_Offset *fd_start, ADIO_Offset *fd_end, + int *send_buf_idx, int *curr_to_proc, + int *done_to_proc, int iter, + MPI_Aint buftype_extent); +static void ADIOI_Heap_merge(ADIOI_Access *others_req, int *count, + ADIO_Offset *srt_off, int *srt_len, int *start_pos, + int nprocs, int nprocs_recv, int total_elements); + + +void ADIOI_BGL_WriteStridedColl(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code) +{ +/* Uses a generalized version of the extended two-phase method described + in "An Extended Two-Phase Method for Accessing Sections of + Out-of-Core Arrays", Rajeev Thakur and Alok Choudhary, + Scientific Programming, (5)4:301--317, Winter 1996. + http://www.mcs.anl.gov/home/thakur/ext2ph.ps */ + + ADIOI_Access *my_req; + /* array of nprocs access structures, one for each other process in + whose file domain this process's request lies */ + + ADIOI_Access *others_req; + /* array of nprocs access structures, one for each other process + whose request lies in this process's file domain. */ + + int i, filetype_is_contig, nprocs, nprocs_for_coll, myrank; + int contig_access_count=0, interleave_count = 0, buftype_is_contig; + int *count_my_req_per_proc, count_my_req_procs, count_others_req_procs; + ADIO_Offset orig_fp, start_offset, end_offset, fd_size, min_st_offset, off; + ADIO_Offset *offset_list = NULL, *st_offsets = NULL, *fd_start = NULL, + *fd_end = NULL, *end_offsets = NULL; + ADIO_Offset *bgl_offsets0 = NULL, *bgl_offsets = NULL; + int ii; + + int *buf_idx = NULL, *len_list = NULL; + + double io_time = 0, all_time, max_all_time; + double tstep1, max_tstep1; + double tstep1_1, max_tstep1_1; + double tstep1_2, max_tstep1_2; + double tstep1_3, max_tstep1_3; + double tstep2, max_tstep2; + double tstep3, max_tstep3; + double tstep4, max_tstep4; + double sum_sz; + +#if BGL_PROFILE + BGLMPIO_T_CIO_RESET( 0, w ) +#endif + +#ifdef PROFILE + MPE_Log_event(13, 0, "start computation"); +#endif + + MPI_Comm_size(fd->comm, &nprocs); + MPI_Comm_rank(fd->comm, &myrank); + +/* the number of processes that actually perform I/O, nprocs_for_coll, + * is stored in the hints off the ADIO_File structure + */ + nprocs_for_coll = fd->hints->cb_nodes; + orig_fp = fd->fp_ind; + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, w, 0, 1, 0, BGLMPIO_CIO_LCOMP, BGLMPIO_CIO_LAST ) +#endif + + + /* only check for interleaving if cb_write isn't disabled */ + if (fd->hints->cb_write != ADIOI_HINT_DISABLE) { + /* For this process's request, calculate the list of offsets and + lengths in the file and determine the start and end offsets. */ + + /* Note: end_offset points to the last byte-offset that will be accessed. + e.g., if start_offset=0 and 100 bytes to be read, end_offset=99*/ + + ADIOI_Calc_my_off_len(fd, count, datatype, file_ptr_type, offset, + &offset_list, &len_list, &start_offset, + &end_offset, &contig_access_count); + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, w, 1, 1, 1, BGLMPIO_CIO_GATHER, BGLMPIO_CIO_LCOMP ) +#endif + + /* each process communicates its start and end offsets to other + processes. The result is an array each of start and end offsets stored + in order of process rank. */ + + st_offsets = (ADIO_Offset *) ADIOI_Malloc(nprocs*sizeof(ADIO_Offset)); + end_offsets = (ADIO_Offset *) ADIOI_Malloc(nprocs*sizeof(ADIO_Offset)); + + if (bglmpio_tunegather) { + bgl_offsets0 = (ADIO_Offset *) ADIOI_Malloc(2*nprocs*sizeof(ADIO_Offset)); + bgl_offsets = (ADIO_Offset *) ADIOI_Malloc(2*nprocs*sizeof(ADIO_Offset)); + for (ii=0; iicomm ); + + for (ii=0; iicomm); + MPI_Allgather(&end_offset, 1, ADIO_OFFSET, end_offsets, 1, + ADIO_OFFSET, fd->comm); + } + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, w, 0, 1, 1, BGLMPIO_CIO_PATANA, BGLMPIO_CIO_GATHER ) +#endif + + /* are the accesses of different processes interleaved? */ + for (i=1; ihints->cb_write == ADIOI_HINT_DISABLE || + (!interleave_count && (fd->hints->cb_write == ADIOI_HINT_AUTO))) + { + /* use independent accesses */ + if (fd->hints->cb_write != ADIOI_HINT_DISABLE) { + ADIOI_Free(offset_list); + ADIOI_Free(len_list); + ADIOI_Free(st_offsets); + ADIOI_Free(end_offsets); + } + + fd->fp_ind = orig_fp; + ADIOI_Datatype_iscontig(fd->filetype, &filetype_is_contig); + + if (buftype_is_contig && filetype_is_contig) { + + if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { + off = fd->disp + (fd->etype_size) * offset; + ADIO_WriteContig(fd, buf, count, datatype, + ADIO_EXPLICIT_OFFSET, + off, status, error_code); + } + else ADIO_WriteContig(fd, buf, count, datatype, ADIO_INDIVIDUAL, + 0, status, error_code); + } + else ADIO_WriteStrided(fd, buf, count, datatype, file_ptr_type, + offset, status, error_code); + + return; + } + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, w, 1, 1, 1, BGLMPIO_CIO_FD_PART, BGLMPIO_CIO_PATANA ) +#endif + +/* Divide the I/O workload among "nprocs_for_coll" processes. This is + done by (logically) dividing the file into file domains (FDs); each + process may directly access only its own file domain. */ + + if (bglmpio_tuneblocking) + ADIOI_BGL_GPFS_Calc_file_domains(st_offsets, end_offsets, nprocs, + nprocs_for_coll, &min_st_offset, + &fd_start, &fd_end, &fd_size, fd->fs_ptr); + else + ADIOI_Calc_file_domains(st_offsets, end_offsets, nprocs, + nprocs_for_coll, &min_st_offset, + &fd_start, &fd_end, &fd_size); + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, w, 0, 1, 1, BGLMPIO_CIO_MYREQ, BGLMPIO_CIO_FD_PART ) +#endif + +/* calculate what portions of the access requests of this process are + located in what file domains */ + + if (bglmpio_tuneblocking) + ADIOI_BGL_Calc_my_req(fd, offset_list, len_list, contig_access_count, + min_st_offset, fd_start, fd_end, fd_size, + nprocs, &count_my_req_procs, + &count_my_req_per_proc, &my_req, + &buf_idx); + else + ADIOI_Calc_my_req(fd, offset_list, len_list, contig_access_count, + min_st_offset, fd_start, fd_end, fd_size, + nprocs, &count_my_req_procs, + &count_my_req_per_proc, &my_req, + &buf_idx); + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, w, 1, 1, 1, BGLMPIO_CIO_OTHREQ, BGLMPIO_CIO_MYREQ ) +#endif + +/* based on everyone's my_req, calculate what requests of other + processes lie in this process's file domain. + count_others_req_procs = number of processes whose requests lie in + this process's file domain (including this process itself) + count_others_req_per_proc[i] indicates how many separate contiguous + requests of proc. i lie in this process's file domain. */ + + if (bglmpio_tuneblocking) + ADIOI_BGL_Calc_others_req(fd, count_my_req_procs, + count_my_req_per_proc, my_req, + nprocs, myrank, + &count_others_req_procs, &others_req); + else + ADIOI_Calc_others_req(fd, count_my_req_procs, + count_my_req_per_proc, my_req, + nprocs, myrank, + &count_others_req_procs, &others_req); + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, w, 1, 1, 1, BGLMPIO_CIO_DEXCH, BGLMPIO_CIO_OTHREQ ) +#endif + + ADIOI_Free(count_my_req_per_proc); + for (i=0; i < nprocs; i++) { + if (my_req[i].count) { + ADIOI_Free(my_req[i].offsets); + ADIOI_Free(my_req[i].lens); + } + } + ADIOI_Free(my_req); + +/* exchange data and write in sizes of no more than coll_bufsize. */ + ADIOI_Exch_and_write(fd, buf, datatype, nprocs, myrank, + others_req, offset_list, + len_list, contig_access_count, min_st_offset, + fd_size, fd_start, fd_end, buf_idx, error_code); + +#if BGL_PROFILE + BGLMPIO_T_CIO_SET_GET( 0, w, 1, 0, 1, BGLMPIO_CIO_LAST, BGLMPIO_CIO_T_DEXCH ) + BGLMPIO_T_CIO_SET_GET( 0, w, 0, 0, 1, BGLMPIO_CIO_LAST, BGLMPIO_CIO_T_MPIO_CRW ) + + BGLMPIO_T_CIO_REPORT( 0, w, fd, myrank ) +#endif + + +/* free all memory allocated for collective I/O */ + + for (i=0; ifp_sys_posn = -1; /* set it to null. */ +} + + + +/* If successful, error_code is set to MPI_SUCCESS. Otherwise an error + * code is created and returned in error_code. + */ +static void ADIOI_Exch_and_write(ADIO_File fd, void *buf, MPI_Datatype + datatype, int nprocs, int myrank, + ADIOI_Access + *others_req, ADIO_Offset *offset_list, + int *len_list, int contig_access_count, + ADIO_Offset + min_st_offset, ADIO_Offset fd_size, + ADIO_Offset *fd_start, ADIO_Offset *fd_end, + int *buf_idx, int *error_code) +{ +/* Send data to appropriate processes and write in sizes of no more + than coll_bufsize. + The idea is to reduce the amount of extra memory required for + collective I/O. If all data were written all at once, which is much + easier, it would require temp space more than the size of user_buf, + which is often unacceptable. For example, to write a distributed + array to a file, where each local array is 8Mbytes, requiring + at least another 8Mbytes of temp space is unacceptable. */ + + int hole, i, j, m, size=0, ntimes, max_ntimes, buftype_is_contig; + ADIO_Offset st_loc=-1, end_loc=-1, off, done, req_off; + char *write_buf=NULL; + int *curr_offlen_ptr, *count, *send_size, req_len, *recv_size; + int *partial_recv, *sent_to_proc, *start_pos, flag; + int *send_buf_idx, *curr_to_proc, *done_to_proc; + MPI_Status status; + ADIOI_Flatlist_node *flat_buf=NULL; + MPI_Aint buftype_extent; + int info_flag, coll_bufsize; + char *value; + static char myname[] = "ADIOI_EXCH_AND_WRITE"; + + *error_code = MPI_SUCCESS; /* changed below if error */ + /* only I/O errors are currently reported */ + +/* calculate the number of writes of size coll_bufsize + to be done by each process and the max among all processes. + That gives the no. of communication phases as well. */ + + value = (char *) ADIOI_Malloc((MPI_MAX_INFO_VAL+1)*sizeof(char)); + MPI_Info_get(fd->info, "cb_buffer_size", MPI_MAX_INFO_VAL, value, + &info_flag); + coll_bufsize = atoi(value); + ADIOI_Free(value); + + + for (i=0; i < nprocs; i++) { + if (others_req[i].count) { + st_loc = others_req[i].offsets[0]; + end_loc = others_req[i].offsets[0]; + break; + } + } + + for (i=0; i < nprocs; i++) + for (j=0; j < others_req[i].count; j++) { + st_loc = ADIOI_MIN(st_loc, others_req[i].offsets[j]); + end_loc = ADIOI_MAX(end_loc, (others_req[i].offsets[j] + + others_req[i].lens[j] - 1)); + } + +/* ntimes=ceiling_div(end_loc - st_loc + 1, coll_bufsize)*/ + + ntimes = (int) ((end_loc - st_loc + coll_bufsize)/coll_bufsize); + + if ((st_loc==-1) && (end_loc==-1)) { + ntimes = 0; /* this process does no writing. */ + } + + MPI_Allreduce(&ntimes, &max_ntimes, 1, MPI_INT, MPI_MAX, + fd->comm); + + if (ntimes) write_buf = (char *) ADIOI_Malloc(coll_bufsize); + + curr_offlen_ptr = (int *) ADIOI_Calloc(nprocs, sizeof(int)); + /* its use is explained below. calloc initializes to 0. */ + + count = (int *) ADIOI_Malloc(nprocs*sizeof(int)); + /* to store count of how many off-len pairs per proc are satisfied + in an iteration. */ + + partial_recv = (int *) ADIOI_Calloc(nprocs, sizeof(int)); + /* if only a portion of the last off-len pair is recd. from a process + in a particular iteration, the length recd. is stored here. + calloc initializes to 0. */ + + send_size = (int *) ADIOI_Malloc(nprocs*sizeof(int)); + /* total size of data to be sent to each proc. in an iteration. + Of size nprocs so that I can use MPI_Alltoall later. */ + + recv_size = (int *) ADIOI_Malloc(nprocs*sizeof(int)); + /* total size of data to be recd. from each proc. in an iteration.*/ + + sent_to_proc = (int *) ADIOI_Calloc(nprocs, sizeof(int)); + /* amount of data sent to each proc so far. Used in + ADIOI_Fill_send_buffer. initialized to 0 here. */ + + send_buf_idx = (int *) ADIOI_Malloc(nprocs*sizeof(int)); + curr_to_proc = (int *) ADIOI_Malloc(nprocs*sizeof(int)); + done_to_proc = (int *) ADIOI_Malloc(nprocs*sizeof(int)); + /* Above three are used in ADIOI_Fill_send_buffer*/ + + start_pos = (int *) ADIOI_Malloc(nprocs*sizeof(int)); + /* used to store the starting value of curr_offlen_ptr[i] in + this iteration */ + + ADIOI_Datatype_iscontig(datatype, &buftype_is_contig); + if (!buftype_is_contig) { + ADIOI_Flatten_datatype(datatype); + flat_buf = ADIOI_Flatlist; + while (flat_buf->type != datatype) flat_buf = flat_buf->next; + } + MPI_Type_extent(datatype, &buftype_extent); + + +/* I need to check if there are any outstanding nonblocking writes to + the file, which could potentially interfere with the writes taking + place in this collective write call. Since this is not likely to be + common, let me do the simplest thing possible here: Each process + completes all pending nonblocking operations before completing. */ + + /*ADIOI_Complete_async(error_code); + if (*error_code != MPI_SUCCESS) return; + MPI_Barrier(fd->comm); + */ + + done = 0; + off = st_loc; + +#ifdef PROFILE + MPE_Log_event(14, 0, "end computation"); +#endif + + for (m=0; m < ntimes; m++) { + /* go through all others_req and check which will be satisfied + by the current write */ + + /* Note that MPI guarantees that displacements in filetypes are in + monotonically nondecreasing order and that, for writes, the + filetypes cannot specify overlapping regions in the file. This + simplifies implementation a bit compared to reads. */ + + /* off = start offset in the file for the data to be written in + this iteration + size = size of data written (bytes) corresponding to off + req_off = off in file for a particular contiguous request + minus what was satisfied in previous iteration + req_size = size corresponding to req_off */ + + /* first calculate what should be communicated */ + +#ifdef PROFILE + MPE_Log_event(13, 0, "start computation"); +#endif + for (i=0; i < nprocs; i++) count[i] = recv_size[i] = 0; + + size = (int) (ADIOI_MIN(coll_bufsize, end_loc-st_loc+1-done)); + + for (i=0; i < nprocs; i++) { + if (others_req[i].count) { + start_pos[i] = curr_offlen_ptr[i]; + for (j=curr_offlen_ptr[i]; jcomm); + + /* create derived datatypes for recv */ + + nprocs_recv = 0; + for (i=0; i off) || + ((srt_off[sum-1] + srt_len[sum-1]) < (off + size))) + { + *hole = 1; + } + else /* See if there are holes between the requests, if there are more than one */ + for (i=0; iatomicity) { + /* bug fix from Wei-keng Liao and Kenin Coloma */ + requests = (MPI_Request *) + ADIOI_Malloc((nprocs_send+1)*sizeof(MPI_Request)); + send_req = requests; + } + else { + requests = (MPI_Request *) + ADIOI_Malloc((nprocs_send+nprocs_recv+1)*sizeof(MPI_Request)); + /* +1 to avoid a 0-size malloc */ + + /* post receives */ + j = 0; + for (i=0; icomm, requests+j); + j++; + } + } + send_req = requests + nprocs_recv; + } + +/* post sends. if buftype_is_contig, data can be directly sent from + user buf at location given by buf_idx. else use send_buf. */ + + if (buftype_is_contig) { + j = 0; + for (i=0; i < nprocs; i++) + if (send_size[i]) { + MPI_Isend(((char *) buf) + buf_idx[i], send_size[i], + MPI_BYTE, i, myrank+i+100*iter, fd->comm, + send_req+j); + j++; + buf_idx[i] += send_size[i]; + } + } + else if (nprocs_send) { + /* buftype is not contig */ + send_buf = (char **) ADIOI_Malloc(nprocs*sizeof(char*)); + for (i=0; i < nprocs; i++) + if (send_size[i]) + send_buf[i] = (char *) ADIOI_Malloc(send_size[i]); + + ADIOI_Fill_send_buffer(fd, buf, flat_buf, send_buf, + offset_list, len_list, send_size, + send_req, + sent_to_proc, nprocs, myrank, + contig_access_count, + min_st_offset, fd_size, fd_start, fd_end, + send_buf_idx, curr_to_proc, done_to_proc, iter, + buftype_extent); + /* the send is done in ADIOI_Fill_send_buffer */ + } + + if (fd->atomicity) { + /* bug fix from Wei-keng Liao and Kenin Coloma */ + j = 0; + for (i=0; icomm, &wkl_status); + j++; + } + } + } + + for (i=0; iatomicity) { + /* bug fix from Wei-keng Liao and Kenin Coloma */ + statuses = (MPI_Status *) ADIOI_Malloc((nprocs_send+1) * \ + sizeof(MPI_Status)); + /* +1 to avoid a 0-size malloc */ + } + else { + statuses = (MPI_Status *) ADIOI_Malloc((nprocs_send+nprocs_recv+1) * \ + sizeof(MPI_Status)); + /* +1 to avoid a 0-size malloc */ + } + +#ifdef NEEDS_MPI_TEST + i = 0; + if (fd->atomicity) { + /* bug fix from Wei-keng Liao and Kenin Coloma */ + while (!i) MPI_Testall(nprocs_send, send_req, &i, statuses); + } + else { + while (!i) MPI_Testall(nprocs_send+nprocs_recv, requests, &i, statuses); + } +#else + if (fd->atomicity) + /* bug fix from Wei-keng Liao and Kenin Coloma */ + MPI_Waitall(nprocs_send, send_req, statuses); + else + MPI_Waitall(nprocs_send+nprocs_recv, requests, statuses); +#endif + + ADIOI_Free(statuses); + ADIOI_Free(requests); + if (!buftype_is_contig && nprocs_send) { + for (i=0; i < nprocs; i++) + if (send_size[i]) ADIOI_Free(send_buf[i]); + ADIOI_Free(send_buf); + } +} + + +#define ADIOI_BUF_INCR \ +{ \ + while (buf_incr) { \ + size_in_buf = ADIOI_MIN(buf_incr, flat_buf_sz); \ + user_buf_idx += size_in_buf; \ + flat_buf_sz -= size_in_buf; \ + if (!flat_buf_sz) { \ + if (flat_buf_idx < (flat_buf->count - 1)) flat_buf_idx++; \ + else { \ + flat_buf_idx = 0; \ + n_buftypes++; \ + } \ + user_buf_idx = flat_buf->indices[flat_buf_idx] + \ + n_buftypes*buftype_extent; \ + flat_buf_sz = flat_buf->blocklens[flat_buf_idx]; \ + } \ + buf_incr -= size_in_buf; \ + } \ +} + + +#define ADIOI_BUF_COPY \ +{ \ + while (size) { \ + size_in_buf = ADIOI_MIN(size, flat_buf_sz); \ + memcpy(&(send_buf[p][send_buf_idx[p]]), \ + ((char *) buf) + user_buf_idx, size_in_buf); \ + send_buf_idx[p] += size_in_buf; \ + user_buf_idx += size_in_buf; \ + flat_buf_sz -= size_in_buf; \ + if (!flat_buf_sz) { \ + if (flat_buf_idx < (flat_buf->count - 1)) flat_buf_idx++; \ + else { \ + flat_buf_idx = 0; \ + n_buftypes++; \ + } \ + user_buf_idx = flat_buf->indices[flat_buf_idx] + \ + n_buftypes*buftype_extent; \ + flat_buf_sz = flat_buf->blocklens[flat_buf_idx]; \ + } \ + size -= size_in_buf; \ + buf_incr -= size_in_buf; \ + } \ + ADIOI_BUF_INCR \ +} + + + +static void ADIOI_Fill_send_buffer(ADIO_File fd, void *buf, ADIOI_Flatlist_node + *flat_buf, char **send_buf, ADIO_Offset + *offset_list, int *len_list, int *send_size, + MPI_Request *requests, int *sent_to_proc, + int nprocs, int myrank, + int contig_access_count, + ADIO_Offset min_st_offset, ADIO_Offset fd_size, + ADIO_Offset *fd_start, ADIO_Offset *fd_end, + int *send_buf_idx, int *curr_to_proc, + int *done_to_proc, int iter, + MPI_Aint buftype_extent) +{ +/* this function is only called if buftype is not contig */ + + int i, p, flat_buf_idx, size; + int flat_buf_sz, buf_incr, size_in_buf, jj, n_buftypes; + ADIO_Offset off, len, rem_len, user_buf_idx; + +/* curr_to_proc[p] = amount of data sent to proc. p that has already + been accounted for so far + done_to_proc[p] = amount of data already sent to proc. p in + previous iterations + user_buf_idx = current location in user buffer + send_buf_idx[p] = current location in send_buf of proc. p */ + + for (i=0; i < nprocs; i++) { + send_buf_idx[i] = curr_to_proc[i] = 0; + done_to_proc[i] = sent_to_proc[i]; + } + jj = 0; + + user_buf_idx = flat_buf->indices[0]; + flat_buf_idx = 0; + n_buftypes = 0; + flat_buf_sz = flat_buf->blocklens[0]; + + /* flat_buf_idx = current index into flattened buftype + flat_buf_sz = size of current contiguous component in + flattened buf */ + + for (i=0; i done_to_proc[p]) { + if (done_to_proc[p] > curr_to_proc[p]) { + size = (int)ADIOI_MIN(curr_to_proc[p] + len - + done_to_proc[p], send_size[p]-send_buf_idx[p]); + buf_incr = done_to_proc[p] - curr_to_proc[p]; + ADIOI_BUF_INCR + buf_incr = (int)(curr_to_proc[p] + len - done_to_proc[p]); + curr_to_proc[p] = done_to_proc[p] + size; + ADIOI_BUF_COPY + } + else { + size = (int)ADIOI_MIN(len,send_size[p]-send_buf_idx[p]); + buf_incr = (int)len; + curr_to_proc[p] += size; + ADIOI_BUF_COPY + } + if (send_buf_idx[p] == send_size[p]) { + MPI_Isend(send_buf[p], send_size[p], MPI_BYTE, p, + myrank+p+100*iter, fd->comm, requests+jj); + jj++; + } + } + else { + curr_to_proc[p] += (int)len; + buf_incr = (int)len; + ADIOI_BUF_INCR + } + } + else { + buf_incr = (int)len; + ADIOI_BUF_INCR + } + off += len; + rem_len -= len; + } + } + for (i=0; i < nprocs; i++) + if (send_size[i]) sent_to_proc[i] = curr_to_proc[i]; +} + + + +static void ADIOI_Heap_merge(ADIOI_Access *others_req, int *count, + ADIO_Offset *srt_off, int *srt_len, int *start_pos, + int nprocs, int nprocs_recv, int total_elements) +{ + typedef struct { + ADIO_Offset *off_list; + int *len_list; + int nelem; + } heap_struct; + + heap_struct *a, tmp; + int i, j, heapsize, l, r, k, smallest; + + a = (heap_struct *) ADIOI_Malloc((nprocs_recv+1)*sizeof(heap_struct)); + + j = 0; + for (i=0; i=0; i--) { + /* Heapify(a, i, heapsize); Algorithm from Cormen et al. pg. 143 + modified for a heap with smallest element at root. I have + removed the recursion so that there are no function calls. + Function calls are too expensive. */ + k = i; + while (1) { + l = 2*(k+1) - 1; + r = 2*(k+1); + + if ((l < heapsize) && + (*(a[l].off_list) < *(a[k].off_list))) + smallest = l; + else smallest = k; + + if ((r < heapsize) && + (*(a[r].off_list) < *(a[smallest].off_list))) + smallest = r; + + if (smallest != k) { + tmp.off_list = a[k].off_list; + tmp.len_list = a[k].len_list; + tmp.nelem = a[k].nelem; + + a[k].off_list = a[smallest].off_list; + a[k].len_list = a[smallest].len_list; + a[k].nelem = a[smallest].nelem; + + a[smallest].off_list = tmp.off_list; + a[smallest].len_list = tmp.len_list; + a[smallest].nelem = tmp.nelem; + + k = smallest; + } + else break; + } + } + + for (i=0; icomm); + + nprocs_recv = 0; + for (i=0; icomm ); + + /* data sieving pre-read */ + /* To avoid a read-modify-write, check if there are holes in the + data to be written. For this, merge the (sorted) offset lists + others_req using a heap-merge. */ + + sum = 0; + for (i=0; i off) || + ((srt_off[sum-1] + srt_len[sum-1]) < (off + size))) + { + *hole = 1; + } + else /* See if there are holes between the requests, if there are more than one */ + for (i=0; iindices[0]; + flat_buf_idx = 0; + n_buftypes = 0; + flat_buf_sz = flat_buf->blocklens[0]; + + /* flat_buf_idx = current index into flattened buftype + flat_buf_sz = size of current contiguous component in + flattened buf */ + + for (i=0; i done_to_proc[p]) { + if (done_to_proc[p] > curr_to_proc[p]) { + size = (int)ADIOI_MIN(curr_to_proc[p] + len - + done_to_proc[p], send_size[p]-send_buf_idx[p]); + buf_incr = done_to_proc[p] - curr_to_proc[p]; + ADIOI_BUF_INCR + buf_incr = (int)(curr_to_proc[p] + len - done_to_proc[p]); + curr_to_proc[p] = done_to_proc[p] + size; + ADIOI_BUF_COPY + } + else { + size = (int)ADIOI_MIN(len,send_size[p]-send_buf_idx[p]); + buf_incr = (int)len; + curr_to_proc[p] += size; + ADIOI_BUF_COPY + } + /* moved to alltoallv */ + /* + if (send_buf_idx[p] == send_size[p]) { + MPI_Isend(send_buf[p], send_size[p], MPI_BYTE, p, + myrank+p+100*iter, fd->comm, requests+jj); + jj++; + } + */ + } + else { + curr_to_proc[p] += (int)len; + buf_incr = (int)len; + ADIOI_BUF_INCR + } + } + else { + buf_incr = (int)len; + ADIOI_BUF_INCR + } + off += len; + rem_len -= len; + } + } + for (i=0; i < nprocs; i++) + if (send_size[i]) sent_to_proc[i] = curr_to_proc[i]; +} diff --git a/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_write.c b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_write.c new file mode 100644 index 0000000000..b482bb3ac3 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bgl/ad_bgl_write.c @@ -0,0 +1,546 @@ +/* ---------------------------------------------------------------- */ +/* (C)Copyright IBM Corp. 2007, 2008 */ +/* ---------------------------------------------------------------- */ +/** + * \file ad_bgl_write.c + * \brief ??? + */ + +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "ad_bgl.h" +#include "adio_extern.h" + +#include "ad_bgl_tuning.h" + +void ADIOI_BGL_WriteContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int *error_code) +{ + int err=-1, datatype_size, len; + static char myname[] = "ADIOI_BGL_WRITECONTIG"; + +#if BGL_PROFILE + /* timing */ + double io_time, io_time2; + + if (bglmpio_timing) { + io_time = MPI_Wtime(); + bglmpio_prof_cw[ BGLMPIO_CIO_DATA_SIZE ] += len; + } +#endif + + MPI_Type_size(datatype, &datatype_size); + len = datatype_size * count; + +#if BGL_PROFILE + + if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { + if (bglmpio_timing2) io_time2 = MPI_Wtime(); + if (fd->fp_sys_posn != offset) + lseek(fd->fd_sys, offset, SEEK_SET); + if (bglmpio_timing2) bglmpio_prof_cw[ BGLMPIO_CIO_T_SEEK ] += (MPI_Wtime() - io_time2); + ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); + if (bglmpio_timing2) io_time2 = MPI_Wtime(); + err = write(fd->fd_sys, buf, len); + if (bglmpio_timing2) bglmpio_prof_cw[ BGLMPIO_CIO_T_POSI_RW ] += (MPI_Wtime() - io_time2); + ADIOI_UNLOCK(fd, offset, SEEK_SET, len); + fd->fp_sys_posn = offset + err; + /* individual file pointer not updated */ + } + else { /* write from curr. location of ind. file pointer */ + offset = fd->fp_ind; + if (bglmpio_timing2) io_time2 = MPI_Wtime(); + if (fd->fp_sys_posn != fd->fp_ind) + lseek(fd->fd_sys, fd->fp_ind, SEEK_SET); + if (bglmpio_timing2) bglmpio_prof_cw[ BGLMPIO_CIO_T_SEEK ] += (MPI_Wtime() - io_time2); + ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); + if (bglmpio_timing2) io_time2 = MPI_Wtime(); + err = write(fd->fd_sys, buf, len); + if (bglmpio_timing2) bglmpio_prof_cw[ BGLMPIO_CIO_T_POSI_RW ] += (MPI_Wtime() - io_time2); + ADIOI_UNLOCK(fd, offset, SEEK_SET, len); + fd->fp_ind += err; + fd->fp_sys_posn = fd->fp_ind; + } + +#else /* BGL_PROFILE */ + + if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { + if (fd->fp_sys_posn != offset) + lseek(fd->fd_sys, offset, SEEK_SET); + ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); + err = write(fd->fd_sys, buf, len); + ADIOI_UNLOCK(fd, offset, SEEK_SET, len); + fd->fp_sys_posn = offset + err; + /* individual file pointer not updated */ + } + else { /* write from curr. location of ind. file pointer */ + offset = fd->fp_ind; + if (fd->fp_sys_posn != fd->fp_ind) + lseek(fd->fd_sys, fd->fp_ind, SEEK_SET); + ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); + err = write(fd->fd_sys, buf, len); + ADIOI_UNLOCK(fd, offset, SEEK_SET, len); + fd->fp_ind += err; + fd->fp_sys_posn = fd->fp_ind; + } + +#endif /* BGL_PROFILE */ + +#if BGL_PROFILE + if (bglmpio_timing) bglmpio_prof_cw[ BGLMPIO_CIO_T_MPIO_RW ] += (MPI_Wtime() - io_time); +#endif + + /* --BEGIN ERROR HANDLING-- */ + if (err == -1) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, + myname, __LINE__, MPI_ERR_IO, + "**io", + "**io %s", strerror(errno)); + return; + } + /* --END ERROR HANDLING-- */ + +#ifdef HAVE_STATUS_SET_BYTES + MPIR_Status_set_bytes(status, datatype, err); +#endif + + *error_code = MPI_SUCCESS; +} + + + + +#define ADIOI_BUFFERED_WRITE \ +{ \ + if (req_off >= writebuf_off + writebuf_len) { \ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); \ + err = write(fd->fd_sys, writebuf, writebuf_len); \ + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + if (err == -1) err_flag = 1; \ + writebuf_off = req_off; \ + writebuf_len = (int) (ADIOI_MIN(max_bufsize,end_offset-writebuf_off+1));\ + if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); \ + err = read(fd->fd_sys, writebuf, writebuf_len); \ + if (err == -1) { \ + *error_code = MPIO_Err_create_code(MPI_SUCCESS, \ + MPIR_ERR_RECOVERABLE, myname, \ + __LINE__, MPI_ERR_IO, \ + "**ioRMWrdwr", 0); \ + return; \ + } \ + } \ + write_sz = (int) (ADIOI_MIN(req_len, writebuf_off + writebuf_len - req_off)); \ + memcpy(writebuf+req_off-writebuf_off, (char *)buf +userbuf_off, write_sz);\ + while (write_sz != req_len) { \ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); \ + err = write(fd->fd_sys, writebuf, writebuf_len); \ + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + if (err == -1) err_flag = 1; \ + req_len -= write_sz; \ + userbuf_off += write_sz; \ + writebuf_off += writebuf_len; \ + writebuf_len = (int) (ADIOI_MIN(max_bufsize,end_offset-writebuf_off+1));\ + if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); \ + err = read(fd->fd_sys, writebuf, writebuf_len); \ + if (err == -1) { \ + *error_code = MPIO_Err_create_code(MPI_SUCCESS, \ + MPIR_ERR_RECOVERABLE, myname, \ + __LINE__, MPI_ERR_IO, \ + "**ioRMWrdwr", 0); \ + return; \ + } \ + write_sz = ADIOI_MIN(req_len, writebuf_len); \ + memcpy(writebuf, (char *)buf + userbuf_off, write_sz);\ + } \ +} + + +/* this macro is used when filetype is contig and buftype is not contig. + it does not do a read-modify-write and does not lock*/ +#define ADIOI_BUFFERED_WRITE_WITHOUT_READ \ +{ \ + if (req_off >= writebuf_off + writebuf_len) { \ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); \ + if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + err = write(fd->fd_sys, writebuf, writebuf_len); \ + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + if (err == -1) err_flag = 1; \ + writebuf_off = req_off; \ + writebuf_len = (int) (ADIOI_MIN(max_bufsize,end_offset-writebuf_off+1));\ + } \ + write_sz = (int) (ADIOI_MIN(req_len, writebuf_off + writebuf_len - req_off)); \ + memcpy(writebuf+req_off-writebuf_off, (char *)buf +userbuf_off, write_sz);\ + while (write_sz != req_len) { \ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); \ + if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + err = write(fd->fd_sys, writebuf, writebuf_len); \ + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + if (err == -1) err_flag = 1; \ + req_len -= write_sz; \ + userbuf_off += write_sz; \ + writebuf_off += writebuf_len; \ + writebuf_len = (int) (ADIOI_MIN(max_bufsize,end_offset-writebuf_off+1));\ + write_sz = ADIOI_MIN(req_len, writebuf_len); \ + memcpy(writebuf, (char *)buf + userbuf_off, write_sz);\ + } \ +} + + + +void ADIOI_BGL_WriteStrided(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code) +{ +/* offset is in units of etype relative to the filetype. */ + + ADIOI_Flatlist_node *flat_buf, *flat_file; + int i, j, k, err=-1, bwr_size, fwr_size=0, st_index=0; + int bufsize, num, size, sum, n_etypes_in_filetype, size_in_filetype; + int n_filetypes, etype_in_filetype; + ADIO_Offset abs_off_in_filetype=0; + int filetype_size, etype_size, buftype_size, req_len; + MPI_Aint filetype_extent, buftype_extent; + int buf_count, buftype_is_contig, filetype_is_contig; + ADIO_Offset userbuf_off; + ADIO_Offset off, req_off, disp, end_offset=0, writebuf_off, start_off; + char *writebuf, *value; + int flag, st_fwr_size, st_n_filetypes, writebuf_len, write_sz; + int new_bwr_size, new_fwr_size, err_flag=0, info_flag, max_bufsize; + static char myname[] = "ADIOI_BGL_WRITESTRIDED"; + + if (fd->hints->ds_write == ADIOI_HINT_DISABLE) { + /* if user has disabled data sieving on reads, use naive + * approach instead. + */ + /*FPRINTF(stderr, "ADIOI_GEN_WriteStrided_naive(%d):\n", __LINE__);*/ + ADIOI_GEN_WriteStrided_naive(fd, + buf, + count, + datatype, + file_ptr_type, + offset, + status, + error_code); + return; + } + /*FPRINTF(stderr, "%s(%d):\n",myname, __LINE__);*/ + + ADIOI_Datatype_iscontig(datatype, &buftype_is_contig); + ADIOI_Datatype_iscontig(fd->filetype, &filetype_is_contig); + + MPI_Type_size(fd->filetype, &filetype_size); + if ( ! filetype_size ) { + *error_code = MPI_SUCCESS; + return; + } + + MPI_Type_extent(fd->filetype, &filetype_extent); + MPI_Type_size(datatype, &buftype_size); + MPI_Type_extent(datatype, &buftype_extent); + etype_size = fd->etype_size; + + bufsize = buftype_size * count; + +/* get max_bufsize from the info object. */ + + value = (char *) ADIOI_Malloc((MPI_MAX_INFO_VAL+1)*sizeof(char)); + MPI_Info_get(fd->info, "ind_wr_buffer_size", MPI_MAX_INFO_VAL, value, + &info_flag); + max_bufsize = atoi(value); + ADIOI_Free(value); + + if (!buftype_is_contig && filetype_is_contig) { + +/* noncontiguous in memory, contiguous in file. */ + + ADIOI_Flatten_datatype(datatype); + flat_buf = ADIOI_Flatlist; + while (flat_buf->type != datatype) flat_buf = flat_buf->next; + + off = (file_ptr_type == ADIO_INDIVIDUAL) ? fd->fp_ind : + fd->disp + etype_size * offset; + + start_off = off; + end_offset = off + bufsize - 1; + writebuf_off = off; + writebuf = (char *) ADIOI_Malloc(max_bufsize); + writebuf_len = (int) (ADIOI_MIN(max_bufsize,end_offset-writebuf_off+1)); + +/* if atomicity is true, lock the region to be accessed */ + if (fd->atomicity) + ADIOI_WRITE_LOCK(fd, start_off, SEEK_SET, end_offset-start_off+1); + + for (j=0; jcount; i++) { + userbuf_off = j*buftype_extent + flat_buf->indices[i]; + req_off = off; + req_len = flat_buf->blocklens[i]; + ADIOI_BUFFERED_WRITE_WITHOUT_READ + off += flat_buf->blocklens[i]; + } + + /* write the buffer out finally */ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); + if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); + err = write(fd->fd_sys, writebuf, writebuf_len); + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, writebuf_off, SEEK_SET, writebuf_len); + if (err == -1) err_flag = 1; + + if (fd->atomicity) + ADIOI_UNLOCK(fd, start_off, SEEK_SET, end_offset-start_off+1); + + ADIOI_Free(writebuf); /* malloced in the buffered_write macro */ + + if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind = off; + if (err_flag) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + } + else *error_code = MPI_SUCCESS; + } + + else { /* noncontiguous in file */ + +/* filetype already flattened in ADIO_Open */ + flat_file = ADIOI_Flatlist; + while (flat_file->type != fd->filetype) flat_file = flat_file->next; + disp = fd->disp; + + if (file_ptr_type == ADIO_INDIVIDUAL) { + offset = fd->fp_ind; /* in bytes */ + n_filetypes = -1; + flag = 0; + while (!flag) { + n_filetypes++; + for (i=0; icount; i++) { + if (disp + flat_file->indices[i] + + (ADIO_Offset) n_filetypes*filetype_extent + flat_file->blocklens[i] + >= offset) { + st_index = i; + fwr_size = (int) (disp + flat_file->indices[i] + + (ADIO_Offset) n_filetypes*filetype_extent + + flat_file->blocklens[i] - offset); + flag = 1; + break; + } + } + } + } + else { + n_etypes_in_filetype = filetype_size/etype_size; + n_filetypes = (int) (offset / n_etypes_in_filetype); + etype_in_filetype = (int) (offset % n_etypes_in_filetype); + size_in_filetype = etype_in_filetype * etype_size; + + sum = 0; + for (i=0; icount; i++) { + sum += flat_file->blocklens[i]; + if (sum > size_in_filetype) { + st_index = i; + fwr_size = sum - size_in_filetype; + abs_off_in_filetype = flat_file->indices[i] + + size_in_filetype - (sum - flat_file->blocklens[i]); + break; + } + } + + /* abs. offset in bytes in the file */ + offset = disp + (ADIO_Offset) n_filetypes*filetype_extent + abs_off_in_filetype; + } + + start_off = offset; + + /* Calculate end_offset, the last byte-offset that will be accessed. + e.g., if start_offset=0 and 100 bytes to be write, end_offset=99*/ + + st_fwr_size = fwr_size; + st_n_filetypes = n_filetypes; + i = 0; + j = st_index; + off = offset; + fwr_size = ADIOI_MIN(st_fwr_size, bufsize); + while (i < bufsize) { + i += fwr_size; + end_offset = off + fwr_size - 1; + + if (j < (flat_file->count - 1)) j++; + else { + j = 0; + n_filetypes++; + } + + off = disp + flat_file->indices[j] + (ADIO_Offset) n_filetypes*filetype_extent; + fwr_size = ADIOI_MIN(flat_file->blocklens[j], bufsize-i); + } + +/* if atomicity is true, lock the region to be accessed */ + if (fd->atomicity) + ADIOI_WRITE_LOCK(fd, start_off, SEEK_SET, end_offset-start_off+1); + + /* initial read for the read-modify-write */ + writebuf_off = offset; + writebuf = (char *) ADIOI_Malloc(max_bufsize); + writebuf_len = (int)(ADIOI_MIN(max_bufsize,end_offset-writebuf_off+1)); + if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); + lseek(fd->fd_sys, writebuf_off, SEEK_SET); + err = read(fd->fd_sys, writebuf, writebuf_len); + if (err == -1) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + myname, __LINE__, + MPI_ERR_IO, + "ADIOI_BGL_WriteStrided: ROMIO tries to optimize this access by doing a read-modify-write, but is unable to read the file. Please give the file read permission and open it with MPI_MODE_RDWR.", 0); + return; + } + + if (buftype_is_contig && !filetype_is_contig) { + +/* contiguous in memory, noncontiguous in file. should be the most + common case. */ + + i = 0; + j = st_index; + off = offset; + n_filetypes = st_n_filetypes; + fwr_size = ADIOI_MIN(st_fwr_size, bufsize); + while (i < bufsize) { + if (fwr_size) { + /* TYPE_UB and TYPE_LB can result in + fwr_size = 0. save system call in such cases */ + /* lseek(fd->fd_sys, off, SEEK_SET); + err = write(fd->fd_sys, ((char *) buf) + i, fwr_size);*/ + + req_off = off; + req_len = fwr_size; + userbuf_off = i; + ADIOI_BUFFERED_WRITE + } + i += fwr_size; + + if (off + fwr_size < disp + flat_file->indices[j] + + flat_file->blocklens[j] + (ADIO_Offset) n_filetypes*filetype_extent) + off += fwr_size; + /* did not reach end of contiguous block in filetype. + no more I/O needed. off is incremented by fwr_size. */ + else { + if (j < (flat_file->count - 1)) j++; + else { + j = 0; + n_filetypes++; + } + off = disp + flat_file->indices[j] + + (ADIO_Offset) n_filetypes*filetype_extent; + fwr_size = ADIOI_MIN(flat_file->blocklens[j], bufsize-i); + } + } + } + else { +/* noncontiguous in memory as well as in file */ + + ADIOI_Flatten_datatype(datatype); + flat_buf = ADIOI_Flatlist; + while (flat_buf->type != datatype) flat_buf = flat_buf->next; + + k = num = buf_count = 0; + i = (int) (flat_buf->indices[0]); + j = st_index; + off = offset; + n_filetypes = st_n_filetypes; + fwr_size = st_fwr_size; + bwr_size = flat_buf->blocklens[0]; + + while (num < bufsize) { + size = ADIOI_MIN(fwr_size, bwr_size); + if (size) { + /* lseek(fd->fd_sys, off, SEEK_SET); + err = write(fd->fd_sys, ((char *) buf) + i, size); */ + + req_off = off; + req_len = size; + userbuf_off = i; + ADIOI_BUFFERED_WRITE + } + + new_fwr_size = fwr_size; + new_bwr_size = bwr_size; + + if (size == fwr_size) { +/* reached end of contiguous block in file */ + if (j < (flat_file->count - 1)) j++; + else { + j = 0; + n_filetypes++; + } + + off = disp + flat_file->indices[j] + + (ADIO_Offset) n_filetypes*filetype_extent; + + new_fwr_size = flat_file->blocklens[j]; + if (size != bwr_size) { + i += size; + new_bwr_size -= size; + } + } + + if (size == bwr_size) { +/* reached end of contiguous block in memory */ + + k = (k + 1)%flat_buf->count; + buf_count++; + i = (int) (buftype_extent*(buf_count/flat_buf->count) + + flat_buf->indices[k]); + new_bwr_size = flat_buf->blocklens[k]; + if (size != fwr_size) { + off += size; + new_fwr_size -= size; + } + } + num += size; + fwr_size = new_fwr_size; + bwr_size = new_bwr_size; + } + } + + /* write the buffer out finally */ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); + if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); + err = write(fd->fd_sys, writebuf, writebuf_len); + + if (!(fd->atomicity)) + ADIOI_UNLOCK(fd, writebuf_off, SEEK_SET, writebuf_len); + else ADIOI_UNLOCK(fd, start_off, SEEK_SET, end_offset-start_off+1); + + if (err == -1) err_flag = 1; + + ADIOI_Free(writebuf); /* malloced in the buffered_write macro */ + + if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind = off; + if (err_flag) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + } + else *error_code = MPI_SUCCESS; + } + + fd->fp_sys_posn = -1; /* set it to null. */ + +#ifdef HAVE_STATUS_SET_BYTES + MPIR_Status_set_bytes(status, datatype, bufsize); +/* This is a temporary way of filling in status. The right way is to + keep track of how much data was actually written by ADIOI_BUFFERED_WRITE. */ +#endif + + if (!buftype_is_contig) ADIOI_Delete_flattened(datatype); +} diff --git a/ompi/mca/io/romio/romio/adio/ad_bglockless/.state-cache b/ompi/mca/io/romio/romio/adio/ad_bglockless/.state-cache new file mode 100644 index 0000000000..7b79bb6e95 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bglockless/.state-cache @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ompi/mca/io/romio/romio/adio/ad_bglockless/Makefile.am b/ompi/mca/io/romio/romio/adio/ad_bglockless/Makefile.am new file mode 100644 index 0000000000..991f779520 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bglockless/Makefile.am @@ -0,0 +1,24 @@ +# +# Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana +# University Research and Technology +# Corporation. All rights reserved. +# Copyright (c) 2004-2005 The University of Tennessee and The University +# of Tennessee Research Foundation. All rights +# reserved. +# Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, +# University of Stuttgart. All rights reserved. +# Copyright (c) 2004-2005 The Regents of the University of California. +# All rights reserved. +# Copyright (c) 2008 Cisco Systems, Inc. All rights reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +include $(top_srcdir)/Makefile.options + +noinst_LTLIBRARIES = libadio_bglockless.la +libadio_bglockless_la_SOURCES = \ + ad_bglockless.c diff --git a/ompi/mca/io/romio/romio/adio/ad_bglockless/Makefile.in b/ompi/mca/io/romio/romio/adio/ad_bglockless/Makefile.in new file mode 100644 index 0000000000..5d8b83fd31 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bglockless/Makefile.in @@ -0,0 +1,49 @@ +CC = @CC@ +AR = @AR@ +RANLIB = @RANLIB@ +LIBNAME = @LIBNAME@ +srcdir = @srcdir@ +CC_SHL = @CC_SHL@ +SHLIBNAME = @SHLIBNAME@ + +INCLUDE_DIR = -I@MPI_INCLUDE_DIR@ -I${srcdir}/../include -I../include -I../../include -I${srcdir}/../../../../include -I../../../../include +CFLAGS = @CPPFLAGS@ @CFLAGS@ $(INCLUDE_DIR) + +top_builddir = @master_topbuild_dir@ +LIBTOOL = @LIBTOOL@ +C_COMPILE_SHL = $(CC_SHL) + +@VPATH@ + +AD_BGLOCKLESS_OBJECTS = ad_bglockless.o + + +default: $(LIBNAME) + @if [ "@ENABLE_SHLIB@" != "none" ] ; then \ + $(MAKE) $(SHLIBNAME).la ;\ + fi + +.SUFFIXES: $(SUFFIXES) .p .lo + +.c.o: + $(CC) $(CFLAGS) -c $< +.c.lo: + $(C_COMPILE_SHL) $(CFLAGS) -c $< -o _s$*.o + @mv -f _s$*.o $*.lo + +$(LIBNAME): $(AD_BGLOCKLESS_OBJECTS) + $(AR) $(LIBNAME) $(AD_BGLOCKLESS_OBJECTS) + $(RANLIB) $(LIBNAME) + +AD_BGLOCKLESS_LOOBJECTS=$(AD_BGLOCKLESS_OBJECTS:.o=.lo) +$(SHLIBNAME).la: $(AD_BGLOCKLESS_LOOBJECTS) + $(AR) $(SHLIBNAME).la $(AD_BGLOCKLESS_LOOBJECTS) + +coverage: + -@for file in ${AD_BGLOCKLESS_OBJECTS:.o=.c} ; do \ + gcov -b -f $$file ; done + +clean: + @rm -f *.o *.lo *.gcno *.gcda *.bb *.bbg + @rm -f ${srcdir}/*.gcno ${srcdir}/*.gcda + @rm -f ${srcdir}/*.bb ${srcdir}/*.bbg diff --git a/ompi/mca/io/romio/romio/adio/ad_bglockless/ad_bglockless.c b/ompi/mca/io/romio/romio/adio/ad_bglockless/ad_bglockless.c new file mode 100644 index 0000000000..725590d1b9 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_bglockless/ad_bglockless.c @@ -0,0 +1,41 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * + * Copyright (C) 2001 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "../ad_bgl/ad_bgl.h" + +/* adioi.h has the ADIOI_Fns_struct define */ +#include "adioi.h" + +struct ADIOI_Fns_struct ADIO_BGLOCKLESS_operations = { + ADIOI_BGL_Open, /* Open */ + ADIOI_GEN_ReadContig, /* ReadContig */ + ADIOI_GEN_WriteContig, /* WriteContig */ + ADIOI_BGL_ReadStridedColl, /* ReadStridedColl */ + ADIOI_BGL_WriteStridedColl, /* WriteStridedColl */ + ADIOI_GEN_SeekIndividual, /* SeekIndividual */ + ADIOI_GEN_Fcntl, /* Fcntl */ + ADIOI_BGL_SetInfo, /* SetInfo */ + ADIOI_GEN_ReadStrided, /* ReadStrided */ + ADIOI_NOLOCK_WriteStrided, /* WriteStrided */ + ADIOI_BGL_Close, /* Close */ +#ifdef ROMIO_HAVE_WORKING_AIO + ADIOI_GEN_IreadContig, /* IreadContig */ + ADIOI_GEN_IwriteContig, /* IwriteContig */ +#else + ADIOI_FAKE_IreadContig, /* IreadContig */ + ADIOI_FAKE_IwriteContig, /* IwriteContig */ +#endif + ADIOI_GEN_IODone, /* ReadDone */ + ADIOI_GEN_IODone, /* WriteDone */ + ADIOI_GEN_IOComplete, /* ReadComplete */ + ADIOI_GEN_IOComplete, /* WriteComplete */ + ADIOI_GEN_IreadStrided, /* IreadStrided */ + ADIOI_GEN_IwriteStrided, /* IwriteStrided */ + ADIOI_GEN_Flush, /* Flush */ + ADIOI_GEN_Resize, /* Resize */ + ADIOI_GEN_Delete, /* Delete */ +}; diff --git a/ompi/mca/io/romio/romio/adio/ad_lustre/.state-cache b/ompi/mca/io/romio/romio/adio/ad_lustre/.state-cache new file mode 100644 index 0000000000..3e2ff262a8 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_lustre/.state-cache @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/ompi/mca/io/romio/romio/adio/ad_lustre/Makefile.am b/ompi/mca/io/romio/romio/adio/ad_lustre/Makefile.am new file mode 100644 index 0000000000..67107e302f --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_lustre/Makefile.am @@ -0,0 +1,31 @@ +# +# Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana +# University Research and Technology +# Corporation. All rights reserved. +# Copyright (c) 2004-2005 The University of Tennessee and The University +# of Tennessee Research Foundation. All rights +# reserved. +# Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, +# University of Stuttgart. All rights reserved. +# Copyright (c) 2004-2005 The Regents of the University of California. +# All rights reserved. +# Copyright (c) 2008 Cisco Systems, Inc. All rights reserved. +# $COPYRIGHT$ +# +# Additional copyrights may follow +# +# $HEADER$ +# + +include $(top_srcdir)/Makefile.options + +EXTRA_DIST = README + +noinst_LTLIBRARIES = libadio_lustre.la +libadio_lustre_la_SOURCES = \ + ad_lustre.c \ + ad_lustre_fcntl.c \ + ad_lustre.h \ + ad_lustre_hints.c \ + ad_lustre_open.c \ + ad_lustre_rwcontig.c diff --git a/ompi/mca/io/romio/romio/adio/ad_lustre/Makefile.in b/ompi/mca/io/romio/romio/adio/ad_lustre/Makefile.in new file mode 100644 index 0000000000..2b5d48888b --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_lustre/Makefile.in @@ -0,0 +1,47 @@ +CC = @CC@ +AR = @AR@ +RANLIB = @RANLIB@ +LIBNAME = @LIBNAME@ +srcdir = @srcdir@ +CC_SHL = @CC_SHL@ +SHLIBNAME = @SHLIBNAME@ + +INCLUDE_DIR = -I@MPI_INCLUDE_DIR@ -I${srcdir}/../include -I../include -I../../include -I${srcdir}/../../../../include -I../../../../include +CFLAGS = @CPPFLAGS@ @CFLAGS@ $(INCLUDE_DIR) + +top_builddir = @master_topbuild_dir@ +LIBTOOL = @LIBTOOL@ +C_COMPILE_SHL = $(CC_SHL) @CFLAGS@ $(INCLUDE_DIR) + +@VPATH@ + +AD_LUSTRE_OBJECTS = ad_lustre.o ad_lustre_open.o \ + ad_lustre_rwcontig.o ad_lustre_hints.o + +default: $(LIBNAME) + @if [ "@ENABLE_SHLIB@" != "none" ] ; then \ + $(MAKE) $(SHLIBNAME).la ;\ + fi + +.SUFFIXES: $(SUFFIXES) .p .lo + +.c.o: + $(CC) $(CFLAGS) -c $< +.c.lo: + $(C_COMPILE_SHL) -c $< -o _s$*.o + @mv -f _s$*.o $*.lo + +$(LIBNAME): $(AD_LUSTRE_OBJECTS) + $(AR) $(LIBNAME) $(AD_LUSTRE_OBJECTS) + $(RANLIB) $(LIBNAME) + +AD_LUSTRE_LOOBJECTS=$(AD_LUSTRE_OBJECTS:.o=.lo) +$(SHLIBNAME).la: $(AD_LUSTRE_LOOBJECTS) + $(AR) $(SHLIBNAME).la $(AD_LUSTRE_LOOBJECTS) + +coverage: + -@for file in ${AD_LUSTRE_OBJECTS:.o=.c} ; do \ + gcov -b -f $$file ; done + +clean: + @rm -f *.o *.lo diff --git a/ompi/mca/io/romio/romio/adio/ad_lustre/README b/ompi/mca/io/romio/romio/adio/ad_lustre/README new file mode 100644 index 0000000000..545ef3485f --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_lustre/README @@ -0,0 +1,40 @@ +Upcoming soon: + o Hierarchical striping as described in the paper from CCGrid2007 + http://ft.ornl.gov/projects/io/pubs/CCGrid-2007-file-joining.pdf +Further out: + o To post the code for ParColl (Partitioned collective IO) + +----------------------------------------------------- +V04: +----------------------------------------------------- + o Direct IO and Lockless IO support + +----------------------------------------------------- +V03: +----------------------------------------------------- + o Correct detection of fs_type when lustre: prefix is not given + o Further fix on stripe alignment + o Tested/Enabled striping hints over Cray XT (Catamount and CNL) + +----------------------------------------------------- +V02: +----------------------------------------------------- +The Lustre ADIO driver has been cleaned up quite a lot. Compared +to the intital posting, here are the changes: + o Removal of dead/redundant code + o Removal of asynchronous IO piece as it appears outdated + o Bug fixes for setting Lustre Hints + o Bug fixes for data sieving + o Improved Setsize operation with one process calling ftruncate + o Improved collective IO with domain partitioning on + Lustre stripe boundary + +Contributing: + o You may contribute via many different ways, such as + testing results, bug reports, and new feature patches. + o We appreciate any courtesy reference of this work. + o Disclaimer: you are welcome to try the code, but at your own risk. + +Contact info: + For more info, visit http://ft.ornl.gov/projects/io/ + diff --git a/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre.c b/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre.c new file mode 100644 index 0000000000..1a465f85aa --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre.c @@ -0,0 +1,39 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 2001 University of Chicago. + * See COPYRIGHT notice in top-level directory. + * + * Copyright (C) 2007 Oak Ridge National Laboratory + */ + +#include "ad_lustre.h" + +struct ADIOI_Fns_struct ADIO_LUSTRE_operations = { + ADIOI_LUSTRE_Open, /* Open */ + ADIOI_LUSTRE_ReadContig, /* ReadContig */ + ADIOI_LUSTRE_WriteContig, /* WriteContig */ + ADIOI_GEN_ReadStridedColl, /* ReadStridedColl */ + ADIOI_GEN_WriteStridedColl, /* WriteStridedColl */ + ADIOI_GEN_SeekIndividual, /* SeekIndividual */ + ADIOI_GEN_Fcntl, /* Fcntl */ + ADIOI_LUSTRE_SetInfo, /* SetInfo */ + ADIOI_GEN_ReadStrided, /* ReadStrided */ + ADIOI_GEN_WriteStrided, /* WriteStrided */ + ADIOI_GEN_Close, /* Close */ +#if defined(ROMIO_HAVE_WORKING_AIO) && !defined(CRAY_XT_LUSTRE) + ADIOI_GEN_IreadContig, /* IreadContig */ + ADIOI_GEN_IwriteContig, /* IwriteContig */ +#else + ADIOI_FAKE_IreadContig, /* IreadContig */ + ADIOI_FAKE_IwriteContig, /* IwriteContig */ +#endif + ADIOI_GEN_IODone, /* ReadDone */ + ADIOI_GEN_IODone, /* WriteDone */ + ADIOI_GEN_IOComplete, /* ReadComplete */ + ADIOI_GEN_IOComplete, /* WriteComplete */ + ADIOI_GEN_IreadStrided, /* IreadStrided */ + ADIOI_GEN_IwriteStrided, /* IwriteStrided */ + ADIOI_GEN_Flush, /* Flush */ + ADIOI_GEN_Resize, /* Resize */ + ADIOI_GEN_Delete, /* Delete */ +}; diff --git a/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre.h b/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre.h new file mode 100644 index 0000000000..a0fbdc40e2 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre.h @@ -0,0 +1,64 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + * + * Copyright (C) 2007 Oak Ridge National Laboratory + */ + +#ifndef AD_UNIX_INCLUDE +#define AD_UNIX_INCLUDE + +/* temp*/ +#define HAVE_ASM_TYPES_H 1 + +#include +#include + +#ifdef __linux__ +# include /* necessary for: */ +# define __USE_GNU /* O_DIRECT and */ +# include /* IO operations */ +# undef __USE_GNU +#endif /* __linux__ */ + +/*#include */ +#include +#include "lustre/lustre_user.h" +#include "adio.h" +/*#include "adioi.h"*/ + +#ifdef HAVE_SIGNAL_H +#include +#endif + +#ifdef HAVE_AIO_H +#include +#ifdef HAVE_SYS_AIO_H +#include +#endif +#endif /* End of HAVE_SYS_AIO_H */ + +void ADIOI_LUSTRE_Open(ADIO_File fd, int *error_code); +void ADIOI_LUSTRE_Close(ADIO_File fd, int *error_code); +void ADIOI_LUSTRE_ReadContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code); +void ADIOI_LUSTRE_WriteContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code); +void ADIOI_LUSTRE_WriteStridedColl(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code); +void ADIOI_LUSTRE_ReadStridedColl(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code); +void ADIOI_LUSTRE_Fcntl(ADIO_File fd, int flag, ADIO_Fcntl_t *fcntl_struct, + int *error_code); +void ADIOI_LUSTRE_SetInfo(ADIO_File fd, MPI_Info users_info, int *error_code); + +#endif /* End of AD_UNIX_INCLUDE */ diff --git a/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_fcntl.c b/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_fcntl.c new file mode 100644 index 0000000000..739d3b00ce --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_fcntl.c @@ -0,0 +1,97 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + * + * Copyright (C) 2007 Oak Ridge National Laboratory + */ + +#include "ad_lustre.h" +#include "adio_extern.h" + +void ADIOI_LUSTRE_Fcntl(ADIO_File fd, int flag, ADIO_Fcntl_t *fcntl_struct, int *error_code) +{ + int i, ntimes; + ADIO_Offset curr_fsize, alloc_size, size, len, done; + ADIO_Status status; + char *buf; +#if defined(MPICH2) || !defined(PRINT_ERR_MSG) + static char myname[] = "ADIOI_LUSTRE_FCNTL"; +#endif + + switch(flag) { + case ADIO_FCNTL_GET_FSIZE: + fcntl_struct->fsize = lseek(fd->fd_sys, 0, SEEK_END); + if (fd->fp_sys_posn != -1) + lseek(fd->fd_sys, fd->fp_sys_posn, SEEK_SET); + if (fcntl_struct->fsize == -1) { + *error_code = MPIR_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, __LINE__, + MPI_ERR_IO, "**io", "**io %s", strerror(errno)); + } + else *error_code = MPI_SUCCESS; + break; + + case ADIO_FCNTL_SET_DISKSPACE: + /* will be called by one process only */ + /* On file systems with no preallocation function, I have to + explicitly write + to allocate space. Since there could be holes in the file, + I need to read up to the current file size, write it back, + and then write beyond that depending on how much + preallocation is needed. + read/write in sizes of no more than ADIOI_PREALLOC_BUFSZ */ + + curr_fsize = lseek(fd->fd_sys, 0, SEEK_END); + alloc_size = fcntl_struct->diskspace; + + size = ADIOI_MIN(curr_fsize, alloc_size); + + ntimes = (size + ADIOI_PREALLOC_BUFSZ - 1)/ADIOI_PREALLOC_BUFSZ; + buf = (char *) ADIOI_Malloc(ADIOI_PREALLOC_BUFSZ); + done = 0; + + for (i=0; i curr_fsize) { + memset(buf, 0, ADIOI_PREALLOC_BUFSZ); + size = alloc_size - curr_fsize; + ntimes = (size + ADIOI_PREALLOC_BUFSZ - 1)/ADIOI_PREALLOC_BUFSZ; + for (i=0; ifp_sys_posn != -1) + lseek(fd->fd_sys, fd->fp_sys_posn, SEEK_SET); + *error_code = MPI_SUCCESS; + break; + + case ADIO_FCNTL_SET_ATOMICITY: + fd->atomicity = (fcntl_struct->atomicity == 0) ? 0 : 1; + *error_code = MPI_SUCCESS; + break; + + default: + FPRINTF(stderr, "Unknown flag passed to ADIOI_LUSTRE_Fcntl\n"); + MPI_Abort(MPI_COMM_WORLD, 1); + } +} diff --git a/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_hints.c b/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_hints.c new file mode 100644 index 0000000000..817b5bd802 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_hints.c @@ -0,0 +1,140 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + * + * Copyright (C) 2007 Oak Ridge National Laboratory + */ + +#include "ad_lustre.h" +#include "adio_extern.h" + +void ADIOI_LUSTRE_SetInfo(ADIO_File fd, MPI_Info users_info, int *error_code) +{ + char *value, *value_in_fd; + int flag, tmp_val[3], str_factor=-1, str_unit=0, start_iodev=-1; + struct lov_user_md lum = { 0 }; + int err, myrank, fd_sys, perm, amode, old_mask; + + value = (char *) ADIOI_Malloc((MPI_MAX_INFO_VAL+1)*sizeof(char)); + if ( (fd->info) == MPI_INFO_NULL) { + /* This must be part of the open call. can set striping parameters + if necessary. */ + MPI_Info_create(&(fd->info)); + + MPI_Info_set(fd->info, "direct_read", "false"); + MPI_Info_set(fd->info, "direct_write", "false"); + fd->direct_read = fd->direct_write = 0; + + /* has user specified striping or server buffering parameters + and do they have the same value on all processes? */ + if (users_info != MPI_INFO_NULL) { + MPI_Info_get(users_info, "striping_unit", MPI_MAX_INFO_VAL, + value, &flag); + if (flag) + str_unit=atoi(value); + + MPI_Info_get(users_info, "striping_factor", MPI_MAX_INFO_VAL, + value, &flag); + if (flag) + str_factor=atoi(value); + + MPI_Info_get(users_info, "start_iodevice", MPI_MAX_INFO_VAL, + value, &flag); + if (flag) + start_iodev=atoi(value); + + MPI_Info_get(users_info, "direct_read", MPI_MAX_INFO_VAL, + value, &flag); + if (flag && (!strcmp(value, "true") || !strcmp(value, "TRUE"))) { + MPI_Info_set(fd->info, "direct_read", "true"); + fd->direct_read = 1; + } + + MPI_Info_get(users_info, "direct_write", MPI_MAX_INFO_VAL, + value, &flag); + if (flag && (!strcmp(value, "true") || !strcmp(value, "TRUE"))) { + MPI_Info_set(fd->info, "direct_write", "true"); + fd->direct_write = 1; + } + } + + MPI_Comm_rank(fd->comm, &myrank); + if (myrank == 0) { + tmp_val[0] = str_factor; + tmp_val[1] = str_unit; + tmp_val[2] = start_iodev; + } + MPI_Bcast(tmp_val, 3, MPI_INT, 0, fd->comm); + + if (tmp_val[0] != str_factor + || tmp_val[1] != str_unit + || tmp_val[2] != start_iodev) { + FPRINTF(stderr, "ADIOI_LUSTRE_SetInfo: All keys" + "-striping_factor:striping_unit:start_iodevice " + "need to be identical across all processes\n"); + MPI_Abort(MPI_COMM_WORLD, 1); + } else if ((str_factor > 0) || (str_unit > 0) || (start_iodev >= 0)) { + /* if user has specified striping info, process 0 tries to set it */ + if (!myrank) { + if (fd->perm == ADIO_PERM_NULL) { + old_mask = umask(022); + umask(old_mask); + perm = old_mask ^ 0666; + } + else perm = fd->perm; + + amode = 0; + if (fd->access_mode & ADIO_CREATE) + amode = amode | O_CREAT; + if (fd->access_mode & ADIO_RDONLY) + amode = amode | O_RDONLY; + if (fd->access_mode & ADIO_WRONLY) + amode = amode | O_WRONLY; + if (fd->access_mode & ADIO_RDWR) + amode = amode | O_RDWR; + if (fd->access_mode & ADIO_EXCL) + amode = amode | O_EXCL; + + /* we need to create file so ensure this is set */ + amode = amode | O_LOV_DELAY_CREATE | O_CREAT; + + fd_sys = open(fd->filename, amode, perm); + if (fd_sys == -1) { + if (errno != EEXIST) + fprintf(stderr, + "Failure to open file %s %d %d\n",strerror(errno), amode, perm); + } else { + lum.lmm_magic = LOV_USER_MAGIC; + lum.lmm_pattern = 0; + lum.lmm_stripe_size = str_unit; + lum.lmm_stripe_count = str_factor; + lum.lmm_stripe_offset = start_iodev; + + err = ioctl(fd_sys, LL_IOC_LOV_SETSTRIPE, &lum); + if (err == -1 && errno != EEXIST) { + fprintf(stderr, "Failure to set stripe info %s \n", strerror(errno)); + } + close(fd_sys); + } + } /* End of striping parameters validation */ + } + + MPI_Barrier(fd->comm); + /* set the values for collective I/O and data sieving parameters */ + ADIOI_GEN_SetInfo(fd, users_info, error_code); + } else { + /* The file has been opened previously and fd->fd_sys is a valid + file descriptor. cannot set striping parameters now. */ + + /* set the values for collective I/O and data sieving parameters */ + ADIOI_GEN_SetInfo(fd, users_info, error_code); + } + + if (ADIOI_Direct_read) fd->direct_read = 1; + if (ADIOI_Direct_write) fd->direct_write = 1; + + ADIOI_Free(value); + + *error_code = MPI_SUCCESS; +} diff --git a/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_open.c b/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_open.c new file mode 100644 index 0000000000..17622f6562 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_open.c @@ -0,0 +1,134 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + * + * Copyright (C) 2007 Oak Ridge National Laboratory + */ + +#include "ad_lustre.h" + +void ADIOI_LUSTRE_Open(ADIO_File fd, int *error_code) +{ + int perm, old_mask, amode, amode_direct; + struct lov_user_md lum = { 0 }; + char *value; + +#if defined(MPICH2) || !defined(PRINT_ERR_MSG) + static char myname[] = "ADIOI_LUSTRE_OPEN"; +#endif + + if (fd->perm == ADIO_PERM_NULL) { + old_mask = umask(022); + umask(old_mask); + perm = old_mask ^ 0666; + } + else perm = fd->perm; + + amode = 0; + if (fd->access_mode & ADIO_CREATE) + amode = amode | O_CREAT; + if (fd->access_mode & ADIO_RDONLY) + amode = amode | O_RDONLY; + if (fd->access_mode & ADIO_WRONLY) + amode = amode | O_WRONLY; + if (fd->access_mode & ADIO_RDWR) + amode = amode | O_RDWR; + if (fd->access_mode & ADIO_EXCL) + amode = amode | O_EXCL; + + amode_direct = amode | O_DIRECT; + + fd->fd_sys = open(fd->filename, amode|O_CREAT, perm); + + if (fd->fd_sys != -1) { + int err; + + value = (char *) ADIOI_Malloc((MPI_MAX_INFO_VAL+1)*sizeof(char)); + + /* get file striping information and set it in info */ + lum.lmm_magic = LOV_USER_MAGIC; + err = ioctl(fd->fd_sys, LL_IOC_LOV_GETSTRIPE, (void *) &lum); + + if (!err) { + sprintf(value, "%d", lum.lmm_stripe_size); + MPI_Info_set(fd->info, "striping_unit", value); + + sprintf(value, "%d", lum.lmm_stripe_count); + MPI_Info_set(fd->info, "striping_factor", value); + + sprintf(value, "%d", lum.lmm_stripe_offset); + MPI_Info_set(fd->info, "start_iodevice", value); + } + ADIOI_Free(value); + + if (fd->access_mode & ADIO_APPEND) + fd->fp_ind = fd->fp_sys_posn = lseek(fd->fd_sys, 0, SEEK_END); + } + + if ((fd->fd_sys != -1) && (fd->access_mode & ADIO_APPEND)) + fd->fp_ind = fd->fp_sys_posn = lseek(fd->fd_sys, 0, SEEK_END); + + fd->fd_direct = -1; + if (fd->direct_write || fd->direct_read) { + fd->fd_direct = open(fd->filename, amode_direct, perm); + if (fd->fd_direct != -1) { + fd->d_mem = fd->d_miniosz = (1<<12); + } else { + perror("cannot open file with O_Direct"); + fd->direct_write = fd->direct_read = 0; + } + } + + /* --BEGIN ERROR HANDLING-- */ + if (fd->fd_sys == -1 || ((fd->fd_direct == -1) && + (fd->direct_write || fd->direct_read))) { + if (errno == ENAMETOOLONG) + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_BAD_FILE, + "**filenamelong", + "**filenamelong %s %d", + fd->filename, + strlen(fd->filename)); + else if (errno == ENOENT) + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_NO_SUCH_FILE, + "**filenoexist", + "**filenoexist %s", + fd->filename); + else if (errno == ENOTDIR || errno == ELOOP) + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + myname, __LINE__, + MPI_ERR_BAD_FILE, + "**filenamedir", + "**filenamedir %s", + fd->filename); + else if (errno == EACCES) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_ACCESS, + "**fileaccess", + "**fileaccess %s", + fd->filename ); + } + else if (errno == EROFS) { + /* Read only file or file system and write access requested */ + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_READ_ONLY, + "**ioneedrd", 0 ); + } + else { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + } + } + /* --END ERROR HANDLING-- */ + else *error_code = MPI_SUCCESS; + +} diff --git a/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_rwcontig.c b/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_rwcontig.c new file mode 100644 index 0000000000..51f9357f65 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_lustre/ad_lustre_rwcontig.c @@ -0,0 +1,187 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + * + * Copyright (C) 2007 Oak Ridge National Laboratory + */ + +#define _XOPEN_SOURCE 600 +#include +#include +#include "ad_lustre.h" + +#define LUSTRE_MEMALIGN (1<<12) /* to use page_shift */ + +static void ADIOI_LUSTRE_Aligned_Mem_File_Write(ADIO_File fd, void *buf, int len, + ADIO_Offset offset, int *err); +static void ADIOI_LUSTRE_Aligned_Mem_File_Write(ADIO_File fd, void *buf, int len, + ADIO_Offset offset, int *err) +{ + int ntimes, rem, newrem, i, size, nbytes; + if (!(len % fd->d_miniosz) && (len >= fd->d_miniosz)) { + *err = pwrite(fd->fd_direct, buf, len, offset); + } else if (len < fd->d_miniosz) { + *err = pwrite(fd->fd_sys, buf, len, offset); + } else { + rem = len % fd->d_miniosz; + size = len - rem; + nbytes = pwrite(fd->fd_direct, buf, size, offset); + nbytes += pwrite(fd->fd_sys, ((char *)buf) + size, rem, offset+size); + *err = nbytes; + } +} + +static void ADIOI_LUSTRE_Aligned_Mem_File_Read(ADIO_File fd, void *buf, int len, + ADIO_Offset offset, int *err); +static void ADIOI_LUSTRE_Aligned_Mem_File_Read(ADIO_File fd, void *buf, int len, + ADIO_Offset offset, int *err) +{ + int ntimes, rem, newrem, i, size, nbytes; + if (!(len % fd->d_miniosz) && (len >= fd->d_miniosz)) + *err = pread(fd->fd_direct, buf, len, offset); + else if (len < fd->d_miniosz) + *err = pread(fd->fd_sys, buf, len, offset); + else { + rem = len % fd->d_miniosz; + size = len - rem; + nbytes = pread(fd->fd_direct, buf, size, offset); + nbytes += pread(fd->fd_sys, ((char *)buf) + size, rem, offset+size); + *err = nbytes; + } +} + + +static int ADIOI_LUSTRE_Directio(ADIO_File fd, void *buf, int len, + off_t offset, int rw); +static int ADIOI_LUSTRE_Directio(ADIO_File fd, void *buf, int len, + off_t offset, int rw) +{ + int err=-1, diff, size=len, nbytes = 0; + void *newbuf; + static char myname[] = "ADIOI_LUSTRE_Directio"; + + if (offset % fd->d_miniosz) { + diff = fd->d_miniosz - (offset % fd->d_miniosz); + diff = ADIOI_MIN(diff, len); + if (rw) + nbytes = pwrite(fd->fd_sys, buf, diff, offset); + else + nbytes = pread(fd->fd_sys, buf, diff, offset); + buf = ((char *) buf) + diff; + offset += diff; + size = len - diff; + } + + if (!size) { + return diff; + } + + if (rw) { /* direct I/O enabled */ + if (!(((long) buf) % fd->d_mem)) { + ADIOI_LUSTRE_Aligned_Mem_File_Write(fd, buf, size, offset, &err); + nbytes += err; + } else { + newbuf = (void *) memalign(LUSTRE_MEMALIGN, size); + if (newbuf) { + memcpy(newbuf, buf, size); + ADIOI_LUSTRE_Aligned_Mem_File_Write(fd, newbuf, size, offset, &err); + nbytes += err; + free(newbuf); + } + else nbytes += pwrite(fd->fd_sys, buf, size, offset); + } + err = nbytes; + } else { + if (!(((long) buf) % fd->d_mem)) { + ADIOI_LUSTRE_Aligned_Mem_File_Read(fd, buf, size, offset, &err); + nbytes += err; + } else { + newbuf = (void *) memalign(LUSTRE_MEMALIGN, size); + if (newbuf) { + ADIOI_LUSTRE_Aligned_Mem_File_Read(fd, newbuf, size, offset, &err); + if (err > 0) memcpy(buf, newbuf, err); + nbytes += err; + free(newbuf); + } + else nbytes += pread(fd->fd_sys, buf, size, offset); + } + err = nbytes; + } + return err; +} + +static void ADIOI_LUSTRE_IOContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, + int io_mode, int *error_code); +static void ADIOI_LUSTRE_IOContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, + int io_mode, int *error_code) +{ + int err=-1, datatype_size, len; + static char myname[] = "ADIOI_LUSTRE_IOCONTIG"; + + MPI_Type_size(datatype, &datatype_size); + len = datatype_size * count; + + if (file_ptr_type == ADIO_INDIVIDUAL) { + offset = fd->fp_ind; + } + + if (!(fd->direct_read || fd->direct_write)) { + if (fd->fp_sys_posn != offset) { + err = lseek(fd->fd_sys, offset, SEEK_SET); + if (err == -1) goto ioerr; + } + + if (io_mode) + err = write(fd->fd_sys, buf, len); + else + err = read(fd->fd_sys, buf, len); + } else { + err = ADIOI_LUSTRE_Directio(fd, buf, len, offset, io_mode); + } + + if (err == -1) goto ioerr; + fd->fp_sys_posn = offset + err; + + if (file_ptr_type == ADIO_INDIVIDUAL) { + fd->fp_ind += err; + } + +#ifdef HAVE_STATUS_SET_BYTES + if (status) MPIR_Status_set_bytes(status, datatype, err); +#endif + *error_code = MPI_SUCCESS; + +ioerr: + /* --BEGIN ERROR HANDLING-- */ + if (err == -1) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + myname, __LINE__, + MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + fd->fp_sys_posn = -1; + return; + } + /* --END ERROR HANDLING-- */ +} + +void ADIOI_LUSTRE_WriteContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int *error_code) +{ + ADIOI_LUSTRE_IOContig(fd, buf, count, datatype, file_ptr_type, + offset, status, 1, error_code); +} + +void ADIOI_LUSTRE_ReadContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int *error_code) +{ + ADIOI_LUSTRE_IOContig(fd, buf, count, datatype, file_ptr_type, + offset, status, 0, error_code); +} diff --git a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs.c b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs.c index 11ad2b4e77..cb505980db 100644 --- a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs.c +++ b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs.c @@ -22,8 +22,10 @@ struct ADIOI_Fns_struct ADIO_NFS_operations = { ADIOI_NFS_ReadStrided, /* ReadStrided */ ADIOI_NFS_WriteStrided, /* WriteStrided */ ADIOI_GEN_Close, /* Close */ - ADIOI_NFS_IreadContig, /* IreadContig */ - ADIOI_NFS_IwriteContig, /* IwriteContig */ + /* Even with lockd running and NFS mounted 'noac', we have been unable to + * gaurantee correct behavior over NFS with asyncronous I/O operations */ + ADIOI_FAKE_IreadContig, /* IreadContig */ + ADIOI_FAKE_IwriteContig, /* IwriteContig */ ADIOI_NFS_ReadDone, /* ReadDone */ ADIOI_NFS_WriteDone, /* WriteDone */ ADIOI_NFS_ReadComplete, /* ReadComplete */ diff --git a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs.h b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs.h index 4fd65e0c74..e6fdda2903 100644 --- a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs.h +++ b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs.h @@ -15,6 +15,9 @@ #ifdef HAVE_SIGNAL_H #include #endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif #ifdef HAVE_AIO_H #include #endif @@ -29,7 +32,7 @@ #endif int ADIOI_NFS_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, - int wr, void *handle); + int wr, MPI_Request *request); #ifdef SX4 #define lseek llseek diff --git a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_done.c b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_done.c index 30dbb975ec..79baf2ec86 100644 --- a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_done.c +++ b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_done.c @@ -9,119 +9,9 @@ int ADIOI_NFS_ReadDone(ADIO_Request *request, ADIO_Status *status, int *error_code) { -#ifdef ROMIO_HAVE_WORKING_AIO - int done=0; - int err; - static char myname[] = "ADIOI_NFS_READDONE"; -#ifdef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_HANDLE - struct aiocb *tmp1; -#endif -#endif - - if (*request == ADIO_REQUEST_NULL) { *error_code = MPI_SUCCESS; return 1; - } - -#ifndef ROMIO_HAVE_WORKING_AIO -# ifdef HAVE_STATUS_SET_BYTES - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -# endif - (*request)->fd->async_count--; - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; - *error_code = MPI_SUCCESS; - return 1; - -#else - -#ifndef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_FILDES -/* old IBM API */ - if ((*request)->queued) { - tmp1 = (struct aiocb *) (*request)->handle; - errno = aio_error(tmp1->aio_handle); - if (errno == EINPROG) { - done = 0; - *error_code = MPI_SUCCESS; - } - else { - err = aio_return(tmp1->aio_handle); - (*request)->nbytes = err; - errno = aio_error(tmp1->aio_handle); - - done = 1; - - if (err == -1) { - *error_code = MPIO_Err_create_code(MPI_SUCCESS, - MPIR_ERR_RECOVERABLE, - myname, __LINE__, - MPI_ERR_IO, "**io", - "**io %s", strerror(errno)); - } - else *error_code = MPI_SUCCESS; - } - } - else { - done = 1; - *error_code = MPI_SUCCESS; - } -#ifdef HAVE_STATUS_SET_BYTES - if (done && ((*request)->nbytes != -1)) - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - -#else -/* everything other than old IBM */ - if ((*request)->queued) { - errno = aio_error((const struct aiocb *) (*request)->handle); - if (errno == EINPROGRESS) { - done = 0; - *error_code = MPI_SUCCESS; - } - else { - err = aio_return((struct aiocb *) (*request)->handle); - (*request)->nbytes = err; - errno = aio_error((struct aiocb *) (*request)->handle); - - done = 1; - - if (err == -1) { - *error_code = MPIO_Err_create_code(MPI_SUCCESS, - MPIR_ERR_RECOVERABLE, - myname, __LINE__, - MPI_ERR_IO, "**io", - "**io %s", strerror(errno)); - } - else *error_code = MPI_SUCCESS; - } - } - else { - done = 1; - *error_code = MPI_SUCCESS; - } -#ifdef HAVE_STATUS_SET_BYTES - if (done && ((*request)->nbytes != -1)) - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - -#endif - - if (done) { - /* if request is still queued in the system, it is also there - on ADIOI_Async_list. Delete it from there. */ - if ((*request)->queued) ADIOI_Del_req_from_list(request); - - (*request)->fd->async_count--; - if ((*request)->handle) ADIOI_Free((*request)->handle); - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; - } - return done; -#endif - } - - int ADIOI_NFS_WriteDone(ADIO_Request *request, ADIO_Status *status, int *error_code) { diff --git a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_fcntl.c b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_fcntl.c index 2926ff474f..992c09bfb6 100644 --- a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_fcntl.c +++ b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_fcntl.c @@ -18,10 +18,22 @@ void ADIOI_NFS_Fcntl(ADIO_File fd, int flag, ADIO_Fcntl_t *fcntl_struct, int *er switch(flag) { case ADIO_FCNTL_GET_FSIZE: ADIOI_READ_LOCK(fd, 0, SEEK_SET, 1); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif fcntl_struct->fsize = lseek(fd->fd_sys, 0, SEEK_END); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif ADIOI_UNLOCK(fd, 0, SEEK_SET, 1); if (fd->fp_sys_posn != -1) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif lseek(fd->fd_sys, fd->fp_sys_posn, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif } if (fcntl_struct->fsize == -1) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, diff --git a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_getsh.c b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_getsh.c index a79c7ba427..60b62546c2 100644 --- a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_getsh.c +++ b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_getsh.c @@ -32,7 +32,13 @@ void ADIOI_NFS_Get_shared_fp(ADIO_File fd, int incr, ADIO_Offset *shared_fp, if (*error_code != MPI_SUCCESS) return; *shared_fp = 0; ADIOI_WRITE_LOCK(fd->shared_fp_fd, 0, SEEK_SET, sizeof(ADIO_Offset)); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err = read(fd->shared_fp_fd->fd_sys, shared_fp, sizeof(ADIO_Offset)); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif /* if the file is empty, the above read may return error (reading beyond end of file). In that case, shared_fp = 0, set above, is the correct value. */ @@ -40,10 +46,22 @@ void ADIOI_NFS_Get_shared_fp(ADIO_File fd, int incr, ADIO_Offset *shared_fp, else { ADIOI_WRITE_LOCK(fd->shared_fp_fd, 0, SEEK_SET, sizeof(ADIO_Offset)); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif err = lseek(fd->shared_fp_fd->fd_sys, 0, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif if (err == 0) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err = read(fd->shared_fp_fd->fd_sys, shared_fp, sizeof(ADIO_Offset)); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif } if (err == -1) { ADIOI_UNLOCK(fd->shared_fp_fd, 0, SEEK_SET, sizeof(ADIO_Offset)); @@ -57,9 +75,21 @@ void ADIOI_NFS_Get_shared_fp(ADIO_File fd, int incr, ADIO_Offset *shared_fp, new_fp = *shared_fp + incr; +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif err = lseek(fd->shared_fp_fd->fd_sys, 0, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif if (err == 0) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err = write(fd->shared_fp_fd->fd_sys, &new_fp, sizeof(ADIO_Offset)); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif } ADIOI_UNLOCK(fd->shared_fp_fd, 0, SEEK_SET, sizeof(ADIO_Offset)); if (err == -1) { diff --git a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_iread.c b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_iread.c index b768d63115..4896354a33 100644 --- a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_iread.c +++ b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_iread.c @@ -6,50 +6,24 @@ #include "ad_nfs.h" +#ifdef ROMIO_HAVE_WORKING_AIO +/* nearly identical to ADIOI_GEN_IreadContig, except we lock around I/O */ void ADIOI_NFS_IreadContig(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, ADIO_Offset offset, ADIO_Request *request, int *error_code) { int len, typesize; -#ifndef ROMIO_HAVE_WORKING_AIO - ADIO_Status status; -#else int aio_errno = 0; static char myname[] = "ADIOI_NFS_IREADCONTIG"; -#endif - - (*request) = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_READ; - (*request)->fd = fd; - (*request)->datatype = datatype; MPI_Type_size(datatype, &typesize); len = count * typesize; -#ifndef ROMIO_HAVE_WORKING_AIO - /* no support for nonblocking I/O. Use blocking I/O. */ - - ADIOI_NFS_ReadContig(fd, buf, len, MPI_BYTE, file_ptr_type, offset, - &status, error_code); - (*request)->queued = 0; -#ifdef HAVE_STATUS_SET_BYTES - if (*error_code == MPI_SUCCESS) { - MPI_Get_elements(&status, MPI_BYTE, &len); - (*request)->nbytes = len; - } -#endif - - fd->fp_sys_posn = -1; - -#else if (file_ptr_type == ADIO_INDIVIDUAL) offset = fd->fp_ind; - aio_errno = ADIOI_NFS_aio(fd, buf, len, offset, 0, &((*request)->handle)); + aio_errno = ADIOI_NFS_aio(fd, buf, len, offset, 0, request); if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind += len; - (*request)->queued = 1; - ADIOI_Add_req_to_list(request); - fd->fp_sys_posn = -1; if (aio_errno != 0) { @@ -59,7 +33,5 @@ void ADIOI_NFS_IreadContig(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ } else *error_code = MPI_SUCCESS; -#endif - - fd->async_count++; } +#endif diff --git a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_iwrite.c b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_iwrite.c index 5ad0674a2e..4fd4677eff 100644 --- a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_iwrite.c +++ b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_iwrite.c @@ -6,51 +6,31 @@ #include "ad_nfs.h" +#include "../../mpi-io/mpioimpl.h" +#include "../../mpi-io/mpioprof.h" +#include "mpiu_greq.h" + +#include + +#ifdef ROMIO_HAVE_WORKING_AIO +static MPIX_Grequest_class ADIOI_GEN_greq_class = 0; +/* this routine is nearly identical to ADIOI_GEN_IwriteContig, except we lock + * around I/O */ void ADIOI_NFS_IwriteContig(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, ADIO_Offset offset, ADIO_Request *request, int *error_code) { int len, typesize; -#ifndef ROMIO_HAVE_WORKING_AIO - ADIO_Status status; -#else int aio_errno = 0; static char myname[] = "ADIOI_NFS_IWRITECONTIG"; -#endif - - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_WRITE; - (*request)->fd = fd; - (*request)->datatype = datatype; MPI_Type_size(datatype, &typesize); len = count * typesize; -#ifndef ROMIO_HAVE_WORKING_AIO - /* HP, FreeBSD, Linux */ - /* no support for nonblocking I/O. Use blocking I/O. */ - - ADIOI_NFS_WriteContig(fd, buf, len, MPI_BYTE, file_ptr_type, offset, - &status, - error_code); - (*request)->queued = 0; -#ifdef HAVE_STATUS_SET_BYTES - if (*error_code == MPI_SUCCESS) { - MPI_Get_elements(&status, MPI_BYTE, &len); - (*request)->nbytes = len; - } -#endif - - fd->fp_sys_posn = -1; - -#else if (file_ptr_type == ADIO_INDIVIDUAL) offset = fd->fp_ind; - aio_errno = ADIOI_NFS_aio(fd, buf, len, offset, 1, &((*request)->handle)); + aio_errno = ADIOI_NFS_aio(fd, buf, len, offset, 1, request); if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind += len; - (*request)->queued = 1; - ADIOI_Add_req_to_list(request); - fd->fp_sys_posn = -1; if (aio_errno != 0) { @@ -60,11 +40,9 @@ void ADIOI_NFS_IwriteContig(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ } else *error_code = MPI_SUCCESS; -#endif - - fd->async_count++; + return; } - +#endif /* This function is for implementation convenience. It is not user-visible. * It takes care of the differences in the interface for nonblocking I/O @@ -74,21 +52,22 @@ void ADIOI_NFS_IwriteContig(ADIO_File fd, void *buf, int count, */ #ifdef ROMIO_HAVE_WORKING_AIO int ADIOI_NFS_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, - int wr, void *handle) + int wr, MPI_Request *request) { int err=-1, fd_sys; int error_code, this_errno; struct aiocb *aiocbp; - + ADIOI_AIO_Request *aio_req; + fd_sys = fd->fd_sys; + aio_req = (ADIOI_AIO_Request*)ADIOI_Calloc(sizeof(ADIOI_AIO_Request), 1); aiocbp = (struct aiocb *) ADIOI_Calloc(sizeof(struct aiocb), 1); aiocbp->aio_offset = offset; aiocbp->aio_buf = buf; aiocbp->aio_nbytes = len; - /* This madness is mostly here to deal with IBM AIO implementation */ #ifdef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_WHENCE aiocbp->aio_whence = SEEK_SET; #endif @@ -112,7 +91,7 @@ int ADIOI_NFS_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, if (wr) ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); else ADIOI_READ_LOCK(fd, offset, SEEK_SET, len); -#ifdef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_FILDES +#ifndef ROMIO_HAVE_AIO_CALLS_NEED_FILEDES if (wr) err = aio_write(aiocbp); else err = aio_read(aiocbp); #else @@ -128,43 +107,23 @@ int ADIOI_NFS_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, if (this_errno == EAGAIN) { /* exceeded the max. no. of outstanding requests. complete all previous async. requests and try again. */ - - ADIOI_Complete_async(&error_code); - if (error_code != MPI_SUCCESS) return -EIO; - - while (err == -1 && this_errno == EAGAIN) { - - if (wr) ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); - else ADIOI_READ_LOCK(fd, offset, SEEK_SET, len); - -#ifdef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_FILDES - if (wr) err = aio_write(aiocbp); - else err = aio_read(aiocbp); -#else - /* Broken IBM interface */ - if (wr) err = aio_write(fd_sys, aiocbp); - else err = aio_read(fd_sys, aiocbp); -#endif - this_errno = errno; - ADIOI_UNLOCK(fd, offset, SEEK_SET, len); - - if (err == -1 && this_errno == EAGAIN) { - /* sleep and try again */ - sleep(1); - } - else if (err == -1) { - /* real error */ - return -errno; - } - } - } - else { + ADIO_WriteContig(fd, buf, len, MPI_BYTE, ADIO_EXPLICIT_OFFSET, + offset, NULL, &error_code); + MPIO_Completed_request_create(&fd, len, &error_code, request); + return 0; + } else { return -this_errno; } } - - *((struct aiocb **) handle) = aiocbp; - + aio_req->aiocbp = aiocbp; + if (ADIOI_GEN_greq_class == 0) { + MPIX_Grequest_class_create(ADIOI_GEN_aio_query_fn, + ADIOI_GEN_aio_free_fn, MPIU_Greq_cancel_fn, + ADIOI_GEN_aio_poll_fn, ADIOI_GEN_aio_wait_fn, + &ADIOI_GEN_greq_class); + } + MPIX_Grequest_class_allocate(ADIOI_GEN_greq_class, aio_req, request); + memcpy(&(aio_req->req), request, sizeof(MPI_Request)); return 0; } #endif diff --git a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_open.c b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_open.c index d6778b0370..399dc27663 100644 --- a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_open.c +++ b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_open.c @@ -32,11 +32,24 @@ void ADIOI_NFS_Open(ADIO_File fd, int *error_code) if (fd->access_mode & ADIO_EXCL) amode = amode | O_EXCL; +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_open_a, 0, NULL ); +#endif fd->fd_sys = open(fd->filename, amode, perm); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_open_b, 0, NULL ); +#endif fd->fd_direct = -1; - if ((fd->fd_sys != -1) && (fd->access_mode & ADIO_APPEND)) + if ((fd->fd_sys != -1) && (fd->access_mode & ADIO_APPEND)) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif fd->fp_ind = fd->fp_sys_posn = lseek(fd->fd_sys, 0, SEEK_END); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } if (fd->fd_sys == -1) { /* Check for special error codes for those MPI error diff --git a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_read.c b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_read.c index fa741578b1..cf8f01895d 100644 --- a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_read.c +++ b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_read.c @@ -19,24 +19,50 @@ void ADIOI_NFS_ReadContig(ADIO_File fd, void *buf, int count, len = datatype_size * count; if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { - if (fd->fp_sys_posn != offset) + if (fd->fp_sys_posn != offset) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif lseek(fd->fd_sys, offset, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } if (fd->atomicity) ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); else ADIOI_READ_LOCK(fd, offset, SEEK_SET, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err = read(fd->fd_sys, buf, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif ADIOI_UNLOCK(fd, offset, SEEK_SET, len); fd->fp_sys_posn = offset + err; /* individual file pointer not updated */ } else { /* read from curr. location of ind. file pointer */ offset = fd->fp_ind; - if (fd->fp_sys_posn != fd->fp_ind) + if (fd->fp_sys_posn != fd->fp_ind) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif lseek(fd->fd_sys, fd->fp_ind, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } if (fd->atomicity) ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); else ADIOI_READ_LOCK(fd, offset, SEEK_SET, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err = read(fd->fd_sys, buf, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif ADIOI_UNLOCK(fd, offset, SEEK_SET, len); fd->fp_ind += err; fd->fp_sys_posn = fd->fp_ind; @@ -60,6 +86,46 @@ void ADIOI_NFS_ReadContig(ADIO_File fd, void *buf, int count, +#ifdef ADIOI_MPE_LOGGING +#define ADIOI_BUFFERED_READ \ +{ \ + if (req_off >= readbuf_off + readbuf_len) { \ + readbuf_off = req_off; \ + readbuf_len = (int) (ADIOI_MIN(max_bufsize, end_offset-readbuf_off+1));\ + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); \ + lseek(fd->fd_sys, readbuf_off, SEEK_SET);\ + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); \ + if (!(fd->atomicity)) ADIOI_READ_LOCK(fd, readbuf_off, SEEK_SET, readbuf_len);\ + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); \ + err = read(fd->fd_sys, readbuf, readbuf_len);\ + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); \ + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, readbuf_off, SEEK_SET, readbuf_len);\ + if (err == -1) err_flag = 1; \ + } \ + while (req_len > readbuf_off + readbuf_len - req_off) { \ + partial_read = (int) (readbuf_off + readbuf_len - req_off); \ + tmp_buf = (char *) ADIOI_Malloc(partial_read); \ + memcpy(tmp_buf, readbuf+readbuf_len-partial_read, partial_read); \ + ADIOI_Free(readbuf); \ + readbuf = (char *) ADIOI_Malloc(partial_read + max_bufsize); \ + memcpy(readbuf, tmp_buf, partial_read); \ + ADIOI_Free(tmp_buf); \ + readbuf_off += readbuf_len-partial_read; \ + readbuf_len = (int) (partial_read + ADIOI_MIN(max_bufsize, \ + end_offset-readbuf_off+1)); \ + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); \ + lseek(fd->fd_sys, readbuf_off+partial_read, SEEK_SET);\ + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); \ + if (!(fd->atomicity)) ADIOI_READ_LOCK(fd, readbuf_off+partial_read, SEEK_SET, readbuf_len-partial_read);\ + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); \ + err = read(fd->fd_sys, readbuf+partial_read, readbuf_len-partial_read);\ + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); \ + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, readbuf_off+partial_read, SEEK_SET, readbuf_len-partial_read);\ + if (err == -1) err_flag = 1; \ + } \ + memcpy((char *)buf + userbuf_off, readbuf+req_off-readbuf_off, req_len); \ +} +#else #define ADIOI_BUFFERED_READ \ { \ if (req_off >= readbuf_off + readbuf_len) { \ @@ -90,6 +156,7 @@ void ADIOI_NFS_ReadContig(ADIO_File fd, void *buf, int count, } \ memcpy((char *)buf + userbuf_off, readbuf+req_off-readbuf_off, req_len); \ } +#endif void ADIOI_NFS_ReadStrided(ADIO_File fd, void *buf, int count, @@ -160,9 +227,21 @@ void ADIOI_NFS_ReadStrided(ADIO_File fd, void *buf, int count, if (fd->atomicity) ADIOI_WRITE_LOCK(fd, start_off, SEEK_SET, end_offset-start_off+1); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif lseek(fd->fd_sys, readbuf_off, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif if (!(fd->atomicity)) ADIOI_READ_LOCK(fd, readbuf_off, SEEK_SET, readbuf_len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err = read(fd->fd_sys, readbuf, readbuf_len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif if (!(fd->atomicity)) ADIOI_UNLOCK(fd, readbuf_off, SEEK_SET, readbuf_len); if (err == -1) err_flag = 1; @@ -274,9 +353,21 @@ void ADIOI_NFS_ReadStrided(ADIO_File fd, void *buf, int count, readbuf = (char *) ADIOI_Malloc(max_bufsize); readbuf_len = (int) (ADIOI_MIN(max_bufsize, end_offset-readbuf_off+1)); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif lseek(fd->fd_sys, offset, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif if (!(fd->atomicity)) ADIOI_READ_LOCK(fd, offset, SEEK_SET, readbuf_len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err = read(fd->fd_sys, readbuf, readbuf_len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif if (!(fd->atomicity)) ADIOI_UNLOCK(fd, offset, SEEK_SET, readbuf_len); if (err == -1) err_flag = 1; diff --git a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_setsh.c b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_setsh.c index 816d7cdcb2..a0beff77da 100644 --- a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_setsh.c +++ b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_setsh.c @@ -47,8 +47,20 @@ void ADIOI_NFS_Set_shared_fp(ADIO_File fd, ADIO_Offset offset, int *error_code) if (*error_code != MPI_SUCCESS) return; ADIOI_WRITE_LOCK(fd->shared_fp_fd, 0, SEEK_SET, sizeof(ADIO_Offset)); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif lseek(fd->shared_fp_fd->fd_sys, 0, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err = write(fd->shared_fp_fd->fd_sys, &offset, sizeof(ADIO_Offset)); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif ADIOI_UNLOCK(fd->shared_fp_fd, 0, SEEK_SET, sizeof(ADIO_Offset)); if (err == -1) { diff --git a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_wait.c b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_wait.c index bafe394191..ad8da83d78 100644 --- a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_wait.c +++ b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_wait.c @@ -9,6 +9,8 @@ void ADIOI_NFS_ReadComplete(ADIO_Request *request, ADIO_Status *status, int *error_code) { + return; +#if 0 #ifdef ROMIO_HAVE_WORKING_AIO int err; static char myname[] = "ADIOI_NFS_READCOMPLETE"; @@ -121,6 +123,7 @@ void ADIOI_NFS_ReadComplete(ADIO_Request *request, ADIO_Status *status, *request = ADIO_REQUEST_NULL; *error_code = MPI_SUCCESS; #endif +#endif } diff --git a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_write.c b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_write.c index aa13a42f10..f856685e59 100644 --- a/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_write.c +++ b/ompi/mca/io/romio/romio/adio/ad_nfs/ad_nfs_write.c @@ -19,20 +19,46 @@ void ADIOI_NFS_WriteContig(ADIO_File fd, void *buf, int count, len = datatype_size * count; if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { - if (fd->fp_sys_posn != offset) + if (fd->fp_sys_posn != offset) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif lseek(fd->fd_sys, offset, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err = write(fd->fd_sys, buf, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif ADIOI_UNLOCK(fd, offset, SEEK_SET, len); fd->fp_sys_posn = offset + err; /* individual file pointer not updated */ } else { /* write from curr. location of ind. file pointer */ offset = fd->fp_ind; - if (fd->fp_sys_posn != fd->fp_ind) + if (fd->fp_sys_posn != fd->fp_ind) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif lseek(fd->fd_sys, fd->fp_ind, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } ADIOI_WRITE_LOCK(fd, offset, SEEK_SET, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err = write(fd->fd_sys, buf, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif ADIOI_UNLOCK(fd, offset, SEEK_SET, len); fd->fp_ind += err; fd->fp_sys_posn = fd->fp_ind; @@ -58,6 +84,69 @@ void ADIOI_NFS_WriteContig(ADIO_File fd, void *buf, int count, +#ifdef ADIOI_MPE_LOGGING +#define ADIOI_BUFFERED_WRITE \ +{ \ + if (req_off >= writebuf_off + writebuf_len) { \ + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); \ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); \ + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); \ + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); \ + err = write(fd->fd_sys, writebuf, writebuf_len); \ + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); \ + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + if (err == -1) err_flag = 1; \ + writebuf_off = req_off; \ + writebuf_len = (int) (ADIOI_MIN(max_bufsize,end_offset-writebuf_off+1));\ + if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); \ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); \ + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); \ + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); \ + err = read(fd->fd_sys, writebuf, writebuf_len); \ + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); \ + if (err == -1) { \ + *error_code = MPIO_Err_create_code(MPI_SUCCESS, \ + MPIR_ERR_RECOVERABLE, myname, \ + __LINE__, MPI_ERR_IO, \ + "**ioRMWrdwr", 0); \ + return; \ + } \ + } \ + write_sz = (int) (ADIOI_MIN(req_len, writebuf_off + writebuf_len - req_off)); \ + memcpy(writebuf+req_off-writebuf_off, (char *)buf +userbuf_off, write_sz);\ + while (write_sz != req_len) { \ + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); \ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); \ + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); \ + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); \ + err = write(fd->fd_sys, writebuf, writebuf_len); \ + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); \ + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + if (err == -1) err_flag = 1; \ + req_len -= write_sz; \ + userbuf_off += write_sz; \ + writebuf_off += writebuf_len; \ + writebuf_len = (int) (ADIOI_MIN(max_bufsize,end_offset-writebuf_off+1));\ + if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); \ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); \ + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); \ + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); \ + err = read(fd->fd_sys, writebuf, writebuf_len); \ + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); \ + if (err == -1) { \ + *error_code = MPIO_Err_create_code(MPI_SUCCESS, \ + MPIR_ERR_RECOVERABLE, myname, \ + __LINE__, MPI_ERR_IO, \ + "**ioRMWrdwr", 0); \ + return; \ + } \ + write_sz = ADIOI_MIN(req_len, writebuf_len); \ + memcpy(writebuf, (char *)buf + userbuf_off, write_sz);\ + } \ +} +#else #define ADIOI_BUFFERED_WRITE \ { \ if (req_off >= writebuf_off + writebuf_len) { \ @@ -103,10 +192,47 @@ void ADIOI_NFS_WriteContig(ADIO_File fd, void *buf, int count, memcpy(writebuf, (char *)buf + userbuf_off, write_sz);\ } \ } - +#endif /* this macro is used when filetype is contig and buftype is not contig. it does not do a read-modify-write and does not lock*/ +#ifdef ADIOI_MPE_LOGGING +#define ADIOI_BUFFERED_WRITE_WITHOUT_READ \ +{ \ + if (req_off >= writebuf_off + writebuf_len) { \ + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); \ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); \ + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); \ + if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); \ + err = write(fd->fd_sys, writebuf, writebuf_len); \ + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); \ + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + if (err == -1) err_flag = 1; \ + writebuf_off = req_off; \ + writebuf_len = (int) (ADIOI_MIN(max_bufsize,end_offset-writebuf_off+1));\ + } \ + write_sz = (int) (ADIOI_MIN(req_len, writebuf_off + writebuf_len - req_off)); \ + memcpy(writebuf+req_off-writebuf_off, (char *)buf +userbuf_off, write_sz);\ + while (write_sz != req_len) { \ + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); \ + lseek(fd->fd_sys, writebuf_off, SEEK_SET); \ + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); \ + if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); \ + err = write(fd->fd_sys, writebuf, writebuf_len); \ + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); \ + if (!(fd->atomicity)) ADIOI_UNLOCK(fd, writebuf_off, SEEK_SET, writebuf_len); \ + if (err == -1) err_flag = 1; \ + req_len -= write_sz; \ + userbuf_off += write_sz; \ + writebuf_off += writebuf_len; \ + writebuf_len = (int) (ADIOI_MIN(max_bufsize,end_offset-writebuf_off+1));\ + write_sz = ADIOI_MIN(req_len, writebuf_len); \ + memcpy(writebuf, (char *)buf + userbuf_off, write_sz);\ + } \ +} +#else #define ADIOI_BUFFERED_WRITE_WITHOUT_READ \ { \ if (req_off >= writebuf_off + writebuf_len) { \ @@ -134,7 +260,7 @@ void ADIOI_NFS_WriteContig(ADIO_File fd, void *buf, int count, memcpy(writebuf, (char *)buf + userbuf_off, write_sz);\ } \ } - +#endif void ADIOI_NFS_WriteStrided(ADIO_File fd, void *buf, int count, @@ -214,9 +340,21 @@ void ADIOI_NFS_WriteStrided(ADIO_File fd, void *buf, int count, } /* write the buffer out finally */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif lseek(fd->fd_sys, writebuf_off, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err = write(fd->fd_sys, writebuf, writebuf_len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif if (!(fd->atomicity)) ADIOI_UNLOCK(fd, writebuf_off, SEEK_SET, writebuf_len); if (err == -1) err_flag = 1; @@ -318,8 +456,20 @@ void ADIOI_NFS_WriteStrided(ADIO_File fd, void *buf, int count, writebuf = (char *) ADIOI_Malloc(max_bufsize); writebuf_len = (int)(ADIOI_MIN(max_bufsize,end_offset-writebuf_off+1)); if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif lseek(fd->fd_sys, writebuf_off, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err = read(fd->fd_sys, writebuf, writebuf_len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif if (err == -1) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, @@ -438,9 +588,21 @@ void ADIOI_NFS_WriteStrided(ADIO_File fd, void *buf, int count, } /* write the buffer out finally */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif lseek(fd->fd_sys, writebuf_off, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif if (!(fd->atomicity)) ADIOI_WRITE_LOCK(fd, writebuf_off, SEEK_SET, writebuf_len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err = write(fd->fd_sys, writebuf, writebuf_len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif if (!(fd->atomicity)) ADIOI_UNLOCK(fd, writebuf_off, SEEK_SET, writebuf_len); diff --git a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs.h b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs.h index fe62cc5dd9..836d1401ed 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs.h +++ b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs.h @@ -61,6 +61,8 @@ void ADIOI_NTFS_IwriteStrided(ADIO_File fd, void *buf, int count, void ADIOI_NTFS_Flush(ADIO_File fd, int *error_code); void ADIOI_NTFS_Resize(ADIO_File fd, ADIO_Offset size, int *error_code); -const char * ADIOI_NTFS_Strerror(int error); +#define FORMAT_MESSAGE_MIN_SIZE 100 +#define ADIOI_NTFS_ERR_MSG_MAX FORMAT_MESSAGE_MIN_SIZE +void ADIOI_NTFS_Strerror(int error, char *errMsg, int errMsgLen); #endif diff --git a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_close.c b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_close.c index 6dc2691f98..37994b852b 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_close.c +++ b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_close.c @@ -16,11 +16,13 @@ void ADIOI_NTFS_Close(ADIO_File fd, int *error_code) /* --BEGIN ERROR HANDLING-- */ if (err == FALSE) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); return; } /* --END ERROR HANDLING-- */ diff --git a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_done.c b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_done.c index 9cd5c3cf2c..ab2bbb7c21 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_done.c +++ b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_done.c @@ -9,92 +9,12 @@ int ADIOI_NTFS_ReadDone(ADIO_Request *request, ADIO_Status *status, int *error_code) { - DWORD ret_val; - int done = 0; - static char myname[] = "ADIOI_NTFS_ReadDone"; - - if (*request == ADIO_REQUEST_NULL) - { - *error_code = MPI_SUCCESS; - return 1; - } - - if ((*request)->queued) - { - (*request)->nbytes = 0; - ret_val = GetOverlappedResult((*request)->fd, (*request)->handle, &(*request)->nbytes, FALSE); - - if (!ret_val) - { - /* --BEGIN ERROR HANDLING-- */ - ret_val = GetLastError(); - if (ret_val == ERROR_IO_INCOMPLETE) - { - done = 0; - *error_code = MPI_SUCCESS; - } - else - { - *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, - myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(ret_val)); - } - /* --END ERROR HANDLING-- */ - } - else - { - done = 1; - *error_code = MPI_SUCCESS; - } - } - else - { - done = 1; - *error_code = MPI_SUCCESS; - } -#ifdef HAVE_STATUS_SET_BYTES - if (done && ((*request)->nbytes != -1)) - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - - if (done) - { - /* if request is still queued in the system, it is also there - on ADIOI_Async_list. Delete it from there. */ - if ((*request)->queued) ADIOI_Del_req_from_list(request); - - (*request)->fd->async_count--; - if ((*request)->handle) - { - if (!CloseHandle(((OVERLAPPED*)((*request)->handle))->hEvent)) - { - ret_val = GetLastError(); - *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, - myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(ret_val)); - } - ADIOI_Free((*request)->handle); - } - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; - } - return done; + return 0; } int ADIOI_NTFS_WriteDone(ADIO_Request *request, ADIO_Status *status, int *error_code) { - static char myname[] = "ADIOI_NTFS_WriteDone"; - int ret_val; - ret_val = ADIOI_NTFS_ReadDone(request, status, error_code); - /* --BEGIN ERROR HANDLING-- */ - if (*error_code != MPI_SUCCESS) - { - *error_code = MPIO_Err_create_code(*error_code, MPIR_ERR_RECOVERABLE, - myname, __LINE__, MPI_ERR_IO, - "**io", 0); - } - /* --END ERROR HANDLING-- */ - return ret_val; + return 0; } diff --git a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_fcntl.c b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_fcntl.c index 711aac7c60..5841475bd0 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_fcntl.c +++ b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_fcntl.c @@ -22,12 +22,14 @@ void ADIOI_NTFS_Fcntl(ADIO_File fd, int flag, ADIO_Fcntl_t *fcntl_struct, int *e dwTemp = DWORDHIGH(fd->fp_sys_posn); if (SetFilePointer(fd->fd_sys, DWORDLOW(fd->fp_sys_posn), &dwTemp, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); if (err != NO_ERROR) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); + "**io", "**io %s", errMsg); return; } } @@ -35,11 +37,13 @@ void ADIOI_NTFS_Fcntl(ADIO_File fd, int flag, ADIO_Fcntl_t *fcntl_struct, int *e /* --BEGIN ERROR HANDLING-- */ if (fcntl_struct->fsize == INVALID_SET_FILE_POINTER) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; dwTemp = GetLastError(); + ADIOI_NTFS_Strerror(dwTemp, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(dwTemp)); + "**io %s", errMsg); return; } /* --END ERROR HANDLING-- */ diff --git a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_flush.c b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_flush.c index 0bd14a0702..ecabf1d5a9 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_flush.c +++ b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_flush.c @@ -18,11 +18,13 @@ void ADIOI_NTFS_Flush(ADIO_File fd, int *error_code) /* --BEGIN ERROR HANDLING-- */ if (err == FALSE) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); return; } /* --END ERROR HANDLING-- */ diff --git a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_iread.c b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_iread.c index 1a1ef06703..174f6447cb 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_iread.c +++ b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_iread.c @@ -14,18 +14,6 @@ void ADIOI_NTFS_IreadContig(ADIO_File fd, void *buf, int count, int err; static char myname[] = "ADIOI_NTFS_IreadContig"; - (*request) = ADIOI_Malloc_request(); - if ((*request) == NULL) - { - *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, - myname, __LINE__, MPI_ERR_IO, - "**nomem", "**nomem %s", "ADIOI_Request"); - return; - } - (*request)->optype = ADIOI_READ; - (*request)->fd = fd; - (*request)->datatype = datatype; - MPI_Type_size(datatype, &typesize); len = count * typesize; @@ -33,15 +21,12 @@ void ADIOI_NTFS_IreadContig(ADIO_File fd, void *buf, int count, { offset = fd->fp_ind; } - err = ADIOI_NTFS_aio(fd, buf, len, offset, 0, &((*request)->handle)); + err = ADIOI_NTFS_aio(fd, buf, len, offset, 0, request); if (file_ptr_type == ADIO_INDIVIDUAL) { fd->fp_ind += len; } - (*request)->queued = 1; - ADIOI_Add_req_to_list(request); - /* --BEGIN ERROR HANDLING-- */ if (err != MPI_SUCCESS) { @@ -54,5 +39,4 @@ void ADIOI_NTFS_IreadContig(ADIO_File fd, void *buf, int count, *error_code = MPI_SUCCESS; fd->fp_sys_posn = -1; /* set it to null. */ - fd->async_count++; } diff --git a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_iwrite.c b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_iwrite.c index 1bc74471e1..33ce0f1acd 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_iwrite.c +++ b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_iwrite.c @@ -6,6 +6,171 @@ #include "ad_ntfs.h" +#include "../../mpi-io/mpioimpl.h" +#include "../../mpi-io/mpioprof.h" +#include "mpiu_greq.h" + +static MPIX_Grequest_class ADIOI_NTFS_greq_class = 0; + +/* Fills the input buffer, errMsg, with the error message + corresponding to error code, error */ +void ADIOI_NTFS_Strerror(int error, char *errMsg, int errMsgLen) +{ + LPTSTR str; + int num_bytes; + num_bytes = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, + error, + 0, + &str, + FORMAT_MESSAGE_MIN_SIZE, + 0); + if (num_bytes == 0) + { + strncpy(errMsg, "\0", errMsgLen); + } + else + { + strncpy(errMsg, str, errMsgLen); + LocalFree(str); + } +} + +/* poll for completion of a single outstanding AIO request */ +int ADIOI_NTFS_aio_poll_fn(void *extra_state, MPI_Status *status) +{ + ADIOI_AIO_Request *aio_req; + int mpi_errno = MPI_SUCCESS; + + /* FIXME: Validate the args -- has it already been done by the + caller ? */ + + aio_req = (ADIOI_AIO_Request *)extra_state; + + /* XXX: test for AIO completion here */ + if(!GetOverlappedResult( aio_req->fd, aio_req->lpOvl, + &(aio_req->nbytes), FALSE)){ + if(GetLastError() == ERROR_IO_INCOMPLETE){ + /* IO in progress */ + /* TODO: need to diddle with status somehow */ + }else{ + /* Error occured */ + /* TODO: unsure how to handle this */ + } + }else{ + MPIR_Nest_incr(); + mpi_errno = MPI_Grequest_complete(aio_req->req); + if (mpi_errno != MPI_SUCCESS) { + mpi_errno = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + "ADIOI_NTFS_aio_poll_fn", __LINE__, + MPI_ERR_IO, "**mpi_grequest_complete", + 0); + } + MPIR_Nest_decr(); + } + return mpi_errno; +} + + +/* Wait for completion of one of the outstanding AIO requests */ +int ADIOI_NTFS_aio_wait_fn(int count, void **array_of_states, + double timeout, MPI_Status *status) +{ + int i, mpi_errno = MPI_SUCCESS; + ADIOI_AIO_Request **aio_reqlist; + LPHANDLE lpHandles; + DWORD retObject=0; + + /* FIXME: Validate the args -- has it already been done by the + caller ? */ + aio_reqlist = (ADIOI_AIO_Request **)array_of_states; + lpHandles = (LPHANDLE) ADIOI_Calloc(count, sizeof(HANDLE)); + if (lpHandles == NULL) + { + mpi_errno = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, + "ADIOI_NTFS_aio_wait_fn", __LINE__, MPI_ERR_IO, + "**nomem", "**nomem %s", "Event handles"); + return mpi_errno; + } + /* XXX: set-up arrays of outstanding requests */ + for(i=0; ilpOvl->hEvent; + } + + /* XXX: wait for one request to complete */ + /* FIXME: Is the timeout in seconds ? */ + timeout = (timeout <= 0) ? INFINITE : (timeout * 1000); + + if((retObject = WaitForMultipleObjects(count, lpHandles, + FALSE, timeout)) != WAIT_FAILED){ + retObject = retObject - WAIT_OBJECT_0; + if(GetOverlappedResult( aio_reqlist[retObject]->fd, + aio_reqlist[retObject]->lpOvl, &(aio_reqlist[retObject]->nbytes), + FALSE)){ + /* XXX: mark completed requests as 'done'*/ + MPIR_Nest_incr(); + mpi_errno = MPI_Grequest_complete(aio_reqlist[retObject]->req); + if (mpi_errno != MPI_SUCCESS) { + mpi_errno = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + "ADIOI_NTFS_aio_wait_fn", __LINE__, + MPI_ERR_IO, "**mpi_grequest_complete", + 0); + } + MPIR_Nest_decr(); + }else{ + if(GetLastError() == ERROR_IO_INCOMPLETE){ + /* IO in progress */ + /* TODO: need to diddle with status somehow */ + }else{ + /* Error occured */ + /* TODO: not sure how to handle this */ + } + } + }else{ + /* TODO: How to handle error while waiting ? */ + } + ADIOI_Free(lpHandles); + return mpi_errno; +} + +int ADIOI_NTFS_aio_query_fn(void *extra_state, MPI_Status *status) +{ + ADIOI_AIO_Request *aio_req; + + aio_req = (ADIOI_AIO_Request *)extra_state; + + + MPI_Status_set_elements(status, MPI_BYTE, aio_req->nbytes); + + /* do i need to nest_incr/nest_decr here? */ + /* can never cancel so always true */ + MPI_Status_set_cancelled(status, 0); + + /* choose not to return a value for this */ + status->MPI_SOURCE = MPI_UNDEFINED; + /* tag has no meaning for this generalized request */ + status->MPI_TAG = MPI_UNDEFINED; + /* this generalized request never fails */ + return MPI_SUCCESS; +} + + +int ADIOI_NTFS_aio_free_fn(void *extra_state) +{ + ADIOI_AIO_Request *aio_req; + /* FIXME: Validate the args -- has it already been done by the + caller ? */ + aio_req = (ADIOI_AIO_Request*)extra_state; + CloseHandle(aio_req->lpOvl->hEvent); + ADIOI_Free(aio_req->lpOvl); + ADIOI_Free(aio_req); + return MPI_SUCCESS; +} + void ADIOI_NTFS_IwriteContig(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, ADIO_Offset offset, ADIO_Request *request, @@ -15,18 +180,6 @@ void ADIOI_NTFS_IwriteContig(ADIO_File fd, void *buf, int count, int err; static char myname[] = "ADIOI_NTFS_IwriteContig"; - *request = ADIOI_Malloc_request(); - if ((*request) == NULL) - { - *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, - myname, __LINE__, MPI_ERR_IO, - "**nomem", "**nomem %s", "ADIOI_Request"); - return; - } - (*request)->optype = ADIOI_WRITE; - (*request)->fd = fd; - (*request)->datatype = datatype; - MPI_Type_size(datatype, &typesize); len = count * typesize; @@ -34,15 +187,12 @@ void ADIOI_NTFS_IwriteContig(ADIO_File fd, void *buf, int count, { offset = fd->fp_ind; } - err = ADIOI_NTFS_aio(fd, buf, len, offset, 1, &((*request)->handle)); + err = ADIOI_NTFS_aio(fd, buf, len, offset, 1, request); if (file_ptr_type == ADIO_INDIVIDUAL) { fd->fp_ind += len; } - (*request)->queued = 1; - ADIOI_Add_req_to_list(request); - /* --BEGIN ERROR HANDLING-- */ if (err != MPI_SUCCESS) { @@ -55,7 +205,6 @@ void ADIOI_NTFS_IwriteContig(ADIO_File fd, void *buf, int count, *error_code = MPI_SUCCESS; fd->fp_sys_posn = -1; /* set it to null. */ - fd->async_count++; } @@ -65,56 +214,61 @@ void ADIOI_NTFS_IwriteContig(ADIO_File fd, void *buf, int count, * Returns MPI_SUCCESS on success, mpi_errno on failure. */ int ADIOI_NTFS_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, - int wr, void *handle) + int wr, MPI_Request *request) { static char myname[] = "ADIOI_NTFS_aio"; + + ADIOI_AIO_Request *aio_req; static DWORD dwNumWritten, dwNumRead; BOOL ret_val = FALSE; FDTYPE fd_sys; int mpi_errno = MPI_SUCCESS; - OVERLAPPED *pOvl; DWORD err; fd_sys = fd->fd_sys; - pOvl = (OVERLAPPED *) ADIOI_Calloc(sizeof(OVERLAPPED), 1); - if (pOvl == NULL) + aio_req = (ADIOI_AIO_Request *)ADIOI_Calloc(sizeof(ADIOI_AIO_Request), 1); + if (aio_req == NULL) + { + mpi_errno = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, + myname, __LINE__, MPI_ERR_IO, + "**nomem", "**nomem %s", "AIO_REQ"); + return mpi_errno; + } + aio_req->lpOvl = (LPOVERLAPPED ) ADIOI_Calloc(sizeof(OVERLAPPED), 1); + if (aio_req->lpOvl == NULL) { mpi_errno = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**nomem", "**nomem %s", "OVERLAPPED"); + ADIOI_Free(aio_req); return mpi_errno; } - pOvl->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); - if (pOvl->hEvent == NULL) + aio_req->lpOvl->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (aio_req->lpOvl->hEvent == NULL) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); mpi_errno = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); - ADIOI_Free(pOvl); + "**io", "**io %s", errMsg); + ADIOI_Free(aio_req->lpOvl); + ADIOI_Free(aio_req); return mpi_errno; } - pOvl->Offset = DWORDLOW(offset); - pOvl->OffsetHigh = DWORDHIGH(offset); - + aio_req->lpOvl->Offset = DWORDLOW(offset); + aio_req->lpOvl->OffsetHigh = DWORDHIGH(offset); + aio_req->fd = fd_sys; + + /* XXX: initiate async I/O */ if (wr) { - /*printf("WriteFile(%d bytes)\n", len);fflush(stdout);*/ - ret_val = WriteFile(fd_sys, buf, len, &dwNumWritten, pOvl); + ret_val = WriteFile(fd_sys, buf, len, &dwNumWritten, aio_req->lpOvl); } else { - /* - { - ADIO_Fcntl_t fcntl_struct; - int error_code; - ADIO_Fcntl(fd, ADIO_FCNTL_GET_FSIZE, &fcntl_struct, &error_code); - printf("File size a: %d\n", fcntl_struct.fsize); - } - printf("ReadFile(%d bytes)\n", len);fflush(stdout); - */ - ret_val = ReadFile(fd_sys, buf, len, &dwNumRead, pOvl); + ret_val = ReadFile(fd_sys, buf, len, &dwNumRead, aio_req->lpOvl); } /* --BEGIN ERROR HANDLING-- */ @@ -123,44 +277,32 @@ int ADIOI_NTFS_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, mpi_errno = GetLastError(); if (mpi_errno != ERROR_IO_PENDING) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; + ADIOI_NTFS_Strerror(mpi_errno, errMsg, ADIOI_NTFS_ERR_MSG_MAX); mpi_errno = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(mpi_errno)); + "**io %s", errMsg); return mpi_errno; } mpi_errno = MPI_SUCCESS; } /* --END ERROR HANDLING-- */ - *((OVERLAPPED **) handle) = pOvl; - + /* XXX: set up generalized request class and request */ + if (ADIOI_NTFS_greq_class == 0) { + mpi_errno = MPIX_Grequest_class_create(ADIOI_NTFS_aio_query_fn, + ADIOI_NTFS_aio_free_fn, MPIU_Greq_cancel_fn, + ADIOI_NTFS_aio_poll_fn, ADIOI_NTFS_aio_wait_fn, + &ADIOI_NTFS_greq_class); + if(mpi_errno != MPI_SUCCESS){ + /* FIXME: Pass appropriate error code to user */ + } + } + mpi_errno = MPIX_Grequest_class_allocate(ADIOI_NTFS_greq_class, aio_req, request); + if(mpi_errno != MPI_SUCCESS){ + /* FIXME: Pass appropriate error code to user */ + } + memcpy(&(aio_req->req), request, sizeof(MPI_Request)); return mpi_errno; } - -const char * ADIOI_NTFS_Strerror(int error) -{ - /* obviously not thread safe to store a message like this */ - static char msg[1024]; - HLOCAL str; - int num_bytes; - num_bytes = FormatMessage( - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_ALLOCATE_BUFFER, - 0, - error, - MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), - (LPTSTR) &str, - 0,0); - if (num_bytes == 0) - { - *msg = '\0'; - } - else - { - memcpy(msg, str, num_bytes+1); - LocalFree(str); - strtok(msg, "\r\n"); - } - return msg; -} diff --git a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_open.c b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_open.c index aa4dde4957..1f1db067f4 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_open.c +++ b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_open.c @@ -71,12 +71,14 @@ void ADIOI_NTFS_Open(ADIO_File fd, int *error_code) fd->fp_ind = fd->fp_sys_posn = SetFilePointer(fd->fd_sys, 0, NULL, FILE_END); if (fd->fp_ind == INVALID_SET_FILE_POINTER) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); if (err != NO_ERROR) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); + "**io", "**io %s", errMsg); return; } } @@ -85,11 +87,13 @@ void ADIOI_NTFS_Open(ADIO_File fd, int *error_code) /* --BEGIN ERROR HANDLING-- */ if (fd->fd_sys == INVALID_HANDLE_VALUE) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); return; } /* --END ERROR HANDLING-- */ diff --git a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_read.c b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_read.c index 2923105525..3a22cad781 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_read.c +++ b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_read.c @@ -37,10 +37,12 @@ void ADIOI_NTFS_ReadContig(ADIO_File fd, void *buf, int count, pOvl->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); if (pOvl->hEvent == NULL) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); + "**io", "**io %s", errMsg); ADIOI_Free(pOvl); return; } @@ -54,12 +56,14 @@ void ADIOI_NTFS_ReadContig(ADIO_File fd, void *buf, int count, dwTemp = DWORDHIGH(offset); if (SetFilePointer(fd->fd_sys, DWORDLOW(offset), &dwTemp, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); if (err != NO_ERROR) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); + "**io", "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -79,7 +83,9 @@ void ADIOI_NTFS_ReadContig(ADIO_File fd, void *buf, int count, /* --BEGIN ERROR HANDLING-- */ if (err == FALSE) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); switch (err) { case ERROR_IO_PENDING: @@ -92,7 +98,7 @@ void ADIOI_NTFS_ReadContig(ADIO_File fd, void *buf, int count, *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -103,13 +109,15 @@ void ADIOI_NTFS_ReadContig(ADIO_File fd, void *buf, int count, /* --BEGIN ERROR HANDLING-- */ if (err == FALSE) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); if (err != ERROR_HANDLE_EOF) /* Ignore EOF errors */ { *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -118,10 +126,12 @@ void ADIOI_NTFS_ReadContig(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ if (!CloseHandle(pOvl->hEvent)) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); + "**io", "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -139,12 +149,14 @@ void ADIOI_NTFS_ReadContig(ADIO_File fd, void *buf, int count, dwTemp = DWORDHIGH(fd->fp_ind); if (SetFilePointer(fd->fd_sys, DWORDLOW(fd->fp_ind), &dwTemp, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); if (err != NO_ERROR) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); + "**io", "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -164,7 +176,9 @@ void ADIOI_NTFS_ReadContig(ADIO_File fd, void *buf, int count, /* --BEGIN ERROR HANDLING-- */ if (err == FALSE) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); switch (err) { case ERROR_IO_PENDING: @@ -177,7 +191,7 @@ void ADIOI_NTFS_ReadContig(ADIO_File fd, void *buf, int count, *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -188,13 +202,15 @@ void ADIOI_NTFS_ReadContig(ADIO_File fd, void *buf, int count, /* --BEGIN ERROR HANDLING-- */ if (err == FALSE) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); if (err != ERROR_HANDLE_EOF) /* Ignore EOF errors */ { *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -203,10 +219,12 @@ void ADIOI_NTFS_ReadContig(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ if (!CloseHandle(pOvl->hEvent)) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); + "**io", "**io %s", errMsg); ADIOI_Free(pOvl); return; } @@ -226,11 +244,13 @@ void ADIOI_NTFS_ReadContig(ADIO_File fd, void *buf, int count, /* --BEGIN ERROR HANDLING-- */ if (err == FALSE) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); return; } /* --END ERROR HANDLING-- */ diff --git a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_resize.c b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_resize.c index 19df2778ec..68efa00e9a 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_resize.c +++ b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_resize.c @@ -22,10 +22,12 @@ void ADIOI_NTFS_Resize(ADIO_File fd, ADIO_Offset size, int *error_code) err = GetLastError(); if (err != NO_ERROR) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); return; } } @@ -35,11 +37,13 @@ void ADIOI_NTFS_Resize(ADIO_File fd, ADIO_Offset size, int *error_code) /* --BEGIN ERROR HANDLING-- */ if (result == FALSE) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); return; } /* --END ERROR HANDLING-- */ diff --git a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_wait.c b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_wait.c index 2eb9dddc37..20a95cc656 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_wait.c +++ b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_wait.c @@ -9,80 +9,12 @@ void ADIOI_NTFS_ReadComplete(ADIO_Request *request, ADIO_Status *status, int *error_code) { - DWORD ret_val; - static char myname[] = "ADIOI_NTFS_ReadComplete"; - - if (*request == ADIO_REQUEST_NULL) - { - *error_code = MPI_SUCCESS; - return; - } - - if ((*request)->queued) - { - ret_val = GetOverlappedResult((*request)->fd, (*request)->handle, - &(*request)->nbytes, TRUE); - - if (!ret_val) - (*request)->nbytes = -1; - - /* --BEGIN ERROR HANDLING-- */ - if (ret_val == FALSE) - { - ret_val = GetLastError(); - *error_code = MPIO_Err_create_code(MPI_SUCCESS, - MPIR_ERR_RECOVERABLE, myname, - __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(ret_val)); - return; - } - /* --END ERROR HANDLING-- */ - } /* if ((*request)->queued) ... */ - *error_code = MPI_SUCCESS; -#ifdef HAVE_STATUS_SET_BYTES - if ((*request)->nbytes != -1) - { - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); - } -#endif - - if ((*request)->queued != -1) - { - /* queued = -1 is an internal hack used when the request must - be completed, but the request object should not be - freed. This is used in ADIOI_Complete_async, because the user - will call MPI_Wait later, which would require status to - be filled. Ugly but works. queued = -1 should be used only - in ADIOI_Complete_async. - This should not affect the user in any way. */ - - /* if request is still queued in the system, it is also there - on ADIOI_Async_list. Delete it from there. */ - if ((*request)->queued) ADIOI_Del_req_from_list(request); - - (*request)->fd->async_count--; - if ((*request)->handle) - { - CloseHandle(((OVERLAPPED*)((*request)->handle))->hEvent); - ADIOI_Free((*request)->handle); - } - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; - } + return; } void ADIOI_NTFS_WriteComplete(ADIO_Request *request, ADIO_Status *status, int *error_code) { - static char myname[] = "ADIOI_NTFS_WriteComplete"; - ADIOI_NTFS_ReadComplete(request, status, error_code); - /* --BEGIN ERROR HANDLING-- */ - if (*error_code != MPI_SUCCESS) - { - *error_code = MPIO_Err_create_code(*error_code, - MPIR_ERR_RECOVERABLE, myname, - __LINE__, MPI_ERR_IO, "**io", 0); - } - /* --END ERROR HANDLING-- */ + return; } diff --git a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_write.c b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_write.c index aad0edc37c..f971e8a525 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_write.c +++ b/ompi/mca/io/romio/romio/adio/ad_ntfs/ad_ntfs_write.c @@ -37,10 +37,12 @@ void ADIOI_NTFS_WriteContig(ADIO_File fd, void *buf, int count, pOvl->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); if (pOvl->hEvent == NULL) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); + "**io", "**io %s", errMsg); ADIOI_Free(pOvl); return; } @@ -57,9 +59,11 @@ void ADIOI_NTFS_WriteContig(ADIO_File fd, void *buf, int count, err = GetLastError(); if (err != NO_ERROR) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); + "**io", "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -74,10 +78,12 @@ void ADIOI_NTFS_WriteContig(ADIO_File fd, void *buf, int count, err = GetLastError(); if (err != ERROR_IO_PENDING) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -88,11 +94,13 @@ void ADIOI_NTFS_WriteContig(ADIO_File fd, void *buf, int count, /* --BEGIN ERROR HANDLING-- */ if (err == FALSE) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -100,10 +108,12 @@ void ADIOI_NTFS_WriteContig(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ if (!CloseHandle(pOvl->hEvent)) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); + "**io", "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -124,9 +134,11 @@ void ADIOI_NTFS_WriteContig(ADIO_File fd, void *buf, int count, err = GetLastError(); if (err != NO_ERROR) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); + "**io", "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -141,10 +153,12 @@ void ADIOI_NTFS_WriteContig(ADIO_File fd, void *buf, int count, err = GetLastError(); if (err != ERROR_IO_PENDING) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -155,11 +169,13 @@ void ADIOI_NTFS_WriteContig(ADIO_File fd, void *buf, int count, /* --BEGIN ERROR HANDLING-- */ if (err == FALSE) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); CloseHandle(pOvl->hEvent); ADIOI_Free(pOvl); return; @@ -167,10 +183,12 @@ void ADIOI_NTFS_WriteContig(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ if (!CloseHandle(pOvl->hEvent)) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, - "**io", "**io %s", ADIOI_NTFS_Strerror(err)); + "**io", "**io %s", errMsg); ADIOI_Free(pOvl); return; } @@ -190,11 +208,13 @@ void ADIOI_NTFS_WriteContig(ADIO_File fd, void *buf, int count, /* --BEGIN ERROR HANDLING-- */ if (err == FALSE) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; err = GetLastError(); + ADIOI_NTFS_Strerror(err, errMsg, ADIOI_NTFS_ERR_MSG_MAX); *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**io", - "**io %s", ADIOI_NTFS_Strerror(err)); + "**io %s", errMsg); return; } /* --END ERROR HANDLING-- */ diff --git a/ompi/mca/io/romio/romio/adio/ad_panfs/Makefile.am b/ompi/mca/io/romio/romio/adio/ad_panfs/Makefile.am index 93fc151482..1573fa22e4 100644 --- a/ompi/mca/io/romio/romio/adio/ad_panfs/Makefile.am +++ b/ompi/mca/io/romio/romio/adio/ad_panfs/Makefile.am @@ -9,6 +9,7 @@ # University of Stuttgart. All rights reserved. # Copyright (c) 2004-2005 The Regents of the University of California. # All rights reserved. +# Copyright (c) 2008 Cisco Systems, Inc. All rights reserved. # $COPYRIGHT$ # # Additional copyrights may follow @@ -23,4 +24,7 @@ libadio_panfs_la_SOURCES = \ ad_panfs.c \ ad_panfs.h \ ad_panfs_hints.c \ - ad_panfs_open.c + ad_panfs_open.c \ + ad_panfs_read.c \ + ad_panfs_resize.c \ + ad_panfs_write.c diff --git a/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs.c b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs.c index 8a8165b0db..7af1045014 100644 --- a/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs.c +++ b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs.c @@ -13,8 +13,8 @@ struct ADIOI_Fns_struct ADIO_PANFS_operations = { ADIOI_PANFS_Open, /* Open */ - ADIOI_GEN_ReadContig, /* ReadContig */ - ADIOI_GEN_WriteContig, /* WriteContig */ + ADIOI_PANFS_ReadContig, /* ReadContig */ + ADIOI_PANFS_WriteContig, /* WriteContig */ ADIOI_GEN_ReadStridedColl, /* ReadStridedColl */ ADIOI_GEN_WriteStridedColl, /* WriteStridedColl */ ADIOI_GEN_SeekIndividual, /* SeekIndividual */ @@ -23,8 +23,13 @@ struct ADIOI_Fns_struct ADIO_PANFS_operations = { ADIOI_GEN_ReadStrided, /* ReadStrided */ ADIOI_GEN_WriteStrided, /* WriteStrided */ ADIOI_GEN_Close, /* Close */ +#ifdef ROMIO_HAVE_WORKING_AIO ADIOI_GEN_IreadContig, /* IreadContig */ ADIOI_GEN_IwriteContig, /* IwriteContig */ +#else + ADIOI_FAKE_IreadContig, /* IreadContig */ + ADIOI_FAKE_IwriteContig, /* IwriteContig */ +#endif ADIOI_GEN_IODone, /* ReadDone */ ADIOI_GEN_IODone, /* WriteDone */ ADIOI_GEN_IOComplete, /* ReadComplete */ @@ -32,6 +37,6 @@ struct ADIOI_Fns_struct ADIO_PANFS_operations = { ADIOI_GEN_IreadStrided, /* IreadStrided */ ADIOI_GEN_IwriteStrided, /* IwriteStrided */ ADIOI_GEN_Flush, /* Flush */ - ADIOI_GEN_Resize, /* Resize */ + ADIOI_PANFS_Resize, /* Resize */ ADIOI_GEN_Delete, /* Delete */ }; diff --git a/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs.h b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs.h index 50ad283c5f..97f61ddb3d 100644 --- a/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs.h +++ b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs.h @@ -6,8 +6,8 @@ * See COPYRIGHT notice in top-level directory. */ -#ifndef AD_UNIX_INCLUDE -#define AD_UNIX_INCLUDE +#ifndef AD_PANFS_INCLUDE +#define AD_PANFS_INCLUDE #include #include @@ -25,28 +25,32 @@ typedef struct adiocb adiocb_t; #endif #endif -int ADIOI_PANFS_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, - int wr, void *handle); - void ADIOI_PANFS_Open(ADIO_File fd, int *error_code); -void ADIOI_PANFS_IwriteContig(ADIO_File fd, void *buf, int count, - MPI_Datatype datatype, int file_ptr_type, - ADIO_Offset offset, ADIO_Request *request, int - *error_code); -void ADIOI_PANFS_IreadContig(ADIO_File fd, void *buf, int count, - MPI_Datatype datatype, int file_ptr_type, - ADIO_Offset offset, ADIO_Request *request, int - *error_code); -int ADIOI_PANFS_ReadDone(ADIO_Request *request, ADIO_Status *status, int - *error_code); -int ADIOI_PANFS_WriteDone(ADIO_Request *request, ADIO_Status *status, int - *error_code); -void ADIOI_PANFS_ReadComplete(ADIO_Request *request, ADIO_Status *status, int - *error_code); -void ADIOI_PANFS_WriteComplete(ADIO_Request *request, ADIO_Status *status, - int *error_code); -void ADIOI_PANFS_Fcntl(ADIO_File fd, int flag, ADIO_Fcntl_t *fcntl_struct, int - *error_code); void ADIOI_PANFS_SetInfo(ADIO_File fd, MPI_Info users_info, int *error_code); +void ADIOI_PANFS_ReadContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, + int *error_code); +void ADIOI_PANFS_Resize(ADIO_File fd, ADIO_Offset size, int *error_code); +void ADIOI_PANFS_WriteContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, + int *error_code); + +/* Delay 1 ms */ +#define AD_PANFS_RETRY_DELAY 1000 + +#define AD_PANFS_RETRY(_op_,_rc_) \ +{ \ + _rc_ = (_op_); \ + while(_rc_ == -1 && errno == EAGAIN) \ + { \ + if(usleep(AD_PANFS_RETRY_DELAY) == -1) \ + { \ + break; \ + } \ + _rc_ = (_op_); \ + } \ +} #endif diff --git a/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_hints.c b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_hints.c index e4bedf4fa2..9b02a414d7 100644 --- a/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_hints.c +++ b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_hints.c @@ -8,7 +8,6 @@ #include "ad_panfs.h" #include -#include "opal/mca/base/mca_base_param.h" void ADIOI_PANFS_SetInfo(ADIO_File fd, MPI_Info users_info, int *error_code) { @@ -117,7 +116,7 @@ void ADIOI_PANFS_SetInfo(ADIO_File fd, MPI_Info users_info, int *error_code) MPI_Info_get(users_info, "panfs_layout_visit_policy", MPI_MAX_INFO_VAL, value, &flag); - if (flag && (layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID1_5_PARITY_STRIPE)) { + if (flag && (layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID1_5_PARITY_STRIPE || layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID10)) { layout_visit_policy = strtoul(value,NULL,10); tmp_val = layout_visit_policy; MPI_Bcast(&tmp_val, 1, MPI_INT, 0, fd->comm); diff --git a/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_open.c b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_open.c index 4e50836c80..b5f2a124b9 100644 --- a/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_open.c +++ b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_open.c @@ -33,6 +33,9 @@ void ADIOI_PANFS_Open(ADIO_File fd, int *error_code) unsigned long int layout_parity_stripe_depth = 0; unsigned long int layout_total_num_comps = 0; pan_fs_client_layout_visit_t layout_visit_policy = PAN_FS_CLIENT_LAYOUT_VISIT__ROUND_ROBIN; + int myrank; + + MPI_Comm_rank(fd->comm, &myrank); *error_code = MPI_SUCCESS; value = (char *) ADIOI_Malloc((MPI_MAX_INFO_VAL+1)*sizeof(char)); @@ -68,10 +71,10 @@ void ADIOI_PANFS_Open(ADIO_File fd, int *error_code) } ADIOI_Free(value); - amode = amode | O_CREAT; + amode = amode | O_CREAT; /* Check for valid set of hints */ if ((layout_type < PAN_FS_CLIENT_LAYOUT_TYPE__DEFAULT) || - (layout_type > PAN_FS_CLIENT_LAYOUT_TYPE__RAID1_5_PARITY_STRIPE)) + (layout_type > PAN_FS_CLIENT_LAYOUT_TYPE__RAID10)) { FPRINTF(stderr, "%s: panfs_layout_type is not a valid value: %u.\n", myname, layout_type); MPI_Abort(MPI_COMM_WORLD, 1); @@ -121,99 +124,136 @@ void ADIOI_PANFS_Open(ADIO_File fd, int *error_code) MPI_Abort(MPI_COMM_WORLD, 1); } } - if ((layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID0) || - (layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID1_5_PARITY_STRIPE)) - { - int myrank; - - MPI_Comm_rank(fd->comm, &myrank); - if (myrank == 0) { - pan_fs_client_layout_create_args_t file_create_args; - int fd_dir; - char* slash; - struct stat stat_buf; - int err; - char *value, *path, *file_name_ptr; - - /* Check that the file does not exist before - * trying to create it. The ioctl itself should - * be able to handle this condition. Currently, - * the ioctl will return successfully if the file - * has been previously created. Filed bug 33862 - * to track the problem. - */ - err = stat(fd->filename,&stat_buf); - if((err == -1) && (errno != ENOENT)) + if (layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID10) + { + if ((layout_stripe_unit == 0) || (layout_total_num_comps == 0)) + { + if(layout_stripe_unit == 0) { - FPRINTF(stderr,"%s: Unexpected I/O Error calling stat() on PanFS file: %s.\n", myname, strerror(errno)); - MPI_Abort(MPI_COMM_WORLD, 1); + FPRINTF(stderr, "%s: MPI_Info does not contain the panfs_layout_stripe_unit hint which is necessary to specify a valid RAID10 layout to the PAN_FS_CLIENT_LAYOUT_CREATE_FILE ioctl.\n", myname); } - else if (err == 0) + if(layout_total_num_comps == 0) { - FPRINTF(stderr,"%s: Cannot create PanFS file with ioctl when file already exists.\n", myname); + FPRINTF(stderr, "%s: MPI_Info does not contain the panfs_layout_total_num_comps hint which is necessary to specify a valid RAID10 layout to the PAN_FS_CLIENT_LAYOUT_CREATE_FILE ioctl.\n", myname); + } + MPI_Abort(MPI_COMM_WORLD, 1); + } + if ((layout_visit_policy < PAN_FS_CLIENT_LAYOUT_VISIT__ROUND_ROBIN) || + (layout_visit_policy > PAN_FS_CLIENT_LAYOUT_VISIT__ROUND_ROBIN_WITH_HASHED_OFFSET)) + { + FPRINTF(stderr, "%s: panfs_layout_visit_policy is not a valid value: %u.\n", myname, layout_visit_policy); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + /* Create the file via ioctl() or open(). ADIOI_PANFS_Open's caller + * already optimizes performance by only calling this function with + * ADIO_CREATE on rank 0. Therefore, we don't need to worry about + * implementing that optimization here. */ + if((layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID0) || (layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID1_5_PARITY_STRIPE) + || (layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID10)) { + pan_fs_client_layout_create_args_t file_create_args; + int fd_dir; + char* slash; + struct stat stat_buf; + int err; + char *value, *path, *file_name_ptr; + + /* Check that the file does not exist before + * trying to create it. The ioctl itself should + * be able to handle this condition. Currently, + * the ioctl will return successfully if the file + * has been previously created. Filed bug 33862 + * to track the problem. + */ + err = stat(fd->filename,&stat_buf); + if((err == -1) && (errno != ENOENT)) + { + FPRINTF(stderr,"%s: Unexpected I/O Error calling stat() on PanFS file: %s.\n", myname, strerror(errno)); + MPI_Abort(MPI_COMM_WORLD, 1); + } + else if (err == 0) + { + FPRINTF(stderr,"%s: Cannot create PanFS file with ioctl when file already exists.\n", myname); + MPI_Abort(MPI_COMM_WORLD, 1); + } + else + { + /* (err == -1) && (errno == ENOENT) */ + /* File does not exist */ + path = ADIOI_Strdup(fd->filename); + slash = strrchr(path, '/'); + if (!slash) + ADIOI_Strncpy(path, ".", 2); + else { + if (slash == path) + *(path + 1) = '\0'; + else *slash = '\0'; + } + + /* create PanFS object */ + bzero(&file_create_args,sizeof(pan_fs_client_layout_create_args_t)); + /* open directory */ + fd_dir = open(path, O_RDONLY); + if (fd_dir < 0) { + FPRINTF(stderr, "%s: I/O Error opening parent directory to create PanFS file using ioctl: %s.\n", myname, strerror(errno)); MPI_Abort(MPI_COMM_WORLD, 1); } else { - /* (err == -1) && (errno == ENOENT) */ - /* File does not exist */ - path = ADIOI_Strdup(fd->filename); - slash = strrchr(path, '/'); - if (!slash) - ADIOI_Strncpy(path, ".", 2); - else { - if (slash == path) - *(path + 1) = '\0'; - else *slash = '\0'; + char *file_name_ptr = fd->filename; + slash = strrchr(fd->filename, '/'); + if (slash) + { + file_name_ptr = slash + 1; } - - /* create PanFS object */ - bzero(&file_create_args,sizeof(pan_fs_client_layout_create_args_t)); - /* open directory */ - fd_dir = open(path, O_RDONLY); - if (fd_dir < 0) { - FPRINTF(stderr, "%s: I/O Error opening parent directory to create PanFS file using ioctl: %s.\n", myname, strerror(errno)); + /* create file in the directory */ + file_create_args.mode = perm; + file_create_args.version = PAN_FS_CLIENT_LAYOUT_VERSION; + file_create_args.flags = PAN_FS_CLIENT_LAYOUT_CREATE_F__NONE; + ADIOI_Strncpy(file_create_args.filename, file_name_ptr, strlen(fd->filename)+1); + file_create_args.layout.agg_type = layout_type; + file_create_args.layout.layout_is_valid = 1; + if(layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID1_5_PARITY_STRIPE) + { + file_create_args.layout.u.raid1_5_parity_stripe.total_num_comps = layout_total_num_comps; + file_create_args.layout.u.raid1_5_parity_stripe.parity_stripe_width = layout_parity_stripe_width; + file_create_args.layout.u.raid1_5_parity_stripe.parity_stripe_depth = layout_parity_stripe_depth; + file_create_args.layout.u.raid1_5_parity_stripe.stripe_unit = layout_stripe_unit; + file_create_args.layout.u.raid1_5_parity_stripe.layout_visit_policy = layout_visit_policy; + } + else if(layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID0) + { + file_create_args.layout.u.raid0.total_num_comps = layout_total_num_comps; + file_create_args.layout.u.raid0.stripe_unit = layout_stripe_unit; + } + else if(layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID10) + { + file_create_args.layout.u.raid10.total_num_comps = layout_total_num_comps; + file_create_args.layout.u.raid10.stripe_unit = layout_stripe_unit; + file_create_args.layout.u.raid10.layout_visit_policy = layout_visit_policy; + } + err = ioctl(fd_dir, PAN_FS_CLIENT_LAYOUT_CREATE_FILE, &file_create_args); + if (err < 0) { + FPRINTF(stderr, "%s: I/O Error doing ioctl on parent directory to create PanFS file using ioctl: %s.\n", myname, strerror(errno)); MPI_Abort(MPI_COMM_WORLD, 1); } - else - { - char *file_name_ptr = fd->filename; - slash = strrchr(fd->filename, '/'); - if (slash) - { - file_name_ptr = slash + 1; - } - /* create file in the directory */ - file_create_args.mode = perm; - file_create_args.version = PAN_FS_CLIENT_LAYOUT_VERSION; - file_create_args.flags = PAN_FS_CLIENT_LAYOUT_CREATE_F__NONE; - ADIOI_Strncpy(file_create_args.filename, file_name_ptr, strlen(fd->filename)+1); - file_create_args.layout.agg_type = layout_type; - file_create_args.layout.layout_is_valid = 1; - if(layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID1_5_PARITY_STRIPE) - { - file_create_args.layout.u.raid1_5_parity_stripe.total_num_comps = layout_total_num_comps; - file_create_args.layout.u.raid1_5_parity_stripe.parity_stripe_width = layout_parity_stripe_width; - file_create_args.layout.u.raid1_5_parity_stripe.parity_stripe_depth = layout_parity_stripe_depth; - file_create_args.layout.u.raid1_5_parity_stripe.stripe_unit = layout_stripe_unit; - file_create_args.layout.u.raid1_5_parity_stripe.layout_visit_policy = layout_visit_policy; - } - else if(layout_type == PAN_FS_CLIENT_LAYOUT_TYPE__RAID0) - { - file_create_args.layout.u.raid0.total_num_comps = layout_total_num_comps; - file_create_args.layout.u.raid0.stripe_unit = layout_stripe_unit; - } - err = ioctl(fd_dir, PAN_FS_CLIENT_LAYOUT_CREATE_FILE, &file_create_args); - if (err < 0) { - FPRINTF(stderr, "%s: I/O Error doing ioctl on parent directory to create PanFS file using ioctl: %s.\n", myname, strerror(errno)); - MPI_Abort(MPI_COMM_WORLD, 1); - } - err = close(fd_dir); - } - ADIOI_Free(path); + err = close(fd_dir); } + ADIOI_Free(path); + } + } + else + { + int create_fd = open(fd->filename,amode,perm); + if(create_fd != -1) + { + close(create_fd); + } + else + { + FPRINTF(stderr, "%s: I/O Error creating PanFS file using open: %s.\n", myname, strerror(errno)); + MPI_Abort(MPI_COMM_WORLD, 1); } - MPI_Barrier(fd->comm); } } if (fd->access_mode & ADIO_RDONLY) @@ -279,6 +319,14 @@ void ADIOI_PANFS_Open(ADIO_File fd, int *error_code) ADIOI_Snprintf(temp_buffer,TEMP_BUFFER_SIZE,"%u",file_query_args.layout.u.raid1_5_parity_stripe.layout_visit_policy); MPI_Info_set(fd->info, "panfs_layout_visit_policy", temp_buffer); break; + case PAN_FS_CLIENT_LAYOUT_TYPE__RAID10: + ADIOI_Snprintf(temp_buffer,TEMP_BUFFER_SIZE,"%u",file_query_args.layout.u.raid10.stripe_unit); + MPI_Info_set(fd->info, "panfs_layout_stripe_unit", temp_buffer); + ADIOI_Snprintf(temp_buffer,TEMP_BUFFER_SIZE,"%u",file_query_args.layout.u.raid10.total_num_comps); + MPI_Info_set(fd->info, "panfs_layout_total_num_comps", temp_buffer); + ADIOI_Snprintf(temp_buffer,TEMP_BUFFER_SIZE,"%u",file_query_args.layout.u.raid10.layout_visit_policy); + MPI_Info_set(fd->info, "panfs_layout_visit_policy", temp_buffer); + break; } } } diff --git a/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_read.c b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_read.c new file mode 100644 index 0000000000..dd5635e22d --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_read.c @@ -0,0 +1,68 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "ad_panfs.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +void ADIOI_PANFS_ReadContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, + int *error_code) +{ + int err = -1, datatype_size, len; + static char myname[] = "ADIOI_PANFS_READCONTIG"; + + MPI_Type_size(datatype, &datatype_size); + len = datatype_size * count; + + if (file_ptr_type == ADIO_INDIVIDUAL) { + offset = fd->fp_ind; + } + + if (fd->fp_sys_posn != offset) { + err = lseek(fd->fd_sys, offset, SEEK_SET); + /* --BEGIN ERROR HANDLING-- */ + if (err == -1) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + myname, __LINE__, + MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + fd->fp_sys_posn = -1; + return; + } + /* --END ERROR HANDLING-- */ + } + + AD_PANFS_RETRY(read(fd->fd_sys, buf, len),err) + /* --BEGIN ERROR HANDLING-- */ + if (err == -1) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + myname, __LINE__, + MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + fd->fp_sys_posn = -1; + return; + } + /* --END ERROR HANDLING-- */ + + fd->fp_sys_posn = offset + err; + + if (file_ptr_type == ADIO_INDIVIDUAL) { + fd->fp_ind += err; + } + +#ifdef HAVE_STATUS_SET_BYTES + if (err != -1) MPIR_Status_set_bytes(status, datatype, err); +#endif + + *error_code = MPI_SUCCESS; +} diff --git a/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_resize.c b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_resize.c new file mode 100644 index 0000000000..bc566cf574 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_resize.c @@ -0,0 +1,49 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * + * Copyright (C) 2004 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "ad_panfs.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +void ADIOI_PANFS_Resize(ADIO_File fd, ADIO_Offset size, int *error_code) +{ + int err; + int myrank; + struct stat stat_buf; + static char myname[] = "ADIOI_PANFS_RESIZE"; + + MPI_Comm_rank(fd->comm, &myrank); + if (!myrank) + { + AD_PANFS_RETRY(ftruncate(fd->fd_sys,size),err); + MPI_Barrier(fd->comm); + } + else + { + MPI_Barrier(fd->comm); + AD_PANFS_RETRY(fstat(fd->fd_sys,&stat_buf),err); + if(((ADIO_Offset)stat_buf.st_size) != size) + { + /* This should never happen otherwise there is a coherency problem. */ + FPRINTF(stderr, "%s: Rank %d: Resize failed: requested=%llu actual=%llu.\n",myname,myrank,size,stat_buf.st_size); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + + /* --BEGIN ERROR HANDLING-- */ + if (err == -1) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, + myname, __LINE__, MPI_ERR_IO, + "**io", "**io %s", strerror(errno)); + return; + } + /* --END ERROR HANDLING-- */ + + *error_code = MPI_SUCCESS; +} diff --git a/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_write.c b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_write.c new file mode 100644 index 0000000000..2b186b7203 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_panfs/ad_panfs_write.c @@ -0,0 +1,68 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * + * Copyright (C) 2004 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "ad_panfs.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +void ADIOI_PANFS_WriteContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, + int *error_code) +{ + int err = -1, datatype_size, len; + static char myname[] = "ADIOI_PANFS_WRITECONTIG"; + + MPI_Type_size(datatype, &datatype_size); + len = datatype_size * count; + + if (file_ptr_type == ADIO_INDIVIDUAL) { + offset = fd->fp_ind; + } + + if (fd->fp_sys_posn != offset) { + err = lseek(fd->fd_sys, offset, SEEK_SET); + /* --BEGIN ERROR HANDLING-- */ + if (err == -1) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + myname, __LINE__, + MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + fd->fp_sys_posn = -1; + return; + } + /* --END ERROR HANDLING-- */ + } + + AD_PANFS_RETRY(write(fd->fd_sys, buf, len),err) + /* --BEGIN ERROR HANDLING-- */ + if (err == -1) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + myname, __LINE__, + MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + fd->fp_sys_posn = -1; + return; + } + /* --END ERROR HANDLING-- */ + + fd->fp_sys_posn = offset + err; + + if (file_ptr_type == ADIO_INDIVIDUAL) { + fd->fp_ind += err; + } + +#ifdef HAVE_STATUS_SET_BYTES + if (err != -1 && status) MPIR_Status_set_bytes(status, datatype, err); +#endif + + *error_code = MPI_SUCCESS; +} diff --git a/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_iread.c b/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_iread.c index 87b8c39486..c181ea8799 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_iread.c +++ b/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_iread.c @@ -39,7 +39,7 @@ void ADIOI_PFS_IreadContig(ADIO_File fd, void *buf, int count, /* exceeded the max. no. of outstanding requests. */ /* complete all previous async. requests */ - ADIOI_Complete_async(error_code); + /*ADIOI_Complete_async(error_code); */ if (*error_code != MPI_SUCCESS) return; /* try again */ diff --git a/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_open.c b/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_open.c index 4c5aab9399..c56c0f991f 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_open.c +++ b/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_open.c @@ -6,9 +6,6 @@ */ #include "ad_pfs.h" -#ifdef PROFILE -#include "mpe.h" -#endif void ADIOI_PFS_Open(ADIO_File fd, int *error_code) { @@ -39,15 +36,9 @@ void ADIOI_PFS_Open(ADIO_File fd, int *error_code) MPI_Comm_size(MPI_COMM_WORLD, &np_total); MPI_Comm_size(fd->comm, &np_comm); -#ifdef PROFILE - MPE_Log_event(1, 0, "start open"); -#endif if (np_total == np_comm) fd->fd_sys = _gopen(fd->filename, amode, M_ASYNC, perm); else fd->fd_sys = open(fd->filename, amode, perm); -#ifdef PROFILE - MPE_Log_event(2, 0, "end open"); -#endif fd->fd_direct = -1; if (fd->fd_sys != -1) { diff --git a/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_read.c b/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_read.c index c3e65fef34..e148dec309 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_read.c +++ b/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_read.c @@ -6,9 +6,6 @@ */ #include "ad_pfs.h" -#ifdef PROFILE -#include "mpe.h" -#endif void ADIOI_PFS_ReadContig(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, @@ -22,41 +19,17 @@ void ADIOI_PFS_ReadContig(ADIO_File fd, void *buf, int count, if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { if (fd->fp_sys_posn != offset) { -#ifdef PROFILE - MPE_Log_event(11, 0, "start seek"); -#endif lseek(fd->fd_sys, offset, SEEK_SET); -#ifdef PROFILE - MPE_Log_event(12, 0, "end seek"); -#endif } -#ifdef PROFILE - MPE_Log_event(3, 0, "start read"); -#endif err = _cread(fd->fd_sys, buf, len); -#ifdef PROFILE - MPE_Log_event(4, 0, "end read"); -#endif fd->fp_sys_posn = offset + err; /* individual file pointer not updated */ } else { /* read from curr. location of ind. file pointer */ if (fd->fp_sys_posn != fd->fp_ind) { -#ifdef PROFILE - MPE_Log_event(11, 0, "start seek"); -#endif lseek(fd->fd_sys, fd->fp_ind, SEEK_SET); -#ifdef PROFILE - MPE_Log_event(12, 0, "end seek"); -#endif } -#ifdef PROFILE - MPE_Log_event(3, 0, "start read"); -#endif err = _cread(fd->fd_sys, buf, len); -#ifdef PROFILE - MPE_Log_event(4, 0, "end read"); -#endif fd->fp_ind += err; fd->fp_sys_posn = fd->fp_ind; } diff --git a/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_write.c b/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_write.c index ef5dd31308..257114b301 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_write.c +++ b/ompi/mca/io/romio/romio/adio/ad_pfs/ad_pfs_write.c @@ -6,9 +6,6 @@ */ #include "ad_pfs.h" -#ifdef PROFILE -#include "mpe.h" -#endif void ADIOI_PFS_WriteContig(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, @@ -23,41 +20,17 @@ void ADIOI_PFS_WriteContig(ADIO_File fd, void *buf, int count, if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { if (fd->fp_sys_posn != offset) { -#ifdef PROFILE - MPE_Log_event(11, 0, "start seek"); -#endif lseek(fd->fd_sys, offset, SEEK_SET); -#ifdef PROFILE - MPE_Log_event(12, 0, "end seek"); -#endif } -#ifdef PROFILE - MPE_Log_event(5, 0, "start write"); -#endif err = _cwrite(fd->fd_sys, buf, len); -#ifdef PROFILE - MPE_Log_event(6, 0, "end write"); -#endif fd->fp_sys_posn = offset + err; /* individual file pointer not updated */ } else { /* write from curr. location of ind. file pointer */ if (fd->fp_sys_posn != fd->fp_ind) { -#ifdef PROFILE - MPE_Log_event(11, 0, "start seek"); -#endif lseek(fd->fd_sys, fd->fp_ind, SEEK_SET); -#ifdef PROFILE - MPE_Log_event(12, 0, "end seek"); -#endif } -#ifdef PROFILE - MPE_Log_event(5, 0, "start write"); -#endif err = _cwrite(fd->fd_sys, buf, len); -#ifdef PROFILE - MPE_Log_event(6, 0, "end write"); -#endif fd->fp_ind += err; fd->fp_sys_posn = fd->fp_ind; } diff --git a/ompi/mca/io/romio/romio/adio/ad_piofs/ad_piofs_open.c b/ompi/mca/io/romio/romio/adio/ad_piofs/ad_piofs_open.c index 955af9b082..6572234632 100644 --- a/ompi/mca/io/romio/romio/adio/ad_piofs/ad_piofs_open.c +++ b/ompi/mca/io/romio/romio/adio/ad_piofs/ad_piofs_open.c @@ -6,9 +6,6 @@ */ #include "ad_piofs.h" -#ifdef PROFILE -#include "mpe.h" -#endif void ADIOI_PIOFS_Open(ADIO_File fd, int *error_code) { @@ -38,16 +35,9 @@ void ADIOI_PIOFS_Open(ADIO_File fd, int *error_code) if (fd->access_mode & ADIO_EXCL) amode = amode | O_EXCL; -#ifdef PROFILE - MPE_Log_event(1, 0, "start open"); -#endif fd->fd_sys = open(fd->filename, amode, perm); fd->fd_direct = -1; -#ifdef PROFILE - MPE_Log_event(2, 0, "end open"); -#endif - llseek(fd->fd_sys, 0, SEEK_SET); /* required to initiate use of 64-bit offset */ diff --git a/ompi/mca/io/romio/romio/adio/ad_piofs/ad_piofs_read.c b/ompi/mca/io/romio/romio/adio/ad_piofs/ad_piofs_read.c index 8b7977f4a8..c25e7e17e2 100644 --- a/ompi/mca/io/romio/romio/adio/ad_piofs/ad_piofs_read.c +++ b/ompi/mca/io/romio/romio/adio/ad_piofs/ad_piofs_read.c @@ -6,9 +6,6 @@ */ #include "ad_piofs.h" -#ifdef PROFILE -#include "mpe.h" -#endif void ADIOI_PIOFS_ReadContig(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, @@ -24,41 +21,17 @@ void ADIOI_PIOFS_ReadContig(ADIO_File fd, void *buf, int count, if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { if (fd->fp_sys_posn != offset) { -#ifdef PROFILE - MPE_Log_event(11, 0, "start seek"); -#endif llseek(fd->fd_sys, offset, SEEK_SET); -#ifdef PROFILE - MPE_Log_event(12, 0, "end seek"); -#endif } -#ifdef PROFILE - MPE_Log_event(3, 0, "start read"); -#endif err = read(fd->fd_sys, buf, len); -#ifdef PROFILE - MPE_Log_event(4, 0, "end read"); -#endif fd->fp_sys_posn = offset + err; /* individual file pointer not updated */ } else { /* read from curr. location of ind. file pointer */ if (fd->fp_sys_posn != fd->fp_ind) { -#ifdef PROFILE - MPE_Log_event(11, 0, "start seek"); -#endif llseek(fd->fd_sys, fd->fp_ind, SEEK_SET); -#ifdef PROFILE - MPE_Log_event(12, 0, "end seek"); -#endif } -#ifdef PROFILE - MPE_Log_event(3, 0, "start read"); -#endif err = read(fd->fd_sys, buf, len); -#ifdef PROFILE - MPE_Log_event(4, 0, "end read"); -#endif fd->fp_ind += err; fd->fp_sys_posn = fd->fp_ind; } diff --git a/ompi/mca/io/romio/romio/adio/ad_piofs/ad_piofs_write.c b/ompi/mca/io/romio/romio/adio/ad_piofs/ad_piofs_write.c index f2547e69e9..3f6417108b 100644 --- a/ompi/mca/io/romio/romio/adio/ad_piofs/ad_piofs_write.c +++ b/ompi/mca/io/romio/romio/adio/ad_piofs/ad_piofs_write.c @@ -7,9 +7,6 @@ #include "ad_piofs.h" #include "adio_extern.h" -#ifdef PROFILE -#include "mpe.h" -#endif void ADIOI_PIOFS_WriteContig(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, @@ -25,41 +22,17 @@ void ADIOI_PIOFS_WriteContig(ADIO_File fd, void *buf, int count, if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { if (fd->fp_sys_posn != offset) { -#ifdef PROFILE - MPE_Log_event(11, 0, "start seek"); -#endif llseek(fd->fd_sys, offset, SEEK_SET); -#ifdef PROFILE - MPE_Log_event(12, 0, "end seek"); -#endif } -#ifdef PROFILE - MPE_Log_event(5, 0, "start write"); -#endif err = write(fd->fd_sys, buf, len); -#ifdef PROFILE - MPE_Log_event(6, 0, "end write"); -#endif fd->fp_sys_posn = offset + err; /* individual file pointer not updated */ } else { /* write from curr. location of ind. file pointer */ if (fd->fp_sys_posn != fd->fp_ind) { -#ifdef PROFILE - MPE_Log_event(11, 0, "start seek"); -#endif llseek(fd->fd_sys, fd->fp_ind, SEEK_SET); -#ifdef PROFILE - MPE_Log_event(12, 0, "end seek"); -#endif } -#ifdef PROFILE - MPE_Log_event(5, 0, "start write"); -#endif err = write(fd->fd_sys, buf, len); -#ifdef PROFILE - MPE_Log_event(6, 0, "end write"); -#endif fd->fp_ind += err; fd->fp_sys_posn = fd->fp_ind; } @@ -255,18 +228,8 @@ void ADIOI_PIOFS_WriteStrided(ADIO_File fd, void *buf, int count, if (fwr_size) { /* TYPE_UB and TYPE_LB can result in fwr_size = 0. save system call in such cases */ -#ifdef PROFILE - MPE_Log_event(11, 0, "start seek"); -#endif llseek(fd->fd_sys, off, SEEK_SET); -#ifdef PROFILE - MPE_Log_event(12, 0, "end seek"); - MPE_Log_event(5, 0, "start write"); -#endif err = write(fd->fd_sys, ((char *) buf) + i, fwr_size); -#ifdef PROFILE - MPE_Log_event(6, 0, "end write"); -#endif if (err == -1) err_flag = 1; } i += fwr_size; @@ -304,18 +267,8 @@ void ADIOI_PIOFS_WriteStrided(ADIO_File fd, void *buf, int count, while (num < bufsize) { size = ADIOI_MIN(fwr_size, bwr_size); if (size) { -#ifdef PROFILE - MPE_Log_event(11, 0, "start seek"); -#endif llseek(fd->fd_sys, off, SEEK_SET); -#ifdef PROFILE - MPE_Log_event(12, 0, "end seek"); - MPE_Log_event(5, 0, "start write"); -#endif err = write(fd->fd_sys, ((char *) buf) + indx, size); -#ifdef PROFILE - MPE_Log_event(6, 0, "end write"); -#endif if (err == -1) err_flag = 1; } diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_close.c b/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_close.c index b46498ffb3..517278d0c9 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_close.c +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_close.c @@ -12,7 +12,13 @@ void ADIOI_PVFS_Close(ADIO_File fd, int *error_code) int err; static char myname[] = "ADIOI_PVFS_CLOSE"; +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_close_a, 0, NULL ); +#endif err = pvfs_close(fd->fd_sys); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_close_b, 0, NULL ); +#endif fd->fd_sys = -1; if (err == -1) { diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_fcntl.c b/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_fcntl.c index 5c92a5115b..33e32732d6 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_fcntl.c +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_fcntl.c @@ -15,9 +15,22 @@ void ADIOI_PVFS_Fcntl(ADIO_File fd, int flag, ADIO_Fcntl_t *fcntl_struct, switch(flag) { case ADIO_FCNTL_GET_FSIZE: +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif fcntl_struct->fsize = pvfs_lseek64(fd->fd_sys, 0, SEEK_END); - if (fd->fp_sys_posn != -1) +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + if (fd->fp_sys_posn != -1) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif pvfs_lseek64(fd->fd_sys, fd->fp_sys_posn, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } if (fcntl_struct->fsize == -1) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_open.c b/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_open.c index 142e162fe9..e65a7f3d74 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_open.c +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_open.c @@ -49,11 +49,24 @@ void ADIOI_PVFS_Open(ADIO_File fd, int *error_code) value, &flag); if (flag && (atoi(value) >= 0)) pstat.base = atoi(value); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_open_a, 0, NULL ); +#endif fd->fd_sys = pvfs_open64(fd->filename, amode, perm, &pstat, NULL); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_open_b, 0, NULL ); +#endif fd->fd_direct = -1; - if ((fd->fd_sys != -1) && (fd->access_mode & ADIO_APPEND)) + if ((fd->fd_sys != -1) && (fd->access_mode & ADIO_APPEND)) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif fd->fp_ind = fd->fp_sys_posn = pvfs_lseek64(fd->fd_sys, 0, SEEK_END); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif + } if (fd->fd_sys != -1) { pvfs_ioctl(fd->fd_sys, GETMETA, &pstat); diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_read.c b/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_read.c index b0e1522e9d..a0e3fb8456 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_read.c +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_read.c @@ -27,16 +27,42 @@ void ADIOI_PVFS_ReadContig(ADIO_File fd, void *buf, int count, len = datatype_size * count; if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { - if (fd->fp_sys_posn != offset) + if (fd->fp_sys_posn != offset) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif pvfs_lseek64(fd->fd_sys, offset, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err = pvfs_read(fd->fd_sys, buf, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif fd->fp_sys_posn = offset + err; /* individual file pointer not updated */ } else { /* read from curr. location of ind. file pointer */ - if (fd->fp_sys_posn != fd->fp_ind) + if (fd->fp_sys_posn != fd->fp_ind) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif pvfs_lseek64(fd->fd_sys, fd->fp_ind, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err = pvfs_read(fd->fd_sys, buf, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif fd->fp_ind += err; fd->fp_sys_posn = fd->fp_ind; } diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_write.c b/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_write.c index 3b4cf63fd5..df261593ef 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_write.c +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs/ad_pvfs_write.c @@ -27,16 +27,42 @@ void ADIOI_PVFS_WriteContig(ADIO_File fd, void *buf, int count, len = datatype_size * count; if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { - if (fd->fp_sys_posn != offset) + if (fd->fp_sys_posn != offset) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif pvfs_lseek64(fd->fd_sys, offset, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err = pvfs_write(fd->fd_sys, buf, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif fd->fp_sys_posn = offset + err; /* individual file pointer not updated */ } else { /* write from curr. location of ind. file pointer */ - if (fd->fp_sys_posn != fd->fp_ind) + if (fd->fp_sys_posn != fd->fp_ind) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif pvfs_lseek64(fd->fd_sys, fd->fp_ind, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err = pvfs_write(fd->fd_sys, buf, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif fd->fp_ind += err; fd->fp_sys_posn = fd->fp_ind; } @@ -130,9 +156,23 @@ void ADIOI_PVFS_WriteStrided(ADIO_File fd, void *buf, int count, /* seek to the right spot in the file */ if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { off = fd->disp + etype_size * offset; +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif pvfs_lseek64(fd->fd_sys, off, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif } - else off = pvfs_lseek64(fd->fd_sys, fd->fp_ind, SEEK_SET); + else { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif + off = pvfs_lseek64(fd->fd_sys, fd->fp_ind, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } /* loop through all the flattened pieces. combine into buffer until * no more will fit, then write. @@ -144,9 +184,15 @@ void ADIOI_PVFS_WriteStrided(ADIO_File fd, void *buf, int count, for (i=0; icount; i++) { if (flat_buf->blocklens[i] > combine_buf_remain && combine_buf != combine_buf_ptr) { /* there is data in the buffer; write out the buffer so far */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err = pvfs_write(fd->fd_sys, combine_buf, fd->hints->ind_wr_buffer_size - combine_buf_remain); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif if (err == -1) err_flag = 1; /* reset our buffer info */ @@ -159,9 +205,15 @@ void ADIOI_PVFS_WriteStrided(ADIO_File fd, void *buf, int count, /* special case: blocklen is as big as or bigger than the combine buf; * write directly */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err = pvfs_write(fd->fd_sys, ((char *) buf) + j*buftype_extent + flat_buf->indices[i], flat_buf->blocklens[i]); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif if (err == -1) err_flag = 1; off += flat_buf->blocklens[i]; /* keep up with the final file offset too */ } @@ -179,9 +231,15 @@ void ADIOI_PVFS_WriteStrided(ADIO_File fd, void *buf, int count, if (combine_buf_ptr != combine_buf) { /* data left in buffer to write */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err = pvfs_write(fd->fd_sys, combine_buf, fd->hints->ind_wr_buffer_size - combine_buf_remain); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif if (err == -1) err_flag = 1; } @@ -264,17 +322,19 @@ void ADIOI_PVFS_WriteStrided(ADIO_File fd, void *buf, int count, if (fwr_size) { /* TYPE_UB and TYPE_LB can result in fwr_size = 0. save system call in such cases */ -#ifdef PROFILE - MPE_Log_event(11, 0, "start seek"); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); #endif pvfs_lseek64(fd->fd_sys, off, SEEK_SET); -#ifdef PROFILE - MPE_Log_event(12, 0, "end seek"); - MPE_Log_event(5, 0, "start write"); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); #endif err = pvfs_write(fd->fd_sys, ((char *) buf) + i, fwr_size); -#ifdef PROFILE - MPE_Log_event(6, 0, "end write"); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); #endif if (err == -1) err_flag = 1; } @@ -313,17 +373,19 @@ void ADIOI_PVFS_WriteStrided(ADIO_File fd, void *buf, int count, while (num < bufsize) { size = ADIOI_MIN(fwr_size, bwr_size); if (size) { -#ifdef PROFILE - MPE_Log_event(11, 0, "start seek"); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); #endif pvfs_lseek64(fd->fd_sys, off, SEEK_SET); -#ifdef PROFILE - MPE_Log_event(12, 0, "end seek"); - MPE_Log_event(5, 0, "start write"); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); #endif err = pvfs_write(fd->fd_sys, ((char *) buf) + indx, size); -#ifdef PROFILE - MPE_Log_event(6, 0, "end write"); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); #endif if (err == -1) err_flag = 1; } @@ -473,9 +535,23 @@ void ADIOI_PVFS_WriteStridedListIO(ADIO_File fd, void *buf, int count, if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { off = fd->disp + etype_size * offset; +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif pvfs_lseek64(fd->fd_sys, fd->fp_ind, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif } - else off = pvfs_lseek64(fd->fd_sys, fd->fp_ind, SEEK_SET); + else { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif + off = pvfs_lseek64(fd->fd_sys, fd->fp_ind, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } file_list_count = 1; file_offsets = off; diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs2/Makefile.am b/ompi/mca/io/romio/romio/adio/ad_pvfs2/Makefile.am index 41e6fc0a52..ea21dda326 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pvfs2/Makefile.am +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs2/Makefile.am @@ -9,6 +9,7 @@ # University of Stuttgart. All rights reserved. # Copyright (c) 2004-2005 The Regents of the University of California. # All rights reserved. +# Copyright (c) 2008 Cisco Systems, Inc. All rights reserved. # $COPYRIGHT$ # # Additional copyrights may follow diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2.c b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2.c index d977a41f47..915be8e0b4 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2.c +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2.c @@ -22,8 +22,13 @@ struct ADIOI_Fns_struct ADIO_PVFS2_operations = { ADIOI_PVFS2_ReadStrided, /* ReadStrided */ ADIOI_PVFS2_WriteStrided, /* WriteStrided */ ADIOI_PVFS2_Close, /* Close */ +#ifdef ROMIO_HAVE_WORKING_AIO + ADIOI_PVFS2_IReadContig, /* IreadContig */ + ADIOI_PVFS2_IWriteContig, /* IwriteContig */ +#else ADIOI_FAKE_IreadContig, /* IreadContig */ ADIOI_FAKE_IwriteContig, /* IwriteContig */ +#endif ADIOI_FAKE_IODone, /* ReadDone */ ADIOI_FAKE_IODone, /* WriteDone */ ADIOI_FAKE_IOComplete, /* ReadComplete */ diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2.h b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2.h index 7a01d3d3e6..0718bf1b2e 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2.h +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2.h @@ -37,4 +37,16 @@ void ADIOI_PVFS2_Flush(ADIO_File fd, int *error_code); void ADIOI_PVFS2_Delete(char *filename, int *error_code); void ADIOI_PVFS2_Resize(ADIO_File fd, ADIO_Offset size, int *error_code); void ADIOI_PVFS2_SetInfo(ADIO_File fd, MPI_Info users_info, int *error_code); +void ADIOI_PVFS2_IReadContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, MPI_Request *request, + int *error_code); +void ADIOI_PVFS2_IWriteContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, MPI_Request *request, + int *error_code); +void ADIOI_PVFS2_AIO_contig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, MPI_Request *request, + int flag, int *error_code); #endif diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_aio.c b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_aio.c new file mode 100644 index 0000000000..880d1e28f6 --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_aio.c @@ -0,0 +1,220 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- + * vim: ts=8 sts=4 sw=4 noexpandtab + * + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include "adio.h" +#include "adio_extern.h" +#include "ad_pvfs2.h" +#include + +#include "ad_pvfs2_common.h" +#include "mpiu_greq.h" +#include "../../mpi-io/mpioimpl.h" + +#define READ 0 +#define WRITE 1 + +#ifdef ROMIO_HAVE_WORKING_AIO +static int ADIOI_PVFS2_greq_class = 0; +int ADIOI_PVFS2_aio_free_fn(void *extra_state); +int ADIOI_PVFS2_aio_poll_fn(void *extra_state, MPI_Status *status); +int ADIOI_PVFS2_aio_wait_fn(int count, void ** array_of_states, + double timeout, MPI_Status *status); + +void ADIOI_PVFS2_IReadContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, MPI_Request *request, + int *error_code) +{ + ADIOI_PVFS2_AIO_contig(fd, buf, count, datatype, file_ptr_type, + offset, request, READ, error_code); +} + +void ADIOI_PVFS2_IWriteContig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, MPI_Request *request, + int *error_code) +{ + ADIOI_PVFS2_AIO_contig(fd, buf, count, datatype, file_ptr_type, + offset, request, WRITE, error_code); +} + +void ADIOI_PVFS2_AIO_contig(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, MPI_Request *request, + int flag, int *error_code) +{ + + int ret, datatype_size, len; + ADIOI_PVFS2_fs *pvfs_fs; + ADIOI_AIO_Request *aio_req; + static char myname[] = "ADIOI_PVFS2_AIO_contig"; + + pvfs_fs = (ADIOI_PVFS2_fs*)fd->fs_ptr; + + aio_req = (ADIOI_AIO_Request*)ADIOI_Calloc(sizeof(ADIOI_AIO_Request), 1); + + MPI_Type_size(datatype, &datatype_size); + len = datatype_size * count; + + ret = PVFS_Request_contiguous(len, PVFS_BYTE, &(aio_req->mem_req)); + /* --BEGIN ERROR HANDLING-- */ + if (ret != 0) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + myname, __LINE__, + ADIOI_PVFS2_error_convert(ret), + "Error in pvfs_request_contig (memory)", 0); + return; + } + /* --END ERROR HANDLING-- */ + + ret = PVFS_Request_contiguous(len, PVFS_BYTE, &(aio_req->file_req)); + /* --BEGIN ERROR HANDLING-- */ + if (ret != 0) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + myname, __LINE__, + ADIOI_PVFS2_error_convert(ret), + "Error in pvfs_request_contig (file)", 0); + return; + } + /* --END ERROR HANDLING-- */ + + if (file_ptr_type == ADIO_INDIVIDUAL) { + /* copy individual file pointer into offset variable, continue */ + offset = fd->fp_ind; + } + if (flag == READ) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_iread_a, 0, NULL ); +#endif + ret = PVFS_isys_read(pvfs_fs->object_ref, aio_req->file_req, offset, + buf, aio_req->mem_req, &(pvfs_fs->credentials), + &(aio_req->resp_io), &(aio_req->op_id), NULL); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_iread_b, 0, NULL ); +#endif + } else if (flag == WRITE) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_iwrite_a, 0, NULL ); +#endif + ret = PVFS_isys_write(pvfs_fs->object_ref, aio_req->file_req, offset, + buf, aio_req->mem_req, &(pvfs_fs->credentials), + &(aio_req->resp_io), &(aio_req->op_id), NULL); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_iwrite_b, 0, NULL ); +#endif + } + + /* --BEGIN ERROR HANDLING-- */ + if (ret < 0 ) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + myname, __LINE__, + ADIOI_PVFS2_error_convert(ret), + "Error in PVFS_isys_io", 0); + goto fn_exit; + } + /* --END ERROR HANDLING-- */ + + /* posted. defered completion */ + if (ret == 0) { + if (ADIOI_PVFS2_greq_class == 0) { + MPIX_Grequest_class_create(ADIOI_GEN_aio_query_fn, + ADIOI_PVFS2_aio_free_fn, MPIU_Greq_cancel_fn, + ADIOI_PVFS2_aio_poll_fn, ADIOI_PVFS2_aio_wait_fn, + &ADIOI_PVFS2_greq_class); + } + MPIX_Grequest_class_allocate(ADIOI_PVFS2_greq_class, aio_req, request); + memcpy(&(aio_req->req), request, sizeof(request)); + } + + /* immediate completion */ + if (ret == 1) { + MPIO_Completed_request_create(&fd, len, error_code, request); + } + + if (file_ptr_type == ADIO_INDIVIDUAL) { + fd->fp_ind += len; + } + fd->fp_sys_posn = offset + len; + + *error_code = MPI_SUCCESS; +fn_exit: + return; +} + +int ADIOI_PVFS2_aio_free_fn(void *extra_state) +{ + ADIOI_AIO_Request *aio_req; + aio_req = (ADIOI_AIO_Request*)extra_state; + + PVFS_Request_free(&(aio_req->mem_req)); + PVFS_Request_free(&(aio_req->file_req)); + ADIOI_Free(aio_req); + + return MPI_SUCCESS; +} + +int ADIOI_PVFS2_aio_poll_fn(void *extra_state, MPI_Status *status) +{ + ADIOI_AIO_Request *aio_req; + int ret, error; + + aio_req = (ADIOI_AIO_Request *)extra_state; + + /* BUG: cannot PVFS_sys_testsome: does not work for a specific request */ + ret = PVFS_sys_wait(aio_req->op_id, __FUNCTION__, &error); + if (ret == 0) { + aio_req->nbytes = aio_req->resp_io.total_completed; + MPIR_Nest_incr(); + MPI_Grequest_complete(aio_req->req); + MPIR_Nest_decr(); + return MPI_SUCCESS; + } else + return MPI_UNDEFINED; /* TODO: what's this error? */ +} + +/* wait for multiple requests to complete */ +int ADIOI_PVFS2_aio_wait_fn(int count, void ** array_of_states, + double timeout, MPI_Status *status) +{ + + ADIOI_AIO_Request **aio_reqlist; + PVFS_sys_op_id *op_id_array; + int i,j, greq_count; + int *error_array; + + aio_reqlist = (ADIOI_AIO_Request **)array_of_states; + + op_id_array = (PVFS_sys_op_id*)ADIOI_Calloc(count, sizeof(PVFS_sys_op_id)); + error_array = (int *)ADIOI_Calloc(count, sizeof(int)); + greq_count = count; + + /* PVFS-2.6: testsome actually tests all requests and fills in op_id_array + * with the ones that have completed. count is an in/out parameter. + * returns with the number of completed operations. what a mess! */ + PVFS_sys_testsome(op_id_array, &count, NULL, error_array, INT_MAX); + for (i=0; i< count; i++) { + for (j=0; jop_id) { + aio_reqlist[j]->nbytes = + aio_reqlist[j]->resp_io.total_completed; + MPIR_Nest_incr(); + MPI_Grequest_complete(aio_reqlist[j]->req); + MPIR_Nest_decr(); + } + } + } + return MPI_SUCCESS; /* TODO: no idea how to deal with errors */ +} + +#endif + +/* + * vim: ts=8 sts=4 sw=4 noexpandtab + */ diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_common.c b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_common.c index 3b782e2655..adbd104520 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_common.c +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_common.c @@ -9,6 +9,7 @@ #include #include #include +#include /* maybe give romio access to the globalconfig struct */ /* keyval hack to both tell us if we've already initialized pvfs2 and also diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_open.c b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_open.c index 6a9934523c..1175fe252f 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_open.c +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_open.c @@ -30,7 +30,7 @@ typedef struct open_status_s open_status; * handle to everyone else in the communicator */ static void fake_an_open(PVFS_fs_id fs_id, char *pvfs_name, int access_mode, - int nr_datafiles, int strip_size, + int nr_datafiles, PVFS_size strip_size, ADIOI_PVFS2_fs *pvfs2_fs, open_status *o_status) { @@ -82,9 +82,15 @@ static void fake_an_open(PVFS_fs_id fs_id, char *pvfs_name, int access_mode, } /* Perform file creation */ +#ifdef HAVE_PVFS2_CREATE_WITHOUT_LAYOUT ret = PVFS_sys_create(resp_getparent.basename, resp_getparent.parent_ref, attribs, &(pvfs2_fs->credentials), dist, &resp_create); +#else + ret = PVFS_sys_create(resp_getparent.basename, + resp_getparent.parent_ref, attribs, + &(pvfs2_fs->credentials), dist, NULL, &resp_create); +#endif /* if many creates are happening in this directory, the earlier * sys_lookup may have returned ENOENT, but the sys_create could @@ -175,6 +181,9 @@ void ADIOI_PVFS2_Open(ADIO_File fd, int *error_code) ADIOI_PVFS2_makecredentials(&(pvfs2_fs->credentials)); /* one process resolves name and will later bcast to others */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_open_a, 0, NULL ); +#endif if (rank == fd->hints->ranklist[0] && fd->fs_ptr == NULL) { /* given the filename, figure out which pvfs filesystem it is on */ ret = PVFS_util_resolve(fd->filename, &cur_fs, @@ -194,6 +203,9 @@ void ADIOI_PVFS2_Open(ADIO_File fd, int *error_code) pvfs2_fs->object_ref = o_status.object_ref; fd->fs_ptr = pvfs2_fs; } +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_open_b, 0, NULL ); +#endif /* broadcast status and (possibly valid) object reference */ MPI_Address(&o_status.error, &offsets[0]); diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_read.c b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_read.c index c2cd4cc09c..2dd7e55ca1 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_read.c +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_read.c @@ -56,8 +56,14 @@ void ADIOI_PVFS2_ReadContig(ADIO_File fd, void *buf, int count, offset = fd->fp_ind; } +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif ret = PVFS_sys_read(pvfs_fs->object_ref, file_req, offset, buf, mem_req, &(pvfs_fs->credentials), &resp_io); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (ret != 0 ) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, @@ -140,7 +146,7 @@ void ADIOI_PVFS2_ReadStrided(ADIO_File fd, void *buf, int count, if (!filetype_is_contig) { flat_file = ADIOI_Flatlist; while (flat_file->type != fd->filetype) flat_file = flat_file->next; - if (flat_file->count == 1) + if (flat_file->count == 1 && !buftype_is_contig) filetype_is_contig = 1; } @@ -214,9 +220,15 @@ void ADIOI_PVFS2_ReadStrided(ADIO_File fd, void *buf, int count, err_flag = PVFS_Request_contiguous(file_lengths, PVFS_BYTE, &file_req); if (err_flag < 0) break; +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err_flag = PVFS_sys_read(pvfs_fs->object_ref, file_req, file_offsets, PVFS_BOTTOM, mem_req, &(pvfs_fs->credentials), &resp_io); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (err_flag != 0) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, @@ -284,11 +296,11 @@ void ADIOI_PVFS2_ReadStrided(ADIO_File fd, void *buf, int count, n_filetypes++; for (i=0; icount; i++) { if (disp + flat_file->indices[i] + - (ADIO_Offset) n_filetypes*filetype_extent + + ((ADIO_Offset) n_filetypes)*filetype_extent + flat_file->blocklens[i] >= offset) { st_index = i; frd_size = (int) (disp + flat_file->indices[i] + - (ADIO_Offset) n_filetypes*filetype_extent + ((ADIO_Offset) n_filetypes)*filetype_extent + flat_file->blocklens[i] - offset); flag = 1; break; @@ -315,7 +327,7 @@ void ADIOI_PVFS2_ReadStrided(ADIO_File fd, void *buf, int count, } /* abs. offset in bytes in the file */ - offset = disp + (ADIO_Offset) n_filetypes*filetype_extent + + offset = disp + ((ADIO_Offset) n_filetypes)*filetype_extent + abs_off_in_filetype; } /* else [file_ptr_type != ADIO_INDIVIDUAL] */ @@ -340,7 +352,11 @@ void ADIOI_PVFS2_ReadStrided(ADIO_File fd, void *buf, int count, /* determine how many blocks in file to read */ f_data_read = ADIOI_MIN(st_frd_size, bufsize); total_blks_to_read = 1; - j++; + if (j < (flat_file->count-1)) j++; + else { + j = 0; + n_filetypes++; + } while (f_data_read < bufsize) { f_data_read += flat_file->blocklens[j]; total_blks_to_read++; @@ -383,7 +399,8 @@ void ADIOI_PVFS2_ReadStrided(ADIO_File fd, void *buf, int count, } for (k=0; kindices[j]; file_lengths[k] = flat_file->blocklens[j]; mem_lengths += file_lengths[k]; @@ -424,9 +441,15 @@ void ADIOI_PVFS2_ReadStrided(ADIO_File fd, void *buf, int count, /* PVFS_Request_hindexed already expresses the offsets into the * file, so we should not pass in an offset if we are using * hindexed for the file type */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err_flag = PVFS_sys_read(pvfs_fs->object_ref, file_req, 0, mem_offsets, mem_req, &(pvfs_fs->credentials), &resp_io); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (err_flag != 0) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, @@ -455,8 +478,9 @@ void ADIOI_PVFS2_ReadStrided(ADIO_File fd, void *buf, int count, } for (k=0; kindices[j]; + file_offsets[k] = disp + + ((ADIO_Offset)n_filetypes)*filetype_extent + + flat_file->indices[j]; if (k == (extra_blks - 1)) { file_lengths[k] = bufsize - (int32_t) mem_lengths - (int32_t) mem_offsets + (int32_t) buf; @@ -497,8 +521,14 @@ void ADIOI_PVFS2_ReadStrided(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ /* as above, use 0 for 'offset' when using hindexed file type */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err_flag = PVFS_sys_read(pvfs_fs->object_ref, file_req, 0, mem_offsets, mem_req, &(pvfs_fs->credentials), &resp_io); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (err_flag != 0) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, @@ -848,8 +878,8 @@ void ADIOI_PVFS2_ReadStrided(ADIO_File fd, void *buf, int count, k = (k + 1)%flat_buf->count; } /* for (i=0; iindices[j] + n_filetypes * - filetype_extent; + file_offsets[i] = disp + flat_file->indices[j] + + ((ADIO_Offset)n_filetypes) * filetype_extent; if (!i) { file_lengths[0] = frd_size; file_offsets[0] += flat_file->blocklens[j] - frd_size; @@ -899,8 +929,14 @@ void ADIOI_PVFS2_ReadStrided(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ /* offset will be expressed in memory and file datatypes */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err_flag = PVFS_sys_read(pvfs_fs->object_ref, file_req, 0, PVFS_BOTTOM, mem_req, &(pvfs_fs->credentials), &resp_io); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (err_flag != 0) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, @@ -924,7 +960,16 @@ void ADIOI_PVFS2_ReadStrided(ADIO_File fd, void *buf, int count, ADIOI_Free(file_lengths); /* Other ADIO routines will convert absolute bytes into counts of datatypes */ - if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind += total_bytes_read; + /* when incrementing fp_ind, need to also take into account the file type: + * consider an N-element 1-d subarray with a lb and ub: ( |---xxxxx-----| + * if we wrote N elements, offset needs to point at beginning of type, not + * at empty region at offset N+1) */ + if (file_ptr_type == ADIO_INDIVIDUAL) { + /* this is closer, but still incorrect for the cases where a small + * amount of a file type is "leftover" after a write */ + fd->fp_ind = disp + flat_file->indices[j] + + ((ADIO_Offset)n_filetypes)*filetype_extent; + } if (err_flag == 0) *error_code = MPI_SUCCESS; error_state: diff --git a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_write.c b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_write.c index 3705c4f35c..aaa4c75a41 100644 --- a/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_write.c +++ b/ompi/mca/io/romio/romio/adio/ad_pvfs2/ad_pvfs2_write.c @@ -51,8 +51,14 @@ void ADIOI_PVFS2_WriteContig(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif ret = PVFS_sys_write(pvfs_fs->object_ref, file_req, offset, buf, mem_req, &(pvfs_fs->credentials), &resp_io); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (ret != 0) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, @@ -67,8 +73,14 @@ void ADIOI_PVFS2_WriteContig(ADIO_File fd, void *buf, int count, fd->fp_sys_posn = offset + (int) resp_io.total_completed; } else { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif ret = PVFS_sys_write(pvfs_fs->object_ref, file_req, fd->fp_ind, buf, mem_req, &(pvfs_fs->credentials), &resp_io); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (ret != 0) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, @@ -161,7 +173,7 @@ void ADIOI_PVFS2_WriteStrided(ADIO_File fd, void *buf, int count, if (!filetype_is_contig) { flat_file = ADIOI_Flatlist; while (flat_file->type != fd->filetype) flat_file = flat_file->next; - if (flat_file->count == 1) + if (flat_file->count == 1 && !buftype_is_contig) filetype_is_contig = 1; } @@ -258,11 +270,17 @@ void ADIOI_PVFS2_WriteStrided(ADIO_File fd, void *buf, int count, } /* --END ERROR HANDLING-- */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err_flag = PVFS_sys_write(pvfs_fs->object_ref, file_req, file_offsets, PVFS_BOTTOM, mem_req, &(pvfs_fs->credentials), &resp_io); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif total_bytes_written += resp_io.total_completed; /* in the case of error or the last write list call, @@ -330,11 +348,11 @@ void ADIOI_PVFS2_WriteStrided(ADIO_File fd, void *buf, int count, n_filetypes++; for (i=0; icount; i++) { if (disp + flat_file->indices[i] + - (ADIO_Offset) n_filetypes*filetype_extent + + ((ADIO_Offset) n_filetypes)*filetype_extent + flat_file->blocklens[i] >= offset) { st_index = i; fwr_size = disp + flat_file->indices[i] + - (ADIO_Offset) n_filetypes*filetype_extent + ((ADIO_Offset) n_filetypes)*filetype_extent + flat_file->blocklens[i] - offset; flag = 1; break; @@ -361,7 +379,7 @@ void ADIOI_PVFS2_WriteStrided(ADIO_File fd, void *buf, int count, } /* abs. offset in bytes in the file */ - offset = disp + (ADIO_Offset) n_filetypes*filetype_extent + + offset = disp + ((ADIO_Offset) n_filetypes)*filetype_extent + abs_off_in_filetype; } /* else [file_ptr_type != ADIO_INDIVIDUAL] */ @@ -387,7 +405,11 @@ void ADIOI_PVFS2_WriteStrided(ADIO_File fd, void *buf, int count, /* determine how many blocks in file to write */ f_data_wrote = ADIOI_MIN(st_fwr_size, bufsize); total_blks_to_write = 1; - j++; + if (j < (flat_file->count -1)) j++; + else { + j = 0; + n_filetypes++; + } while (f_data_wrote < bufsize) { f_data_wrote += flat_file->blocklens[j]; total_blks_to_write++; @@ -430,8 +452,9 @@ void ADIOI_PVFS2_WriteStrided(ADIO_File fd, void *buf, int count, } for (k=0; kindices[j]; + file_offsets[k] = disp + + ((ADIO_Offset)n_filetypes)*filetype_extent + + flat_file->indices[j]; file_lengths[k] = flat_file->blocklens[j]; mem_lengths += file_lengths[k]; } @@ -472,9 +495,15 @@ void ADIOI_PVFS2_WriteStrided(ADIO_File fd, void *buf, int count, /* PVFS_Request_hindexed already expresses the offsets into the * file, so we should not pass in an offset if we are using * hindexed for the file type */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err_flag = PVFS_sys_write(pvfs_fs->object_ref, file_req, 0, mem_offsets, mem_req, &(pvfs_fs->credentials), &resp_io); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (err_flag != 0) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, @@ -503,8 +532,9 @@ void ADIOI_PVFS2_WriteStrided(ADIO_File fd, void *buf, int count, } for (k=0; kindices[j]; + file_offsets[k] = disp + + ((ADIO_Offset)n_filetypes)*filetype_extent + + flat_file->indices[j]; if (k == (extra_blks - 1)) { file_lengths[k] = bufsize - (int32_t) mem_lengths - (int32_t) mem_offsets + (int32_t) buf; @@ -547,9 +577,15 @@ void ADIOI_PVFS2_WriteStrided(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ /* as above, use 0 for 'offset' when using hindexed file type*/ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err_flag = PVFS_sys_write(pvfs_fs->object_ref, file_req, 0, mem_offsets, mem_req, &(pvfs_fs->credentials), &resp_io); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (err_flag != 0) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, @@ -904,8 +940,8 @@ void ADIOI_PVFS2_WriteStrided(ADIO_File fd, void *buf, int count, k = (k + 1)%flat_buf->count; } /* for (i=0; iindices[j] + n_filetypes * - filetype_extent; + file_offsets[i] = disp + flat_file->indices[j] + + ((ADIO_Offset)n_filetypes) * filetype_extent; if (!i) { file_lengths[0] = fwr_size; file_offsets[0] += flat_file->blocklens[j] - fwr_size; @@ -959,9 +995,15 @@ void ADIOI_PVFS2_WriteStrided(ADIO_File fd, void *buf, int count, /* offset will be expressed in memory and file datatypes */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err_flag = PVFS_sys_write(pvfs_fs->object_ref, file_req, 0, PVFS_BOTTOM, mem_req, &(pvfs_fs->credentials), &resp_io); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (err_flag != 0) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, @@ -986,7 +1028,16 @@ void ADIOI_PVFS2_WriteStrided(ADIO_File fd, void *buf, int count, ADIOI_Free(file_offsets); ADIOI_Free(file_lengths); - if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind += total_bytes_written; + /* when incrementing fp_ind, need to also take into account the file type: + * consider an N-element 1-d subarray with a lb and ub: ( |---xxxxx-----| + * if we wrote N elements, offset needs to point at beginning of type, not + * at empty region at offset N+1) */ + if (file_ptr_type == ADIO_INDIVIDUAL) { + /* this is closer, but still incorrect for the cases where a small + * amount of a file type is "leftover" after a write */ + fd->fp_ind = disp + flat_file->indices[j] + + ((ADIO_Offset)n_filetypes)*filetype_extent; + } *error_code = MPI_SUCCESS; error_state: diff --git a/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_done.c b/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_done.c index 36ddb14c01..65ab055998 100644 --- a/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_done.c +++ b/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_done.c @@ -15,29 +15,14 @@ int ADIOI_TESTFS_ReadDone(ADIO_Request *request, ADIO_Status *status, int *error_code = MPI_SUCCESS; - if (*request == ADIO_REQUEST_NULL) { - MPI_Comm_size( MPI_COMM_WORLD, &nprocs ); - MPI_Comm_rank( MPI_COMM_WORLD, &myrank ); - FPRINTF(stdout, "[%d/%d] ADIOI_TESTFS_ReadDone called on ADIO_REQUEST_NULL\n", - myrank, nprocs); - return 1; - } - - MPI_Comm_size((*request)->fd->comm, &nprocs); - MPI_Comm_rank((*request)->fd->comm, &myrank); - - FPRINTF(stdout, "[%d/%d] ADIOI_TESTFS_ReadDone called on %s\n", - myrank, nprocs, (*request)->fd->filename); - -#ifdef HAVE_STATUS_SET_BYTES - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - (*request)->fd->async_count--; - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; + MPI_Comm_size( MPI_COMM_WORLD, &nprocs ); + MPI_Comm_rank( MPI_COMM_WORLD, &myrank ); + FPRINTF(stdout, "[%d/%d] ADIOI_TESTFS_ReadDone called on ADIO_REQUEST_NULL\n", + myrank, nprocs); return 1; } + int ADIOI_TESTFS_WriteDone(ADIO_Request *request, ADIO_Status *status, int *error_code) { @@ -45,24 +30,10 @@ int ADIOI_TESTFS_WriteDone(ADIO_Request *request, ADIO_Status *status, int *error_code = MPI_SUCCESS; - if (*request == ADIO_REQUEST_NULL) { - MPI_Comm_size( MPI_COMM_WORLD, &nprocs ); - MPI_Comm_rank( MPI_COMM_WORLD, &myrank ); - FPRINTF(stdout, "[%d/%d] ADIOI_TESTFS_WriteDone called on ADIO_REQUEST_NULL\n", + MPI_Comm_size( MPI_COMM_WORLD, &nprocs ); + MPI_Comm_rank( MPI_COMM_WORLD, &myrank ); + FPRINTF(stdout, + "[%d/%d] ADIOI_TESTFS_WriteDone called on ADIO_REQUEST_NULL\n", myrank, nprocs); - return 1; - } - - MPI_Comm_size((*request)->fd->comm, &nprocs); - MPI_Comm_rank((*request)->fd->comm, &myrank); - FPRINTF(stdout, "[%d/%d] ADIOI_TESTFS_WriteDone called on %s\n", - myrank, nprocs, (*request)->fd->filename); - -#ifdef HAVE_STATUS_SET_BYTES - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - (*request)->fd->async_count--; - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; return 1; } diff --git a/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_iread.c b/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_iread.c index 768518f630..09a744d5d7 100644 --- a/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_iread.c +++ b/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_iread.c @@ -22,15 +22,9 @@ void ADIOI_TESTFS_IreadContig(ADIO_File fd, void *buf, int count, *error_code = MPI_SUCCESS; - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_WRITE; - (*request)->fd = fd; - (*request)->queued = 0; - (*request)->datatype = datatype; - - MPI_Type_size(datatype, &typesize); MPI_Comm_size(fd->comm, &nprocs); MPI_Comm_rank(fd->comm, &myrank); + MPI_Type_size(datatype, &typesize); FPRINTF(stdout, "[%d/%d] ADIOI_TESTFS_IreadContig called on %s\n", myrank, nprocs, fd->filename); FPRINTF(stdout, "[%d/%d] calling ADIOI_TESTFS_ReadContig\n", @@ -40,13 +34,7 @@ void ADIOI_TESTFS_IreadContig(ADIO_File fd, void *buf, int count, ADIOI_TESTFS_ReadContig(fd, buf, len, MPI_BYTE, file_ptr_type, offset, &status, error_code); -#ifdef HAVE_STATUS_SET_BYTES - if (*error_code == MPI_SUCCESS) { - MPI_Get_elements(&status, MPI_BYTE, &len); - (*request)->nbytes = len; - } -#endif - fd->async_count++; + MPIO_Completed_request_create(&fd, len, error_code, request); } void ADIOI_TESTFS_IreadStrided(ADIO_File fd, void *buf, int count, @@ -56,34 +44,18 @@ void ADIOI_TESTFS_IreadStrided(ADIO_File fd, void *buf, int count, { ADIO_Status status; int myrank, nprocs; -#ifdef HAVE_STATUS_SET_BYTES int typesize; -#endif - - *error_code = MPI_SUCCESS; - - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_WRITE; - (*request)->fd = fd; - (*request)->queued = 0; - (*request)->datatype = datatype; MPI_Comm_size(fd->comm, &nprocs); MPI_Comm_rank(fd->comm, &myrank); + MPI_Type_size(datatype, &typesize); FPRINTF(stdout, "[%d/%d] ADIOI_TESTFS_IreadStrided called on %s\n", myrank, nprocs, fd->filename); FPRINTF(stdout, "[%d/%d] calling ADIOI_TESTFS_ReadStrided\n", myrank, nprocs); ADIOI_TESTFS_ReadStrided(fd, buf, count, datatype, file_ptr_type, - offset, &status, error_code); - -#ifdef HAVE_STATUS_SET_BYTES - if (*error_code == MPI_SUCCESS) { - MPI_Type_size(datatype, &typesize); - (*request)->nbytes = count * typesize; - } -#endif - fd->async_count++; + offset, &status, error_code); + MPIO_Completed_request_create(&fd, count*typesize, error_code, request); } diff --git a/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_iwrite.c b/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_iwrite.c index abd09a3f7d..15e013104b 100644 --- a/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_iwrite.c +++ b/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_iwrite.c @@ -8,6 +8,9 @@ #include "ad_testfs.h" #include "adioi.h" +#include "mpiu_greq.h" +#include "../../mpi-io/mpioimpl.h" + /* ADIOI_TESTFS_IwriteContig() * * Implemented by immediately calling WriteContig() @@ -22,12 +25,6 @@ void ADIOI_TESTFS_IwriteContig(ADIO_File fd, void *buf, int count, *error_code = MPI_SUCCESS; - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_WRITE; - (*request)->fd = fd; - (*request)->queued = 0; - (*request)->datatype = datatype; - MPI_Type_size(datatype, &typesize); MPI_Comm_size(fd->comm, &nprocs); MPI_Comm_rank(fd->comm, &myrank); @@ -39,14 +36,8 @@ void ADIOI_TESTFS_IwriteContig(ADIO_File fd, void *buf, int count, len = count * typesize; ADIOI_TESTFS_WriteContig(fd, buf, len, MPI_BYTE, file_ptr_type, offset, &status, error_code); + MPIO_Completed_request_create(&fd, len, error_code, request); -#ifdef HAVE_STATUS_SET_BYTES - if (*error_code == MPI_SUCCESS) { - MPI_Get_elements(&status, MPI_BYTE, &len); - (*request)->nbytes = len; - } -#endif - fd->async_count++; } void ADIOI_TESTFS_IwriteStrided(ADIO_File fd, void *buf, int count, @@ -56,20 +47,14 @@ void ADIOI_TESTFS_IwriteStrided(ADIO_File fd, void *buf, int count, { ADIO_Status status; int myrank, nprocs; -#ifdef HAVE_STATUS_SET_BYTES int typesize; -#endif *error_code = MPI_SUCCESS; - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_WRITE; - (*request)->fd = fd; - (*request)->queued = 0; - (*request)->datatype = datatype; - MPI_Comm_size(fd->comm, &nprocs); MPI_Comm_rank(fd->comm, &myrank); + MPI_Type_size(datatype, &typesize); + FPRINTF(stdout, "[%d/%d] ADIOI_TESTFS_IwriteStrided called on %s\n", myrank, nprocs, fd->filename); FPRINTF(stdout, "[%d/%d] calling ADIOI_TESTFS_WriteStrided\n", @@ -77,12 +62,6 @@ void ADIOI_TESTFS_IwriteStrided(ADIO_File fd, void *buf, int count, ADIOI_TESTFS_WriteStrided(fd, buf, count, datatype, file_ptr_type, offset, &status, error_code); + MPIO_Completed_request_create(&fd, count*typesize, error_code, request); -#ifdef HAVE_STATUS_SET_BYTES - if (*error_code == MPI_SUCCESS) { - MPI_Type_size(datatype, &typesize); - (*request)->nbytes = count * typesize; - } -#endif - fd->async_count++; } diff --git a/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_wait.c b/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_wait.c index 5e1b28de60..ac520f7039 100644 --- a/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_wait.c +++ b/ompi/mca/io/romio/romio/adio/ad_testfs/ad_testfs_wait.c @@ -15,22 +15,12 @@ void ADIOI_TESTFS_ReadComplete(ADIO_Request *request, ADIO_Status *status, int *error_code = MPI_SUCCESS; - if (*request == ADIO_REQUEST_NULL) { - FPRINTF(stdout, "[xx/xx] ADIOI_TESTFS_ReadComplete called on ADIO_REQUEST_NULL\n"); - return; - } + MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + MPI_Comm_rank(MPI_COMM_WORLD, &myrank); + FPRINTF(stdout, "[%d/%d] ADIOI_TESTFS_ReadComplete called \n", + myrank, nprocs); - MPI_Comm_size((*request)->fd->comm, &nprocs); - MPI_Comm_rank((*request)->fd->comm, &myrank); - FPRINTF(stdout, "[%d/%d] ADIOI_TESTFS_ReadComplete called on %s\n", - myrank, nprocs, (*request)->fd->filename); - -#ifdef HAVE_STATUS_SET_BYTES - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - (*request)->fd->async_count--; - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; + /* do something with status set bytes? */ } void ADIOI_TESTFS_WriteComplete(ADIO_Request *request, ADIO_Status *status, int @@ -40,20 +30,10 @@ void ADIOI_TESTFS_WriteComplete(ADIO_Request *request, ADIO_Status *status, int *error_code = MPI_SUCCESS; - if (*request == ADIO_REQUEST_NULL) { - FPRINTF(stdout, "[xx/xx] ADIOI_TESTFS_WriteComplete called on ADIO_REQUEST_NULL\n"); - return; - } + MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + MPI_Comm_rank(MPI_COMM_WORLD, &myrank); + FPRINTF(stdout, "[%d/%d] ADIOI_TESTFS_WriteComplete called\n", + myrank, nprocs); - MPI_Comm_size((*request)->fd->comm, &nprocs); - MPI_Comm_rank((*request)->fd->comm, &myrank); - FPRINTF(stdout, "[%d/%d] ADIOI_TESTFS_WriteComplete called on %s\n", - myrank, nprocs, (*request)->fd->filename); - -#ifdef HAVE_STATUS_SET_BYTES - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - (*request)->fd->async_count--; - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; + /* do something with status_set_bytes? */ } diff --git a/ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs.c b/ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs.c index 3e016abfef..ce7bd03094 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs.c +++ b/ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs.c @@ -22,8 +22,13 @@ struct ADIOI_Fns_struct ADIO_UFS_operations = { ADIOI_GEN_ReadStrided, /* ReadStrided */ ADIOI_GEN_WriteStrided, /* WriteStrided */ ADIOI_GEN_Close, /* Close */ +#ifdef ROMIO_HAVE_WORKING_AIO ADIOI_GEN_IreadContig, /* IreadContig */ ADIOI_GEN_IwriteContig, /* IwriteContig */ +#else + ADIOI_FAKE_IreadContig, /* IreadContig */ + ADIOI_FAKE_IwriteContig, /* IwriteContig */ +#endif ADIOI_GEN_IODone, /* ReadDone */ ADIOI_GEN_IODone, /* WriteDone */ ADIOI_GEN_IOComplete, /* ReadComplete */ diff --git a/ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs.h b/ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs.h index 73a9b7b23d..56b988539c 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs.h +++ b/ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs.h @@ -15,6 +15,9 @@ #ifdef HAVE_SIGNAL_H #include #endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif #ifdef HAVE_AIO_H #include #endif diff --git a/ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs_open.c b/ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs_open.c index 4e915942cc..1a154fa97e 100644 --- a/ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs_open.c +++ b/ompi/mca/io/romio/romio/adio/ad_ufs/ad_ufs_open.c @@ -31,11 +31,25 @@ void ADIOI_UFS_Open(ADIO_File fd, int *error_code) if (fd->access_mode & ADIO_EXCL) amode = amode | O_EXCL; + +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_open_a, 0, NULL ); +#endif fd->fd_sys = open(fd->filename, amode, perm); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_open_b, 0, NULL ); +#endif fd->fd_direct = -1; - if ((fd->fd_sys != -1) && (fd->access_mode & ADIO_APPEND)) + if ((fd->fd_sys != -1) && (fd->access_mode & ADIO_APPEND)) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif fd->fp_ind = fd->fp_sys_posn = lseek(fd->fd_sys, 0, SEEK_END); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } /* --BEGIN ERROR HANDLING-- */ if (fd->fd_sys == -1) { diff --git a/ompi/mca/io/romio/romio/adio/ad_xfs/ad_xfs_iwrite.c b/ompi/mca/io/romio/romio/adio/ad_xfs/ad_xfs_iwrite.c index d5367b318c..61980621f7 100644 --- a/ompi/mca/io/romio/romio/adio/ad_xfs/ad_xfs_iwrite.c +++ b/ompi/mca/io/romio/romio/adio/ad_xfs/ad_xfs_iwrite.c @@ -117,7 +117,7 @@ int ADIOI_XFS_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, /* exceeded the max. no. of outstanding requests. complete all previous async. requests and try again. */ - ADIOI_Complete_async(&error_code); + /* ADIOI_Complete_async(&error_code); */ if (error_code != MPI_SUCCESS) return -EIO; if (wr) err = aio_write64(aiocbp); diff --git a/ompi/mca/io/romio/romio/adio/common/Makefile.am b/ompi/mca/io/romio/romio/adio/common/Makefile.am index 1854cfd8d6..9dca94a663 100644 --- a/ompi/mca/io/romio/romio/adio/common/Makefile.am +++ b/ompi/mca/io/romio/romio/adio/common/Makefile.am @@ -9,6 +9,7 @@ # University of Stuttgart. All rights reserved. # Copyright (c) 2004-2005 The Regents of the University of California. # All rights reserved. +# Copyright (c) 2008 Cisco Systems, Inc. All rights reserved. # $COPYRIGHT$ # # Additional copyrights may follow @@ -18,6 +19,10 @@ include $(top_srcdir)/Makefile.options +# Deliberately did not include async_list.c and req_malloc.c because +# MPICH2-1.0.7 doesn't compile async_list.c and req_malloc.c is only +# required for MPIX_Grequest stuff (which is MPICH2-specific). + noinst_LTLIBRARIES = libadio_common.la libadio_common_la_SOURCES = \ ad_aggregate.c \ @@ -53,20 +58,21 @@ libadio_common_la_SOURCES = \ ad_wait_fake.c \ ad_write.c \ ad_write_coll.c \ + ad_write_nolock.c \ ad_write_str.c \ ad_write_str_naive.c \ adi_close.c \ - async_list.c \ byte_offset.c \ cb_config_list.c \ eof_offset.c \ error.c \ flatten.c \ get_fp_posn.c \ + greq_fns.c \ iscontig.c \ lock.c \ malloc.c \ - req_malloc.c \ shfp_fname.c \ status_setb.c \ - strfns.c + strfns.c \ + system_hints.c diff --git a/ompi/mca/io/romio/romio/adio/common/ad_aggregate.c b/ompi/mca/io/romio/romio/adio/common/ad_aggregate.c index b281a21580..7252f50e2c 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_aggregate.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_aggregate.c @@ -6,9 +6,6 @@ #include "adio.h" #include "adio_extern.h" -#ifdef PROFILE -#include "mpe.h" -#endif #undef AGG_DEBUG @@ -128,6 +125,10 @@ void ADIOI_Calc_file_domains(ADIO_Offset *st_offsets, ADIO_Offset done by (logically) dividing the file into file domains (FDs); each process may directly access only its own file domain. */ + /* XXX: one idea: tweak the file domains so that no fd is smaller than + * a threshold (one presumably well-suited to a file system). We don't + * do that, but this routine would be the place for it */ + ADIO_Offset min_st_offset, max_end_offset, *fd_start, *fd_end, fd_size; int i; @@ -367,7 +368,7 @@ void ADIOI_Calc_others_req(ADIO_File fd, int count_my_req_procs, int *count_others_req_per_proc, count_others_req_procs; int i, j; - MPI_Request *send_requests, *recv_requests; + MPI_Request *requests; MPI_Status *statuses; ADIOI_Access *others_req; @@ -399,47 +400,40 @@ void ADIOI_Calc_others_req(ADIO_File fd, int count_my_req_procs, /* now send the calculated offsets and lengths to respective processes */ - send_requests = (MPI_Request *) - ADIOI_Malloc(2*(count_my_req_procs+1)*sizeof(MPI_Request)); - recv_requests = (MPI_Request *) - ADIOI_Malloc(2*(count_others_req_procs+1)*sizeof(MPI_Request)); + requests = (MPI_Request *) + ADIOI_Malloc(1+2*(count_my_req_procs+count_others_req_procs)*sizeof(MPI_Request)); /* +1 to avoid a 0-size malloc */ j = 0; for (i=0; icomm, &recv_requests[j]); + ADIO_OFFSET, i, i+myrank, fd->comm, &requests[j]); j++; MPI_Irecv(others_req[i].lens, others_req[i].count, - MPI_INT, i, i+myrank+1, fd->comm, &recv_requests[j]); + MPI_INT, i, i+myrank+1, fd->comm, &requests[j]); j++; } } - j = 0; for (i=0; i < nprocs; i++) { if (my_req[i].count) { MPI_Isend(my_req[i].offsets, my_req[i].count, - ADIO_OFFSET, i, i+myrank, fd->comm, &send_requests[j]); + ADIO_OFFSET, i, i+myrank, fd->comm, &requests[j]); j++; MPI_Isend(my_req[i].lens, my_req[i].count, - MPI_INT, i, i+myrank+1, fd->comm, &send_requests[j]); + MPI_INT, i, i+myrank+1, fd->comm, &requests[j]); j++; } } - statuses = (MPI_Status *) ADIOI_Malloc((1 + 2* \ - ADIOI_MAX(count_my_req_procs,count_others_req_procs)) * \ - sizeof(MPI_Status)); -/* +1 to avoid a 0-size malloc */ + if (j) { + statuses = (MPI_Status *) ADIOI_Malloc(j * sizeof(MPI_Status)); + MPI_Waitall(j, requests, statuses); + ADIOI_Free(statuses); + } - MPI_Waitall(2*count_my_req_procs, send_requests, statuses); - MPI_Waitall(2*count_others_req_procs, recv_requests, statuses); - - ADIOI_Free(send_requests); - ADIOI_Free(recv_requests); - ADIOI_Free(statuses); + ADIOI_Free(requests); ADIOI_Free(count_others_req_per_proc); *count_others_req_procs_ptr = count_others_req_procs; diff --git a/ompi/mca/io/romio/romio/adio/common/ad_close.c b/ompi/mca/io/romio/romio/adio/common/ad_close.c index 0176907b3c..a6e12fac28 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_close.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_close.c @@ -12,10 +12,6 @@ #include #endif -#ifdef PROFILE -#include "mpe.h" -#endif - void ADIO_Close(ADIO_File fd, int *error_code) { int i, j, k, combiner, myrank, err, is_contig; @@ -65,9 +61,9 @@ void ADIO_Close(ADIO_File fd, int *error_code) ADIOI_Ftable[fd->fortran_handle] = MPI_FILE_NULL; } - ADIOI_Free(fd->hints->ranklist); - ADIOI_Free(fd->hints->cb_config_list); - ADIOI_Free(fd->hints); + if (fd->hints && fd->hints->ranklist) ADIOI_Free(fd->hints->ranklist); + if (fd->hints && fd->hints->cb_config_list) ADIOI_Free(fd->hints->cb_config_list); + if (fd->hints) ADIOI_Free(fd->hints); MPI_Comm_free(&(fd->comm)); /* deferred open: if we created an aggregator communicator, free it */ if (fd->agg_comm != MPI_COMM_NULL) { diff --git a/ompi/mca/io/romio/romio/adio/common/ad_darray.c b/ompi/mca/io/romio/romio/adio/common/ad_darray.c index 0d78e2ea35..b9b3c46d7d 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_darray.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_darray.c @@ -8,11 +8,11 @@ #include "adio.h" #include "adio_extern.h" -int MPIOI_Type_block(int *array_of_gsizes, int dim, int ndims, int nprocs, +static int MPIOI_Type_block(int *array_of_gsizes, int dim, int ndims, int nprocs, int rank, int darg, int order, MPI_Aint orig_extent, MPI_Datatype type_old, MPI_Datatype *type_new, MPI_Aint *st_offset); -int MPIOI_Type_cyclic(int *array_of_gsizes, int dim, int ndims, int nprocs, +static int MPIOI_Type_cyclic(int *array_of_gsizes, int dim, int ndims, int nprocs, int rank, int darg, int order, MPI_Aint orig_extent, MPI_Datatype type_old, MPI_Datatype *type_new, MPI_Aint *st_offset); @@ -146,7 +146,7 @@ int ADIO_Type_create_darray(int size, int rank, int ndims, /* Returns MPI_SUCCESS on success, an MPI error code on failure. Code above * needs to call MPIO_Err_return_xxx. */ -int MPIOI_Type_block(int *array_of_gsizes, int dim, int ndims, int nprocs, +static int MPIOI_Type_block(int *array_of_gsizes, int dim, int ndims, int nprocs, int rank, int darg, int order, MPI_Aint orig_extent, MPI_Datatype type_old, MPI_Datatype *type_new, MPI_Aint *st_offset) @@ -208,7 +208,7 @@ int MPIOI_Type_block(int *array_of_gsizes, int dim, int ndims, int nprocs, /* Returns MPI_SUCCESS on success, an MPI error code on failure. Code above * needs to call MPIO_Err_return_xxx. */ -int MPIOI_Type_cyclic(int *array_of_gsizes, int dim, int ndims, int nprocs, +static int MPIOI_Type_cyclic(int *array_of_gsizes, int dim, int ndims, int nprocs, int rank, int darg, int order, MPI_Aint orig_extent, MPI_Datatype type_old, MPI_Datatype *type_new, MPI_Aint *st_offset) diff --git a/ompi/mca/io/romio/romio/adio/common/ad_done.c b/ompi/mca/io/romio/romio/adio/common/ad_done.c index c4f87941f3..43d7a66b1d 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_done.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_done.c @@ -13,6 +13,9 @@ #ifdef HAVE_SIGNAL_H #include #endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif #ifdef HAVE_AIO_H #include #endif @@ -39,117 +42,6 @@ int ADIOI_GEN_IODone(ADIO_Request *request, ADIO_Status *status, int *error_code) { -#ifdef ROMIO_HAVE_WORKING_AIO - int done=0; - int err; - static char myname[] = "ADIOI_GEN_IODONE"; -#ifdef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_HANDLE - struct aiocb *tmp1; -#endif -#endif + return 0; - if (*request == ADIO_REQUEST_NULL) { - *error_code = MPI_SUCCESS; - return 1; - } - -#ifndef ROMIO_HAVE_WORKING_AIO -#ifdef HAVE_STATUS_SET_BYTES - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - (*request)->fd->async_count--; - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; - *error_code = MPI_SUCCESS; - return 1; - -#else /* matches ifndef ROMIO_HAVE_WORKING_AIO */ - -#ifndef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_FILDES -/* old IBM API */ - if ((*request)->queued) { - tmp1 = (struct aiocb *) (*request)->handle; - errno = aio_error(tmp1->aio_handle); - if (errno == EINPROG) { - done = 0; - *error_code = MPI_SUCCESS; - } - else { - err = aio_return(tmp1->aio_handle); - (*request)->nbytes = err; - errno = aio_error(tmp1->aio_handle); - - done = 1; - - if (err == -1) { - *error_code = MPIO_Err_create_code(MPI_SUCCESS, - MPIR_ERR_RECOVERABLE, - myname, __LINE__, - MPI_ERR_IO, "**io", - "**io %s", strerror(errno)); - return; - } - else *error_code = MPI_SUCCESS; - } - } /* if ((*request)->queued) */ - else { - done = 1; - *error_code = MPI_SUCCESS; - } -#ifdef HAVE_STATUS_SET_BYTES - if (done && ((*request)->nbytes != -1)) - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - -#else /* matches ifndef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_HANDLE */ - -/* everthing other than old IBM */ - if ((*request)->queued) { - errno = aio_error((const struct aiocb *) (*request)->handle); - if (errno == EINPROGRESS) { - done = 0; - *error_code = MPI_SUCCESS; - } - else { - err = aio_return((struct aiocb *) (*request)->handle); - (*request)->nbytes = err; - errno = aio_error((struct aiocb *) (*request)->handle); - - done = 1; - - if (err == -1) { - *error_code = MPIO_Err_create_code(MPI_SUCCESS, - MPIR_ERR_RECOVERABLE, - myname, __LINE__, - MPI_ERR_IO, "**io", - "**io %s", strerror(errno)); - return 1; - } - else *error_code = MPI_SUCCESS; - } - } /* if ((*request)->queued) */ - else { - done = 1; - *error_code = MPI_SUCCESS; - } -#ifdef HAVE_STATUS_SET_BYTES - if (done && ((*request)->nbytes != -1)) - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - -#endif /* matches ifndef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_HANDLE */ - - if (done) { - /* if request is still queued in the system, it is also there - on ADIOI_Async_list. Delete it from there. */ - if ((*request)->queued) ADIOI_Del_req_from_list(request); - - (*request)->fd->async_count--; - if ((*request)->handle) ADIOI_Free((*request)->handle); - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; - } - return done; - -#endif /* matches ifndef ROMIO_HAVE_WORKING_AIO */ } diff --git a/ompi/mca/io/romio/romio/adio/common/ad_done_fake.c b/ompi/mca/io/romio/romio/adio/common/ad_done_fake.c index 5ba7f3886b..0f707f2fde 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_done_fake.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_done_fake.c @@ -15,16 +15,6 @@ int ADIOI_FAKE_IODone(ADIO_Request *request, ADIO_Status *status, int *error_code) { - if (*request != ADIO_REQUEST_NULL) { -#ifdef HAVE_STATUS_SET_BYTES - MPIR_Status_set_bytes(status, (*request)->datatype, - (*request)->nbytes); -#endif - (*request)->fd->async_count--; - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; - } - - *error_code = MPI_SUCCESS; - return 1; + /* should not ever get called now */ + return 1; } diff --git a/ompi/mca/io/romio/romio/adio/common/ad_end.c b/ompi/mca/io/romio/romio/adio/common/ad_end.c index 7fa72c3c28..3b0778a659 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_end.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_end.c @@ -13,10 +13,7 @@ void ADIO_End(int *error_code) { ADIOI_Flatlist_node *curr, *next; - ADIOI_Malloc_async *tmp; - ADIOI_Malloc_req *tmp1; ADIOI_Datarep *datarep, *datarep_next; - static char myname[] = "ADIO_END"; /* FPRINTF(stderr, "reached end\n"); */ @@ -31,39 +28,8 @@ void ADIO_End(int *error_code) } ADIOI_Flatlist = NULL; - /* --BEGIN ERROR HANDLING-- */ - if (ADIOI_Async_list_head) { - *error_code = MPIO_Err_create_code(MPI_SUCCESS, - MPIR_ERR_RECOVERABLE, - myname, __LINE__, - MPI_ERR_IO, - "Error: outstanding nonblocking I/O operations", 0); - return; - } - /* --END ERROR HANDLING-- */ - -/* free list of available ADIOI_Async_nodes. */ - while (ADIOI_Malloc_async_head) { - ADIOI_Free(ADIOI_Malloc_async_head->ptr); - tmp = ADIOI_Malloc_async_head; - ADIOI_Malloc_async_head = ADIOI_Malloc_async_head->next; - ADIOI_Free(tmp); - } - ADIOI_Async_avail_head = ADIOI_Async_avail_tail = NULL; - ADIOI_Malloc_async_head = ADIOI_Malloc_async_tail = NULL; - -/* free all available request objects */ - while (ADIOI_Malloc_req_head) { - ADIOI_Free(ADIOI_Malloc_req_head->ptr); - tmp1 = ADIOI_Malloc_req_head; - ADIOI_Malloc_req_head = ADIOI_Malloc_req_head->next; - ADIOI_Free(tmp1); - } - ADIOI_Malloc_req_head = ADIOI_Malloc_req_tail = NULL; - -/* free file, request, and info tables used for Fortran interface */ +/* free file and info tables used for Fortran interface */ if (ADIOI_Ftable) ADIOI_Free(ADIOI_Ftable); - if (ADIOI_Reqtable) ADIOI_Free(ADIOI_Reqtable); #ifndef HAVE_MPI_INFO if (MPIR_Infotable) ADIOI_Free(MPIR_Infotable); #endif diff --git a/ompi/mca/io/romio/romio/adio/common/ad_fcntl.c b/ompi/mca/io/romio/romio/adio/common/ad_fcntl.c index aab3f06dc1..f526c71d76 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_fcntl.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_fcntl.c @@ -18,9 +18,22 @@ void ADIOI_GEN_Fcntl(ADIO_File fd, int flag, ADIO_Fcntl_t *fcntl_struct, switch(flag) { case ADIO_FCNTL_GET_FSIZE: +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif fcntl_struct->fsize = lseek(fd->fd_sys, 0, SEEK_END); - if (fd->fp_sys_posn != -1) +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + if (fd->fp_sys_posn != -1) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif lseek(fd->fd_sys, fd->fp_sys_posn, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + } if (fcntl_struct->fsize == -1) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, diff --git a/ompi/mca/io/romio/romio/adio/common/ad_fstype.c b/ompi/mca/io/romio/romio/adio/common/ad_fstype.c index 99cf1c573c..45ae974c49 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_fstype.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_fstype.c @@ -66,6 +66,10 @@ # define XFS_SUPER_MAGIC 0x58465342 # endif +#if !defined(PVFS2_SUPER_MAGIC) +#define PVFS2_SUPER_MAGIC (0x20030528) +#endif + #ifdef ROMIO_HAVE_STRUCT_STATVFS_WITH_F_BASETYPE # ifdef HAVE_SYS_STATVFS_H # include @@ -90,9 +94,17 @@ # endif #endif +/* ADIO_FileSysType_parentdir is only used if one of these is defined. + By including this test, we avoid warnings about unused static functions + from the compiler */ +#if defined(ROMIO_HAVE_STRUCT_STATVFS_WITH_F_BASETYPE) || \ + defined(HAVE_STRUCT_STATFS) || \ + defined(ROMIO_HAVE_STRUCT_STAT_WITH_ST_FSTYPE) #ifndef ROMIO_NTFS +#define ROMIO_NEEDS_ADIOPARENTDIR static void ADIO_FileSysType_parentdir(char *filename, char **dirnamep); #endif +#endif static void ADIO_FileSysType_prefix(char *filename, int *fstype, int *error_code); static void ADIO_FileSysType_fncall(char *filename, int *fstype, @@ -111,7 +123,8 @@ Output Parameters: Note that the caller should free the memory located at the pointer returned after the string is no longer needed. */ -#ifndef ROMIO_NTFS +#ifdef ROMIO_NEEDS_ADIOPARENTDIR + #ifndef PATH_MAX #define PATH_MAX 65535 #endif @@ -128,7 +141,7 @@ Output Parameters: /* no way to check if it is a link, so say false */ # define S_ISLNK(mode) 0 # endif -#endif +#endif /* !(S_ISLNK) */ /* ADIO_FileSysType_parentdir * @@ -187,6 +200,17 @@ static void ADIO_FileSysType_parentdir(char *filename, char **dirnamep) } #endif /* ROMIO_NTFS */ +#ifdef ROMIO_BGL /* BlueGene support for pvfs through ufs */ +static void check_for_lockless_exceptions(long stat_type, int *fstype) +{ + /* exception for lockless PVFS file system. PVFS is the only exception we + * make right now, but any future FS developers looking to override + * BlueGene fs detection can do it here */ + if (stat_type == PVFS2_SUPER_MAGIC) + /* use lock-free driver on bluegene to support pvfs */ + *fstype = ADIO_BGLOCKLESS; +} +#endif /* ADIO_FileSysType_fncall - determines the file system type for a given file using a system-dependent function call @@ -300,6 +324,16 @@ static void ADIO_FileSysType_fncall(char *filename, int *fstype, int *error_code return; } # endif + +# ifdef ROMIO_BGL + /* BlueGene is a special case: all file systems are AD_BGL, except for + * certain exceptions */ + *fstype = ADIO_BGL; + check_for_lockless_exceptions(fsbuf.f_type, fstype); + *error_code = MPI_SUCCESS; + return; +# endif + /* FPRINTF(stderr, "%d\n", fsbuf.f_type);*/ # ifdef NFS_SUPER_MAGIC if (fsbuf.f_type == NFS_SUPER_MAGIC) { @@ -308,6 +342,15 @@ static void ADIO_FileSysType_fncall(char *filename, int *fstype, int *error_code } # endif +/*#if defined(LINUX) && defined(ROMIO_LUSTRE)*/ +#ifdef ROMIO_LUSTRE +#define LL_SUPER_MAGIC 0x0BD00BD0 + if (fsbuf.f_type == LL_SUPER_MAGIC) { + *fstype = ADIO_LUSTRE; + return; + } +# endif + # ifdef PAN_KERNEL_FS_CLIENT_SUPER_MAGIC if (fsbuf.f_type == PAN_KERNEL_FS_CLIENT_SUPER_MAGIC) { *fstype = ADIO_PANFS; @@ -397,6 +440,26 @@ static void ADIO_FileSysType_fncall(char *filename, int *fstype, int *error_code #endif } +/* all proceeses opening, creating, or deleting a file end up invoking several + * stat system calls (unless a fs prefix is given). Cary out this file system + * detection in a more scalable way by having rank 0 stat the file and broadcast the result (fs type and error code) to the other mpi processes */ + +static void ADIO_FileSysType_fncall_scalable(MPI_Comm comm, char *filename, int * file_system, int * error_code) +{ + int rank; + int buf[2]; + MPI_Comm_rank(comm, &rank); + + if (rank == 0) { + ADIO_FileSysType_fncall(filename, file_system, error_code); + buf[0] = *file_system; + buf[1] = *error_code; + } + MPI_Bcast(buf, 2, MPI_INT, 0, comm); + *file_system = buf[0]; + *error_code = buf[1]; +} + /* ADIO_FileSysType_prefix - determines file system type for a file using a prefix on the file name. upper layer should have already determined @@ -458,6 +521,18 @@ static void ADIO_FileSysType_prefix(char *filename, int *fstype, int *error_code { *fstype = ADIO_GRIDFTP; } + else if (!strncmp(filename, "lustre:", 7) + || !strncmp(filename, "LUSTRE:", 7)) + { + *fstype = ADIO_LUSTRE; + } + else if (!strncmp(filename, "bgl:", 4) || !strncmp(filename, "BGL:", 4)) { + *fstype = ADIO_BGL; + } + else if (!strncmp(filename, "bglockless:", 11) || + !strncmp(filename, "BGLOCKLESS:", 11)) { + *fstype = ADIO_BGLOCKLESS; + } else { #ifdef ROMIO_NTFS *fstype = ADIO_NTFS; @@ -494,23 +569,65 @@ tables in a reasonable way. -- Rob, 06/06/2001 void ADIO_ResolveFileType(MPI_Comm comm, char *filename, int *fstype, ADIOI_Fns **ops, int *error_code) { - int myerrcode, file_system, min_code; + int myerrcode, file_system, min_code, max_code; char *tmp; static char myname[] = "ADIO_RESOLVEFILETYPE"; file_system = -1; tmp = strchr(filename, ':'); if (!tmp) { + int have_nfs_enabled=0; + *error_code = MPI_SUCCESS; /* no prefix; use system-dependent function call to determine type */ - ADIO_FileSysType_fncall(filename, &file_system, &myerrcode); - if (myerrcode != MPI_SUCCESS) { - *error_code = myerrcode; - return; - } + /* Optimization: we can reduce the 'storm of stats' that result from + * thousands of mpi processes determinig file type this way. Let us + * have just one process stat the file and broadcast the result to + * everyone else. + * - Note that we will not catch cases like + * http://www.mcs.anl.gov/web-mail-archive/lists/mpich-discuss/2007/08/msg00042.html + * where file systems are not mounted or available on other processes, + * but we'll catch those a few functions later in ADIO_Open + * - Note that if we have NFS enabled, we might have a situation where, + * for example, /home/user/data.out is UFS on one process but NFS on + * others, so we won't perform this optimization if NFS is enabled. + * - Another point: error codes and file system types are broadcast to + * all members of the communicator, so we get to skip the allreduce + * steps*/ - /* ensure that everyone came up with the same file system type */ - MPI_Allreduce(&file_system, &min_code, 1, MPI_INT, MPI_MIN, comm); - if (min_code == ADIO_NFS) file_system = ADIO_NFS; +#ifdef ROMIO_NFS + have_nfs_enabled=1; +#endif + if (!have_nfs_enabled) { + ADIO_FileSysType_fncall_scalable(comm, filename, &file_system, &myerrcode); + if (myerrcode != MPI_SUCCESS) { + *error_code = myerrcode; + return; + } + } else { + ADIO_FileSysType_fncall(filename, &file_system, &myerrcode); + if (myerrcode != MPI_SUCCESS) { + *error_code = myerrcode; + + /* the check for file system type will hang if any process got + * an error in ADIO_FileSysType_fncall. Processes encountering + * an error will return early, before the collective file + * system type check below. This case could happen if a full + * path exists on one node but not on others, and no prefix + * like ufs: was provided. see discussion at + * http://www.mcs.anl.gov/web-mail-archive/lists/mpich-discuss/2007/08/msg00042.html + */ + + MPI_Allreduce(error_code, &max_code, 1, MPI_INT, MPI_MAX, comm); + if (max_code != MPI_SUCCESS) { + *error_code = max_code; + return; + } + /* ensure everyone came up with the same file system type */ + MPI_Allreduce(&file_system, &min_code, 1, MPI_INT, + MPI_MIN, comm); + if (min_code == ADIO_NFS) file_system = ADIO_NFS; + } + } } else { @@ -647,6 +764,27 @@ void ADIO_ResolveFileType(MPI_Comm comm, char *filename, int *fstype, *ops = &ADIO_TESTFS_operations; #endif } + if (file_system == ADIO_BGL) { +#ifndef ROMIO_BGL + *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, + myname, __LINE__, MPI_ERR_IO, + "**iofstypeunsupported", 0); + return; +#else + *ops = &ADIO_BGL_operations; +#endif + } + if (file_system == ADIO_BGLOCKLESS) { +#ifndef ROMIO_BGLOCKLESS + *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, + myname, __LINE__, MPI_ERR_IO, + "**iofstypeunsupported", 0); + return; +#else + *ops = &ADIO_BGLOCKLESS_operations; +#endif + } + if (file_system == ADIO_GRIDFTP) { #ifndef ROMIO_GRIDFTP *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, @@ -655,9 +793,20 @@ void ADIO_ResolveFileType(MPI_Comm comm, char *filename, int *fstype, return; #else *ops = &ADIO_GRIDFTP_operations; +#endif + } + if (file_system == ADIO_LUSTRE) { +#ifndef ROMIO_LUSTRE + *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, MPI_ERR_IO, "**iofstypeunsupported", 0); + return; +#else + *ops = &ADIO_LUSTRE_operations; #endif } *error_code = MPI_SUCCESS; *fstype = file_system; return; } +/* + * vim: ts=8 sts=4 sw=4 noexpandtab + */ diff --git a/ompi/mca/io/romio/romio/adio/common/ad_get_sh_fp.c b/ompi/mca/io/romio/romio/adio/common/ad_get_sh_fp.c index e336837588..6e2ba6f9a3 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_get_sh_fp.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_get_sh_fp.c @@ -28,6 +28,14 @@ void ADIO_Get_shared_fp(ADIO_File fd, int incr, ADIO_Offset *shared_fp, } #endif +#ifdef ROMIO_BGL + /* BGLOCKLESS won't support shared fp */ + if (fd->file_system == ADIO_BGL) { + ADIOI_BGL_Get_shared_fp(fd, incr, shared_fp, error_code); + return; + } +#endif + if (fd->shared_fp_fd == ADIO_FILE_NULL) { MPI_Comm_dup(MPI_COMM_SELF, &dupcommself); fd->shared_fp_fd = ADIO_Open(MPI_COMM_SELF, dupcommself, diff --git a/ompi/mca/io/romio/romio/adio/common/ad_hints.c b/ompi/mca/io/romio/romio/adio/common/ad_hints.c index 0f6093072f..c221b6b4d3 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_hints.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_hints.c @@ -272,7 +272,7 @@ void ADIOI_GEN_SetInfo(ADIO_File fd, MPI_Info users_info, int *error_code) MPI_Comm_size(fd->comm, &nprocs); nprocs_is_valid = 1; } - if (intval < nprocs) { + if (intval <= nprocs) { MPI_Info_set(info, "cb_nodes", value); fd->hints->cb_nodes = intval; } diff --git a/ompi/mca/io/romio/romio/adio/common/ad_init.c b/ompi/mca/io/romio/romio/adio/common/ad_init.c index 1270bea91a..1ca0cbb2f9 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_init.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_init.c @@ -9,20 +9,6 @@ #include "adio_extern.h" ADIOI_Flatlist_node *ADIOI_Flatlist = NULL; -ADIOI_Async_node *ADIOI_Async_list_head = NULL, *ADIOI_Async_list_tail = NULL; - /* list of outstanding asynchronous requests */ -ADIOI_Async_node *ADIOI_Async_avail_head = NULL, - *ADIOI_Async_avail_tail = NULL; - /* list of available (already malloced) nodes for above async list */ -ADIOI_Malloc_async *ADIOI_Malloc_async_head = NULL, - *ADIOI_Malloc_async_tail = NULL; - /* list of malloced areas for async_list, which must be freed in ADIO_End */ - -ADIOI_Req_node *ADIOI_Req_avail_head = NULL, *ADIOI_Req_avail_tail = NULL; - /* list of available (already malloced) request objects */ -ADIOI_Malloc_req *ADIOI_Malloc_req_head = NULL, *ADIOI_Malloc_req_tail = NULL; - /* list of malloced areas for requests, which must be freed in ADIO_End */ - ADIOI_Datarep *ADIOI_Datarep_head = NULL; /* list of datareps registered by the user */ @@ -36,7 +22,7 @@ MPI_Info *MPIR_Infotable = NULL; int MPIR_Infotable_ptr = 0, MPIR_Infotable_max = 0; #endif -#ifdef ROMIO_XFS +#if defined(ROMIO_XFS) || defined(ROMIO_LUSTRE) int ADIOI_Direct_read = 0, ADIOI_Direct_write = 0; #endif @@ -46,7 +32,7 @@ MPI_Errhandler ADIOI_DFLT_ERR_HANDLER = MPI_ERRORS_RETURN; void ADIO_Init(int *argc, char ***argv, int *error_code) { -#ifdef ROMIO_XFS +#if defined(ROMIO_XFS) || defined(ROMIO_LUSTRE) char *c; #endif @@ -60,7 +46,7 @@ void ADIO_Init(int *argc, char ***argv, int *error_code) ADIOI_Flatlist->blocklens = NULL; ADIOI_Flatlist->indices = NULL; -#ifdef ROMIO_XFS +#if defined(ROMIO_XFS) || defined(ROMIO_LUSTRE) c = getenv("MPIO_DIRECT_READ"); if (c && (!strcmp(c, "true") || !strcmp(c, "TRUE"))) ADIOI_Direct_read = 1; @@ -71,5 +57,46 @@ void ADIO_Init(int *argc, char ***argv, int *error_code) else ADIOI_Direct_write = 0; #endif +#ifdef ADIOI_MPE_LOGGING + { + MPE_Log_get_state_eventIDs( &ADIOI_MPE_open_a, &ADIOI_MPE_open_b ); + MPE_Log_get_state_eventIDs( &ADIOI_MPE_read_a, &ADIOI_MPE_read_b ); + MPE_Log_get_state_eventIDs( &ADIOI_MPE_write_a, &ADIOI_MPE_write_b ); + MPE_Log_get_state_eventIDs( &ADIOI_MPE_lseek_a, &ADIOI_MPE_lseek_b ); + MPE_Log_get_state_eventIDs( &ADIOI_MPE_close_a, &ADIOI_MPE_close_b ); + MPE_Log_get_state_eventIDs( &ADIOI_MPE_writelock_a, + &ADIOI_MPE_writelock_b ); + MPE_Log_get_state_eventIDs( &ADIOI_MPE_readlock_a, + &ADIOI_MPE_readlock_b ); + MPE_Log_get_state_eventIDs( &ADIOI_MPE_unlock_a, &ADIOI_MPE_unlock_b ); + MPE_Log_get_state_eventIDs( &ADIOI_MPE_postwrite_a, + &ADIOI_MPE_postwrite_b ); + + int comm_world_rank; + PMPI_Comm_rank( MPI_COMM_WORLD, &comm_world_rank ); + + if ( comm_world_rank == 0 ) { + MPE_Describe_state( ADIOI_MPE_open_a, ADIOI_MPE_open_b, + "open", "orange" ); + MPE_Describe_state( ADIOI_MPE_read_a, ADIOI_MPE_read_b, + "read", "green" ); + MPE_Describe_state( ADIOI_MPE_write_a, ADIOI_MPE_write_b, + "write", "blue" ); + MPE_Describe_state( ADIOI_MPE_lseek_a, ADIOI_MPE_lseek_b, + "lseek", "red" ); + MPE_Describe_state( ADIOI_MPE_close_a, ADIOI_MPE_close_b, + "close", "grey" ); + MPE_Describe_state( ADIOI_MPE_writelock_a, ADIOI_MPE_writelock_b, + "writelock", "plum" ); + MPE_Describe_state( ADIOI_MPE_readlock_a, ADIOI_MPE_readlock_b, + "readlock", "magenta" ); + MPE_Describe_state( ADIOI_MPE_unlock_a, ADIOI_MPE_unlock_b, + "unlock", "purple" ); + MPE_Describe_state( ADIOI_MPE_postwrite_a, ADIOI_MPE_postwrite_b, + "postwrite", "ivory" ); + } + } +#endif + *error_code = MPI_SUCCESS; } diff --git a/ompi/mca/io/romio/romio/adio/common/ad_iread.c b/ompi/mca/io/romio/romio/adio/common/ad_iread.c index 6561310def..34fce2c1da 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_iread.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_iread.c @@ -13,6 +13,9 @@ #ifdef HAVE_SIGNAL_H #include #endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif #ifdef HAVE_AIO_H #include #endif @@ -20,6 +23,9 @@ #include #endif +#include "mpiu_greq.h" + +#ifdef ROMIO_HAVE_WORKING_AIO /* ADIOI_GEN_IreadContig * * This code handles two distinct cases. If ROMIO_HAVE_WORKING_AIO is not @@ -31,48 +37,20 @@ */ void ADIOI_GEN_IreadContig(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, - ADIO_Offset offset, ADIO_Request *request, + ADIO_Offset offset, MPI_Request *request, int *error_code) { int len, typesize; -#ifndef ROMIO_HAVE_WORKING_AIO - ADIO_Status status; -#else int aio_errno = 0; static char myname[] = "ADIOI_GEN_IREADCONTIG"; -#endif - - (*request) = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_READ; - (*request)->fd = fd; - (*request)->datatype = datatype; MPI_Type_size(datatype, &typesize); len = count * typesize; -#ifndef ROMIO_HAVE_WORKING_AIO - /* no support for nonblocking I/O. Use blocking I/O. */ - - ADIO_ReadContig(fd, buf, len, MPI_BYTE, file_ptr_type, offset, - &status, error_code); - (*request)->queued = 0; -#ifdef HAVE_STATUS_SET_BYTES - if (*error_code == MPI_SUCCESS) { - MPI_Get_elements(&status, MPI_BYTE, &len); - (*request)->nbytes = len; - } -#endif - - fd->fp_sys_posn = -1; - -#else if (file_ptr_type == ADIO_INDIVIDUAL) offset = fd->fp_ind; - aio_errno = ADIOI_GEN_aio(fd, buf, len, offset, 0, &((*request)->handle)); + aio_errno = ADIOI_GEN_aio(fd, buf, len, offset, 0, request); if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind += len; - (*request)->queued = 1; - ADIOI_Add_req_to_list(request); - fd->fp_sys_posn = -1; /* --BEGIN ERROR HANDLING-- */ @@ -83,11 +61,8 @@ void ADIOI_GEN_IreadContig(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ *error_code = MPI_SUCCESS; -#endif /* NO_AIO */ - - fd->async_count++; } - +#endif /* Generic implementation of IreadStrided calls the blocking ReadStrided * immediately. @@ -98,16 +73,8 @@ void ADIOI_GEN_IreadStrided(ADIO_File fd, void *buf, int count, int *error_code) { ADIO_Status status; -#ifdef HAVE_STATUS_SET_BYTES int typesize; -#endif - - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_READ; - (*request)->fd = fd; - (*request)->datatype = datatype; - (*request)->queued = 0; - (*request)->handle = 0; + MPI_Offset nbytes=0; /* Call the blocking function. It will create an error code * if necessary. @@ -115,12 +82,9 @@ void ADIOI_GEN_IreadStrided(ADIO_File fd, void *buf, int count, ADIO_ReadStrided(fd, buf, count, datatype, file_ptr_type, offset, &status, error_code); - fd->async_count++; - -#ifdef HAVE_STATUS_SET_BYTES if (*error_code == MPI_SUCCESS) { MPI_Type_size(datatype, &typesize); - (*request)->nbytes = count * typesize; + nbytes = count*typesize; } -#endif + MPIO_Completed_request_create(&fd, nbytes, error_code, request); } diff --git a/ompi/mca/io/romio/romio/adio/common/ad_iread_fake.c b/ompi/mca/io/romio/romio/adio/common/ad_iread_fake.c index 8d594ec1d8..1a35164ccc 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_iread_fake.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_iread_fake.c @@ -6,6 +6,7 @@ */ #include "adio.h" +#include "mpiu_greq.h" /* Generic implementation of IreadContig calls the blocking ReadContig * immediately. @@ -16,13 +17,8 @@ void ADIOI_FAKE_IreadContig(ADIO_File fd, void *buf, int count, int *error_code) { ADIO_Status status; - int len, typesize; - - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_READ; - (*request)->fd = fd; - (*request)->queued = 0; - (*request)->datatype = datatype; + int typesize; + MPI_Offset len; MPI_Type_size(datatype, &typesize); len = count * typesize; @@ -32,15 +28,10 @@ void ADIOI_FAKE_IreadContig(ADIO_File fd, void *buf, int count, */ ADIO_ReadContig(fd, buf, len, MPI_BYTE, file_ptr_type, offset, &status, error_code); - - fd->async_count++; - -#ifdef HAVE_STATUS_SET_BYTES - if (*error_code == MPI_SUCCESS) { - MPI_Get_elements(&status, MPI_BYTE, &len); - (*request)->nbytes = len; + if (*error_code != MPI_SUCCESS) { + len=0; } -#endif + MPIO_Completed_request_create(&fd, len, error_code, request); } @@ -53,29 +44,17 @@ void ADIOI_FAKE_IreadStrided(ADIO_File fd, void *buf, int count, int *error_code) { ADIO_Status status; -#ifdef HAVE_STATUS_SET_BYTES int typesize; -#endif - - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_READ; - (*request)->fd = fd; - (*request)->datatype = datatype; - (*request)->queued = 0; - (*request)->handle = 0; + MPI_Offset nbytes=0; /* Call the blocking function. It will create an error code * if necessary. */ ADIO_ReadStrided(fd, buf, count, datatype, file_ptr_type, offset, &status, error_code); - - fd->async_count++; - -#ifdef HAVE_STATUS_SET_BYTES if (*error_code == MPI_SUCCESS) { MPI_Type_size(datatype, &typesize); - (*request)->nbytes = count * typesize; + nbytes = count*typesize; } -#endif + MPIO_Completed_request_create(&fd, nbytes, error_code, request); } diff --git a/ompi/mca/io/romio/romio/adio/common/ad_iwrite.c b/ompi/mca/io/romio/romio/adio/common/ad_iwrite.c index 9cdd998884..e97a062960 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_iwrite.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_iwrite.c @@ -13,24 +13,36 @@ #ifdef HAVE_SIGNAL_H #include #endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif #ifdef HAVE_AIO_H #include #endif #ifdef HAVE_SYS_AIO_H #include #endif +#include +#include "../../mpi-io/mpioimpl.h" +#include "../../mpi-io/mpioprof.h" +#include "mpiu_greq.h" /* Workaround for incomplete set of definitions if __REDIRECT is not defined and large file support is used in aio.h */ #if !defined(__REDIRECT) && defined(__USE_FILE_OFFSET64) #define aiocb aiocb64 #endif +#ifdef ROMIO_HAVE_WORKING_AIO + +static MPIX_Grequest_class ADIOI_GEN_greq_class = 0; + /* ADIOI_GEN_IwriteContig * - * This code handles two distinct cases. If ROMIO_HAVE_WORKING_AIO is not - * defined, then I/O is performed in a blocking manner. Otherwise we post - * an asynchronous I/O operations using the appropriate aio routines. + * This code handles only the case where ROMIO_HAVE_WORKING_AIO is + * defined. We post an asynchronous I/O operations using the appropriate aio + * routines. Otherwise, the ADIOI_Fns_struct will point to the FAKE + * version. */ void ADIOI_GEN_IwriteContig(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, @@ -38,44 +50,16 @@ void ADIOI_GEN_IwriteContig(ADIO_File fd, void *buf, int count, int *error_code) { int len, typesize; -#ifndef ROMIO_HAVE_WORKING_AIO - ADIO_Status status; -#else int aio_errno = 0; static char myname[] = "ADIOI_GEN_IWRITECONTIG"; -#endif - - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_WRITE; - (*request)->fd = fd; - (*request)->datatype = datatype; MPI_Type_size(datatype, &typesize); len = count * typesize; -#ifndef ROMIO_HAVE_WORKING_AIO - /* no support for nonblocking I/O. Use blocking I/O. */ - - ADIO_WriteContig(fd, buf, len, MPI_BYTE, file_ptr_type, offset, - &status, error_code); - (*request)->queued = 0; -# ifdef HAVE_STATUS_SET_BYTES - if (*error_code == MPI_SUCCESS) { - MPI_Get_elements(&status, MPI_BYTE, &len); - (*request)->nbytes = len; - } -# endif - - fd->fp_sys_posn = -1; - -#else if (file_ptr_type == ADIO_INDIVIDUAL) offset = fd->fp_ind; - aio_errno = ADIOI_GEN_aio(fd, buf, len, offset, 1, &((*request)->handle)); + aio_errno = ADIOI_GEN_aio(fd, buf, len, offset, 1, request); if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind += len; - (*request)->queued = 1; - ADIOI_Add_req_to_list(request); - fd->fp_sys_posn = -1; /* --BEGIN ERROR HANDLING-- */ @@ -86,29 +70,26 @@ void ADIOI_GEN_IwriteContig(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ *error_code = MPI_SUCCESS; -#endif /* NO_AIO */ - - fd->async_count++; } - - /* This function is for implementation convenience. * It takes care of the differences in the interface for nonblocking I/O * on various Unix machines! If wr==1 write, wr==0 read. * * Returns 0 on success, -errno on failure. */ -#ifdef ROMIO_HAVE_WORKING_AIO int ADIOI_GEN_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, - int wr, void *handle) + int wr, MPI_Request *request) { int err=-1, fd_sys; int error_code; struct aiocb *aiocbp; + ADIOI_AIO_Request *aio_req; + fd_sys = fd->fd_sys; + aio_req = (ADIOI_AIO_Request*)ADIOI_Calloc(sizeof(ADIOI_AIO_Request), 1); aiocbp = (struct aiocb *) ADIOI_Calloc(sizeof(struct aiocb), 1); aiocbp->aio_offset = offset; aiocbp->aio_buf = buf; @@ -134,7 +115,10 @@ int ADIOI_GEN_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, # endif #endif -#ifdef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_FILDES +#ifndef ROMIO_HAVE_AIO_CALLS_NEED_FILEDES +#ifndef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_FILDES +#error 'No fildes set for aio structure' +#endif if (wr) err = aio_write(aiocbp); else err = aio_read(aiocbp); #else @@ -145,40 +129,30 @@ int ADIOI_GEN_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, if (err == -1) { if (errno == EAGAIN) { - /* exceeded the max. no. of outstanding requests. - complete all previous async. requests and try again. */ - - ADIOI_Complete_async(&error_code); - if (error_code != MPI_SUCCESS) return -EIO; - - while (err == -1 && errno == EAGAIN) { - -#ifdef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_FILDES - if (wr) err = aio_write(aiocbp); - else err = aio_read(aiocbp); -#else - /* Broken IBM interface */ - if (wr) err = aio_write(fd_sys, aiocbp); - else err = aio_read(fd_sys, aiocbp); -#endif - - if (err == -1 && errno == EAGAIN) { - /* sleep and try again */ - sleep(1); - } - else if (err == -1) { - /* real error */ - return -errno; - } - } - } - else { + /* exceeded the max. no. of outstanding requests. + treat this as a blocking request and return. */ + if (wr) + ADIO_WriteContig(fd, buf, len, MPI_BYTE, + ADIO_EXPLICIT_OFFSET, offset, NULL, &error_code); + else + ADIO_ReadContig(fd, buf, len, MPI_BYTE, + ADIO_EXPLICIT_OFFSET, offset, NULL, &error_code); + + MPIO_Completed_request_create(&fd, len, &error_code, request); + return 0; + } else { return -errno; } } - - *((struct aiocb **) handle) = aiocbp; - + aio_req->aiocbp = aiocbp; + if (ADIOI_GEN_greq_class == 0) { + MPIX_Grequest_class_create(ADIOI_GEN_aio_query_fn, + ADIOI_GEN_aio_free_fn, MPIU_Greq_cancel_fn, + ADIOI_GEN_aio_poll_fn, ADIOI_GEN_aio_wait_fn, + &ADIOI_GEN_greq_class); + } + MPIX_Grequest_class_allocate(ADIOI_GEN_greq_class, aio_req, request); + memcpy(&(aio_req->req), request, sizeof(MPI_Request)); return 0; } #endif @@ -189,20 +163,12 @@ int ADIOI_GEN_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, */ void ADIOI_GEN_IwriteStrided(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, - ADIO_Offset offset, ADIO_Request *request, + ADIO_Offset offset, MPI_Request *request, int *error_code) { ADIO_Status status; -#ifdef HAVE_STATUS_SET_BYTES int typesize; -#endif - - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_WRITE; - (*request)->fd = fd; - (*request)->datatype = datatype; - (*request)->queued = 0; - (*request)->handle = 0; + MPI_Offset nbytes=0; /* Call the blocking function. It will create an error code * if necessary. @@ -210,12 +176,152 @@ void ADIOI_GEN_IwriteStrided(ADIO_File fd, void *buf, int count, ADIO_WriteStrided(fd, buf, count, datatype, file_ptr_type, offset, &status, error_code); - fd->async_count++; - -#ifdef HAVE_STATUS_SET_BYTES if (*error_code == MPI_SUCCESS) { MPI_Type_size(datatype, &typesize); - (*request)->nbytes = count * typesize; + nbytes = count * typesize; } -#endif + MPIO_Completed_request_create(&fd, nbytes, error_code, request); } + +#ifdef ROMIO_HAVE_WORKING_AIO +/* generic POSIX aio completion test routine */ +int ADIOI_GEN_aio_poll_fn(void *extra_state, MPI_Status *status) +{ + ADIOI_AIO_Request *aio_req; + int errcode=MPI_SUCCESS; + + aio_req = (ADIOI_AIO_Request *)extra_state; + + /* aio_error returns an ERRNO value */ + errno = aio_error(aio_req->aiocbp); + if (errno == EINPROGRESS) { + /* TODO: need to diddle with status somehow */ + } + else if (errno == ECANCELED) { + /* TODO: unsure how to handle this */ + } else if (errno == 0) { + int n = aio_return(aio_req->aiocbp); + aio_req->nbytes = n; + MPIR_Nest_incr(); + errcode = MPI_Grequest_complete(aio_req->req); + /* --BEGIN ERROR HANDLING-- */ + if (errcode != MPI_SUCCESS) { + errcode = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + "ADIOI_GEN_aio_poll_fn", __LINE__, + MPI_ERR_IO, "**mpi_grequest_complete", + 0); + } + /* --END ERROR HANDLING-- */ + MPIR_Nest_decr(); + } + return errcode; +} + +/* wait for multiple requests to complete */ +int ADIOI_GEN_aio_wait_fn(int count, void ** array_of_states, + double timeout, MPI_Status *status) +{ + const struct aiocb **cblist; + int err, errcode=MPI_SUCCESS; + int nr_complete=0; + double starttime; + struct timespec aio_timer; + struct timespec *aio_timer_p = NULL; + + ADIOI_AIO_Request **aio_reqlist; + int i; + + aio_reqlist = (ADIOI_AIO_Request **)array_of_states; + + cblist = (const struct aiocb**) ADIOI_Calloc(count, sizeof(struct aiocb*)); + + starttime = MPI_Wtime(); + if (timeout >0) { + aio_timer.tv_sec = (time_t)timeout; + aio_timer.tv_nsec = timeout - aio_timer.tv_sec; + aio_timer_p = &aio_timer; + } + for (i=0; i< count; i++) + { + cblist[i] = aio_reqlist[i]->aiocbp; + } + + while(nr_complete < count) { + do { + err = aio_suspend(cblist, count, aio_timer_p); + } while (err < 0 && errno == EINTR); + if (err == 0) + { /* run through the list of requests, and mark all the completed + ones as done */ + for (i=0; i< count; i++) + { + /* aio_error returns an ERRNO value */ + if (aio_reqlist[i]->aiocbp == NULL) + continue; + errno = aio_error(aio_reqlist[i]->aiocbp); + if (errno == 0) { + int n = aio_return(aio_reqlist[i]->aiocbp); + aio_reqlist[i]->nbytes = n; + MPIR_Nest_incr(); + errcode = MPI_Grequest_complete(aio_reqlist[i]->req); + if (errcode != MPI_SUCCESS) { + errcode = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, + "ADIOI_GEN_aio_wait_fn", + __LINE__, MPI_ERR_IO, + "**mpi_grequest_complete", 0); + } + MPIR_Nest_decr(); + ADIOI_Free(aio_reqlist[i]->aiocbp); + aio_reqlist[i]->aiocbp = NULL; + cblist[i] = NULL; + nr_complete++; + } + /* TODO: need to handle error conditions somehow*/ + } + } /* TODO: also need to handle errors here */ + if ( (timeout > 0) && (timeout < (MPI_Wtime() - starttime) )) + break; + } + + if (cblist != NULL) ADIOI_Free(cblist); + return errcode; +} + +int ADIOI_GEN_aio_query_fn(void *extra_state, MPI_Status *status) +{ + ADIOI_AIO_Request *aio_req; + + aio_req = (ADIOI_AIO_Request *)extra_state; + + + MPI_Status_set_elements(status, MPI_BYTE, aio_req->nbytes); + + /* do i need to nest_incr/nest_decr here? */ + /* can never cancel so always true */ + MPI_Status_set_cancelled(status, 0); + + /* choose not to return a value for this */ + status->MPI_SOURCE = MPI_UNDEFINED; + /* tag has no meaning for this generalized request */ + status->MPI_TAG = MPI_UNDEFINED; + /* this generalized request never fails */ + return MPI_SUCCESS; +} + +int ADIOI_GEN_aio_free_fn(void *extra_state) +{ + ADIOI_AIO_Request *aio_req; + aio_req = (ADIOI_AIO_Request*)extra_state; + + if (aio_req->aiocbp != NULL) + ADIOI_Free(aio_req->aiocbp); + ADIOI_Free(aio_req); + + return MPI_SUCCESS; +} +#endif /* working AIO */ +/* + * vim: ts=8 sts=4 sw=4 noexpandtab + */ diff --git a/ompi/mca/io/romio/romio/adio/common/ad_iwrite_fake.c b/ompi/mca/io/romio/romio/adio/common/ad_iwrite_fake.c index afde5dddb2..5fce4e3291 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_iwrite_fake.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_iwrite_fake.c @@ -7,6 +7,8 @@ #include "adio.h" +#include "mpiu_greq.h" + /* Generic implementation of IwriteContig calls the blocking WriteContig * immediately. */ @@ -17,12 +19,7 @@ void ADIOI_FAKE_IwriteContig(ADIO_File fd, void *buf, int count, { ADIO_Status status; int len, typesize; - - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_WRITE; - (*request)->fd = fd; - (*request)->queued = 0; - (*request)->datatype = datatype; + MPI_Offset nbytes=0; MPI_Type_size(datatype, &typesize); len = count * typesize; @@ -32,15 +29,12 @@ void ADIOI_FAKE_IwriteContig(ADIO_File fd, void *buf, int count, */ ADIO_WriteContig(fd, buf, len, MPI_BYTE, file_ptr_type, offset, &status, error_code); - - fd->async_count++; - -#ifdef HAVE_STATUS_SET_BYTES if (*error_code == MPI_SUCCESS) { - MPI_Get_elements(&status, MPI_BYTE, &len); - (*request)->nbytes = len; + MPI_Type_size(datatype, &typesize); + nbytes = count*typesize; } -#endif + MPIO_Completed_request_create(&fd, nbytes, error_code, request); + } @@ -53,29 +47,17 @@ void ADIOI_FAKE_IwriteStrided(ADIO_File fd, void *buf, int count, int *error_code) { ADIO_Status status; -#ifdef HAVE_STATUS_SET_BYTES int typesize; -#endif - - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_WRITE; - (*request)->fd = fd; - (*request)->datatype = datatype; - (*request)->queued = 0; - (*request)->handle = 0; + MPI_Offset nbytes=0; /* Call the blocking function. It will create an error code * if necessary. */ ADIO_WriteStrided(fd, buf, count, datatype, file_ptr_type, offset, &status, error_code); - - fd->async_count++; - -#ifdef HAVE_STATUS_SET_BYTES if (*error_code == MPI_SUCCESS) { MPI_Type_size(datatype, &typesize); - (*request)->nbytes = count * typesize; + nbytes = count * typesize; } -#endif + MPIO_Completed_request_create(&fd, nbytes, error_code, request); } diff --git a/ompi/mca/io/romio/romio/adio/common/ad_open.c b/ompi/mca/io/romio/romio/adio/common/ad_open.c index 58aa6eeb9f..d89f6ac64c 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_open.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_open.c @@ -14,6 +14,9 @@ static int is_aggregator(int rank, ADIO_File fd); static int uses_generic_read(ADIO_File fd); static int uses_generic_write(ADIO_File fd); +static int build_cb_config_list(ADIO_File fd, + MPI_Comm orig_comm, MPI_Comm comm, + int rank, int procs, int *error_code); MPI_File ADIO_Open(MPI_Comm orig_comm, MPI_Comm comm, char *filename, int file_system, @@ -24,12 +27,10 @@ MPI_File ADIO_Open(MPI_Comm orig_comm, { MPI_File mpi_fh; ADIO_File fd; - ADIO_cb_name_array array; int orig_amode_excl, orig_amode_wronly, err, rank, procs; - char *value; static char myname[] = "ADIO_OPEN"; - int rank_ct, max_error_code; - int *tmp_ranklist; + int max_error_code; + MPI_Info dupinfo; MPI_Comm aggregator_comm = MPI_COMM_NULL; /* just for deferred opens */ *error_code = MPI_SUCCESS; @@ -66,6 +67,8 @@ MPI_File ADIO_Open(MPI_Comm orig_comm, fd->err_handler = ADIOI_DFLT_ERR_HANDLER; + MPI_Comm_rank(comm, &rank); + MPI_Comm_size(comm, &procs); /* create and initialize info object */ fd->hints = (ADIOI_Hints *)ADIOI_Malloc(sizeof(struct ADIOI_Hints_struct)); if (fd->hints == NULL) { @@ -75,7 +78,19 @@ MPI_File ADIO_Open(MPI_Comm orig_comm, fd->hints->ranklist = NULL; fd->hints->initialized = 0; fd->info = MPI_INFO_NULL; - ADIO_SetInfo(fd, info, &err); + + if (info == MPI_INFO_NULL) + *error_code = MPI_Info_create(&dupinfo); + else + *error_code = MPI_Info_dup(info, &dupinfo); + if (*error_code != MPI_SUCCESS) + goto fn_exit; + + ADIOI_process_system_hints(dupinfo); + ADIO_SetInfo(fd, dupinfo, &err); + *error_code = MPI_Info_free(&dupinfo); + if (*error_code != MPI_SUCCESS) + goto fn_exit; /* deferred open: * we can only do this optimization if 'fd->hints->deferred_open' is set @@ -93,51 +108,15 @@ MPI_File ADIO_Open(MPI_Comm orig_comm, * always use the propper communicator */ fd->hints->deferred_open = 0; -/* gather the processor name array if we don't already have it */ -/* this has to be done here so that we can cache the name array in both - * the dup'd communicator (in case we want it later) and the original - * communicator - */ - ADIOI_cb_gather_name_array(orig_comm, comm, &array); - -/* parse the cb_config_list and create a rank map on rank 0 */ - MPI_Comm_rank(comm, &rank); - if (rank == 0) { - MPI_Comm_size(comm, &procs); - tmp_ranklist = (int *) ADIOI_Malloc(sizeof(int) * procs); - if (tmp_ranklist == NULL) { - /* NEED TO HANDLE ENOMEM ERRORS */ - } - - rank_ct = ADIOI_cb_config_list_parse(fd->hints->cb_config_list, - array, tmp_ranklist, - fd->hints->cb_nodes); - - /* store the ranklist using the minimum amount of memory */ - if (rank_ct > 0) { - fd->hints->ranklist = (int *) ADIOI_Malloc(sizeof(int) * rank_ct); - memcpy(fd->hints->ranklist, tmp_ranklist, sizeof(int) * rank_ct); - } - ADIOI_Free(tmp_ranklist); - fd->hints->cb_nodes = rank_ct; - /* TEMPORARY -- REMOVE WHEN NO LONGER UPDATING INFO FOR FS-INDEP. */ - value = (char *) ADIOI_Malloc((MPI_MAX_INFO_VAL+1)*sizeof(char)); - ADIOI_Snprintf(value, MPI_MAX_INFO_VAL+1, "%d", rank_ct); - MPI_Info_set(fd->info, "cb_nodes", value); - ADIOI_Free(value); + /* on BlueGene, the cb_config_list is built when hints are processed. No + * one else does that right now */ + if (fd->hints->ranklist == NULL) { + build_cb_config_list(fd, orig_comm, comm, rank, procs, error_code); + if (*error_code != MPI_SUCCESS) + goto fn_exit; } - - ADIOI_cb_bcast_rank_map(fd); - if (fd->hints->cb_nodes <= 0) { - *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, - myname, __LINE__, MPI_ERR_IO, - "**ioagnomatch", 0); - fd = ADIO_FILE_NULL; - goto fn_exit; - } - - + /* deferred open: if we are an aggregator, create a new communicator. * we'll use this aggregator communicator for opens and closes. * otherwise, we have a NULL communicator until we try to do independent @@ -172,6 +151,37 @@ MPI_File ADIO_Open(MPI_Comm orig_comm, */ /* pvfs2 handles opens specially, so it is actually more efficent for that * file system if we skip this optimization */ + /* NFS handles opens especially poorly, so we cannot use this optimization + * on that FS */ + if (fd->file_system == ADIO_NFS) { + /* no optimizations for NFS: */ + if ((access_mode & ADIO_CREATE) && (access_mode & ADIO_EXCL)) { + /* the open should fail if the file exists. Only *1* process should + check this. Otherwise, if all processes try to check and the file + does not exist, one process will create the file and others who + reach later will return error. */ + if(rank == fd->hints->ranklist[0]) { + fd->access_mode = access_mode; + (*(fd->fns->ADIOI_xxx_Open))(fd, error_code); + MPI_Bcast(error_code, 1, MPI_INT, \ + fd->hints->ranklist[0], fd->comm); + /* if no error, close the file and reopen normally below */ + if (*error_code == MPI_SUCCESS) + (*(fd->fns->ADIOI_xxx_Close))(fd, error_code); + } + else MPI_Bcast(error_code, 1, MPI_INT, + fd->hints->ranklist[0], fd->comm); + if (*error_code != MPI_SUCCESS) { + goto fn_exit; + } + else { + /* turn off EXCL for real open */ + access_mode = access_mode ^ ADIO_EXCL; + } + } + } else { + + /* the actual optimized create on one, open on all */ if (access_mode & ADIO_CREATE && fd->file_system != ADIO_PVFS2) { if(rank == fd->hints->ranklist[0]) { /* remove delete_on_close flag if set */ @@ -201,6 +211,7 @@ MPI_File ADIO_Open(MPI_Comm orig_comm, access_mode ^= ADIO_EXCL; } } + } /* if we are doing deferred open, non-aggregators should return now */ if (fd->hints->deferred_open ) { @@ -320,3 +331,58 @@ static int uses_generic_write(ADIO_File fd) } return 0; } + +static int build_cb_config_list(ADIO_File fd, + MPI_Comm orig_comm, MPI_Comm comm, + int rank, int procs, int *error_code) +{ + ADIO_cb_name_array array; + int *tmp_ranklist; + int rank_ct; + char *value; + static char myname[] = "ADIO_OPEN cb_config_list"; + + /* gather the processor name array if we don't already have it */ + /* this has to be done early in ADIO_Open so that we can cache the name + * array in both the dup'd communicator (in case we want it later) and the + * original communicator */ + ADIOI_cb_gather_name_array(orig_comm, comm, &array); + +/* parse the cb_config_list and create a rank map on rank 0 */ + if (rank == 0) { + tmp_ranklist = (int *) ADIOI_Malloc(sizeof(int) * procs); + if (tmp_ranklist == NULL) { + /* NEED TO HANDLE ENOMEM ERRORS */ + } + + rank_ct = ADIOI_cb_config_list_parse(fd->hints->cb_config_list, + array, tmp_ranklist, + fd->hints->cb_nodes); + + /* store the ranklist using the minimum amount of memory */ + if (rank_ct > 0) { + fd->hints->ranklist = (int *) ADIOI_Malloc(sizeof(int) * rank_ct); + memcpy(fd->hints->ranklist, tmp_ranklist, sizeof(int) * rank_ct); + } + ADIOI_Free(tmp_ranklist); + fd->hints->cb_nodes = rank_ct; + /* TEMPORARY -- REMOVE WHEN NO LONGER UPDATING INFO FOR FS-INDEP. */ + value = (char *) ADIOI_Malloc((MPI_MAX_INFO_VAL+1)*sizeof(char)); + ADIOI_Snprintf(value, MPI_MAX_INFO_VAL+1, "%d", rank_ct); + MPI_Info_set(fd->info, "cb_nodes", value); + ADIOI_Free(value); + } + + ADIOI_cb_bcast_rank_map(fd); + if (fd->hints->cb_nodes <= 0) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, + myname, __LINE__, MPI_ERR_IO, + "**ioagnomatch", 0); + fd = ADIO_FILE_NULL; + } + return 0; +} + +/* + * vim: ts=8 sts=4 sw=4 noexpandtab + */ diff --git a/ompi/mca/io/romio/romio/adio/common/ad_read.c b/ompi/mca/io/romio/romio/adio/common/ad_read.c index d9af9cc03d..0cef18abb7 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_read.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_read.c @@ -27,7 +27,13 @@ void ADIOI_GEN_ReadContig(ADIO_File fd, void *buf, int count, } if (fd->fp_sys_posn != offset) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif err = lseek(fd->fd_sys, offset, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (err == -1) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, @@ -41,7 +47,13 @@ void ADIOI_GEN_ReadContig(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ } +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_a, 0, NULL ); +#endif err = read(fd->fd_sys, buf, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_read_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (err == -1) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, diff --git a/ompi/mca/io/romio/romio/adio/common/ad_read_coll.c b/ompi/mca/io/romio/romio/adio/common/ad_read_coll.c index 0164bc33b8..303fb53ba5 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_read_coll.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_read_coll.c @@ -7,9 +7,6 @@ #include "adio.h" #include "adio_extern.h" -#ifdef PROFILE -#include "mpe.h" -#endif /* prototypes of functions used for collective reads only. */ static void ADIOI_Read_and_exch(ADIO_File fd, void *buf, MPI_Datatype @@ -79,10 +76,6 @@ void ADIOI_GEN_ReadStridedColl(ADIO_File fd, void *buf, int count, int bufsize, size; #endif -#ifdef PROFILE - MPE_Log_event(13, 0, "start computation"); -#endif - MPI_Comm_size(fd->comm, &nprocs); MPI_Comm_rank(fd->comm, &myrank); @@ -570,10 +563,6 @@ static void ADIOI_Read_and_exch(ADIO_File fd, void *buf, MPI_Datatype MPI_Comm_rank(fd->comm, &rank); -#ifdef PROFILE - MPE_Log_event(14, 0, "end computation"); -#endif - for (m=0; m diff --git a/ompi/mca/io/romio/romio/adio/common/ad_set_sh_fp.c b/ompi/mca/io/romio/romio/adio/common/ad_set_sh_fp.c index c837dcb6e0..8e87912fc9 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_set_sh_fp.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_set_sh_fp.c @@ -23,6 +23,14 @@ void ADIO_Set_shared_fp(ADIO_File fd, ADIO_Offset offset, int *error_code) } #endif +#ifdef ROMIO_BGL + /* BGLOCKLESS won't support shared fp */ + if (fd->file_system == ADIO_BGL) { + ADIOI_BGL_Set_shared_fp(fd, offset, error_code); + return; + } +#endif + if (fd->shared_fp_fd == ADIO_FILE_NULL) { MPI_Comm_dup(MPI_COMM_SELF, &dupcommself); fd->shared_fp_fd = ADIO_Open(MPI_COMM_SELF, dupcommself, diff --git a/ompi/mca/io/romio/romio/adio/common/ad_wait.c b/ompi/mca/io/romio/romio/adio/common/ad_wait.c index 92c2cacb4d..f2a65d1e1f 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_wait.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_wait.c @@ -13,6 +13,9 @@ #ifdef HAVE_SIGNAL_H #include #endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif #ifdef HAVE_AIO_H #include #endif @@ -39,115 +42,6 @@ void ADIOI_GEN_IOComplete(ADIO_Request *request, ADIO_Status *status, int *error_code) { -#ifdef ROMIO_HAVE_WORKING_AIO - int err; -#ifdef ROMIO_HAVE_STRUCT_AIOCB_WITH_AIO_HANDLE - struct aiocb *tmp1; -#endif - static char myname[] = "ADIOI_GEN_IOCOMPLETE"; -#endif - - if (*request == ADIO_REQUEST_NULL) { - *error_code = MPI_SUCCESS; return; - } - -#ifdef ROMIO_HAVE_AIO_SUSPEND_TWO_ARGS -/* old IBM */ - if ((*request)->queued) { - do { - err = aio_suspend(1, (struct aiocb **) &((*request)->handle)); - } while ((err == -1) && (errno == EINTR)); - - tmp1 = (struct aiocb *) (*request)->handle; - if (err != -1) { - err = aio_return(tmp1->aio_handle); - (*request)->nbytes = err; - errno = aio_error(tmp1->aio_handle); - } - else (*request)->nbytes = -1; - -/* on DEC, it is required to call aio_return to dequeue the request. - IBM man pages don't indicate what function to use for dequeue. - I'm assuming it is aio_return! POSIX says aio_return may be called - only once on a given handle. */ - - if (err == -1) { - *error_code = MPIO_Err_create_code(MPI_SUCCESS, - MPIR_ERR_RECOVERABLE, myname, - __LINE__, MPI_ERR_IO, "**io", - "**io %s", strerror(errno)); - return; - } - else *error_code = MPI_SUCCESS; - } /* if ((*request)->queued) */ - else *error_code = MPI_SUCCESS; - -#ifdef HAVE_STATUS_SET_BYTES - if ((*request)->nbytes != -1) - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - -#elif defined(ROMIO_HAVE_WORKING_AIO) -/* all other aio types */ - if ((*request)->queued) { - do { - err = aio_suspend((const struct aiocb **) &((*request)->handle), 1, 0); - } while ((err == -1) && (errno == EINTR)); - - if (err != -1) { - err = aio_return((struct aiocb *) (*request)->handle); - (*request)->nbytes = err; - errno = aio_error((struct aiocb *) (*request)->handle); - } - else (*request)->nbytes = -1; - - if (err == -1) { - *error_code = MPIO_Err_create_code(MPI_SUCCESS, - MPIR_ERR_RECOVERABLE, myname, - __LINE__, MPI_ERR_IO, "**io", - "**io %s", strerror(errno)); - return; - } - else *error_code = MPI_SUCCESS; - } /* if ((*request)->queued) */ - else *error_code = MPI_SUCCESS; -#ifdef HAVE_STATUS_SET_BYTES - if ((*request)->nbytes != -1) - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif -#endif - -#ifdef ROMIO_HAVE_WORKING_AIO - if ((*request)->queued != -1) { - - /* queued = -1 is an internal hack used when the request must - be completed, but the request object should not be - freed. This is used in ADIOI_Complete_async, because the user - will call MPI_Wait later, which would require status to - be filled. Ugly but works. queued = -1 should be used only - in ADIOI_Complete_async. - This should not affect the user in any way. */ - - /* if request is still queued in the system, it is also there - on ADIOI_Async_list. Delete it from there. */ - if ((*request)->queued) ADIOI_Del_req_from_list(request); - - (*request)->fd->async_count--; - if ((*request)->handle) ADIOI_Free((*request)->handle); - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; - } - -#else -/* no aio */ - -#ifdef HAVE_STATUS_SET_BYTES - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - (*request)->fd->async_count--; - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; - *error_code = MPI_SUCCESS; -#endif + } diff --git a/ompi/mca/io/romio/romio/adio/common/ad_wait_fake.c b/ompi/mca/io/romio/romio/adio/common/ad_wait_fake.c index 06c0d2ecdd..f0be5ecbf7 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_wait_fake.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_wait_fake.c @@ -15,18 +15,6 @@ void ADIOI_FAKE_IOComplete(ADIO_Request *request, ADIO_Status *status, int *error_code) { - if (*request == ADIO_REQUEST_NULL) { *error_code = MPI_SUCCESS; - return; - } - -#ifdef HAVE_STATUS_SET_BYTES - MPIR_Status_set_bytes(status, (*request)->datatype, (*request)->nbytes); -#endif - - (*request)->fd->async_count--; - - ADIOI_Free_request((ADIOI_Req_node *) (*request)); - *request = ADIO_REQUEST_NULL; - *error_code = MPI_SUCCESS; + return; } diff --git a/ompi/mca/io/romio/romio/adio/common/ad_write.c b/ompi/mca/io/romio/romio/adio/common/ad_write.c index 9ae63408a2..ea2fc1f7c4 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_write.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_write.c @@ -27,7 +27,13 @@ void ADIOI_GEN_WriteContig(ADIO_File fd, void *buf, int count, } if (fd->fp_sys_posn != offset) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif err = lseek(fd->fd_sys, offset, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (err == -1) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, @@ -41,7 +47,13 @@ void ADIOI_GEN_WriteContig(ADIO_File fd, void *buf, int count, /* --END ERROR HANDLING-- */ } +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif err = write(fd->fd_sys, buf, len); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif /* --BEGIN ERROR HANDLING-- */ if (err == -1) { *error_code = MPIO_Err_create_code(MPI_SUCCESS, diff --git a/ompi/mca/io/romio/romio/adio/common/ad_write_coll.c b/ompi/mca/io/romio/romio/adio/common/ad_write_coll.c index 304adce79c..f71ec67860 100644 --- a/ompi/mca/io/romio/romio/adio/common/ad_write_coll.c +++ b/ompi/mca/io/romio/romio/adio/common/ad_write_coll.c @@ -7,14 +7,11 @@ #include "adio.h" #include "adio_extern.h" -#ifdef PROFILE -#include "mpe.h" -#endif /* prototypes of functions used for collective writes only. */ static void ADIOI_Exch_and_write(ADIO_File fd, void *buf, MPI_Datatype - datatype, int nprocs, int myrank, - int interleave_count, ADIOI_Access + datatype, int nprocs, int myrank, + ADIOI_Access *others_req, ADIO_Offset *offset_list, int *len_list, int contig_access_count, ADIO_Offset min_st_offset, ADIO_Offset fd_size, @@ -79,10 +76,6 @@ void ADIOI_GEN_WriteStridedColl(ADIO_File fd, void *buf, int count, int old_error, tmp_error; -#ifdef PROFILE - MPE_Log_event(13, 0, "start computation"); -#endif - MPI_Comm_size(fd->comm, &nprocs); MPI_Comm_rank(fd->comm, &myrank); @@ -198,7 +191,6 @@ void ADIOI_GEN_WriteStridedColl(ADIO_File fd, void *buf, int count, /* exchange data and write in sizes of no more than coll_bufsize. */ ADIOI_Exch_and_write(fd, buf, datatype, nprocs, myrank, - interleave_count, others_req, offset_list, len_list, contig_access_count, min_st_offset, fd_size, fd_start, fd_end, buf_idx, error_code); @@ -219,6 +211,9 @@ void ADIOI_GEN_WriteStridedColl(ADIO_File fd, void *buf, int count, /* optimization: if only one process performing i/o, we can perform * a less-expensive Bcast */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_postwrite_a, 0, NULL ); +#endif if (fd->hints->cb_nodes == 1) MPI_Bcast(error_code, 1, MPI_INT, fd->hints->ranklist[0], fd->comm); @@ -227,6 +222,9 @@ void ADIOI_GEN_WriteStridedColl(ADIO_File fd, void *buf, int count, MPI_Allreduce(&tmp_error, error_code, 1, MPI_INT, MPI_MAX, fd->comm); } +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_postwrite_b, 0, NULL ); +#endif if ( (old_error != MPI_SUCCESS) && (old_error != MPI_ERR_IO) ) *error_code = old_error; @@ -274,9 +272,9 @@ void ADIOI_GEN_WriteStridedColl(ADIO_File fd, void *buf, int count, * code is created and returned in error_code. */ static void ADIOI_Exch_and_write(ADIO_File fd, void *buf, MPI_Datatype - datatype, int nprocs, int myrank, - int interleave_count, - ADIOI_Access + datatype, int nprocs, + int myrank, + ADIOI_Access *others_req, ADIO_Offset *offset_list, int *len_list, int contig_access_count, ADIO_Offset @@ -293,7 +291,6 @@ static void ADIOI_Exch_and_write(ADIO_File fd, void *buf, MPI_Datatype array to a file, where each local array is 8Mbytes, requiring at least another 8Mbytes of temp space is unacceptable. */ - /* TODO: 'hole' not used outside of ADIOI_W_Exchange_data */ int hole, i, j, m, size=0, ntimes, max_ntimes, buftype_is_contig; ADIO_Offset st_loc=-1, end_loc=-1, off, done, req_off; char *write_buf=NULL; @@ -396,17 +393,14 @@ static void ADIOI_Exch_and_write(ADIO_File fd, void *buf, MPI_Datatype common, let me do the simplest thing possible here: Each process completes all pending nonblocking operations before completing. */ - ADIOI_Complete_async(error_code); + /*ADIOI_Complete_async(error_code); if (*error_code != MPI_SUCCESS) return; MPI_Barrier(fd->comm); + */ done = 0; off = st_loc; -#ifdef PROFILE - MPE_Log_event(14, 0, "end computation"); -#endif - for (m=0; m < ntimes; m++) { /* go through all others_req and check which will be satisfied by the current write */ @@ -425,9 +419,6 @@ static void ADIOI_Exch_and_write(ADIO_File fd, void *buf, MPI_Datatype /* first calculate what should be communicated */ -#ifdef PROFILE - MPE_Log_event(13, 0, "start computation"); -#endif for (i=0; i < nprocs; i++) count[i] = recv_size[i] = 0; size = (int) (ADIOI_MIN(coll_bufsize, end_loc-st_loc+1-done)); @@ -487,10 +478,6 @@ static void ADIOI_Exch_and_write(ADIO_File fd, void *buf, MPI_Datatype } } -#ifdef PROFILE - MPE_Log_event(14, 0, "end computation"); - MPE_Log_event(7, 0, "start communication"); -#endif ADIOI_W_Exchange_data(fd, buf, write_buf, flat_buf, offset_list, len_list, send_size, recv_size, off, size, count, start_pos, partial_recv, @@ -501,38 +488,23 @@ static void ADIOI_Exch_and_write(ADIO_File fd, void *buf, MPI_Datatype done_to_proc, &hole, m, buftype_extent, buf_idx, error_code); if (*error_code != MPI_SUCCESS) return; -#ifdef PROFILE - MPE_Log_event(8, 0, "end communication"); -#endif flag = 0; for (i=0; i sum_recv) *hole = 1; + } ADIOI_Free(srt_off); ADIOI_Free(srt_len); diff --git a/ompi/mca/io/romio/romio/adio/common/ad_write_nolock.c b/ompi/mca/io/romio/romio/adio/common/ad_write_nolock.c new file mode 100644 index 0000000000..9aa69d8f7b --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/common/ad_write_nolock.c @@ -0,0 +1,396 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + */ + +#include + +#include "adio.h" +#include "adio_extern.h" + + +/* #define IO_DEBUG 1 */ +void ADIOI_NOLOCK_WriteStrided(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code) +{ +/* borrowed from old-school PVFS (v1) code. A driver for file systems that + * cannot or do not support client-side buffering + * Does not do data sieving optimization + * Does contain write-combining optimization for noncontig in memory, contig in + * file + */ + +/* offset is in units of etype relative to the filetype. */ + + ADIOI_Flatlist_node *flat_buf, *flat_file; + int i, j, k, err=-1, bwr_size, fwr_size=0, st_index=0; + int bufsize, num, size, sum, n_etypes_in_filetype, size_in_filetype; + int n_filetypes, etype_in_filetype; + ADIO_Offset abs_off_in_filetype=0; + int filetype_size, etype_size, buftype_size; + MPI_Aint filetype_extent, buftype_extent, indx; + int buf_count, buftype_is_contig, filetype_is_contig; + ADIO_Offset off, disp; + int flag, new_bwr_size, new_fwr_size, err_flag=0; + static char myname[] = "ADIOI_PVFS_WRITESTRIDED"; +#ifdef IO_DEBUG + int rank,nprocs; +#endif + + /* --BEGIN ERROR HANDLING-- */ + if (fd->atomicity) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, + myname, __LINE__, + MPI_ERR_INTERN, + "Atomic mode set in I/O function", 0); + return; + } + /* --END ERROR HANDLING-- */ + + ADIOI_Datatype_iscontig(datatype, &buftype_is_contig); + ADIOI_Datatype_iscontig(fd->filetype, &filetype_is_contig); + + MPI_Type_size(fd->filetype, &filetype_size); + if ( ! filetype_size ) { + *error_code = MPI_SUCCESS; + return; + } + +#ifdef IO_DEBUG + MPI_Comm_rank(fd->comm, &rank); + MPI_Comm_size(fd->comm, &nprocs); +#endif + + MPI_Type_extent(fd->filetype, &filetype_extent); + MPI_Type_size(datatype, &buftype_size); + MPI_Type_extent(datatype, &buftype_extent); + etype_size = fd->etype_size; + + bufsize = buftype_size * count; + + if (!buftype_is_contig && filetype_is_contig) { + char *combine_buf, *combine_buf_ptr; + ADIO_Offset combine_buf_remain; +/* noncontiguous in memory, contiguous in file. use writev */ + + ADIOI_Flatten_datatype(datatype); + flat_buf = ADIOI_Flatlist; + while (flat_buf->type != datatype) flat_buf = flat_buf->next; + + /* allocate our "combine buffer" to pack data into before writing */ + combine_buf = (char *) ADIOI_Malloc(fd->hints->ind_wr_buffer_size); + combine_buf_ptr = combine_buf; + combine_buf_remain = fd->hints->ind_wr_buffer_size; + + /* seek to the right spot in the file */ + if (file_ptr_type == ADIO_EXPLICIT_OFFSET) { + off = fd->disp + etype_size * offset; + lseek64(fd->fd_sys, off, SEEK_SET); + } + else off = lseek64(fd->fd_sys, fd->fp_ind, SEEK_SET); + + /* loop through all the flattened pieces. combine into buffer until + * no more will fit, then write. + * + * special case of a given piece being bigger than the combine buffer + * is also handled. + */ + for (j=0; jcount; i++) { + if (flat_buf->blocklens[i] > combine_buf_remain && combine_buf != combine_buf_ptr) { + /* there is data in the buffer; write out the buffer so far */ +#ifdef IO_DEBUG + printf("[%d/%d] nc mem c file (0) writing loc = %Ld sz = %Ld\n", + rank, nprocs, off, + fd->hints->ind_wr_buffer_size-combine_buf_remain); +#endif +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif + err = write(fd->fd_sys, + combine_buf, + fd->hints->ind_wr_buffer_size - combine_buf_remain); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif + if (err == -1) err_flag = 1; + + /* reset our buffer info */ + combine_buf_ptr = combine_buf; + combine_buf_remain = fd->hints->ind_wr_buffer_size; + } + + /* TODO: heuristic for when to not bother to use combine buffer? */ + if (flat_buf->blocklens[i] >= combine_buf_remain) { + /* special case: blocklen is as big as or bigger than the combine buf; + * write directly + */ +#ifdef IO_DEBUG + printf("[%d/%d] nc mem c file (1) writing loc = %Ld sz = %d\n", + rank, nprocs, off, + flat_buf->blocklens[i]); +#endif +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif + err = write(fd->fd_sys, + ((char *) buf) + j*buftype_extent + flat_buf->indices[i], + flat_buf->blocklens[i]); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif + if (err == -1) err_flag = 1; + off += flat_buf->blocklens[i]; /* keep up with the final file offset too */ + } + else { + /* copy more data into combine buffer */ + memcpy(combine_buf_ptr, + ((char *) buf) + j*buftype_extent + flat_buf->indices[i], + flat_buf->blocklens[i]); + combine_buf_ptr += flat_buf->blocklens[i]; + combine_buf_remain -= flat_buf->blocklens[i]; + off += flat_buf->blocklens[i]; /* keep up with the final file offset too */ + } + } + } + + if (combine_buf_ptr != combine_buf) { + /* data left in buffer to write */ +#ifdef IO_DEBUG + printf("[%d/%d] nc mem c file (2) writing loc = %Ld sz = %Ld\n", + rank, nprocs, off, + fd->hints->ind_wr_buffer_size-combine_buf_remain); +#endif +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif + err = write(fd->fd_sys, + combine_buf, + fd->hints->ind_wr_buffer_size - combine_buf_remain); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif + if (err == -1) err_flag = 1; + } + + if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind = off; + + ADIOI_Free(combine_buf); + + if (err_flag) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + } + else *error_code = MPI_SUCCESS; + } /* if (!buftype_is_contig && filetype_is_contig) ... */ + + else { /* noncontiguous in file */ + +/* split up into several contiguous writes */ + +/* find starting location in the file */ + +/* filetype already flattened in ADIO_Open */ + flat_file = ADIOI_Flatlist; + while (flat_file->type != fd->filetype) flat_file = flat_file->next; + disp = fd->disp; + + if (file_ptr_type == ADIO_INDIVIDUAL) { + offset = fd->fp_ind; /* in bytes */ + n_filetypes = -1; + flag = 0; + while (!flag) { + n_filetypes++; + for (i=0; icount; i++) { + if (disp + flat_file->indices[i] + + (ADIO_Offset) n_filetypes*filetype_extent + flat_file->blocklens[i] + >= offset) { + st_index = i; + fwr_size = disp + flat_file->indices[i] + + (ADIO_Offset) n_filetypes*filetype_extent + + flat_file->blocklens[i] - offset; + flag = 1; + break; + } + } + } + } + else { + n_etypes_in_filetype = filetype_size/etype_size; + n_filetypes = (int) (offset / n_etypes_in_filetype); + etype_in_filetype = (int) (offset % n_etypes_in_filetype); + size_in_filetype = etype_in_filetype * etype_size; + + sum = 0; + for (i=0; icount; i++) { + sum += flat_file->blocklens[i]; + if (sum > size_in_filetype) { + st_index = i; + fwr_size = sum - size_in_filetype; + abs_off_in_filetype = flat_file->indices[i] + + size_in_filetype - (sum - flat_file->blocklens[i]); + break; + } + } + + /* abs. offset in bytes in the file */ + offset = disp + (ADIO_Offset) n_filetypes*filetype_extent + abs_off_in_filetype; + } + + if (buftype_is_contig && !filetype_is_contig) { + +/* contiguous in memory, noncontiguous in file. should be the most + common case. */ + + i = 0; + j = st_index; + off = offset; + fwr_size = ADIOI_MIN(fwr_size, bufsize); + while (i < bufsize) { + if (fwr_size) { + /* TYPE_UB and TYPE_LB can result in + fwr_size = 0. save system call in such cases */ +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event(ADIOI_MPE_lseek_a, 0, NULL); +#endif +#ifdef IO_DEBUG + printf("[%d/%d] c mem nc file writing loc = %Ld sz = %d\n", + rank, nprocs, off, fwr_size); +#endif + err = lseek(fd->fd_sys, off, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event(ADIOI_MPE_lseek_b, 0, NULL); +#endif + if (err == -1) err_flag = 1; +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event(ADIOI_MPE_write_a, 0, NULL); +#endif + err = write(fd->fd_sys, ((char *) buf) + i, fwr_size); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event(ADIOI_MPE_write_b, 0, NULL); +#endif + if (err == -1) err_flag = 1; + } + i += fwr_size; + + if (off + fwr_size < disp + flat_file->indices[j] + + flat_file->blocklens[j] + (ADIO_Offset) n_filetypes*filetype_extent) + off += fwr_size; + /* did not reach end of contiguous block in filetype. + no more I/O needed. off is incremented by fwr_size. */ + else { + if (j < (flat_file->count - 1)) j++; + else { + j = 0; + n_filetypes++; + } + off = disp + flat_file->indices[j] + + (ADIO_Offset) n_filetypes*filetype_extent; + fwr_size = ADIOI_MIN(flat_file->blocklens[j], bufsize-i); + } + } + } + else { +/* noncontiguous in memory as well as in file */ + + ADIOI_Flatten_datatype(datatype); + flat_buf = ADIOI_Flatlist; + while (flat_buf->type != datatype) flat_buf = flat_buf->next; + + k = num = buf_count = 0; + indx = flat_buf->indices[0]; + j = st_index; + off = offset; + bwr_size = flat_buf->blocklens[0]; + + while (num < bufsize) { + size = ADIOI_MIN(fwr_size, bwr_size); + if (size) { +#ifdef IO_DEBUG + printf("[%d/%d] nc mem nc file writing loc = %Ld sz = %d\n", + rank, nprocs, off, size); +#endif +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_a, 0, NULL ); +#endif + lseek(fd->fd_sys, off, SEEK_SET); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_lseek_b, 0, NULL ); +#endif + if (err == -1) err_flag = 1; +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_a, 0, NULL ); +#endif + err = write(fd->fd_sys, ((char *) buf) + indx, size); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_write_b, 0, NULL ); +#endif + if (err == -1) err_flag = 1; + } + + new_fwr_size = fwr_size; + new_bwr_size = bwr_size; + + if (size == fwr_size) { +/* reached end of contiguous block in file */ + if (j < (flat_file->count - 1)) j++; + else { + j = 0; + n_filetypes++; + } + + off = disp + flat_file->indices[j] + + (ADIO_Offset) n_filetypes*filetype_extent; + + new_fwr_size = flat_file->blocklens[j]; + if (size != bwr_size) { + indx += size; + new_bwr_size -= size; + } + } + + if (size == bwr_size) { +/* reached end of contiguous block in memory */ + + k = (k + 1)%flat_buf->count; + buf_count++; + indx = buftype_extent*(buf_count/flat_buf->count) + + flat_buf->indices[k]; + new_bwr_size = flat_buf->blocklens[k]; + if (size != fwr_size) { + off += size; + new_fwr_size -= size; + } + } + num += size; + fwr_size = new_fwr_size; + bwr_size = new_bwr_size; + } + } + + if (file_ptr_type == ADIO_INDIVIDUAL) fd->fp_ind = off; + if (err_flag) { + *error_code = MPIO_Err_create_code(MPI_SUCCESS, + MPIR_ERR_RECOVERABLE, myname, + __LINE__, MPI_ERR_IO, "**io", + "**io %s", strerror(errno)); + } + else *error_code = MPI_SUCCESS; + } + + fd->fp_sys_posn = -1; /* set it to null. */ + +#ifdef HAVE_STATUS_SET_BYTES + MPIR_Status_set_bytes(status, datatype, bufsize); +/* This is a temporary way of filling in status. The right way is to + keep track of how much data was actually written by ADIOI_BUFFERED_WRITE. */ +#endif + + if (!buftype_is_contig) ADIOI_Delete_flattened(datatype); +} diff --git a/ompi/mca/io/romio/romio/adio/common/adi_close.c b/ompi/mca/io/romio/romio/adio/common/adi_close.c index 497ec4d798..ce82d31652 100644 --- a/ompi/mca/io/romio/romio/adio/common/adi_close.c +++ b/ompi/mca/io/romio/romio/adio/common/adi_close.c @@ -11,9 +11,6 @@ #include #endif -#ifdef PROFILE -#include "mpe.h" -#endif void ADIOI_GEN_Close(ADIO_File fd, int *error_code) @@ -21,18 +18,22 @@ void ADIOI_GEN_Close(ADIO_File fd, int *error_code) int err, derr=0; static char myname[] = "ADIOI_GEN_CLOSE"; -#ifdef PROFILE - MPE_Log_event(9, 0, "start close"); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_close_a, 0, NULL ); #endif - err = close(fd->fd_sys); - if (fd->fd_direct >= 0) { - derr = close(fd->fd_direct); - } - -#ifdef PROFILE - MPE_Log_event(10, 0, "end close"); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_close_b, 0, NULL ); #endif + if (fd->fd_direct >= 0) { +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_close_a, 0, NULL ); +#endif + derr = close(fd->fd_direct); +#ifdef ADIOI_MPE_LOGGING + MPE_Log_event( ADIOI_MPE_close_b, 0, NULL ); +#endif + } fd->fd_sys = -1; fd->fd_direct = -1; diff --git a/ompi/mca/io/romio/romio/adio/common/flatten.c b/ompi/mca/io/romio/romio/adio/common/flatten.c index e147b6e6ca..e644d2b138 100644 --- a/ompi/mca/io/romio/romio/adio/common/flatten.c +++ b/ompi/mca/io/romio/romio/adio/common/flatten.c @@ -257,6 +257,7 @@ void ADIOI_Flatten(MPI_Datatype datatype, ADIOI_Flatlist_node *flat, break; case MPI_COMBINER_HVECTOR: + case MPI_COMBINER_HVECTOR_INTEGER: top_count = ints[0]; MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner); @@ -371,7 +372,59 @@ void ADIOI_Flatten(MPI_Datatype datatype, ADIOI_Flatlist_node *flat, } break; + case MPI_COMBINER_INDEXED_BLOCK: + top_count = ints[0]; + MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, + &old_ntypes, &old_combiner); + ADIOI_Datatype_iscontig(types[0], &old_is_contig); + MPI_Type_extent(types[0], &old_extent); + + prev_index = *curr_index; + if ((old_combiner != MPI_COMBINER_NAMED) && (!old_is_contig)) + ADIOI_Flatten(types[0], flat, + st_offset+ints[1+1]*old_extent, curr_index); + + if (prev_index == *curr_index) { +/* simplest case, indexed type made up of basic or contiguous types */ + j = *curr_index; + for (i=j; iindices[i] = st_offset + ints[1+1+i-j]*old_extent; + flat->blocklens[i] = (int) (ints[1]*old_extent); + } + *curr_index = i; + } + else { +/* vector of noncontiguous derived types */ + + j = *curr_index; + num = *curr_index - prev_index; + +/* The noncontiguous types have to be replicated blocklens[i] times + and then strided. Replicate the first one. */ + for (m=1; mindices[j] = flat->indices[j-num] + old_extent; + flat->blocklens[j] = flat->blocklens[j-num]; + j++; + } + } + *curr_index = j; + +/* Now repeat with strides. */ + num = *curr_index - prev_index; + for (i=1; iindices[j] = flat->indices[j-num] + (ints[2+i]-ints[1+i])*old_extent; + flat->blocklens[j] = flat->blocklens[j-num]; + j++; + } + } + *curr_index = j; + } + break; + case MPI_COMBINER_HINDEXED: + case MPI_COMBINER_HINDEXED_INTEGER: top_count = ints[0]; MPI_Type_get_envelope(types[0], &old_nints, &old_nadds, &old_ntypes, &old_combiner); @@ -433,6 +486,7 @@ void ADIOI_Flatten(MPI_Datatype datatype, ADIOI_Flatlist_node *flat, break; case MPI_COMBINER_STRUCT: + case MPI_COMBINER_STRUCT_INTEGER: top_count = ints[0]; for (n=0; nMPI_ERROR = *error_code; +#ifdef HAVE_STATUS_SET_BYTES + MPIR_Status_set_bytes(status, MPI_BYTE, bytes); +#endif + /* --BEGIN ERROR HANDLING-- */ + if (*error_code != MPI_SUCCESS) + *error_code = MPIO_Err_return_file(*fh, *error_code); + /* --END ERROR HANDLING-- */ + MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, + MPIU_Greq_cancel_fn, status, request); + MPI_Grequest_complete(*request); +} diff --git a/ompi/mca/io/romio/romio/adio/common/lock.c b/ompi/mca/io/romio/romio/adio/common/lock.c index f80d7ca762..d21ec4d0ed 100644 --- a/ompi/mca/io/romio/romio/adio/common/lock.c +++ b/ompi/mca/io/romio/romio/adio/common/lock.c @@ -64,6 +64,7 @@ int ADIOI_Set_lock(FDTYPE fd, int cmd, int type, ADIO_Offset offset, int whence, if (!ret_val) { + char errMsg[ADIOI_NTFS_ERR_MSG_MAX]; /* FPRINTF(stderr, "File locking failed in ADIOI_Set_lock.\n"); MPI_Abort(MPI_COMM_WORLD, 1); @@ -80,8 +81,9 @@ int ADIOI_Set_lock(FDTYPE fd, int cmd, int type, ADIO_Offset offset, int whence, } ret_val = GetLastError(); } + ADIOI_NTFS_Strerror(ret_val, errMsg, ADIOI_NTFS_ERR_MSG_MAX); error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, myname, __LINE__, - MPI_ERR_IO, "**io", "**io %s", ADIOI_NTFS_Strerror(ret_val)); + MPI_ERR_IO, "**io", "**io %s", errMsg); } CloseHandle(Overlapped.hEvent); @@ -118,6 +120,7 @@ int ADIOI_Set_lock(FDTYPE fd, int cmd, int type, ADIO_Offset offset, int whence, lock.l_len = len; #endif + errno = 0; do { err = fcntl(fd, cmd, &lock); } while (err && (errno == EINTR)); @@ -125,7 +128,20 @@ int ADIOI_Set_lock(FDTYPE fd, int cmd, int type, ADIO_Offset offset, int whence, if (err && (errno != EBADF)) { /* FIXME: This should use the error message system, especially for MPICH2 */ - FPRINTF(stderr, "File locking failed in ADIOI_Set_lock. If the file system is NFS, you need to use NFS version 3, ensure that the lockd daemon is running on all the machines, and mount the directory with the 'noac' option (no attribute caching).\n"); + FPRINTF(stderr, "File locking failed in ADIOI_Set_lock(fd %X,cmd %s/%X,type %s/%X,whence %X) with return value %X and errno %X.\n" + "If the file system is NFS, you need to use NFS version 3, ensure that the lockd daemon is running on all the machines, and mount the directory with the 'noac' option (no attribute caching).\n", + fd, + ((cmd == F_GETLK )? "F_GETLK" : + ((cmd == F_SETLK )? "F_SETLK" : + ((cmd == F_SETLKW )? "F_SETLKW" : "UNEXPECTED"))), + cmd, + ((type == F_RDLCK )? "F_RDLCK" : + ((type == F_WRLCK )? "F_WRLCK" : + ((type == F_UNLCK )? "F_UNLOCK" : "UNEXPECTED"))), + type, + whence, err, errno); + perror("ADIOI_Set_lock:"); + FPRINTF(stderr,"ADIOI_Set_lock:offset %llu, length %llu\n",(unsigned long long)offset, (unsigned long long)len); MPI_Abort(MPI_COMM_WORLD, 1); } @@ -154,7 +170,23 @@ int ADIOI_Set_lock64(FDTYPE fd, int cmd, int type, ADIO_Offset offset, } while (err && (errno == EINTR)); if (err && (errno != EBADF)) { - FPRINTF(stderr, "File locking failed in ADIOI_Set_lock64\n"); + FPRINTF(stderr, "File locking failed in ADIOI_Set_lock64(fd %X,cmd %s/%X,type %s/%X,whence %X) with return value %X and errno %X.\n" + "If the file system is NFS, you need to use NFS version 3, ensure that the lockd daemon is running on all the machines, and mount the directory with the 'noac' option (no attribute caching).\n", + fd, + ((cmd == F_GETLK )? "F_GETLK" : + ((cmd == F_SETLK )? "F_SETLK" : + ((cmd == F_SETLKW )? "F_SETLKW" : + ((cmd == F_GETLK64 )? "F_GETLK64" : + ((cmd == F_SETLK64 )? "F_SETLK64" : + ((cmd == F_SETLKW64)? "F_SETLKW64" : "UNEXPECTED")))))), + cmd, + ((type == F_RDLCK )? "F_RDLCK" : + ((type == F_WRLCK )? "F_WRLCK" : + ((type == F_UNLCK )? "F_UNLOCK" : "UNEXPECTED"))), + type, + whence, err, errno); + perror("ADIOI_Set_lock64:"); + FPRINTF(stderr,"ADIOI_Set_lock:offset %llu, length %llu\n",(unsigned long long)offset, (unsigned long long)len); MPI_Abort(MPI_COMM_WORLD, 1); } diff --git a/ompi/mca/io/romio/romio/adio/common/system_hints.c b/ompi/mca/io/romio/romio/adio/common/system_hints.c new file mode 100644 index 0000000000..361f16addb --- /dev/null +++ b/ompi/mca/io/romio/romio/adio/common/system_hints.c @@ -0,0 +1,149 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- + * vim: ts=8 sts=4 sw=4 noexpandtab + * + * Copyright (C) 2007 UChicago/Argonne LLC. + * See COPYRIGHT notice in top-level directory. + */ + +#include + +#include + +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_IO_H +#include +#endif + +#ifndef PATH_MAX +#define PATH_MAX 65535 +#endif + +/*#define SYSHINT_DEBUG 1 */ + +#define ROMIO_HINT_DEFAULT_CFG "/etc/romio-hints" +#define ROMIO_HINT_ENV_VAR "ROMIO_HINTS" + +/* if user set the environment variable, use its value to find the + * file-of-hints. Otherwise, we'll look for the default config file. i.e. let + * the user override systemwide hint processing */ + +static int find_file(void) +{ + int fd=-1; + char * hintfile; + + hintfile = getenv(ROMIO_HINT_ENV_VAR); + if(hintfile) + fd = open(hintfile, O_RDONLY); + if (fd < 0 ) + fd = open(ROMIO_HINT_DEFAULT_CFG, O_RDONLY); + + return fd; +} + +/* parse the file-of-hints. Format is zero or more lines of " \n". + * A # in collumn zero is a comment and the line will be ignored. Do our best + * to ignore badly formed lines too. + * + * The caller provides an 'info' object. Each key-value pair found by the + * parser will get added to the info object. any keys already set will be left + * alone on the assumption that the caller knows best. + * + * because MPI-IO hints are optional, we can get away with limited error + * reporting. */ +static int file_to_info(int fd, MPI_Info info) +{ + char *buffer, *token, *key, *val, *garbage; + char *pos1, *pos2; + int flag, ret; + char dummy; + struct stat statbuf; + + /* assumption: config files will be small (less than 1MB) */ + fstat(fd, &statbuf); + /* add 1 to size to make room for NULL termination */ + buffer = (char *)calloc(statbuf.st_size + 1, sizeof (char)); + if (buffer == NULL) return -1; + + ret = read(fd, buffer, statbuf.st_size); + if (ret < 0) return -1; + token = strtok_r(buffer, "\n", &pos1); + do { + if ( (key = strtok_r(token, " \t", &pos2)) == NULL) + /* malformed line: found no items */ + continue; + if (token[0] == '#') + /* ignore '#'-delimited comments */ + continue; + if ( (val = strtok_r(NULL, " \t", &pos2)) == NULL) + /* malformed line: found key without value */ + continue; + if ( (garbage = strtok_r(NULL, " \t", &pos2)) != NULL) + /* malformed line: more than two items */ + continue; + +#ifdef SYSHINT_DEBUG + printf("found: key=%s val=%s\n", key, val); +#endif + /* don't actually care what the value is. only want to know if key + * exists: we leave it alone if so*/ + MPI_Info_get(info, key, 0, &dummy, &flag); + if (flag == 1) continue; + MPI_Info_set(info, key, val); + } while ((token = strtok_r(NULL, "\n", &pos1)) != NULL); + free(buffer); + return 0; +} + +void ADIOI_process_system_hints(MPI_Info info) +{ + int hintfd; + + hintfd = find_file(); + if (hintfd < 0) { +#ifdef SYSHINT_DEBUG + perror("ADIOI_process_system_hints"); +#endif + return; + } + file_to_info(hintfd, info); + close(hintfd); +} + +/* OMPI: Commented out so that we don't get compiler warnings */ +#if 0 +/* debug function: a routine I want in the library to make my life easier when + * using a source debugger. please ignore any "defined but not used" warnings + */ +static void dump_keys(MPI_Info info) { + int i, nkeys, flag; + char key[MPI_MAX_INFO_KEY]; + char value[MPI_MAX_INFO_VAL]; + + MPI_Info_get_nkeys(info, &nkeys); + + for (i=0; i +#endif +typedef struct ADIOI_AIO_req_str { + /* very wierd: if this MPI_Request is a pointer, some C++ compilers + * will clobber it when the MPICH2 C++ bindings are used */ + MPI_Request req; + MPI_Offset nbytes; + /* should probably make this a union */ +#ifdef ROMIO_HAVE_WORKING_AIO + struct aiocb *aiocbp; +#endif +#ifdef ROMIO_PVFS2 + PVFS_sys_op_id op_id; + PVFS_sysresp_io resp_io; + PVFS_Request file_req; + PVFS_Request mem_req; +#endif +#ifdef ROMIO_NTFS + /* Ptr to Overlapped struct */ + LPOVERLAPPED lpOvl; + /* Ptr to file handle */ + HANDLE fd; +#endif +} ADIOI_AIO_Request; struct ADIOI_Fns_struct { void (*ADIOI_xxx_Open) (ADIO_File fd, int *error_code); @@ -184,13 +183,13 @@ struct ADIOI_Fns_struct { #define ADIOI_MIN(a, b) ((a) < (b) ? (a) : (b)) #define ADIOI_MAX(a, b) ((a) > (b) ? (a) : (b)) -#define ADIOI_PREALLOC_BUFSZ 4194304 /* buffer size used to +#define ADIOI_PREALLOC_BUFSZ 16777216 /* buffer size used to preallocate disk space */ /* default values for some hints */ - /* buffer size for collective I/O = 4MB */ -#define ADIOI_CB_BUFFER_SIZE_DFLT "4194304" + /* buffer size for collective I/O = 16 MB */ +#define ADIOI_CB_BUFFER_SIZE_DFLT "16777216" /* buffer size for data sieving in independent reads = 4MB */ #define ADIOI_IND_RD_BUFFER_SIZE_DFLT "4194304" /* buffer size for data sieving in independent writes = 512KB. default is @@ -292,13 +291,7 @@ void ADIOI_Flatten(MPI_Datatype type, ADIOI_Flatlist_node *flat, ADIO_Offset st_offset, int *curr_index); void ADIOI_Delete_flattened(MPI_Datatype datatype); int ADIOI_Count_contiguous_blocks(MPI_Datatype type, int *curr_index); -ADIOI_Async_node *ADIOI_Malloc_async_node(void); -void ADIOI_Free_async_node(ADIOI_Async_node *node); -void ADIOI_Add_req_to_list(ADIO_Request *request); void ADIOI_Complete_async(int *error_code); -void ADIOI_Del_req_from_list(ADIO_Request *request); -struct ADIOI_RequestD *ADIOI_Malloc_request(void); -void ADIOI_Free_request(ADIOI_Req_node *node); void *ADIOI_Malloc_fn(size_t size, int lineno, char *fname); void *ADIOI_Calloc_fn(size_t nelem, size_t elsize, int lineno, char *fname); void *ADIOI_Realloc_fn(void *ptr, size_t size, int lineno, char *fname); @@ -308,6 +301,8 @@ void ADIOI_Get_position(ADIO_File fd, ADIO_Offset *offset); void ADIOI_Get_eof_offset(ADIO_File fd, ADIO_Offset *eof_offset); void ADIOI_Get_byte_offset(ADIO_File fd, ADIO_Offset offset, ADIO_Offset *disp); +void ADIOI_process_system_hints(MPI_Info info); + void ADIOI_GEN_Fcntl(ADIO_File fd, int flag, ADIO_Fcntl_t *fcntl_struct, int *error_code); @@ -318,7 +313,7 @@ void ADIOI_GEN_ReadContig(ADIO_File fd, void *buf, int count, ADIO_Offset offset, ADIO_Status *status, int *error_code); int ADIOI_GEN_aio(ADIO_File fd, void *buf, int len, ADIO_Offset offset, - int wr, void *handle); + int wr, MPI_Request *request); void ADIOI_GEN_IreadContig(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, ADIO_Offset offset, ADIO_Request *request, @@ -347,6 +342,11 @@ int ADIOI_GEN_IODone(ADIO_Request *request, ADIO_Status *status, int *error_code); void ADIOI_GEN_IOComplete(ADIO_Request *request, ADIO_Status *status, int *error_code); +int ADIOI_GEN_aio_poll_fn(void *extra_state, ADIO_Status *status); +int ADIOI_GEN_aio_wait_fn(int count, void **array_of_states, double timeout, + ADIO_Status *status); +int ADIOI_GEN_aio_query_fn(void *extra_state, ADIO_Status *status); +int ADIOI_GEN_aio_free_fn(void *extra_state); void ADIOI_GEN_ReadStrided_naive(ADIO_File fd, void *buf, int count, MPI_Datatype buftype, int file_ptr_type, ADIO_Offset offset, ADIO_Status *status, int @@ -359,6 +359,10 @@ void ADIOI_GEN_WriteStrided_naive(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, ADIO_Offset offset, ADIO_Status *status, int *error_code); +void ADIOI_NOLOCK_WriteStrided(ADIO_File fd, void *buf, int count, + MPI_Datatype datatype, int file_ptr_type, + ADIO_Offset offset, ADIO_Status *status, int + *error_code); void ADIOI_GEN_ReadStridedColl(ADIO_File fd, void *buf, int count, MPI_Datatype datatype, int file_ptr_type, ADIO_Offset offset, ADIO_Status *status, int @@ -493,7 +497,6 @@ int MPIOI_File_write_all_end(MPI_File fh, void *buf, char *myname, MPI_Status *status); -#ifndef HAVE_MPI_GREQUEST int MPIOI_File_iwrite(MPI_File fh, MPI_Offset offset, int file_ptr_type, @@ -501,7 +504,7 @@ int MPIOI_File_iwrite(MPI_File fh, int count, MPI_Datatype datatype, char *myname, - MPIO_Request *request); + MPI_Request *request); int MPIOI_File_iread(MPI_File fh, MPI_Offset offset, int file_ptr_type, @@ -509,8 +512,7 @@ int MPIOI_File_iread(MPI_File fh, int count, MPI_Datatype datatype, char *myname, - MPIO_Request *request); -#endif + MPI_Request *request); @@ -547,12 +549,27 @@ int MPIOI_File_iread(MPI_File fh, #else +#ifdef ADIOI_MPE_LOGGING +# define ADIOI_WRITE_LOCK(fd, offset, whence, len) do { \ + MPE_Log_event( ADIOI_MPE_writelock_a, 0, NULL ); \ + ADIOI_Set_lock((fd)->fd_sys, F_SETLKW, F_WRLCK, offset, whence, len); \ + MPE_Log_event( ADIOI_MPE_writelock_b, 0, NULL ); } while( 0 ) +# define ADIOI_READ_LOCK(fd, offset, whence, len) \ + MPE_Log_event( ADIOI_MPE_readlock_a, 0, NULL ); do { \ + ADIOI_Set_lock((fd)->fd_sys, F_SETLKW, F_RDLCK, offset, whence, len); \ + MPE_Log_event( ADIOI_MPE_readlock_b, 0, NULL ); } while( 0 ) +# define ADIOI_UNLOCK(fd, offset, whence, len) do { \ + MPE_Log_event( ADIOI_MPE_unlock_a, 0, NULL ); \ + ADIOI_Set_lock((fd)->fd_sys, F_SETLK, F_UNLCK, offset, whence, len); \ + MPE_Log_event( ADIOI_MPE_unlock_b, 0, NULL ); } while( 0 ) +#else # define ADIOI_WRITE_LOCK(fd, offset, whence, len) \ ADIOI_Set_lock((fd)->fd_sys, F_SETLKW, F_WRLCK, offset, whence, len) # define ADIOI_READ_LOCK(fd, offset, whence, len) \ ADIOI_Set_lock((fd)->fd_sys, F_SETLKW, F_RDLCK, offset, whence, len) # define ADIOI_UNLOCK(fd, offset, whence, len) \ ADIOI_Set_lock((fd)->fd_sys, F_SETLK, F_UNLCK, offset, whence, len) +#endif #endif @@ -604,5 +621,30 @@ int ADIOI_Snprintf( char *str, size_t size, const char *format, ... ) #include "adioi_error.h" +/* MPE logging variables */ + +#ifdef ADIOI_MPE_LOGGING +#include "mpe.h" + +int ADIOI_MPE_open_a; +int ADIOI_MPE_open_b; +int ADIOI_MPE_read_a; +int ADIOI_MPE_read_b; +int ADIOI_MPE_write_a; +int ADIOI_MPE_write_b; +int ADIOI_MPE_lseek_a; +int ADIOI_MPE_lseek_b; +int ADIOI_MPE_close_a; +int ADIOI_MPE_close_b; +int ADIOI_MPE_writelock_a; +int ADIOI_MPE_writelock_b; +int ADIOI_MPE_readlock_a; +int ADIOI_MPE_readlock_b; +int ADIOI_MPE_unlock_a; +int ADIOI_MPE_unlock_b; +int ADIOI_MPE_postwrite_a; +int ADIOI_MPE_postwrite_b; +#endif + #endif diff --git a/ompi/mca/io/romio/romio/adio/include/adioi_errmsg.h b/ompi/mca/io/romio/romio/adio/include/adioi_errmsg.h index b332425271..e17c1b01ca 100644 --- a/ompi/mca/io/romio/romio/adio/include/adioi_errmsg.h +++ b/ompi/mca/io/romio/romio/adio/include/adioi_errmsg.h @@ -62,6 +62,7 @@ MPI_ERR_IO MPIR_ERR_FILETYPE "Filetype must be constructed out of one or more etypes" MPIR_ERR_NO_TESTFS "ROMIO has not been configured to use the TESTFS file system" MPIR_ERR_DEFERRED "independent IO attempted even though no_indep_rw hint given" + MPIR_ERR_NO_BGL "ROMIO has not been configured to use the BGL file system" MPI_ERR_COMM MPIR_ERR_COMM_NULL (null communicator. from MPICH) diff --git a/ompi/mca/io/romio/romio/adio/include/adioi_fs_proto.h b/ompi/mca/io/romio/romio/adio/include/adioi_fs_proto.h index fd7f214e6f..2fc7f7f0f8 100644 --- a/ompi/mca/io/romio/romio/adio/include/adioi_fs_proto.h +++ b/ompi/mca/io/romio/romio/adio/include/adioi_fs_proto.h @@ -49,6 +49,11 @@ extern struct ADIOI_Fns_struct ADIO_SFS_operations; /* prototypes are in adio/ad_sfs/ad_sfs.h */ #endif +#ifdef ROMIO_LUSTRE +extern struct ADIOI_Fns_struct ADIO_LUSTRE_operations; +/* prototypes are in adio/ad_lustre/ad_lustre.h */ +#endif + #ifdef ROMIO_NTFS extern struct ADIOI_Fns_struct ADIO_NTFS_operations; /* prototypes are in adio/ad_ntfs/ad_ntfs.h */ @@ -69,6 +74,16 @@ extern struct ADIOI_Fns_struct ADIO_TESTFS_operations; /* prototypes are in adio/ad_testfs/ad_testfs.h */ #endif +#ifdef ROMIO_BGL +extern struct ADIOI_Fns_struct ADIO_BGL_operations; +/* prototypes are in adio/ad_bgl/ad_bgl.h */ +#endif + +#ifdef ROMIO_BGLOCKLESS +extern struct ADIOI_Fns_struct ADIO_BGLOCKLESS_operations; +/* no extra prototypes for this fs at this time */ +#endif + #ifdef ROMIO_GRIDFTP /* prototypes are in adio/ad_gridftp/ad_gridftp.h */ extern struct ADIOI_Fns_struct ADIO_GRIDFTP_operations; diff --git a/ompi/mca/io/romio/romio/adio/include/mpio_error.h b/ompi/mca/io/romio/romio/adio/include/mpio_error.h index f6eeba5120..66c7a10433 100644 --- a/ompi/mca/io/romio/romio/adio/include/mpio_error.h +++ b/ompi/mca/io/romio/romio/adio/include/mpio_error.h @@ -63,6 +63,8 @@ #define MPIR_ERR_FILETYPE 33 #define MPIR_ERR_NO_NTFS 35 #define MPIR_ERR_NO_TESTFS 36 +#define MPIR_ERR_NO_LUSTRE 37 +#define MPIR_ERR_NO_BGL 38 /* MPI_ERR_COMM */ #ifndef MPIR_ERR_COMM_NULL diff --git a/ompi/mca/io/romio/romio/adio/include/mpipr.h b/ompi/mca/io/romio/romio/adio/include/mpipr.h index d775dfbcd9..f14fe22f23 100644 --- a/ompi/mca/io/romio/romio/adio/include/mpipr.h +++ b/ompi/mca/io/romio/romio/adio/include/mpipr.h @@ -367,18 +367,23 @@ #define MPI_Info_dup PMPI_Info_dup #undef MPI_Info_free #define MPI_Info_free PMPI_Info_free -#undef MPI_Grequest_start -#define MPI_Grequest_start PMPI_Grequest_start -#undef MPI_Grequest_complete -#define MPI_Grequest_complete PMPI_Grequest_complete -#undef MPI_Status_set_cancelled -#define MPI_Status_set_cancelled PMPI_Status_set_cancelled /* #undef MPI_Info_c2f #define MPI_Info_c2f PMPI_Info_c2f #undef MPI_Info_f2c #define MPI_Info_f2c PMPI_Info_f2c */ #endif +#undef MPI_Grequest_start +#define MPI_Grequest_start PMPI_Grequest_start +#undef MPI_Grequest_complete +#define MPI_Grequest_complete PMPI_Grequest_complete +#undef MPI_Status_set_cancelled +#define MPI_Status_set_cancelled PMPI_Status_set_cancelled + +#undef MPIX_Grequest_class_create +#define MPIX_Grequest_class_create PMPIX_Grequest_class_create +#undef MPIX_Grequest_class_allocate +#define MPIX_Grequest_class_allocate PMPIX_Grequest_class_allocate #ifdef MPIO_FORTRAN_SRC /* only in MPI-IO Fortran source directory */ #undef MPI_File_c2f diff --git a/ompi/mca/io/romio/romio/mpi-io/mpiu_greq.h b/ompi/mca/io/romio/romio/adio/include/mpiu_greq.h similarity index 94% rename from ompi/mca/io/romio/romio/mpi-io/mpiu_greq.h rename to ompi/mca/io/romio/romio/adio/include/mpiu_greq.h index ee4f89d5cb..d924e277a7 100644 --- a/ompi/mca/io/romio/romio/mpi-io/mpiu_greq.h +++ b/ompi/mca/io/romio/romio/adio/include/mpiu_greq.h @@ -4,7 +4,6 @@ * (C) 2003 by Argonne National Laboratory. * See COPYRIGHT in top-level directory. */ -#include "mpioimpl.h" #ifndef _MPIU_GREQUEST_H #define _MPIU_GREQUEST_H diff --git a/ompi/mca/io/romio/romio/common/dataloop/.state-cache b/ompi/mca/io/romio/romio/common/dataloop/.state-cache new file mode 100644 index 0000000000..37fee8c743 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/.state-cache @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ompi/mca/io/romio/romio/common/dataloop/Makefile.in b/ompi/mca/io/romio/romio/common/dataloop/Makefile.in new file mode 100644 index 0000000000..0746bac2e5 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/Makefile.in @@ -0,0 +1,121 @@ +CC = @CC@ +AR = @AR@ +LIBNAME = @LIBNAME@ +RANLIB = @RANLIB@ +srcdir = @srcdir@ +CC_SHL = @CC_SHL@ +SHLIBNAME = @SHLIBNAME@ + +# QUIET_COMPILE = 1 +# ifdef V +# QUIET_COMPILE = 0 +# endif +# +# ifeq ($(QUIET_COMPILE),1) +# # say a one-line description of the action, do not echo the command +# Q=@echo +# E=@ +# else +# # do not say the short Q lines, but do echo the entire command +# Q=@echo >/dev/null +# E= +# endif +Q=@echo +E=@ + +INCLUDE_DIR = -I@MPI_INCLUDE_DIR@ -I../../include -I${srcdir}/../../mpi-io \ + -I${srcdir}/../../adio/include -I../../adio/include \ + -I${srcdir}/../../../../../src/include \ + -I../../../../../src/include + +CFLAGS = @CPPFLAGS@ @CFLAGS@ $(MPIOPROFILE) $(INCLUDE_DIR) + +LIBTOOL = @LIBTOOL@ +C_COMPILE_SHL = $(CC_SHL) + +@VPATH@ + +DLOOP_OBJECTS = darray_support.o dataloop.o \ + dataloop_create.o dataloop_create_blockindexed.o \ + dataloop_create_contig.o dataloop_create_indexed.o \ + dataloop_create_pairtype.o dataloop_create_struct.o \ + dataloop_create_vector.o romio_dataloop.o segment.o \ + subarray_support.o typesize_support.o segment_ops.o \ + romio_segment_ops.o + +DLOOP_RENAMED_OBJECTS = R_darray_support.o \ + R_dataloop.o R_dataloop_create.o \ + R_dataloop_create_blockindexed.o \ + R_dataloop_create_contig.o R_dataloop_create_indexed.o \ + R_dataloop_create_pairtype.o R_dataloop_create_struct.o \ + R_dataloop_create_vector.o romio_dataloop.o R_segment.o \ + R_subarray_support.o R_typesize_support.o R_segment_ops.o \ + romio_segment_ops.o + +all: $(LIBNAME) + @if [ "@ENABLE_SHLIB@" != "none" ] ; then \ + $(MAKE) $(SHLIBNAME).la ;\ + fi + +.SUFFIXES: $(SUFFIXES) .p .lo + +.c.o: +# $(Q) "$(CC) $@" + $(Q) $(CC) $(CFLAGS) -c $< + $(E)$(CC) $(CFLAGS) -c $< +.c.lo: + $(C_COMPILE_SHL) $(CFLAGS) -c $< -o _s$*.o + @mv -f _s$*.o $*.lo + +.c.p: + @cp $(srcdir)/$*.c _$*.c + $(CC) $(CFLAGS) -c _$*.c + @rm -f _$*.c + +profile: + sleep 1 + +# we must rename the objects prior to insertion into library +# in order to avoid name clashes with copies already in the +# MPICH2 library. +$(LIBNAME): $(DLOOP_OBJECTS) + @cp darray_support.o R_darray_support.o + @cp dataloop.o R_dataloop.o + @cp dataloop_create.o R_dataloop_create.o + @cp dataloop_create_blockindexed.o R_dataloop_create_blockindexed.o + @cp dataloop_create_contig.o R_dataloop_create_contig.o + @cp dataloop_create_indexed.o R_dataloop_create_indexed.o + @cp dataloop_create_pairtype.o R_dataloop_create_pairtype.o + @cp dataloop_create_struct.o R_dataloop_create_struct.o + @cp dataloop_create_vector.o R_dataloop_create_vector.o + @cp segment.o R_segment.o + @cp subarray_support.o R_subarray_support.o + @cp typesize_support.o R_typesize_support.o + @cp segment_ops.o R_segment_ops.o + $(Q) " AR $@" + $(E)$(AR) $(LIBNAME) $(DLOOP_RENAMED_OBJECTS) + $(Q) " RANLIB $@" + $(E)$(RANLIB) $(LIBNAME) + +DLOOP_LOOBJECTS = $(DLOOP_OBJECTS:.o=.lo) +DLOOP_RENAMED_LOOBJECTS = $(DLOOP_RENAMED_OBJECTS:.o=.lo) +$(SHLIBNAME).la: $(DLOOP_LOOBJECTS) + @cp darray_support.lo R_darray_support.lo + @cp dataloop.lo R_dataloop.lo + @cp dataloop_create.lo R_dataloop_create.lo + @cp dataloop_create_blockindexed.lo R_dataloop_create_blockindexed.lo + @cp dataloop_create_contig.lo R_dataloop_create_contig.lo + @cp dataloop_create_indexed.lo R_dataloop_create_indexed.lo + @cp dataloop_create_pairtype.lo R_dataloop_create_pairtype.lo + @cp dataloop_create_struct.lo R_dataloop_create_struct.lo + @cp dataloop_create_vector.lo R_dataloop_create_vector.lo + @cp segment.lo R_segment.lo + @cp subarray_support.lo R_subarray_support.lo + @cp typesize_support.lo R_typesize_support.lo + @cp segment_ops.lo R_segment_ops.lo + $(AR) $(SHLIBNAME).la $(DLOOP_RENAMED_LOOBJECTS) + +clean: + @rm -f *.o *.lo *.gcno *.gcda *.bb *.bbg + @rm -f ${srcdir}/*.gcno ${srcdir}/*.gcda + @rm -f ${srcdir}/*.bb ${srcdir}/*.bbg diff --git a/ompi/mca/io/romio/romio/common/dataloop/darray_support.c b/ompi/mca/io/romio/romio/common/dataloop/darray_support.c new file mode 100644 index 0000000000..da1270ac06 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/darray_support.c @@ -0,0 +1,301 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + * + * Note: This code originally appeared in ROMIO. + */ + +#include "dataloop.h" + +static int MPIOI_Type_block(int *array_of_gsizes, int dim, int ndims, + int nprocs, int rank, int darg, int order, MPI_Aint orig_extent, + MPI_Datatype type_old, MPI_Datatype *type_new, + MPI_Aint *st_offset); +static int MPIOI_Type_cyclic(int *array_of_gsizes, int dim, int ndims, int nprocs, + int rank, int darg, int order, MPI_Aint orig_extent, + MPI_Datatype type_old, MPI_Datatype *type_new, + MPI_Aint *st_offset); + + +int PREPEND_PREFIX(Type_convert_darray)(int size, + int rank, + int ndims, + int *array_of_gsizes, + int *array_of_distribs, + int *array_of_dargs, + int *array_of_psizes, + int order, + MPI_Datatype oldtype, + MPI_Datatype *newtype) +{ + MPI_Datatype type_old, type_new=MPI_DATATYPE_NULL, types[3]; + int procs, tmp_rank, i, tmp_size, blklens[3], *coords; + MPI_Aint *st_offsets, orig_extent, disps[3]; + + PMPI_Type_extent(oldtype, &orig_extent); + +/* calculate position in Cartesian grid as MPI would (row-major + ordering) */ + coords = (int *) DLOOP_Malloc(ndims*sizeof(int)); + procs = size; + tmp_rank = rank; + for (i=0; i=0; i--) { + switch(array_of_distribs[i]) { + case MPI_DISTRIBUTE_BLOCK: + MPIOI_Type_block(array_of_gsizes, i, ndims, array_of_psizes[i], + coords[i], array_of_dargs[i], order, + orig_extent, type_old, &type_new, + st_offsets+i); + break; + case MPI_DISTRIBUTE_CYCLIC: + MPIOI_Type_cyclic(array_of_gsizes, i, ndims, + array_of_psizes[i], coords[i], + array_of_dargs[i], order, + orig_extent, type_old, &type_new, + st_offsets+i); + break; + case MPI_DISTRIBUTE_NONE: + /* treat it as a block distribution on 1 process */ + MPIOI_Type_block(array_of_gsizes, i, ndims, array_of_psizes[i], + coords[i], MPI_DISTRIBUTE_DFLT_DARG, order, orig_extent, + type_old, &type_new, st_offsets+i); + break; + } + if (i != ndims-1) PMPI_Type_free(&type_old); + type_old = type_new; + } + + /* add displacement and UB */ + disps[1] = st_offsets[ndims-1]; + tmp_size = 1; + for (i=ndims-2; i>=0; i--) { + tmp_size *= array_of_gsizes[i+1]; + disps[1] += tmp_size*st_offsets[i]; + } + } + + disps[1] *= orig_extent; + + disps[2] = orig_extent; + for (i=0; idim; i--) stride *= array_of_gsizes[i]; + PMPI_Type_hvector(mysize, 1, stride, type_old, type_new); + } + + } + + *st_offset = blksize * rank; + /* in terms of no. of elements of type oldtype in this dimension */ + if (mysize == 0) *st_offset = 0; + + return MPI_SUCCESS; +} + + +/* Returns MPI_SUCCESS on success, an MPI error code on failure. Code above + * needs to call MPIO_Err_return_xxx. + */ +static int MPIOI_Type_cyclic(int *array_of_gsizes, int dim, int ndims, int nprocs, + int rank, int darg, int order, MPI_Aint orig_extent, + MPI_Datatype type_old, MPI_Datatype *type_new, + MPI_Aint *st_offset) +{ +/* nprocs = no. of processes in dimension dim of grid + rank = coordinate of this process in dimension dim */ + int blksize, i, blklens[3], st_index, end_index, local_size, rem, count; + MPI_Aint stride, disps[3]; + MPI_Datatype type_tmp, types[3]; + + if (darg == MPI_DISTRIBUTE_DFLT_DARG) blksize = 1; + else blksize = darg; + + /* --BEGIN ERROR HANDLING-- */ + if (blksize <= 0) { + return MPI_ERR_ARG; + } + /* --END ERROR HANDLING-- */ + + st_index = rank*blksize; + end_index = array_of_gsizes[dim] - 1; + + if (end_index < st_index) local_size = 0; + else { + local_size = ((end_index - st_index + 1)/(nprocs*blksize))*blksize; + rem = (end_index - st_index + 1) % (nprocs*blksize); + local_size += (rem < blksize) ? rem : blksize; + } + + count = local_size/blksize; + rem = local_size % blksize; + + stride = nprocs*blksize*orig_extent; + if (order == MPI_ORDER_FORTRAN) + for (i=0; idim; i--) stride *= array_of_gsizes[i]; + + PMPI_Type_hvector(count, blksize, stride, type_old, type_new); + + if (rem) { + /* if the last block is of size less than blksize, include + it separately using MPI_Type_struct */ + + types[0] = *type_new; + types[1] = type_old; + disps[0] = 0; + disps[1] = count*stride; + blklens[0] = 1; + blklens[1] = rem; + + PMPI_Type_struct(2, blklens, disps, types, &type_tmp); + + PMPI_Type_free(type_new); + *type_new = type_tmp; + } + + /* In the first iteration, we need to set the displacement in that + dimension correctly. */ + if ( ((order == MPI_ORDER_FORTRAN) && (dim == 0)) || + ((order == MPI_ORDER_C) && (dim == ndims-1)) ) { + types[0] = MPI_LB; + disps[0] = 0; + types[1] = *type_new; + disps[1] = rank * blksize * orig_extent; + types[2] = MPI_UB; + disps[2] = orig_extent * array_of_gsizes[dim]; + blklens[0] = blklens[1] = blklens[2] = 1; + PMPI_Type_struct(3, blklens, disps, types, &type_tmp); + PMPI_Type_free(type_new); + *type_new = type_tmp; + + *st_offset = 0; /* set it to 0 because it is taken care of in + the struct above */ + } + else { + *st_offset = rank * blksize; + /* st_offset is in terms of no. of elements of type oldtype in + * this dimension */ + } + + if (local_size == 0) *st_offset = 0; + + return MPI_SUCCESS; +} diff --git a/ompi/mca/io/romio/romio/common/dataloop/dataloop.c b/ompi/mca/io/romio/romio/common/dataloop/dataloop.c new file mode 100644 index 0000000000..411b47e2e6 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/dataloop.c @@ -0,0 +1,731 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#include +#include +#include + +#include "./dataloop.h" + +#undef DEBUG_DLOOP_SIZE +#undef DLOOP_DEBUG_MEMORY + +/* Dataloops + * + * The functions here are used for the creation, copying, update, and display + * of DLOOP_Dataloop structures and trees of these structures. + * + * Currently we store trees of dataloops in contiguous regions of memory. They + * are stored in such a way that subtrees are also stored contiguously. This + * makes it somewhat easier to copy these subtrees around. Keep this in mind + * when looking at the functions below. + * + * The structures used in this file are defined in mpid_datatype.h. There is + * no separate mpid_dataloop.h at this time. + * + * OPTIMIZATIONS: + * + * There are spots in the code with OPT tags that indicate where we could + * optimize particular calculations or avoid certain checks. + * + * NOTES: + * + * Don't have locks in place at this time! + */ + +/* Some functions in this file are responsible for allocation of space for + * dataloops. These structures include the dataloop structure itself + * followed by a sequence of variable-sized arrays, depending on the loop + * kind. For example, a dataloop of kind DLOOP_KIND_INDEXED has a + * dataloop structure followed by an array of block sizes and then an array + * of offsets. + * + * For efficiency and ease of cleanup (preserving a single free at + * deallocation), we want to allocate this memory as a single large chunk. + * However, we must perform some alignment of the components of this chunk + * in order to obtain correct and efficient operation across all platforms. + */ + + +/*@ + Dataloop_free - deallocate the resources used to store a dataloop + + Input Parameters: +. dataloop - pointer to dataloop structure +@*/ +void PREPEND_PREFIX(Dataloop_free)(DLOOP_Dataloop **dataloop) +{ + + if (*dataloop == NULL) return; + +#ifdef DLOOP_DEBUG_MEMORY + DLOOP_dbg_printf("DLOOP_Dataloop_free: freeing loop @ %x.\n", + (int) *dataloop); +#endif + + memset(*dataloop, 0, sizeof(DLOOP_Dataloop_common)); + DLOOP_Free(*dataloop); + *dataloop = NULL; + return; +} +/*@ + Dataloop_copy - Copy an arbitrary dataloop structure, updating + pointers as necessary + + Input Parameters: ++ dest - pointer to destination region +. src - pointer to original dataloop structure +- size - size of dataloop structure + + This routine parses the dataloop structure as it goes in order to + determine what exactly it needs to update. + + Notes: + It assumes that the source dataloop was allocated in our usual way; + this means that the entire dataloop is in a contiguous region and that + the root of the tree is first in the array. + + This has some implications: ++ we can use a contiguous copy mechanism to copy the majority of the + structure +- all pointers in the region are relative to the start of the data region + the first dataloop in the array is the root of the tree +@*/ +void PREPEND_PREFIX(Dataloop_copy)(void *dest, + void *src, + int size) +{ + DLOOP_Offset ptrdiff; + +#ifdef DLOOP_DEBUG_MEMORY + DLOOP_dbg_printf("DLOOP_Dataloop_copy: copying from %x to %x (%d bytes).\n", + (int) src, (int) dest, size); +#endif + + /* copy region first */ + memcpy(dest, src, size); + + /* Calculate difference in starting locations. DLOOP_Dataloop_update() + * then traverses the new structure and updates internal pointers by + * adding this difference to them. This way we can just copy the + * structure, including pointers, in one big block. + */ + ptrdiff = (char *) dest - (char *) src; + + /* traverse structure updating pointers */ + PREPEND_PREFIX(Dataloop_update)(dest, ptrdiff); + + return; +} + + +/*@ + Dataloop_update - update pointers after a copy operation + + Input Parameters: ++ dataloop - pointer to loop to update +- ptrdiff - value indicating offset between old and new pointer values + + This function is used to recursively update all the pointers in a + dataloop tree. +@*/ +void PREPEND_PREFIX(Dataloop_update)(DLOOP_Dataloop *dataloop, + DLOOP_Offset ptrdiff) +{ + /* OPT: only declare these variables down in the Struct case */ + int i; + DLOOP_Dataloop **looparray; + + switch(dataloop->kind & DLOOP_KIND_MASK) { + case DLOOP_KIND_CONTIG: + case DLOOP_KIND_VECTOR: + /* + * All these really ugly assignments are really of the form: + * + * ((char *) dataloop->loop_params.c_t.loop) += ptrdiff; + * + * However, some compilers spit out warnings about casting on the + * LHS, so we get this much nastier form instead (using common + * struct for contig and vector): + */ + dataloop->loop_params.cm_t.dataloop = (DLOOP_Dataloop *) + ((char *) dataloop->loop_params.cm_t.dataloop + ptrdiff); + + if (!(dataloop->kind & DLOOP_FINAL_MASK)) + PREPEND_PREFIX(Dataloop_update)(dataloop->loop_params.cm_t.dataloop, ptrdiff); + break; + + case DLOOP_KIND_BLOCKINDEXED: + dataloop->loop_params.bi_t.offset_array = (DLOOP_Offset *) + ((char *) dataloop->loop_params.bi_t.offset_array + ptrdiff); + dataloop->loop_params.bi_t.dataloop = (DLOOP_Dataloop *) + ((char *) dataloop->loop_params.bi_t.dataloop + ptrdiff); + + if (!(dataloop->kind & DLOOP_FINAL_MASK)) + PREPEND_PREFIX(Dataloop_update)(dataloop->loop_params.bi_t.dataloop, ptrdiff); + break; + + case DLOOP_KIND_INDEXED: + dataloop->loop_params.i_t.blocksize_array = (DLOOP_Count *) + ((char *) dataloop->loop_params.i_t.blocksize_array + ptrdiff); + dataloop->loop_params.i_t.offset_array = (DLOOP_Offset *) + ((char *) dataloop->loop_params.i_t.offset_array + ptrdiff); + dataloop->loop_params.i_t.dataloop = (DLOOP_Dataloop *) + ((char *) dataloop->loop_params.i_t.dataloop + ptrdiff); + + if (!(dataloop->kind & DLOOP_FINAL_MASK)) + PREPEND_PREFIX(Dataloop_update)(dataloop->loop_params.i_t.dataloop, ptrdiff); + break; + + case DLOOP_KIND_STRUCT: + dataloop->loop_params.s_t.blocksize_array = (DLOOP_Count *) + ((char *) dataloop->loop_params.s_t.blocksize_array + ptrdiff); + dataloop->loop_params.s_t.offset_array = (DLOOP_Offset *) + ((char *) dataloop->loop_params.s_t.offset_array + ptrdiff); + dataloop->loop_params.s_t.dataloop_array = (DLOOP_Dataloop **) + ((char *) dataloop->loop_params.s_t.dataloop_array + ptrdiff); + + /* fix the N dataloop pointers too */ + looparray = dataloop->loop_params.s_t.dataloop_array; + for (i=0; i < dataloop->loop_params.s_t.count; i++) { + looparray[i] = (DLOOP_Dataloop *) + ((char *) looparray[i] + ptrdiff); + } + + if (dataloop->kind & DLOOP_FINAL_MASK) break; + + for (i=0; i < dataloop->loop_params.s_t.count; i++) { + PREPEND_PREFIX(Dataloop_update)(looparray[i], ptrdiff); + } + break; + default: + /* --BEGIN ERROR HANDLING-- */ + DLOOP_Assert(0); + break; + /* --END ERROR HANDLING-- */ + } + return; +} + +/*@ + Dataloop_alloc - allocate the resources used to store a dataloop with + no old loops associated with it. + + Input Parameters: ++ kind - kind of dataloop to allocate +. count - number of elements in dataloop (kind dependent) +. new_loop_p - address at which to store new dataloop pointer +- new_loop_sz_p - pointer to integer in which to store new loop size + + Notes: + The count parameter passed into this function will often be different + from the count passed in at the MPI layer due to optimizations. +@*/ +void PREPEND_PREFIX(Dataloop_alloc)(int kind, + DLOOP_Count count, + DLOOP_Dataloop **new_loop_p, + int *new_loop_sz_p) +{ + PREPEND_PREFIX(Dataloop_alloc_and_copy)(kind, + count, + NULL, + 0, + new_loop_p, + new_loop_sz_p); + return; +} + +/*@ + Dataloop_alloc_and_copy - allocate the resources used to store a + dataloop and copy in old dataloop as + appropriate + + Input Parameters: ++ kind - kind of dataloop to allocate +. count - number of elements in dataloop (kind dependent) +. old_loop - pointer to old dataloop (or NULL for none) +. old_loop_sz - size of old dataloop (should be zero if old_loop is NULL) +. new_loop_p - address at which to store new dataloop pointer +- new_loop_sz_p - pointer to integer in which to store new loop size + + Notes: + The count parameter passed into this function will often be different + from the count passed in at the MPI layer. +@*/ +void PREPEND_PREFIX(Dataloop_alloc_and_copy)(int kind, + DLOOP_Count count, + DLOOP_Dataloop *old_loop, + int old_loop_sz, + DLOOP_Dataloop **new_loop_p, + int *new_loop_sz_p) +{ + int new_loop_sz = 0; + int align_sz = 8; /* default aligns everything to 8-byte boundaries */ + int epsilon; + int loop_sz = sizeof(DLOOP_Dataloop); + int off_sz = 0, blk_sz = 0, ptr_sz = 0, extent_sz = 0; + + char *pos; + DLOOP_Dataloop *new_loop; + +#ifdef HAVE_MAX_STRUCT_ALIGNMENT + if (align_sz > HAVE_MAX_STRUCT_ALIGNMENT) { + align_sz = HAVE_MAX_STRUCT_ALIGNMENT; + } +#endif + + if (old_loop != NULL) { + DLOOP_Assert((old_loop_sz % align_sz) == 0); + } + + /* calculate the space that we actually need for everything */ + switch (kind) { + case DLOOP_KIND_STRUCT: + /* need space for dataloop pointers and extents */ + ptr_sz = count * sizeof(DLOOP_Dataloop *); + extent_sz = count * sizeof(DLOOP_Offset); + case DLOOP_KIND_INDEXED: + /* need space for block sizes */ + blk_sz = count * sizeof(DLOOP_Count); + case DLOOP_KIND_BLOCKINDEXED: + /* need space for block offsets */ + off_sz = count * sizeof(DLOOP_Offset); + case DLOOP_KIND_CONTIG: + case DLOOP_KIND_VECTOR: + break; + default: + DLOOP_Assert(0); + } + + /* pad everything that we're going to allocate */ + epsilon = loop_sz % align_sz; + if (epsilon) loop_sz += align_sz - epsilon; + + epsilon = off_sz % align_sz; + if (epsilon) off_sz += align_sz - epsilon; + + epsilon = blk_sz % align_sz; + if (epsilon) blk_sz += align_sz - epsilon; + + epsilon = ptr_sz % align_sz; + if (epsilon) ptr_sz += align_sz - epsilon; + + epsilon = extent_sz % align_sz; + if (epsilon) extent_sz += align_sz - epsilon; + + new_loop_sz += loop_sz + off_sz + blk_sz + ptr_sz + + extent_sz + old_loop_sz; + + /* allocate space */ + new_loop = (DLOOP_Dataloop *) DLOOP_Malloc(new_loop_sz); + if (new_loop == NULL) { + *new_loop_p = NULL; + return; + } + +#ifdef DLOOP_DEBUG_MEMORY + DLOOP_dbg_printf("DLOOP_Dataloop_alloc_and_copy: new loop @ %x (tot sz = %d, loop = %d, off = %d, blk = %d, ptr = %d, extent = %d, old = %d)\n", + (int) new_loop, + new_loop_sz, + loop_sz, + off_sz, + blk_sz, + ptr_sz, + extent_sz, + old_loop_sz); +#endif + + /* set all the pointers in the new dataloop structure */ + switch (kind) { + case DLOOP_KIND_STRUCT: + /* order is: + * - pointers + * - blocks + * - offsets + * - extents + */ + new_loop->loop_params.s_t.dataloop_array = + (DLOOP_Dataloop **) (((char *) new_loop) + loop_sz); + new_loop->loop_params.s_t.blocksize_array = + (DLOOP_Count *) (((char *) new_loop) + loop_sz + ptr_sz); + new_loop->loop_params.s_t.offset_array = + (DLOOP_Offset *) (((char *) new_loop) + loop_sz + + ptr_sz + blk_sz); + new_loop->loop_params.s_t.el_extent_array = + (DLOOP_Offset *) (((char *) new_loop) + loop_sz + + ptr_sz + blk_sz + off_sz); + break; + case DLOOP_KIND_INDEXED: + /* order is: + * - blocks + * - offsets + */ + new_loop->loop_params.i_t.blocksize_array = + (DLOOP_Count *) (((char *) new_loop) + loop_sz); + new_loop->loop_params.i_t.offset_array = + (DLOOP_Offset *) (((char *) new_loop) + loop_sz + blk_sz); + if (old_loop == NULL) { + new_loop->loop_params.i_t.dataloop = NULL; + } + else { + new_loop->loop_params.i_t.dataloop = + (DLOOP_Dataloop *) (((char *) new_loop) + + (new_loop_sz - old_loop_sz)); + } + break; + case DLOOP_KIND_BLOCKINDEXED: + new_loop->loop_params.bi_t.offset_array = + (DLOOP_Offset *) (((char *) new_loop) + loop_sz); + if (old_loop == NULL) { + new_loop->loop_params.bi_t.dataloop = NULL; + } + else { + new_loop->loop_params.bi_t.dataloop = + (DLOOP_Dataloop *) (((char *) new_loop) + + (new_loop_sz - old_loop_sz)); + } + break; + case DLOOP_KIND_CONTIG: + if (old_loop == NULL) { + new_loop->loop_params.c_t.dataloop = NULL; + } + else { + new_loop->loop_params.c_t.dataloop = + (DLOOP_Dataloop *) (((char *) new_loop) + + (new_loop_sz - old_loop_sz)); + } + break; + case DLOOP_KIND_VECTOR: + if (old_loop == NULL) { + new_loop->loop_params.v_t.dataloop = NULL; + } + else { + new_loop->loop_params.v_t.dataloop = + (DLOOP_Dataloop *) (((char *) new_loop) + + (new_loop_sz - old_loop_sz)); + } + break; + default: + DLOOP_Assert(0); + } + + pos = ((char *) new_loop) + (new_loop_sz - old_loop_sz); + if (old_loop != NULL) { + PREPEND_PREFIX(Dataloop_copy)(pos, old_loop, old_loop_sz); + } + + *new_loop_p = new_loop; + *new_loop_sz_p = new_loop_sz; + return; +} + +/*@ + Dataloop_struct_alloc - allocate the resources used to store a dataloop and + copy in old dataloop as appropriate. this version + is specifically for use when a struct dataloop is + being created; the space to hold old dataloops in + this case must be described back to the + implementation in order for efficient copying. + + Input Parameters: ++ count - number of elements in dataloop (kind dependent) +. old_loop_sz - size of old dataloop (should be zero if old_loop is NULL) +. basic_ct - number of basic types for which new dataloops are needed +. old_loop_p - address at which to store pointer to old loops +. new_loop_p - address at which to store new struct dataloop pointer +- new_loop_sz_p - address at which to store new loop size + + Notes: + The count parameter passed into this function will often be different + from the count passed in at the MPI layer due to optimizations. + + The caller is responsible for filling in the region pointed to by + old_loop_p (count elements). +@*/ +void PREPEND_PREFIX(Dataloop_struct_alloc)(DLOOP_Count count, + int old_loop_sz, + int basic_ct, + DLOOP_Dataloop **old_loop_p, + DLOOP_Dataloop **new_loop_p, + int *new_loop_sz_p) +{ + int new_loop_sz = 0; + int align_sz = 8; /* default aligns everything to 8-byte boundaries */ + int epsilon; + int loop_sz = sizeof(DLOOP_Dataloop); + int off_sz, blk_sz, ptr_sz, extent_sz, basic_sz; + + DLOOP_Dataloop *new_loop; + +#ifdef HAVE_MAX_STRUCT_ALIGNMENT + if (align_sz > HAVE_MAX_STRUCT_ALIGNMENT) { + align_sz = HAVE_MAX_STRUCT_ALIGNMENT; + } +#endif + + /* calculate the space that we actually need for everything */ + ptr_sz = count * sizeof(DLOOP_Dataloop *); + extent_sz = count * sizeof(DLOOP_Offset); + blk_sz = count * sizeof(DLOOP_Count); + off_sz = count * sizeof(DLOOP_Offset); + basic_sz = sizeof(DLOOP_Dataloop); + + /* pad everything that we're going to allocate */ + epsilon = loop_sz % align_sz; + if (epsilon) loop_sz += align_sz - epsilon; + + epsilon = off_sz % align_sz; + if (epsilon) off_sz += align_sz - epsilon; + + epsilon = blk_sz % align_sz; + if (epsilon) blk_sz += align_sz - epsilon; + + epsilon = ptr_sz % align_sz; + if (epsilon) ptr_sz += align_sz - epsilon; + + epsilon = extent_sz % align_sz; + if (epsilon) extent_sz += align_sz - epsilon; + + epsilon = basic_sz % align_sz; + if (epsilon) basic_sz += align_sz - epsilon; + + /* note: we pad *each* basic type dataloop, because the + * code used to create them assumes that we're going to + * do that. + */ + + new_loop_sz += loop_sz + off_sz + blk_sz + ptr_sz + + extent_sz + (basic_ct * basic_sz) + old_loop_sz; + + /* allocate space */ + new_loop = (DLOOP_Dataloop *) DLOOP_Malloc(new_loop_sz); + if (new_loop == NULL) { + *new_loop_p = NULL; + return; + } + +#ifdef DLOOP_DEBUG_MEMORY + DLOOP_dbg_printf("DLOOP_Dataloop_struct_alloc: new loop @ %x (tot sz = %d, loop = %d, off = %d, blk = %d, ptr = %d, extent = %d, basics = %d, old = %d)\n", + (int) new_loop, + new_loop_sz, + loop_sz, + off_sz, + blk_sz, + ptr_sz, + extent_sz, + basic_sz, + old_loop_sz); +#endif + + /* set all the pointers in the new dataloop structure */ + new_loop->loop_params.s_t.dataloop_array = (DLOOP_Dataloop **) + (((char *) new_loop) + loop_sz); + new_loop->loop_params.s_t.blocksize_array = (DLOOP_Count *) + (((char *) new_loop) + loop_sz + ptr_sz); + new_loop->loop_params.s_t.offset_array = (DLOOP_Offset *) + (((char *) new_loop) + loop_sz + ptr_sz + blk_sz); + new_loop->loop_params.s_t.el_extent_array = (DLOOP_Offset *) + (((char *) new_loop) + loop_sz + ptr_sz + blk_sz + off_sz); + + *old_loop_p = (DLOOP_Dataloop *) + (((char *) new_loop) + loop_sz + ptr_sz + blk_sz + off_sz + extent_sz); + *new_loop_p = new_loop; + *new_loop_sz_p = new_loop_sz; + + return; +} + +/*@ + Dataloop_dup - make a copy of a dataloop + + Returns 0 on success, -1 on failure. +@*/ +void PREPEND_PREFIX(Dataloop_dup)(DLOOP_Dataloop *old_loop, + int old_loop_sz, + DLOOP_Dataloop **new_loop_p) +{ + DLOOP_Dataloop *new_loop; + + DLOOP_Assert(old_loop != NULL); + DLOOP_Assert(old_loop_sz > 0); + + new_loop = (DLOOP_Dataloop *) DLOOP_Malloc(old_loop_sz); + if (new_loop == NULL) { + *new_loop_p = NULL; + return; + } + + PREPEND_PREFIX(Dataloop_copy)(new_loop, old_loop, old_loop_sz); + *new_loop_p = new_loop; + return; +} + +/*@ + Dataloop_stream_size - return the size of the data described by the dataloop + + Input Parameters: ++ dl_p - pointer to dataloop for which we will return the size +- sizefn - function for determining size of types in the corresponding stream + (passing NULL will instead result in el_size values being used) + +@*/ +DLOOP_Offset +PREPEND_PREFIX(Dataloop_stream_size)(struct DLOOP_Dataloop *dl_p, + DLOOP_Offset (*sizefn)(DLOOP_Type el_type)) +{ + DLOOP_Offset ret; + DLOOP_Offset tmp_sz, tmp_ct = 1; + + for (;;) + { + if ((dl_p->kind & DLOOP_KIND_MASK) == DLOOP_KIND_STRUCT) + { + int i; + + tmp_sz = 0; + for (i = 0; i < dl_p->loop_params.s_t.count; i++) + { + ret += dl_p->loop_params.s_t.blocksize_array[i] * + PREPEND_PREFIX(Dataloop_stream_size)(dl_p->loop_params.s_t.dataloop_array[i], sizefn); + } + return tmp_sz * tmp_ct; + } + + switch (dl_p->kind & DLOOP_KIND_MASK) { + case DLOOP_KIND_CONTIG: + tmp_ct *= dl_p->loop_params.c_t.count; +#ifdef DLOOP_DEBUG_SIZE + DLOOP_dbg_printf("stream_size: contig: ct = %d; new tot_ct = %d\n", + (int) dl_p->loop_params.c_t.count, (int) tmp_ct); +#endif + break; + case DLOOP_KIND_VECTOR: + tmp_ct *= dl_p->loop_params.v_t.count * dl_p->loop_params.v_t.blocksize; +#ifdef DLOOP_DEBUG_SIZE + DLOOP_dbg_printf("stream_size: vector: ct = %d; blk = %d; new tot_ct = %d\n", + (int) dl_p->loop_params.v_t.count, + (int) dl_p->loop_params.v_t.blocksize, + (int) tmp_ct); +#endif + break; + case DLOOP_KIND_BLOCKINDEXED: + tmp_ct *= dl_p->loop_params.bi_t.count * dl_p->loop_params.bi_t.blocksize; +#ifdef DLOOP_DEBUG_SIZE + DLOOP_dbg_printf("stream_size: blkindexed: blks = %d; new tot_ct = %d\n", + (int) dl_p->loop_params.bi_t.count * + (int) dl_p->loop_params.bi_t.blocksize, + (int) tmp_ct); +#endif + break; + case DLOOP_KIND_INDEXED: + tmp_ct *= dl_p->loop_params.i_t.total_blocks; +#ifdef DLOOP_DEBUG_SIZE + DLOOP_dbg_printf("stream_size: contig: blks = %d; new tot_ct = %d\n", + (int) dl_p->loop_params.i_t.total_blocks, + (int) tmp_ct); +#endif + break; + default: + /* --BEGIN ERROR HANDLING-- */ + DLOOP_Assert(0); + break; + /* --END ERROR HANDLING-- */ + } + + if (dl_p->kind & DLOOP_FINAL_MASK) break; + else { + DLOOP_Assert(dl_p->loop_params.cm_t.dataloop != NULL); + dl_p = dl_p->loop_params.cm_t.dataloop; + } + } + + /* call fn for size using bottom type, or use size if fnptr is NULL */ + tmp_sz = ((sizefn) ? sizefn(dl_p->el_type) : dl_p->el_size); + + return tmp_sz * tmp_ct; +} + +/* --BEGIN ERROR HANDLING-- */ +/*@ + Dataloop_print - dump a dataloop tree to stdout for debugging + purposes + + Input Parameters: ++ dataloop - root of tree to dump +- depth - starting depth; used to help keep up with where we are in the tree +@*/ +void PREPEND_PREFIX(Dataloop_print)(struct DLOOP_Dataloop *dataloop, + int depth) +{ + int i; + + if (dataloop == NULL) + { + DLOOP_dbg_printf("dataloop is NULL (probably basic type)\n"); + return; + } + + DLOOP_dbg_printf("loc=%p, treedepth=%d, kind=%d, el_extent=%ld\n", + dataloop, (int) depth, (int) dataloop->kind, (long) dataloop->el_extent); + switch(dataloop->kind & DLOOP_KIND_MASK) { + case DLOOP_KIND_CONTIG: + DLOOP_dbg_printf("\tCONTIG: count=%d, datatype=%p\n", + (int) dataloop->loop_params.c_t.count, + dataloop->loop_params.c_t.dataloop); + if (!(dataloop->kind & DLOOP_FINAL_MASK)) + PREPEND_PREFIX(Dataloop_print)(dataloop->loop_params.c_t.dataloop, depth+1); + break; + case DLOOP_KIND_VECTOR: + DLOOP_dbg_printf("\tVECTOR: count=%d, blksz=%d, stride=%d, datatype=%p\n", + (int) dataloop->loop_params.v_t.count, + (int) dataloop->loop_params.v_t.blocksize, + (int) dataloop->loop_params.v_t.stride, + dataloop->loop_params.v_t.dataloop); + if (!(dataloop->kind & DLOOP_FINAL_MASK)) + PREPEND_PREFIX(Dataloop_print)(dataloop->loop_params.v_t.dataloop, depth+1); + break; + case DLOOP_KIND_BLOCKINDEXED: + DLOOP_dbg_printf("\tBLOCKINDEXED: count=%d, blksz=%d, datatype=%p\n", + (int) dataloop->loop_params.bi_t.count, + (int) dataloop->loop_params.bi_t.blocksize, + dataloop->loop_params.bi_t.dataloop); + /* print out offsets later */ + if (!(dataloop->kind & DLOOP_FINAL_MASK)) + PREPEND_PREFIX(Dataloop_print)(dataloop->loop_params.bi_t.dataloop, depth+1); + break; + case DLOOP_KIND_INDEXED: + DLOOP_dbg_printf("\tINDEXED: count=%d, datatype=%p\n", + (int) dataloop->loop_params.i_t.count, + dataloop->loop_params.i_t.dataloop); + /* print out blocksizes and offsets later */ + if (!(dataloop->kind & DLOOP_FINAL_MASK)) + PREPEND_PREFIX(Dataloop_print)(dataloop->loop_params.i_t.dataloop, depth+1); + break; + case DLOOP_KIND_STRUCT: + DLOOP_dbg_printf("\tSTRUCT: count=%d\n", (int) dataloop->loop_params.s_t.count); + DLOOP_dbg_printf("\tblocksizes:\n"); + for (i=0; i < dataloop->loop_params.s_t.count; i++) + DLOOP_dbg_printf("\t\t%d\n", (int) dataloop->loop_params.s_t.blocksize_array[i]); + DLOOP_dbg_printf("\toffsets:\n"); + for (i=0; i < dataloop->loop_params.s_t.count; i++) + DLOOP_dbg_printf("\t\t%d\n", (int) dataloop->loop_params.s_t.offset_array[i]); + DLOOP_dbg_printf("\tdatatypes:\n"); + for (i=0; i < dataloop->loop_params.s_t.count; i++) + DLOOP_dbg_printf("\t\t%p\n", dataloop->loop_params.s_t.dataloop_array[i]); + if (dataloop->kind & DLOOP_FINAL_MASK) break; + + for (i=0; i < dataloop->loop_params.s_t.count; i++) { + PREPEND_PREFIX(Dataloop_print)(dataloop->loop_params.s_t.dataloop_array[i],depth+1); + } + break; + default: + DLOOP_Assert(0); + break; + } + return; +} +/* --END ERROR HANDLING-- */ diff --git a/ompi/mca/io/romio/romio/common/dataloop/dataloop.h b/ompi/mca/io/romio/romio/common/dataloop/dataloop.h new file mode 100644 index 0000000000..53ac8d1fe4 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/dataloop.h @@ -0,0 +1,21 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +/* This file is here only to keep includes in one place inside the + * dataloop subdirectory. This file will be different for each use + * of the dataloop code, but others should be identical. + */ +#ifndef DATALOOP_H +#define DATALOOP_H + +/* This is specific to MPICH2 */ +#include "romio_dataloop.h" + +#endif + + + diff --git a/ompi/mca/io/romio/romio/common/dataloop/dataloop_create.c b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create.c new file mode 100644 index 0000000000..91217d00f1 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create.c @@ -0,0 +1,387 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#include +#include + +#include "./dataloop.h" + +static void DLOOP_Dataloop_create_named(MPI_Datatype type, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag); + +void PREPEND_PREFIX(Dataloop_create)(MPI_Datatype type, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag) +{ + int i; + + int nr_ints, nr_aints, nr_types, combiner; + MPI_Datatype *types; + int *ints; + MPI_Aint *aints; + + DLOOP_Dataloop *old_dlp; + int old_dlsz, old_dldepth; + + int dummy1, dummy2, dummy3, type0_combiner, ndims; + MPI_Datatype tmptype; + + MPI_Aint stride; + MPI_Aint *disps; + + PMPI_Type_get_envelope(type, &nr_ints, &nr_aints, &nr_types, &combiner); + + /* some named types do need dataloops; handle separately. */ + if (combiner == MPI_COMBINER_NAMED) { + DLOOP_Dataloop_create_named(type, dlp_p, dlsz_p, dldepth_p, flag); + return; + } + + /* Q: should we also check for "hasloop", or is the COMBINER + * check above enough to weed out everything that wouldn't + * have a loop? + */ + DLOOP_Handle_get_loopptr_macro(type, old_dlp, flag); + if (old_dlp != NULL) { + /* dataloop already created; just return it. */ + *dlp_p = old_dlp; + DLOOP_Handle_get_loopsize_macro(type, *dlsz_p, flag); + DLOOP_Handle_get_loopdepth_macro(type, *dldepth_p, flag); + return; + } + + PREPEND_PREFIX(Type_access_contents)(type, &ints, &aints, &types); + + /* first check for zero count on types where that makes sense */ + switch(combiner) { + case MPI_COMBINER_CONTIGUOUS: + case MPI_COMBINER_VECTOR: + case MPI_COMBINER_HVECTOR_INTEGER: + case MPI_COMBINER_HVECTOR: + case MPI_COMBINER_INDEXED_BLOCK: + case MPI_COMBINER_INDEXED: + case MPI_COMBINER_HINDEXED_INTEGER: + case MPI_COMBINER_HINDEXED: + case MPI_COMBINER_STRUCT_INTEGER: + case MPI_COMBINER_STRUCT: + if (ints[0] == 0) { + PREPEND_PREFIX(Dataloop_create_contiguous)(0, + MPI_INT, + dlp_p, + dlsz_p, + dldepth_p, + flag); + goto clean_exit; + } + break; + default: + break; + } + + /* recurse, processing types "below" this one before processing + * this one, if those type don't already have dataloops. + * + * note: in the struct case below we'll handle any additional + * types "below" the current one. + */ + PMPI_Type_get_envelope(types[0], &dummy1, &dummy2, &dummy3, + &type0_combiner); + if (type0_combiner != MPI_COMBINER_NAMED) + { + DLOOP_Handle_get_loopptr_macro(types[0], old_dlp, flag); + if (old_dlp == NULL) + { + /* no dataloop already present; create and store one */ + PREPEND_PREFIX(Dataloop_create)(types[0], + &old_dlp, + &old_dlsz, + &old_dldepth, + flag); + + DLOOP_Handle_set_loopptr_macro(types[0], old_dlp, flag); + DLOOP_Handle_set_loopsize_macro(types[0], old_dlsz, flag); + DLOOP_Handle_set_loopdepth_macro(types[0], old_dldepth, flag); + } + } + + switch(combiner) + { + case MPI_COMBINER_DUP: + if (type0_combiner != MPI_COMBINER_NAMED) { + PREPEND_PREFIX(Dataloop_dup)(old_dlp, old_dlsz, dlp_p); + *dlsz_p = old_dlsz; + *dldepth_p = old_dldepth; + } + else { + PREPEND_PREFIX(Dataloop_create_contiguous)(1, + types[0], + dlp_p, dlsz_p, + dldepth_p, + flag); + } + break; + case MPI_COMBINER_RESIZED: + if (type0_combiner != MPI_COMBINER_NAMED) { + PREPEND_PREFIX(Dataloop_dup)(old_dlp, old_dlsz, dlp_p); + *dlsz_p = old_dlsz; + *dldepth_p = old_dldepth; + } + else { + PREPEND_PREFIX(Dataloop_create_contiguous)(1, + types[0], + dlp_p, dlsz_p, + dldepth_p, + flag); + + (*dlp_p)->el_extent = aints[1]; /* extent */ + } + break; + case MPI_COMBINER_CONTIGUOUS: + PREPEND_PREFIX(Dataloop_create_contiguous)(ints[0] /* count */, + types[0] /* oldtype */, + dlp_p, dlsz_p, + dldepth_p, + flag); + break; + case MPI_COMBINER_VECTOR: + PREPEND_PREFIX(Dataloop_create_vector)(ints[0] /* count */, + ints[1] /* blklen */, + ints[2] /* stride */, + 0 /* stride not bytes */, + types[0] /* oldtype */, + dlp_p, dlsz_p, dldepth_p, + flag); + break; + case MPI_COMBINER_HVECTOR_INTEGER: + case MPI_COMBINER_HVECTOR: + /* fortran hvector has integer stride in bytes */ + if (combiner == MPI_COMBINER_HVECTOR_INTEGER) { + stride = (MPI_Aint) ints[2]; + } + else { + stride = aints[0]; + } + + PREPEND_PREFIX(Dataloop_create_vector)(ints[0] /* count */, + ints[1] /* blklen */, + stride, + 1 /* stride in bytes */, + types[0] /* oldtype */, + dlp_p, dlsz_p, dldepth_p, + flag); + break; + case MPI_COMBINER_INDEXED_BLOCK: + PREPEND_PREFIX(Dataloop_create_blockindexed)(ints[0] /* count */, + ints[1] /* blklen */, + &ints[2] /* disps */, + 0 /* disp not bytes */, + types[0] /* oldtype */, + dlp_p, dlsz_p, + dldepth_p, + flag); + break; + case MPI_COMBINER_INDEXED: + PREPEND_PREFIX(Dataloop_create_indexed)(ints[0] /* count */, + &ints[1] /* blklens */, + &ints[ints[0]+1] /* disp */, + 0 /* disp not in bytes */, + types[0] /* oldtype */, + dlp_p, dlsz_p, dldepth_p, + flag); + break; + case MPI_COMBINER_HINDEXED_INTEGER: + case MPI_COMBINER_HINDEXED: + if (combiner == MPI_COMBINER_HINDEXED_INTEGER) { + disps = (MPI_Aint *) DLOOP_Malloc(ints[0] * sizeof(MPI_Aint)); + + for (i=0; i < ints[0]; i++) { + disps[i] = (MPI_Aint) ints[ints[0] + 1 + i]; + } + } + else { + disps = aints; + } + + PREPEND_PREFIX(Dataloop_create_indexed)(ints[0] /* count */, + &ints[1] /* blklens */, + disps, + 1 /* disp in bytes */, + types[0] /* oldtype */, + dlp_p, dlsz_p, dldepth_p, + flag); + + if (combiner == MPI_COMBINER_HINDEXED_INTEGER) { + DLOOP_Free(disps); + } + + break; + case MPI_COMBINER_STRUCT_INTEGER: + case MPI_COMBINER_STRUCT: + for (i = 1; i < ints[0]; i++) { + int type_combiner; + PMPI_Type_get_envelope(types[i], &dummy1, &dummy2, &dummy3, + &type_combiner); + + if (type_combiner != MPI_COMBINER_NAMED) { + DLOOP_Handle_get_loopptr_macro(types[i], old_dlp, flag); + if (old_dlp == NULL) + { + PREPEND_PREFIX(Dataloop_create)(types[i], + &old_dlp, + &old_dlsz, + &old_dldepth, + flag); + + DLOOP_Handle_set_loopptr_macro(types[i], old_dlp, + flag); + DLOOP_Handle_set_loopsize_macro(types[i], old_dlsz, + flag); + DLOOP_Handle_set_loopdepth_macro(types[i], old_dldepth, + flag); + } + } + } + if (combiner == MPI_COMBINER_STRUCT_INTEGER) { + disps = (MPI_Aint *) DLOOP_Malloc(ints[0] * sizeof(MPI_Aint)); + + for (i=0; i < ints[0]; i++) { + disps[i] = (MPI_Aint) ints[ints[0] + 1 + i]; + } + } + else { + disps = aints; + } + + PREPEND_PREFIX(Dataloop_create_struct)(ints[0] /* count */, + &ints[1] /* blklens */, + disps, + types /* oldtype array */, + dlp_p, dlsz_p, dldepth_p, + flag); + + if (combiner == MPI_COMBINER_STRUCT_INTEGER) { + DLOOP_Free(disps); + } + break; + case MPI_COMBINER_SUBARRAY: + ndims = ints[0]; + PREPEND_PREFIX(Type_convert_subarray)(ndims, + &ints[1] /* sizes */, + &ints[1+ndims] /* subsizes */, + &ints[1+2*ndims] /* starts */, + ints[1+3*ndims] /* order */, + types[0], + &tmptype); + + PREPEND_PREFIX(Dataloop_create)(tmptype, + dlp_p, + dlsz_p, + dldepth_p, + flag); + + PMPI_Type_free(&tmptype); + break; + case MPI_COMBINER_DARRAY: + ndims = ints[2]; + PREPEND_PREFIX(Type_convert_darray)(ints[0] /* size */, + ints[1] /* rank */, + ndims, + &ints[3] /* gsizes */, + &ints[3+ndims] /*distribs */, + &ints[3+2*ndims] /* dargs */, + &ints[3+3*ndims] /* psizes */, + ints[3+4*ndims] /* order */, + types[0], + &tmptype); + + PREPEND_PREFIX(Dataloop_create)(tmptype, + dlp_p, + dlsz_p, + dldepth_p, + flag); + + PMPI_Type_free(&tmptype); + break; + case MPI_COMBINER_F90_REAL: + case MPI_COMBINER_F90_COMPLEX: + case MPI_COMBINER_F90_INTEGER: + /* TODO: WHAT DO I DO HERE? */ + default: + DLOOP_Assert(0); + break; + } + + clean_exit: + + PREPEND_PREFIX(Type_release_contents)(type, &ints, &aints, &types); + + /* for now we just leave the intermediate dataloops in place. + * could remove them to save space if we wanted. + */ + + return; +} + +/*@ + DLOOP_Dataloop_create_named - create a dataloop for a "named" type + if necessary. + + "named" types are ones for which MPI_Type_get_envelope() returns a + combiner of MPI_COMBINER_NAMED. some types that fit this category, + such as MPI_SHORT_INT, have multiple elements with potential gaps + and padding. these types need dataloops for correct processing. +@*/ +static void DLOOP_Dataloop_create_named(MPI_Datatype type, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag) +{ + DLOOP_Dataloop *dlp; + + /* special case: pairtypes need dataloops too. + * + * note: not dealing with MPI_2INT because size == extent + * in all cases for that type. + * + * note: MPICH2 always precreates these, so we will never call + * Dataloop_create_pairtype() from here in the MPICH2 + * case. + */ + if (type == MPI_FLOAT_INT || type == MPI_DOUBLE_INT || + type == MPI_LONG_INT || type == MPI_SHORT_INT || + type == MPI_LONG_DOUBLE_INT) + { + DLOOP_Handle_get_loopptr_macro(type, dlp, flag); + if (dlp != NULL) { + /* dataloop already created; just return it. */ + *dlp_p = dlp; + DLOOP_Handle_get_loopsize_macro(type, *dlsz_p, flag); + DLOOP_Handle_get_loopdepth_macro(type, *dldepth_p, flag); + } + else { + PREPEND_PREFIX(Dataloop_create_pairtype)(type, + dlp_p, + dlsz_p, + dldepth_p, + flag); + } + return; + } + /* no other combiners need dataloops; exit. */ + else { + *dlp_p = NULL; + *dlsz_p = 0; + *dldepth_p = 0; + return; + } +} diff --git a/ompi/mca/io/romio/romio/common/dataloop/dataloop_create.h b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create.h new file mode 100644 index 0000000000..3fbd42ee4a --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create.h @@ -0,0 +1,95 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#ifndef DATALOOP_CREATE_H +#define DATALOOP_CREATE_H + +/* Dataloop construction functions */ +void PREPEND_PREFIX(Dataloop_create)(MPI_Datatype type, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag); +int PREPEND_PREFIX(Dataloop_create_contiguous)(int count, + MPI_Datatype oldtype, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag); +int PREPEND_PREFIX(Dataloop_create_vector)(int count, + int blocklength, + MPI_Aint stride, + int strideinbytes, + MPI_Datatype oldtype, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag); +int PREPEND_PREFIX(Dataloop_create_blockindexed)(int count, + int blklen, + void *disp_array, + int dispinbytes, + MPI_Datatype oldtype, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag); +int PREPEND_PREFIX(Dataloop_create_indexed)(int count, + int *blocklength_array, + void *displacement_array, + int dispinbytes, + MPI_Datatype oldtype, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag); +int PREPEND_PREFIX(Dataloop_create_struct)(int count, + int *blklen_array, + MPI_Aint *disp_array, + MPI_Datatype *oldtype_array, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag); +int PREPEND_PREFIX(Dataloop_create_pairtype)(MPI_Datatype type, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag); + +/* Helper functions for dataloop construction */ +int PREPEND_PREFIX(Type_convert_subarray)(int ndims, + int *array_of_sizes, + int *array_of_subsizes, + int *array_of_starts, + int order, + MPI_Datatype oldtype, + MPI_Datatype *newtype); +int PREPEND_PREFIX(Type_convert_darray)(int size, + int rank, + int ndims, + int *array_of_gsizes, + int *array_of_distribs, + int *array_of_dargs, + int *array_of_psizes, + int order, + MPI_Datatype oldtype, + MPI_Datatype *newtype); + +#if 0 +/* Helper functions for accessing datatype contents */ +void PREPEND_PREFIX(Type_access_contents)(MPI_Datatype type, + int **ints_p, + MPI_Aint **aints_p, + MPI_Datatype **types_p); +void PREPEND_PREFIX(Type_release_contents)(MPI_Datatype type, + int **ints_p, + MPI_Aint **aints_p, + MPI_Datatype **types_p); +#endif + +#endif diff --git a/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_blockindexed.c b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_blockindexed.c new file mode 100644 index 0000000000..0cbcc96f6a --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_blockindexed.c @@ -0,0 +1,325 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#include + +#include "./dataloop.h" + +static DLOOP_Count DLOOP_Type_blockindexed_count_contig(DLOOP_Count count, + DLOOP_Count blklen, + void *disp_array, + int dispinbytes, + DLOOP_Offset old_extent); + +static void DLOOP_Type_blockindexed_array_copy(DLOOP_Count count, + void *disp_array, + DLOOP_Offset *out_disp_array, + int dispinbytes, + DLOOP_Offset old_extent); + +/*@ + Dataloop_create_blockindexed - create blockindexed dataloop + + Arguments: ++ int count +. void *displacement_array (array of either MPI_Aints or ints) +. int displacement_in_bytes (boolean) +. MPI_Datatype old_type +. DLOOP_Dataloop **output_dataloop_ptr +. int output_dataloop_size +. int output_dataloop_depth +- int flag + +.N Errors +.N Returns 0 on success, -1 on failure. +@*/ +int PREPEND_PREFIX(Dataloop_create_blockindexed)(int icount, + int iblklen, + void *disp_array, + int dispinbytes, + DLOOP_Type oldtype, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag) +{ + int err, is_builtin, is_vectorizable = 1; + int i, new_loop_sz, old_loop_depth; + + DLOOP_Count contig_count, count, blklen; + DLOOP_Offset old_extent, eff_disp0, eff_disp1, last_stride; + DLOOP_Dataloop *new_dlp; + + count = (DLOOP_Count) icount; /* avoid subsequent casting */ + blklen = (DLOOP_Count) iblklen; + + /* if count or blklen are zero, handle with contig code, call it a int */ + if (count == 0 || blklen == 0) + { + err = PREPEND_PREFIX(Dataloop_create_contiguous)(0, + MPI_INT, + dlp_p, + dlsz_p, + dldepth_p, + flag); + return err; + } + + is_builtin = (DLOOP_Handle_hasloop_macro(oldtype)) ? 0 : 1; + + if (is_builtin) + { + DLOOP_Handle_get_size_macro(oldtype, old_extent); + old_loop_depth = 0; + } + else + { + DLOOP_Handle_get_extent_macro(oldtype, old_extent); + DLOOP_Handle_get_loopdepth_macro(oldtype, old_loop_depth, flag); + } + + contig_count = DLOOP_Type_blockindexed_count_contig(count, + blklen, + disp_array, + dispinbytes, + old_extent); + + /* optimization: + * + * if contig_count == 1 and block starts at displacement 0, + * store it as a contiguous rather than a blockindexed dataloop. + */ + if ((contig_count == 1) && + ((!dispinbytes && ((int *) disp_array)[0] == 0) || + (dispinbytes && ((MPI_Aint *) disp_array)[0] == 0))) + { + err = PREPEND_PREFIX(Dataloop_create_contiguous)(icount * iblklen, + oldtype, + dlp_p, + dlsz_p, + dldepth_p, + flag); + return err; + } + + /* optimization: + * + * if contig_count == 1 store it as a blockindexed with one + * element rather than as a lot of individual blocks. + */ + if (contig_count == 1) + { + /* adjust count and blklen and drop through */ + blklen *= count; + count = 1; + iblklen *= icount; + icount = 1; + } + + /* optimization: + * + * if displacements start at zero and result in a fixed stride, + * store it as a vector rather than a blockindexed dataloop. + */ + eff_disp0 = (dispinbytes) ? ((DLOOP_Offset) ((MPI_Aint *) disp_array)[0]) : + (((DLOOP_Offset) ((int *) disp_array)[0]) * old_extent); + + if (count > 1 && eff_disp0 == (DLOOP_Offset) 0) + { + eff_disp1 = (dispinbytes) ? + ((DLOOP_Offset) ((MPI_Aint *) disp_array)[1]) : + (((DLOOP_Offset) ((int *) disp_array)[1]) * old_extent); + last_stride = eff_disp1 - eff_disp0; + + for (i=2; i < count; i++) { + eff_disp0 = eff_disp1; + eff_disp1 = (dispinbytes) ? + ((DLOOP_Offset) ((MPI_Aint *) disp_array)[i]) : + (((DLOOP_Offset) ((int *) disp_array)[i]) * old_extent); + if (eff_disp1 - eff_disp0 != last_stride) { + is_vectorizable = 0; + break; + } + } + if (is_vectorizable) + { + err = PREPEND_PREFIX(Dataloop_create_vector)(count, + blklen, + last_stride, + 1, /* strideinbytes */ + oldtype, + dlp_p, + dlsz_p, + dldepth_p, + flag); + return err; + } + } + + /* TODO: optimization: + * + * if displacements result in a fixed stride, but first displacement + * is not zero, store it as a blockindexed (blklen == 1) of a vector. + */ + + /* TODO: optimization: + * + * if a blockindexed of a contig, absorb the contig into the blocklen + * parameter and keep the same overall depth + */ + + /* otherwise storing as a blockindexed dataloop */ + + /* Q: HOW CAN WE TELL IF IT IS WORTH IT TO STORE AS AN + * INDEXED WITH FEWER CONTIG BLOCKS (IF CONTIG_COUNT IS SMALL)? + */ + + if (is_builtin) + { + PREPEND_PREFIX(Dataloop_alloc)(DLOOP_KIND_BLOCKINDEXED, + count, + &new_dlp, + &new_loop_sz); + /* --BEGIN ERROR HANDLING-- */ + if (!new_dlp) return -1; + /* --END ERROR HANDLING-- */ + + new_dlp->kind = DLOOP_KIND_BLOCKINDEXED | DLOOP_FINAL_MASK; + + if (flag == DLOOP_DATALOOP_ALL_BYTES) + { + blklen *= old_extent; + new_dlp->el_size = 1; + new_dlp->el_extent = 1; + new_dlp->el_type = MPI_BYTE; + } + else + { + new_dlp->el_size = old_extent; + new_dlp->el_extent = old_extent; + new_dlp->el_type = oldtype; + } + } + else + { + DLOOP_Dataloop *old_loop_ptr = NULL; + int old_loop_sz = 0; + + DLOOP_Handle_get_loopptr_macro(oldtype, old_loop_ptr, flag); + DLOOP_Handle_get_loopsize_macro(oldtype, old_loop_sz, flag); + + PREPEND_PREFIX(Dataloop_alloc_and_copy)(DLOOP_KIND_BLOCKINDEXED, + count, + old_loop_ptr, + old_loop_sz, + &new_dlp, + &new_loop_sz); + /* --BEGIN ERROR HANDLING-- */ + if (!new_dlp) return -1; + /* --END ERROR HANDLING-- */ + + new_dlp->kind = DLOOP_KIND_BLOCKINDEXED; + + DLOOP_Handle_get_size_macro(oldtype, new_dlp->el_size); + DLOOP_Handle_get_extent_macro(oldtype, new_dlp->el_extent); + DLOOP_Handle_get_basic_type_macro(oldtype, new_dlp->el_type); + } + + new_dlp->loop_params.bi_t.count = count; + new_dlp->loop_params.bi_t.blocksize = blklen; + + /* copy in displacement parameters + * + * regardless of dispinbytes, we store displacements in bytes in loop. + */ + DLOOP_Type_blockindexed_array_copy(count, + disp_array, + new_dlp->loop_params.bi_t.offset_array, + dispinbytes, + old_extent); + + *dlp_p = new_dlp; + *dlsz_p = new_loop_sz; + *dldepth_p = old_loop_depth + 1; + + return 0; +} + +/* DLOOP_Type_blockindexed_array_copy + * + * Unlike the indexed version, this one does not compact adjacent + * blocks, because that would really mess up the blockindexed type! + */ +static void DLOOP_Type_blockindexed_array_copy(DLOOP_Count count, + void *in_disp_array, + DLOOP_Offset *out_disp_array, + int dispinbytes, + DLOOP_Offset old_extent) +{ + int i; + if (!dispinbytes) + { + for (i=0; i < count; i++) + { + out_disp_array[i] = + ((DLOOP_Offset) ((int *) in_disp_array)[i]) * old_extent; + } + } + else + { + for (i=0; i < count; i++) + { + out_disp_array[i] = + ((DLOOP_Offset) ((MPI_Aint *) in_disp_array)[i]); + } + } + return; +} + +static DLOOP_Count DLOOP_Type_blockindexed_count_contig(DLOOP_Count count, + DLOOP_Count blklen, + void *disp_array, + int dispinbytes, + DLOOP_Offset old_extent) +{ + int i, contig_count = 1; + + if (!dispinbytes) + { + /* this is from the MPI type, is of type int */ + DLOOP_Offset cur_tdisp = (DLOOP_Offset) ((int *) disp_array)[0]; + + for (i=1; i < count; i++) + { + DLOOP_Offset next_tdisp = (DLOOP_Offset) ((int *) disp_array)[i]; + + if (cur_tdisp + blklen != next_tdisp) + { + contig_count++; + } + cur_tdisp = next_tdisp; + } + } + else + { + /* this is from the MPI type, is of type MPI_Aint */ + DLOOP_Offset cur_bdisp = (DLOOP_Offset) ((MPI_Aint *) disp_array)[0]; + + for (i=1; i < count; i++) + { + DLOOP_Offset next_bdisp = + (DLOOP_Offset) ((MPI_Aint *) disp_array)[i]; + + if (cur_bdisp + (DLOOP_Offset) blklen * old_extent != next_bdisp) + { + contig_count++; + } + cur_bdisp = next_bdisp; + } + } + return contig_count; +} diff --git a/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_contig.c b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_contig.c new file mode 100644 index 0000000000..68630a3ae0 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_contig.c @@ -0,0 +1,160 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#include "./dataloop.h" + +/*@ + Dataloop_contiguous - create the dataloop representation for a + contiguous datatype + + Arguments: ++ int icount, +. MPI_Datatype oldtype, +. DLOOP_Dataloop **dlp_p, +. int *dlsz_p, +. int *dldepth_p, +- int flag + +.N Errors +.N Returns 0 on success, -1 on failure. +@*/ +int PREPEND_PREFIX(Dataloop_create_contiguous)(int icount, + DLOOP_Type oldtype, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag) +{ + DLOOP_Count count; + int is_builtin, apply_contig_coalescing = 0; + int new_loop_sz, new_loop_depth; + + DLOOP_Dataloop *new_dlp; + + count = (DLOOP_Count) icount; /* avoid subsequent casting */ + + is_builtin = (DLOOP_Handle_hasloop_macro(oldtype)) ? 0 : 1; + + if (is_builtin) + { + new_loop_depth = 1; + } + else + { + int old_loop_sz = 0, old_loop_depth = 0; + DLOOP_Offset old_size = 0, old_extent = 0; + DLOOP_Dataloop *old_loop_ptr; + + DLOOP_Handle_get_loopsize_macro(oldtype, old_loop_sz, flag); + DLOOP_Handle_get_loopdepth_macro(oldtype, old_loop_depth, flag); + DLOOP_Handle_get_loopptr_macro(oldtype, old_loop_ptr, flag); + DLOOP_Handle_get_size_macro(oldtype, old_size); + DLOOP_Handle_get_extent_macro(oldtype, old_extent); + + /* if we have a simple combination of contigs, coalesce */ + if (((old_loop_ptr->kind & DLOOP_KIND_MASK) == DLOOP_KIND_CONTIG) + && (old_size == old_extent)) + { + /* will just copy contig and multiply count */ + apply_contig_coalescing = 1; + new_loop_depth = old_loop_depth; + } + else + { + new_loop_depth = old_loop_depth + 1; + } + } + + if (is_builtin) + { + DLOOP_Offset basic_sz = 0; + + PREPEND_PREFIX(Dataloop_alloc)(DLOOP_KIND_CONTIG, + count, + &new_dlp, + &new_loop_sz); + /* --BEGIN ERROR HANDLING-- */ + if (!new_dlp) return -1; + /* --END ERROR HANDLING-- */ + + DLOOP_Handle_get_size_macro(oldtype, basic_sz); + new_dlp->kind = DLOOP_KIND_CONTIG | DLOOP_FINAL_MASK; + + if (flag == DLOOP_DATALOOP_ALL_BYTES) + { + count *= basic_sz; + new_dlp->el_size = 1; + new_dlp->el_extent = 1; + new_dlp->el_type = MPI_BYTE; + } + else + { + new_dlp->el_size = basic_sz; + new_dlp->el_extent = new_dlp->el_size; + new_dlp->el_type = oldtype; + } + + new_dlp->loop_params.c_t.count = count; + } + else + { + /* user-defined base type (oldtype) */ + DLOOP_Dataloop *old_loop_ptr; + int old_loop_sz = 0; + + DLOOP_Handle_get_loopptr_macro(oldtype, old_loop_ptr, flag); + DLOOP_Handle_get_loopsize_macro(oldtype, old_loop_sz, flag); + + if (apply_contig_coalescing) + { + /* make a copy of the old loop and multiply the count */ + PREPEND_PREFIX(Dataloop_dup)(old_loop_ptr, + old_loop_sz, + &new_dlp); + /* --BEGIN ERROR HANDLING-- */ + if (!new_dlp) return -1; + /* --END ERROR HANDLING-- */ + + new_dlp->loop_params.c_t.count *= count; + + new_loop_sz = old_loop_sz; + DLOOP_Handle_get_loopdepth_macro(oldtype, new_loop_depth, flag); + } + else + { + DLOOP_Dataloop *old_loop_ptr; + int old_loop_sz = 0; + + DLOOP_Handle_get_loopptr_macro(oldtype, old_loop_ptr, flag); + DLOOP_Handle_get_loopsize_macro(oldtype, old_loop_sz, flag); + + /* allocate space for new loop including copy of old */ + PREPEND_PREFIX(Dataloop_alloc_and_copy)(DLOOP_KIND_CONTIG, + count, + old_loop_ptr, + old_loop_sz, + &new_dlp, + &new_loop_sz); + /* --BEGIN ERROR HANDLING-- */ + if (!new_dlp) return -1; + /* --END ERROR HANDLING-- */ + + new_dlp->kind = DLOOP_KIND_CONTIG; + DLOOP_Handle_get_size_macro(oldtype, new_dlp->el_size); + DLOOP_Handle_get_extent_macro(oldtype, new_dlp->el_extent); + DLOOP_Handle_get_basic_type_macro(oldtype, new_dlp->el_type); + + new_dlp->loop_params.c_t.count = count; + } + } + + *dlp_p = new_dlp; + *dlsz_p = new_loop_sz; + *dldepth_p = new_loop_depth; + + return 0; +} diff --git a/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_indexed.c b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_indexed.c new file mode 100644 index 0000000000..18cb0dd282 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_indexed.c @@ -0,0 +1,429 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#include + +#include "./dataloop.h" + +static DLOOP_Count DLOOP_Type_indexed_count_contig(DLOOP_Count count, + int *blocklength_array, + void *displacement_array, + int dispinbytes, + DLOOP_Offset old_extent); + +static void DLOOP_Type_indexed_array_copy(DLOOP_Count count, + DLOOP_Count contig_count, + int *input_blocklength_array, + void *input_displacement_array, + DLOOP_Count *output_blocklength_array, + DLOOP_Offset *out_disp_array, + int dispinbytes, + DLOOP_Offset old_extent); + +/*@ + DLOOP_Dataloop_create_indexed + + Arguments: ++ int icount +. int *iblocklength_array +. void *displacement_array (either ints or MPI_Aints) +. int dispinbytes +. MPI_Datatype oldtype +. DLOOP_Dataloop **dlp_p +. int *dlsz_p +. int *dldepth_p +- int flag + +.N Errors +.N Returns 0 on success, -1 on error. +@*/ + +int PREPEND_PREFIX(Dataloop_create_indexed)(int icount, + int *blocklength_array, + void *displacement_array, + int dispinbytes, + MPI_Datatype oldtype, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag) +{ + int err, is_builtin; + int i, new_loop_sz, old_loop_depth, blksz; + + DLOOP_Count old_type_count = 0, contig_count, count; + DLOOP_Offset old_extent; + struct DLOOP_Dataloop *new_dlp; + + count = (DLOOP_Count) icount; /* avoid subsequent casting */ + + + /* if count is zero, handle with contig code, call it an int */ + if (count == 0) + { + err = PREPEND_PREFIX(Dataloop_create_contiguous)(0, + MPI_INT, + dlp_p, + dlsz_p, + dldepth_p, + flag); + return err; + } + + is_builtin = (DLOOP_Handle_hasloop_macro(oldtype)) ? 0 : 1; + + if (is_builtin) + { + DLOOP_Handle_get_extent_macro(oldtype, old_extent); + old_loop_depth = 0; + } + else + { + DLOOP_Handle_get_extent_macro(oldtype, old_extent); + DLOOP_Handle_get_loopdepth_macro(oldtype, old_loop_depth, flag); + } + + for (i=0; i < count; i++) + { + old_type_count += (DLOOP_Count) blocklength_array[i]; + } + + contig_count = DLOOP_Type_indexed_count_contig(count, + blocklength_array, + displacement_array, + dispinbytes, + old_extent); + + /* if contig_count is zero (no data), handle with contig code */ + if (contig_count == 0) + { + err = PREPEND_PREFIX(Dataloop_create_contiguous)(0, + MPI_INT, + dlp_p, + dlsz_p, + dldepth_p, + flag); + return err; + } + + /* optimization: + * + * if contig_count == 1 and block starts at displacement 0, + * store it as a contiguous rather than an indexed dataloop. + */ + if ((contig_count == 1) && + ((!dispinbytes && ((int *) displacement_array)[0] == 0) || + (dispinbytes && ((MPI_Aint *) displacement_array)[0] == 0))) + { + err = PREPEND_PREFIX(Dataloop_create_contiguous)((int) old_type_count, + oldtype, + dlp_p, + dlsz_p, + dldepth_p, + flag); + return err; + } + + /* optimization: + * + * if contig_count == 1 (and displacement != 0), store this as + * a single element blockindexed rather than a lot of individual + * blocks. + */ + if (contig_count == 1) + { + err = PREPEND_PREFIX(Dataloop_create_blockindexed)(1, + (int) old_type_count, + displacement_array, + dispinbytes, + oldtype, + dlp_p, + dlsz_p, + dldepth_p, + flag); + + return err; + } + + /* optimization: + * + * if block length is the same for all blocks, store it as a + * blockindexed rather than an indexed dataloop. + */ + blksz = blocklength_array[0]; + for (i=1; i < count; i++) + { + if (blocklength_array[i] != blksz) + { + blksz--; + break; + } + } + if (blksz == blocklength_array[0]) + { + err = PREPEND_PREFIX(Dataloop_create_blockindexed)(icount, + blksz, + displacement_array, + dispinbytes, + oldtype, + dlp_p, + dlsz_p, + dldepth_p, + flag); + + return err; + } + + /* note: blockindexed looks for the vector optimization */ + + /* TODO: optimization: + * + * if an indexed of a contig, absorb the contig into the blocklen array + * and keep the same overall depth + */ + + /* otherwise storing as an indexed dataloop */ + + if (is_builtin) + { + PREPEND_PREFIX(Dataloop_alloc)(DLOOP_KIND_INDEXED, + count, + &new_dlp, + &new_loop_sz); + /* --BEGIN ERROR HANDLING-- */ + if (!new_dlp) return -1; + /* --END ERROR HANDLING-- */ + + new_dlp->kind = DLOOP_KIND_INDEXED | DLOOP_FINAL_MASK; + + if (flag == DLOOP_DATALOOP_ALL_BYTES) + { + /* blocklengths are modified below */ + new_dlp->el_size = 1; + new_dlp->el_extent = 1; + new_dlp->el_type = MPI_BYTE; + } + else + { + new_dlp->el_size = old_extent; + new_dlp->el_extent = old_extent; + new_dlp->el_type = oldtype; + } + } + else + { + DLOOP_Dataloop *old_loop_ptr = NULL; + int old_loop_sz = 0; + + DLOOP_Handle_get_loopptr_macro(oldtype, old_loop_ptr, flag); + DLOOP_Handle_get_loopsize_macro(oldtype, old_loop_sz, flag); + + PREPEND_PREFIX(Dataloop_alloc_and_copy)(DLOOP_KIND_INDEXED, + contig_count, + old_loop_ptr, + old_loop_sz, + &new_dlp, + &new_loop_sz); + /* --BEGIN ERROR HANDLING-- */ + if (!new_dlp) return -1; + /* --END ERROR HANDLING-- */ + + new_dlp->kind = DLOOP_KIND_INDEXED; + + DLOOP_Handle_get_size_macro(oldtype, new_dlp->el_size); + DLOOP_Handle_get_extent_macro(oldtype, new_dlp->el_extent); + DLOOP_Handle_get_basic_type_macro(oldtype, new_dlp->el_type); + } + + new_dlp->loop_params.i_t.count = contig_count; + new_dlp->loop_params.i_t.total_blocks = old_type_count; + + /* copy in blocklength and displacement parameters (in that order) + * + * regardless of dispinbytes, we store displacements in bytes in loop. + */ + DLOOP_Type_indexed_array_copy(count, + contig_count, + blocklength_array, + displacement_array, + new_dlp->loop_params.i_t.blocksize_array, + new_dlp->loop_params.i_t.offset_array, + dispinbytes, + old_extent); + + if (is_builtin && (flag == DLOOP_DATALOOP_ALL_BYTES)) + { + DLOOP_Count *tmp_blklen_array = + new_dlp->loop_params.i_t.blocksize_array; + + for (i=0; i < contig_count; i++) + { + /* increase block lengths so they are in bytes */ + tmp_blklen_array[i] *= old_extent; + } + } + + *dlp_p = new_dlp; + *dlsz_p = new_loop_sz; + *dldepth_p = old_loop_depth + 1; + + return MPI_SUCCESS; +} + +/* DLOOP_Type_indexed_array_copy() + * + * Copies arrays into place, combining adjacent contiguous regions and + * dropping zero-length regions. + * + * Extent passed in is for the original type. + * + * Output displacements are always output in bytes, while block + * lengths are always output in terms of the base type. + */ +static void DLOOP_Type_indexed_array_copy(DLOOP_Count count, + DLOOP_Count contig_count, + int *in_blklen_array, + void *in_disp_array, + DLOOP_Count *out_blklen_array, + DLOOP_Offset *out_disp_array, + int dispinbytes, + DLOOP_Offset old_extent) +{ + DLOOP_Count i, cur_idx = 0; + + out_blklen_array[0] = (DLOOP_Count) in_blklen_array[0]; + + if (!dispinbytes) + { + out_disp_array[0] = (DLOOP_Offset) + ((int *) in_disp_array)[0] * old_extent; + + for (i = 1; i < count; i++) + { + if (in_blklen_array[i] == 0) + { + continue; + } + else if (out_disp_array[cur_idx] + + ((DLOOP_Offset) out_blklen_array[cur_idx]) * old_extent == + ((DLOOP_Offset) ((int *) in_disp_array)[i]) * old_extent) + { + /* adjacent to current block; add to block */ + out_blklen_array[cur_idx] += (DLOOP_Count) in_blklen_array[i]; + } + else + { + cur_idx++; + DLOOP_Assert(cur_idx < contig_count); + out_disp_array[cur_idx] = + ((DLOOP_Offset) ((int *) in_disp_array)[i]) * old_extent; + out_blklen_array[cur_idx] = in_blklen_array[i]; + } + } + } + else /* input displacements already in bytes */ + { + out_disp_array[0] = (DLOOP_Offset) ((MPI_Aint *) in_disp_array)[0]; + + for (i = 1; i < count; i++) + { + if (in_blklen_array[i] == 0) + { + continue; + } + else if (out_disp_array[cur_idx] + + ((DLOOP_Offset) out_blklen_array[cur_idx]) * old_extent == + ((DLOOP_Offset) ((MPI_Aint *) in_disp_array)[i])) + { + /* adjacent to current block; add to block */ + out_blklen_array[cur_idx] += in_blklen_array[i]; + } + else + { + cur_idx++; + DLOOP_Assert(cur_idx < contig_count); + out_disp_array[cur_idx] = + (DLOOP_Offset) ((MPI_Aint *) in_disp_array)[i]; + out_blklen_array[cur_idx] = (DLOOP_Count) in_blklen_array[i]; + } + } + } + + DLOOP_Assert(cur_idx == contig_count - 1); + return; +} + +/* DLOOP_Type_indexed_count_contig() + * + * Determines the actual number of contiguous blocks represented by the + * blocklength/displacement arrays. This might be less than count (as + * few as 1). + * + * Extent passed in is for the original type. + */ +static DLOOP_Count DLOOP_Type_indexed_count_contig(DLOOP_Count count, + int *blocklength_array, + void *displacement_array, + int dispinbytes, + DLOOP_Offset old_extent) +{ + DLOOP_Count i, contig_count = 1; + DLOOP_Count cur_blklen = (DLOOP_Count) blocklength_array[0]; + + if (!dispinbytes) + { + DLOOP_Offset cur_tdisp = + (DLOOP_Offset) ((int *) displacement_array)[0]; + + for (i = 1; i < count; i++) + { + if (blocklength_array[i] == 0) + { + continue; + } + else if (cur_tdisp + cur_blklen == + (DLOOP_Offset) ((int *) displacement_array)[i]) + { + /* adjacent to current block; add to block */ + cur_blklen += (DLOOP_Count) blocklength_array[i]; + } + else + { + cur_tdisp = (DLOOP_Offset) ((int *) displacement_array)[i]; + cur_blklen = (DLOOP_Count) blocklength_array[i]; + contig_count++; + } + } + } + else + { + DLOOP_Offset cur_bdisp = + (DLOOP_Offset) ((MPI_Aint *) displacement_array)[0]; + + for (i = 1; i < count; i++) + { + if (blocklength_array[i] == 0) + { + continue; + } + else if (cur_bdisp + cur_blklen * old_extent == + (DLOOP_Offset) ((MPI_Aint *) displacement_array)[i]) + { + /* adjacent to current block; add to block */ + cur_blklen += (DLOOP_Count) blocklength_array[i]; + } + else + { + cur_bdisp = + (DLOOP_Offset) ((MPI_Aint *) displacement_array)[i]; + cur_blklen = (DLOOP_Count) blocklength_array[i]; + contig_count++; + } + } + } + return contig_count; +} diff --git a/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_pairtype.c b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_pairtype.c new file mode 100644 index 0000000000..acf3ef0a7a --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_pairtype.c @@ -0,0 +1,82 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#include "dataloop.h" + +#define PAIRTYPE_CONTENTS(mt1_,ut1_,mt2_,ut2_) \ + { \ + struct { ut1_ a; ut2_ b; } foo; \ + disps[0] = 0; \ + disps[1] = (MPI_Aint) ((char *) &foo.b - (char *) &foo.a); \ + types[0] = mt1_; \ + types[1] = mt2_; \ + } + +/*@ + Dataloop_create_pairtype - create dataloop for a pairtype + + Arguments: ++ MPI_Datatype type - the pairtype +. DLOOP_Dataloop **output_dataloop_ptr +. int output_dataloop_size +. int output_dataloop_depth +- int flag + +.N Errors +.N Returns 0 on success, -1 on failure. + + Note: + This function simply creates the appropriate input parameters for + use with Dataloop_create_struct and then calls that function. + + This same function could be used to create dataloops for any type + that actually consists of two distinct elements. +@*/ +int PREPEND_PREFIX(Dataloop_create_pairtype)(MPI_Datatype type, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag) +{ + int blocks[2] = { 1, 1 }; + MPI_Aint disps[2]; + MPI_Datatype types[2]; + + DLOOP_Assert(type == MPI_FLOAT_INT || type == MPI_DOUBLE_INT || + type == MPI_LONG_INT || type == MPI_SHORT_INT || + type == MPI_LONG_DOUBLE_INT || type == MPI_2INT); + + switch(type) { + case MPI_FLOAT_INT: + PAIRTYPE_CONTENTS(MPI_FLOAT, float, MPI_INT, int); + break; + case MPI_DOUBLE_INT: + PAIRTYPE_CONTENTS(MPI_DOUBLE, double, MPI_INT, int); + break; + case MPI_LONG_INT: + PAIRTYPE_CONTENTS(MPI_LONG, long, MPI_INT, int); + break; + case MPI_SHORT_INT: + PAIRTYPE_CONTENTS(MPI_SHORT, short, MPI_INT, int); + break; + case MPI_LONG_DOUBLE_INT: + PAIRTYPE_CONTENTS(MPI_LONG_DOUBLE, long double, MPI_INT, int); + break; + case MPI_2INT: + PAIRTYPE_CONTENTS(MPI_INT, int, MPI_INT, int); + break; + } + + return PREPEND_PREFIX(Dataloop_create_struct)(2, + blocks, + disps, + types, + dlp_p, + dlsz_p, + dldepth_p, + flag); +} diff --git a/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_struct.c b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_struct.c new file mode 100644 index 0000000000..af3a1f1274 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_struct.c @@ -0,0 +1,672 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#include "./dataloop.h" + +#ifndef PREPEND_PREFIX +#error "You must explicitly include a header that sets the PREPEND_PREFIX and includes dataloop_parts.h" +#endif + +static int DLOOP_Dataloop_create_struct_memory_error(void); +static int DLOOP_Dataloop_create_unique_type_struct(int count, + int *blklens, + MPI_Aint *disps, + DLOOP_Type *oldtypes, + int type_pos, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag); +static int DLOOP_Dataloop_create_basic_all_bytes_struct( + int count, + int *blklens, + MPI_Aint *disps, + DLOOP_Type *oldtypes, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag); +static int DLOOP_Dataloop_create_flattened_struct(int count, + int *blklens, + MPI_Aint *disps, + DLOOP_Type *oldtypes, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag); + +/*@ + Dataloop_create_struct - create the dataloop representation for a + struct datatype + + Input Parameters: ++ count - number of blocks in vector +. blklens - number of elements in each block +. disps - offsets of blocks from start of type in bytes +- oldtypes - types (using handle) of datatypes on which vector is based + + Output Parameters: ++ dlp_p - pointer to address in which to place pointer to new dataloop +- dlsz_p - pointer to address in which to place size of new dataloop + + Return Value: + 0 on success, -1 on failure. + + Notes: + This function relies on others, like Dataloop_create_indexed, to create + types in some cases. This call (like all the rest) takes int blklens + and MPI_Aint displacements, so it's possible to overflow when working + with a particularly large struct type in some cases. This isn't detected + or corrected in this code at this time. + +@*/ +int PREPEND_PREFIX(Dataloop_create_struct)(int count, + int *blklens, + MPI_Aint *disps, + DLOOP_Type *oldtypes, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag) +{ + int err, i, nr_basics = 0, nr_derived = 0, type_pos = 0; + + DLOOP_Type first_basic = MPI_DATATYPE_NULL, + first_derived = MPI_DATATYPE_NULL; + + /* variables used in general case only */ + int loop_idx, new_loop_sz, new_loop_depth; + int old_loop_sz = 0, old_loop_depth = 0; + + DLOOP_Dataloop *new_dlp, *curpos; + + /* if count is zero, handle with contig code, call it a int */ + if (count == 0) + { + err = PREPEND_PREFIX(Dataloop_create_contiguous)(0, + MPI_INT, + dlp_p, + dlsz_p, + dldepth_p, + flag); + return err; + } + + /* browse the old types and characterize */ + for (i=0; i < count; i++) + { + /* ignore type elements with a zero blklen */ + if (blklens[i] == 0) continue; + + if (oldtypes[i] != MPI_LB && oldtypes[i] != MPI_UB) + { + int is_builtin; + + is_builtin = + (DLOOP_Handle_hasloop_macro(oldtypes[i])) ? 0 : 1; + + if (is_builtin) + { + if (nr_basics == 0) + { + first_basic = oldtypes[i]; + type_pos = i; + } + else if (oldtypes[i] != first_basic) + { + first_basic = MPI_DATATYPE_NULL; + } + nr_basics++; + } + else /* derived type */ + { + if (nr_derived == 0) + { + first_derived = oldtypes[i]; + type_pos = i; + } + else if (oldtypes[i] != first_derived) + { + first_derived = MPI_DATATYPE_NULL; + } + nr_derived++; + } + } + } + + /* note on optimizations: + * + * because LB, UB, and extent calculations are handled as part of + * the Datatype, we can safely ignore them in all our calculations + * here. + */ + + /* optimization: + * + * if there were only MPI_LBs and MPI_UBs in the struct type, + * treat it as a zero-element contiguous (just as count == 0). + */ + if (nr_basics == 0 && nr_derived == 0) + { + err = PREPEND_PREFIX(Dataloop_create_contiguous)(0, + MPI_INT, + dlp_p, + dlsz_p, + dldepth_p, + flag); + return err; + } + + /* optimization: + * + * if there is only one unique instance of a type in the struct, treat it + * as a blockindexed type. + * + * notes: + * + * if the displacement happens to be zero, the blockindexed code will + * optimize this into a contig. + */ + if (nr_basics + nr_derived == 1) + { + /* type_pos is index to only real type in array */ + err = PREPEND_PREFIX(Dataloop_create_blockindexed) + (1, /* count */ + blklens[type_pos], + &disps[type_pos], + 1, /* displacement in bytes */ + oldtypes[type_pos], + dlp_p, + dlsz_p, + dldepth_p, + flag); + + return err; + } + + /* optimization: + * + * if there only one unique type (more than one instance) in the + * struct, treat it as an indexed type. + * + * notes: + * + * this will apply to a single type with an LB/UB, as those + * are handled elsewhere. + * + */ + if (((nr_derived == 0) && (first_basic != MPI_DATATYPE_NULL)) || + ((nr_basics == 0) && (first_derived != MPI_DATATYPE_NULL))) + { + return DLOOP_Dataloop_create_unique_type_struct(count, + blklens, + disps, + oldtypes, + type_pos, + dlp_p, + dlsz_p, + dldepth_p, + flag); + } + + /* optimization: + * + * if there are no derived types and caller indicated either a + * homogeneous system or the "all bytes" conversion, convert + * everything to bytes and use an indexed type. + */ + if (nr_derived == 0 && ((flag == DLOOP_DATALOOP_HOMOGENEOUS) || + (flag == DLOOP_DATALOOP_ALL_BYTES))) + { + return DLOOP_Dataloop_create_basic_all_bytes_struct(count, + blklens, + disps, + oldtypes, + dlp_p, + dlsz_p, + dldepth_p, + flag); + } + + /* optimization: + * + * if caller asked for homogeneous or all bytes representation, + * flatten the type and store it as an indexed type so that + * there are no branches in the dataloop tree. + */ + if ((flag == DLOOP_DATALOOP_HOMOGENEOUS) || + (flag == DLOOP_DATALOOP_ALL_BYTES)) + { + return DLOOP_Dataloop_create_flattened_struct(count, + blklens, + disps, + oldtypes, + dlp_p, + dlsz_p, + dldepth_p, + flag); + } + + /* scan through types and gather derived type info */ + for (i=0; i < count; i++) + { + /* ignore type elements with a zero blklen */ + if (blklens[i] == 0) continue; + + if (DLOOP_Handle_hasloop_macro(oldtypes[i])) + { + int tmp_loop_depth, tmp_loop_sz; + + DLOOP_Handle_get_loopdepth_macro(oldtypes[i], tmp_loop_depth, flag); + DLOOP_Handle_get_loopsize_macro(oldtypes[i], tmp_loop_sz, flag); + + if (tmp_loop_depth > old_loop_depth) + { + old_loop_depth = tmp_loop_depth; + } + old_loop_sz += tmp_loop_sz; + } + } + + /* general case below: 2 or more distinct types that are either + * basics or derived, and for which we want to preserve the types + * themselves. + */ + + if (nr_basics > 0) + { + /* basics introduce an extra level of depth, so if our new depth + * must be at least 2 if there are basics. + */ + new_loop_depth = ((old_loop_depth+1) > 2) ? (old_loop_depth+1) : 2; + } + else + { + new_loop_depth = old_loop_depth + 1; + } + + PREPEND_PREFIX(Dataloop_struct_alloc)((DLOOP_Count) nr_basics + nr_derived, + old_loop_sz, + nr_basics, + &curpos, + &new_dlp, + &new_loop_sz); + /* --BEGIN ERROR HANDLING-- */ + if (!new_dlp) + { + return DLOOP_Dataloop_create_struct_memory_error(); + } + /* --END ERROR HANDLING-- */ + + + new_dlp->kind = DLOOP_KIND_STRUCT; + new_dlp->el_size = -1; /* not valid for struct */ + new_dlp->el_extent = -1; /* not valid for struct; see el_extent_array */ + new_dlp->el_type = MPI_DATATYPE_NULL; /* not valid for struct */ + + new_dlp->loop_params.s_t.count = (DLOOP_Count) nr_basics + nr_derived; + + /* note: curpos points to first byte in "old dataloop" region of + * newly allocated space. + */ + + for (i=0, loop_idx = 0; i < count; i++) + { + int is_builtin; + + /* ignore type elements with a zero blklen */ + if (blklens[i] == 0) continue; + + is_builtin = (DLOOP_Handle_hasloop_macro(oldtypes[i])) ? 0 : 1; + + if (is_builtin) + { + DLOOP_Dataloop *dummy_dlp; + int dummy_sz, dummy_depth; + + /* LBs and UBs already taken care of -- skip them */ + if (oldtypes[i] == MPI_LB || oldtypes[i] == MPI_UB) + { + continue; + } + + /* build a contig dataloop for this basic and point to that + * + * optimization: + * + * push the count (blklen) from the struct down into the + * contig so we can process more at the leaf. + */ + err = PREPEND_PREFIX(Dataloop_create_contiguous)(blklens[i], + oldtypes[i], + &dummy_dlp, + &dummy_sz, + &dummy_depth, + flag); + + /* --BEGIN ERROR HANDLING-- */ + if (err) { + /* TODO: FREE ALLOCATED RESOURCES */ + return -1; + } + /* --END ERROR HANDLING-- */ + + /* copy the new contig loop into place in the struct memory + * region + */ + PREPEND_PREFIX(Dataloop_copy)(curpos, dummy_dlp, dummy_sz); + new_dlp->loop_params.s_t.dataloop_array[loop_idx] = curpos; + curpos = (DLOOP_Dataloop *) ((char *) curpos + dummy_sz); + + /* we stored the block size in the contig -- use 1 here */ + new_dlp->loop_params.s_t.blocksize_array[loop_idx] = 1; + new_dlp->loop_params.s_t.el_extent_array[loop_idx] = + ((DLOOP_Offset) blklens[i]) * dummy_dlp->el_extent; + PREPEND_PREFIX(Dataloop_free)(&dummy_dlp); + } + else + { + DLOOP_Dataloop *old_loop_ptr; + int old_loop_sz; + DLOOP_Offset old_extent; + + DLOOP_Handle_get_loopptr_macro(oldtypes[i], old_loop_ptr, flag); + DLOOP_Handle_get_loopsize_macro(oldtypes[i], old_loop_sz, flag); + DLOOP_Handle_get_extent_macro(oldtypes[i], old_extent); + + PREPEND_PREFIX(Dataloop_copy)(curpos, old_loop_ptr, old_loop_sz); + new_dlp->loop_params.s_t.dataloop_array[loop_idx] = curpos; + curpos = (DLOOP_Dataloop *) ((char *) curpos + old_loop_sz); + + new_dlp->loop_params.s_t.blocksize_array[loop_idx] = + (DLOOP_Count) blklens[i]; + new_dlp->loop_params.s_t.el_extent_array[loop_idx] = + old_extent; + } + new_dlp->loop_params.s_t.offset_array[loop_idx] = + (DLOOP_Offset) disps[i]; + loop_idx++; + } + + *dlp_p = new_dlp; + *dlsz_p = new_loop_sz; + *dldepth_p = new_loop_depth; + + return 0; +} + +/* --BEGIN ERROR HANDLING-- */ +static int DLOOP_Dataloop_create_struct_memory_error(void) +{ + return -1; +} +/* --END ERROR HANDLING-- */ + +static int DLOOP_Dataloop_create_unique_type_struct(int count, + int *blklens, + MPI_Aint *disps, + DLOOP_Type *oldtypes, + int type_pos, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag) +{ + /* the same type used more than once in the array; type_pos + * indexes to the first of these. + */ + int i, err, *tmp_blklens, cur_pos = 0; + DLOOP_Offset *tmp_disps; + + /* count is an upper bound on number of type instances */ + tmp_blklens = (int *) DLOOP_Malloc(count * sizeof(int)); + /* --BEGIN ERROR HANDLING-- */ + if (!tmp_blklens) { + /* TODO: ??? */ + return DLOOP_Dataloop_create_struct_memory_error(); + } + /* --END ERROR HANDLING-- */ + + tmp_disps = (DLOOP_Offset *) + DLOOP_Malloc(count * sizeof(DLOOP_Offset)); + /* --BEGIN ERROR HANDLING-- */ + if (!tmp_disps) { + DLOOP_Free(tmp_blklens); + /* TODO: ??? */ + return DLOOP_Dataloop_create_struct_memory_error(); + } + /* --END ERROR HANDLING-- */ + + for (i=type_pos; i < count; i++) + { + if (oldtypes[i] == oldtypes[type_pos] && blklens != 0) + { + tmp_blklens[cur_pos] = blklens[i]; + tmp_disps[cur_pos] = disps[i]; + cur_pos++; + } + } + + err = PREPEND_PREFIX(Dataloop_create_indexed)(cur_pos, + tmp_blklens, + tmp_disps, + 1, /* disp in bytes */ + oldtypes[type_pos], + dlp_p, + dlsz_p, + dldepth_p, + flag); + + DLOOP_Free(tmp_blklens); + DLOOP_Free(tmp_disps); + + return err; + +} + +static int DLOOP_Dataloop_create_basic_all_bytes_struct( + int count, + int *blklens, + MPI_Aint *disps, + DLOOP_Type *oldtypes, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag) +{ + int i, err, cur_pos = 0; + int *tmp_blklens; + MPI_Aint *tmp_disps; + + /* count is an upper bound on number of type instances */ + tmp_blklens = (int *) DLOOP_Malloc(count * sizeof(int)); + + /* --BEGIN ERROR HANDLING-- */ + if (!tmp_blklens) + { + return DLOOP_Dataloop_create_struct_memory_error(); + } + /* --END ERROR HANDLING-- */ + + tmp_disps = (MPI_Aint *) DLOOP_Malloc(count * sizeof(MPI_Aint)); + + /* --BEGIN ERROR HANDLING-- */ + if (!tmp_disps) + { + DLOOP_Free(tmp_blklens); + return DLOOP_Dataloop_create_struct_memory_error(); + } + /* --END ERROR HANDLING-- */ + + for (i=0; i < count; i++) + { + if (oldtypes[i] != MPI_LB && oldtypes[i] != MPI_UB && blklens[i] != 0) + { + DLOOP_Offset sz; + + DLOOP_Handle_get_size_macro(oldtypes[i], sz); + tmp_blklens[cur_pos] = (int) sz * blklens[i]; + tmp_disps[cur_pos] = disps[i]; + cur_pos++; + } + } + err = PREPEND_PREFIX(Dataloop_create_indexed)(cur_pos, + tmp_blklens, + tmp_disps, + 1, /* disp in bytes */ + MPI_BYTE, + dlp_p, + dlsz_p, + dldepth_p, + flag); + + DLOOP_Free(tmp_blklens); + DLOOP_Free(tmp_disps); + + return err; +} + +static int DLOOP_Dataloop_create_flattened_struct(int count, + int *blklens, + MPI_Aint *disps, + DLOOP_Type *oldtypes, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag) +{ + /* arbitrary types, convert to bytes and use indexed */ + int i, err, *tmp_blklens, nr_blks = 0; + MPI_Aint *tmp_disps; /* since we're calling another fn that takes + this type as an input parameter */ + DLOOP_Offset bytes; + DLOOP_Segment *segp; + + int first_ind, last_ind; + + segp = PREPEND_PREFIX(Segment_alloc)(); + /* --BEGIN ERROR HANDLING-- */ + if (!segp) { + return DLOOP_Dataloop_create_struct_memory_error(); + } + /* --END ERROR HANDLING-- */ + + /* use segment code once to count contiguous regions */ + for (i=0; i < count; i++) + { + int is_basic; + + /* ignore type elements with a zero blklen */ + if (blklens[i] == 0) continue; + + is_basic = (DLOOP_Handle_hasloop_macro(oldtypes[i])) ? 0 : 1; + + if (is_basic && (oldtypes[i] != MPI_LB && + oldtypes[i] != MPI_UB)) + { + nr_blks++; + } + else /* derived type; get a count of contig blocks */ + { + DLOOP_Count tmp_nr_blks; + + PREPEND_PREFIX(Segment_init)(NULL, + (DLOOP_Count) blklens[i], + oldtypes[i], + segp, + flag); + bytes = SEGMENT_IGNORE_LAST; + + PREPEND_PREFIX(Segment_count_contig_blocks)(segp, + 0, + &bytes, + &tmp_nr_blks); + + nr_blks += tmp_nr_blks; + } + } + + nr_blks += 2; /* safety measure */ + + tmp_blklens = (int *) DLOOP_Malloc(nr_blks * sizeof(int)); + /* --BEGIN ERROR HANDLING-- */ + if (!tmp_blklens) { + return DLOOP_Dataloop_create_struct_memory_error(); + } + /* --END ERROR HANDLING-- */ + + + tmp_disps = (MPI_Aint *) DLOOP_Malloc(nr_blks * sizeof(MPI_Aint)); + /* --BEGIN ERROR HANDLING-- */ + if (!tmp_disps) { + DLOOP_Free(tmp_blklens); + return DLOOP_Dataloop_create_struct_memory_error(); + } + /* --END ERROR HANDLING-- */ + + /* use segment code again to flatten the type */ + first_ind = 0; + for (i=0; i < count; i++) + { + /* we're going to use the segment code to flatten the type. + * we put in our displacement as the buffer location, and use + * the blocklength as the count value to get N contiguous copies + * of the type. + * + * Note that we're going to get back values in bytes, so that will + * be our new element type. + */ + if (oldtypes[i] != MPI_UB && oldtypes[i] != MPI_LB && blklens[i] != 0) + { + PREPEND_PREFIX(Segment_init)((char *) disps[i], + (DLOOP_Count) blklens[i], + oldtypes[i], + segp, + 0 /* homogeneous */); + + last_ind = nr_blks - first_ind; + bytes = SEGMENT_IGNORE_LAST; + PREPEND_PREFIX(Segment_mpi_flatten)(segp, + 0, + &bytes, + &tmp_blklens[first_ind], + &tmp_disps[first_ind], + &last_ind); + first_ind += last_ind; + } + } + nr_blks = first_ind; + +#if 0 + if (MPIU_DBG_SELECTED(DATATYPE,VERBOSE)) { + MPIU_DBG_OUT(DATATYPE,"--- start of flattened type ---"); + for (i=0; i < nr_blks; i++) { + MPIU_DBG_OUT_FMT(DATATYPE,(MPIU_DBG_FDEST, + "a[%d] = (%d, %d)\n", i, + tmp_blklens[i], tmp_disps[i])); + } + MPIU_DBG_OUT(DATATYPE,"--- end of flattened type ---"); + } +#endif + + PREPEND_PREFIX(Segment_free)(segp); + + err = PREPEND_PREFIX(Dataloop_create_indexed)(nr_blks, + tmp_blklens, + tmp_disps, + 1, /* disp in bytes */ + MPI_BYTE, + dlp_p, + dlsz_p, + dldepth_p, + flag); + + DLOOP_Free(tmp_blklens); + DLOOP_Free(tmp_disps); + + return err; +} diff --git a/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_vector.c b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_vector.c new file mode 100644 index 0000000000..ff4af160f6 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/dataloop_create_vector.c @@ -0,0 +1,161 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#include "./dataloop.h" + +/*@ + Dataloop_create_vector + + Arguments: ++ int icount +. int iblocklength +. MPI_Aint astride +. int strideinbytes +. MPI_Datatype oldtype +. DLOOP_Dataloop **dlp_p +. int *dlsz_p +. int *dldepth_p +- int flag + + Returns 0 on success, -1 on failure. + +@*/ +int PREPEND_PREFIX(Dataloop_create_vector)(int icount, + int iblocklength, + MPI_Aint astride, + int strideinbytes, + DLOOP_Type oldtype, + DLOOP_Dataloop **dlp_p, + int *dlsz_p, + int *dldepth_p, + int flag) +{ + int err, is_builtin; + int new_loop_sz, new_loop_depth; + + DLOOP_Count count, blocklength; + DLOOP_Offset stride; + DLOOP_Dataloop *new_dlp; + + count = (DLOOP_Count) icount; /* avoid subsequent casting */ + blocklength = (DLOOP_Count) iblocklength; + stride = (DLOOP_Offset) astride; + + /* if count or blocklength are zero, handle with contig code, + * call it a int + */ + if (count == 0 || blocklength == 0) + { + + err = PREPEND_PREFIX(Dataloop_create_contiguous)(0, + MPI_INT, + dlp_p, + dlsz_p, + dldepth_p, + flag); + return err; + } + + /* optimization: + * + * if count == 1, store as a contiguous rather than a vector dataloop. + */ + if (count == 1) { + err = PREPEND_PREFIX(Dataloop_create_contiguous)(iblocklength, + oldtype, + dlp_p, + dlsz_p, + dldepth_p, + flag); + return err; + } + + is_builtin = (DLOOP_Handle_hasloop_macro(oldtype)) ? 0 : 1; + + if (is_builtin) { + new_loop_sz = sizeof(DLOOP_Dataloop); + new_loop_depth = 1; + } + else { + int old_loop_sz = 0, old_loop_depth = 0; + + DLOOP_Handle_get_loopsize_macro(oldtype, old_loop_sz, flag); + DLOOP_Handle_get_loopdepth_macro(oldtype, old_loop_depth, flag); + + /* TODO: ACCOUNT FOR PADDING IN LOOP_SZ HERE */ + new_loop_sz = sizeof(DLOOP_Dataloop) + old_loop_sz; + new_loop_depth = old_loop_depth + 1; + } + + + if (is_builtin) { + DLOOP_Offset basic_sz = 0; + + PREPEND_PREFIX(Dataloop_alloc)(DLOOP_KIND_VECTOR, + count, + &new_dlp, + &new_loop_sz); + /* --BEGIN ERROR HANDLING-- */ + if (!new_dlp) return -1; + /* --END ERROR HANDLING-- */ + + DLOOP_Handle_get_size_macro(oldtype, basic_sz); + new_dlp->kind = DLOOP_KIND_VECTOR | DLOOP_FINAL_MASK; + + if (flag == DLOOP_DATALOOP_ALL_BYTES) + { + + blocklength *= basic_sz; + new_dlp->el_size = 1; + new_dlp->el_extent = 1; + new_dlp->el_type = MPI_BYTE; + } + else + { + new_dlp->el_size = basic_sz; + new_dlp->el_extent = new_dlp->el_size; + new_dlp->el_type = oldtype; + } + } + else /* user-defined base type (oldtype) */ { + DLOOP_Dataloop *old_loop_ptr; + int old_loop_sz = 0; + + DLOOP_Handle_get_loopptr_macro(oldtype, old_loop_ptr, flag); + DLOOP_Handle_get_loopsize_macro(oldtype, old_loop_sz, flag); + + PREPEND_PREFIX(Dataloop_alloc_and_copy)(DLOOP_KIND_VECTOR, + count, + old_loop_ptr, + old_loop_sz, + &new_dlp, + &new_loop_sz); + /* --BEGIN ERROR HANDLING-- */ + if (!new_dlp) return -1; + /* --END ERROR HANDLING-- */ + + new_dlp->kind = DLOOP_KIND_VECTOR; + DLOOP_Handle_get_size_macro(oldtype, new_dlp->el_size); + DLOOP_Handle_get_extent_macro(oldtype, new_dlp->el_extent); + DLOOP_Handle_get_basic_type_macro(oldtype, new_dlp->el_type); + } + + /* vector-specific members + * + * stride stored in dataloop is always in bytes for local rep of type + */ + new_dlp->loop_params.v_t.count = count; + new_dlp->loop_params.v_t.blocksize = blocklength; + new_dlp->loop_params.v_t.stride = (strideinbytes) ? stride : + stride * new_dlp->el_extent; + + *dlp_p = new_dlp; + *dlsz_p = new_loop_sz; + *dldepth_p = new_loop_depth; + + return 0; +} diff --git a/ompi/mca/io/romio/romio/common/dataloop/dataloop_parts.h b/ompi/mca/io/romio/romio/common/dataloop/dataloop_parts.h new file mode 100644 index 0000000000..3f2a1c3509 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/dataloop_parts.h @@ -0,0 +1,466 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#ifndef DATALOOP_PARTS_H +#define DATALOOP_PARTS_H + +/* Check that all the appropriate defines are present */ +#ifndef PREPEND_PREFIX +#error "PREPEND_PREFIX must be defined before dataloop_parts.h is included." +#endif + +#ifndef DLOOP_Offset +#error "DLOOP_Offset must be defined before dataloop_parts.h is included." +#endif + +#ifndef DLOOP_Count +#error "DLOOP_Count must be defined before dataloop_parts.h is included." +#endif + +#ifndef DLOOP_Handle +#error "DLOOP_Handle must be defined before dataloop_parts.h is included." +#endif + +#ifndef DLOOP_Buffer +#error "DLOOP_Buffer must be defined before dataloop_parts.h is included." +#endif + +#ifndef DLOOP_Type +#error "DLOOP_Type must be defined before dataloop_parts.h is included." +#endif + +/* Redefine all of the internal structures in terms of the prefix */ +#define DLOOP_Dataloop PREPEND_PREFIX(Dataloop) +#define DLOOP_Dataloop_contig PREPEND_PREFIX(Dataloop_contig) +#define DLOOP_Dataloop_vector PREPEND_PREFIX(Dataloop_vector) +#define DLOOP_Dataloop_blockindexed PREPEND_PREFIX(Dataloop_blockindexed) +#define DLOOP_Dataloop_indexed PREPEND_PREFIX(Dataloop_indexed) +#define DLOOP_Dataloop_struct PREPEND_PREFIX(Dataloop_struct) +#define DLOOP_Dataloop_common PREPEND_PREFIX(Dataloop_common) +#define DLOOP_Segment PREPEND_PREFIX(Segment) +#define DLOOP_Dataloop_stackelm PREPEND_PREFIX(Dataloop_stackelm) + +/* These flags are used at creation time to specify what types of + * optimizations may be applied. They are also passed in at Segment_init + * time to specify which dataloop to use. + * + * Note: The flag to MPID_Segment_init() was originally simply "hetero" + * and was a boolean value (0 meaning homogeneous). Some MPICH2 code + * may still rely on HOMOGENEOUS being "0" and HETEROGENEOUS being "1". + */ +#define DLOOP_DATALOOP_HOMOGENEOUS 0 +#define DLOOP_DATALOOP_HETEROGENEOUS 1 +#define DLOOP_DATALOOP_ALL_BYTES 2 + +/* NOTE: ASSUMING LAST TYPE IS SIGNED */ +#define SEGMENT_IGNORE_LAST ((DLOOP_Offset) -1) +/* + * Each of the MPI datatypes can be mapped into one of 5 very simple + * loops. This loop has the following parameters: + * - count + * - blocksize[] + * - offset[] + * - stride + * - datatype[] + * + * where each [] indicates that a field may be *either* an array or a scalar. + * For each such type, we define a struct that describes these parameters + */ + +/*S + DLOOP_Dataloop_contig - Description of a contiguous dataloop + + Fields: ++ count - Number of elements +- dataloop - Dataloop of the elements + + Module: + Datatype + S*/ +typedef struct DLOOP_Dataloop_contig { + DLOOP_Count count; + struct DLOOP_Dataloop *dataloop; +} DLOOP_Dataloop_contig; + +/*S + DLOOP_Dataloop_vector - Description of a vector or strided dataloop + + Fields: ++ count - Number of elements +. blocksize - Number of dataloops in each element +. stride - Stride (in bytes) between each block +- dataloop - Dataloop of each element + + Module: + Datatype + S*/ +typedef struct DLOOP_Dataloop_vector { + DLOOP_Count count; + struct DLOOP_Dataloop *dataloop; + DLOOP_Count blocksize; + DLOOP_Offset stride; +} DLOOP_Dataloop_vector; + +/*S + DLOOP_Dataloop_blockindexed - Description of a block-indexed dataloop + + Fields: ++ count - Number of blocks +. blocksize - Number of elements in each block +. offset_array - Array of offsets (in bytes) to each block +. total_blocks - count of total blocks in the array (cached value) +- dataloop - Dataloop of each element + + Module: + Datatype + + S*/ +typedef struct DLOOP_Dataloop_blockindexed { + DLOOP_Count count; + struct DLOOP_Dataloop *dataloop; + DLOOP_Count blocksize; + DLOOP_Offset *offset_array; +} DLOOP_Dataloop_blockindexed; + +/*S + DLOOP_Dataloop_indexed - Description of an indexed dataloop + + Fields: ++ count - Number of blocks +. blocksize_array - Array giving the number of elements in each block +. offset_array - Array of offsets (in bytes) to each block +. total_blocks - count of total blocks in the array (cached value) +- dataloop - Dataloop of each element + + Module: + Datatype + + S*/ +typedef struct DLOOP_Dataloop_indexed { + DLOOP_Count count; + struct DLOOP_Dataloop *dataloop; + DLOOP_Count *blocksize_array; + DLOOP_Offset *offset_array; + DLOOP_Count total_blocks; +} DLOOP_Dataloop_indexed; + +/*S + DLOOP_Dataloop_struct - Description of a structure dataloop + + Fields: ++ count - Number of blocks +. blocksize_array - Array giving the number of elements in each block +. offset_array - Array of offsets (in bytes) to each block +- dataloop_array - Array of dataloops describing the elements of each block + + Module: + Datatype + + S*/ +typedef struct DLOOP_Dataloop_struct { + DLOOP_Count count; + struct DLOOP_Dataloop **dataloop_array; + DLOOP_Count *blocksize_array; + DLOOP_Offset *offset_array; + DLOOP_Offset *el_extent_array; /* need more than one */ +} DLOOP_Dataloop_struct; + +/* In many cases, we need the count and the next dataloop item. This + common structure gives a quick access to both. Note that all other + structures must use the same ordering of elements. + Question: should we put the pointer first in case + sizeof(pointer)>sizeof(int) ? +*/ +typedef struct DLOOP_Dataloop_common { + DLOOP_Count count; + struct DLOOP_Dataloop *dataloop; +} DLOOP_Dataloop_common; + +/*S + DLOOP_Dataloop - Description of the structure used to hold a dataloop + description + + Fields: ++ kind - Describes the type of the dataloop. This is divided into three + separate bit fields\: +.vb + Dataloop type (e.g., DLOOP_CONTIG etc.). 3 bits + IsFinal (a "leaf" dataloop; see text) 1 bit + Element Size (units for fields.) 2 bits + Element size has 4 values + 0 - Elements are in units of bytes + 1 - Elements are in units of 2 bytes + 2 - Elements are in units of 4 bytes + 3 - Elements are in units of 8 bytes +.ve + The dataloop type is one of 'DLOOP_CONTIG', 'DLOOP_VECTOR', + 'DLOOP_BLOCKINDEXED', 'DLOOP_INDEXED', or 'DLOOP_STRUCT'. +. loop_parms - A union containing the 5 dataloop structures, e.g., + 'DLOOP_Dataloop_contig', 'DLOOP_Dataloop_vector', etc. A sixth element in + this union, 'count', allows quick access to the shared 'count' field in the + five dataloop structure. +. extent - The extent of the dataloop +#if 0 +- handle - handle for the corresponding 'MPI_Datatype'. +#endif + + Module: + Datatype + + S*/ +typedef struct DLOOP_Dataloop { + int kind; /* Contains both the loop type + (contig, vector, blockindexed, indexed, + or struct) and a bit that indicates + whether the dataloop is a leaf type. */ + union { + DLOOP_Count count; + DLOOP_Dataloop_contig c_t; + DLOOP_Dataloop_vector v_t; + DLOOP_Dataloop_blockindexed bi_t; + DLOOP_Dataloop_indexed i_t; + DLOOP_Dataloop_struct s_t; + DLOOP_Dataloop_common cm_t; + } loop_params; + DLOOP_Offset el_size; /* I don't feel like dealing with the bit manip. + * needed to get the packed size right at the moment. + */ + DLOOP_Offset el_extent; + DLOOP_Type el_type; +} DLOOP_Dataloop; + +#define DLOOP_FINAL_MASK 0x00000008 +#define DLOOP_KIND_MASK 0x00000007 +#define DLOOP_KIND_CONTIG 0x1 +#define DLOOP_KIND_VECTOR 0x2 +#define DLOOP_KIND_BLOCKINDEXED 0x3 +#define DLOOP_KIND_INDEXED 0x4 +#define DLOOP_KIND_STRUCT 0x5 + +/* The max datatype depth is the maximum depth of the stack used to + evaluate datatypes. It represents the length of the chain of + datatype dependencies. Defining this and testing when a datatype + is created removes a test in the datatype evaluation loop. */ +#define DLOOP_MAX_DATATYPE_DEPTH 8 + +/*S + DLOOP_Dataloop_stackelm - Structure for an element of the stack used + to process dataloops + + Fields: ++ curcount - Current loop count value (between 0 and + loop.loop_params.count-1) +. orig_count - original count value (cached so we don't have to look it up) +. curoffset - Offset into memory relative to the pointer to the buffer + passed in by the user. Used to maintain our position as we + move up and down the stack. NEED MORE NOTES ON THIS!!! +. orig_offset - original offset, set before the stackelm is processed, so that + we know where the offset was. this is used in processing indexed + types and possibly others. it is set for all types, but not + referenced in some cases. +. curblock - Current block value...NEED MORE NOTES ON THIS!!! +. orig_block - original block value (caches so we don't have to look it up); + INVALID FOR INDEX AND STRUCT TYPES. +- loop_p - pointer to Loop-based description of the dataloop + +S*/ +typedef struct DLOOP_Dataloop_stackelm { + int may_require_reloading; /* indicates that items below might + * need reloading (e.g. this is a struct) + */ + + DLOOP_Count curcount; + DLOOP_Offset curoffset; + DLOOP_Count curblock; + + DLOOP_Count orig_count; + DLOOP_Offset orig_offset; + DLOOP_Count orig_block; + + struct DLOOP_Dataloop *loop_p; +} DLOOP_Dataloop_stackelm; + +/*S + DLOOP_Segment - Description of the Segment datatype + + Notes: + This has no corresponding MPI datatype. + + Module: + Segment + + Questions: + Should this have an id for allocation and similarity purposes? + S*/ +typedef struct DLOOP_Segment { + void *ptr; /* pointer to datatype buffer */ + DLOOP_Handle handle; + DLOOP_Offset stream_off; /* next offset into data stream resulting from datatype + * processing. in other words, how many bytes have + * we created/used by parsing so far? that amount + 1. + */ + DLOOP_Dataloop_stackelm stackelm[DLOOP_MAX_DATATYPE_DEPTH]; + int cur_sp; /* Current stack pointer when using dataloop */ + int valid_sp; /* maximum valid stack pointer. This is used to + maintain information on the stack after it has + been placed there by following the datatype field + in a DLOOP_Dataloop_st for any type except struct */ + + struct DLOOP_Dataloop builtin_loop; /* used for both predefined types (which + * won't have a loop already) and for + * situations where a count is passed in + * and we need to create a contig loop + * to handle it + */ + /* other, device-specific information */ +} DLOOP_Segment; + +/* Dataloop functions (dataloop.c) */ +void PREPEND_PREFIX(Dataloop_copy)(void *dest, + void *src, + int size); +void PREPEND_PREFIX(Dataloop_update)(DLOOP_Dataloop *dataloop, + DLOOP_Offset ptrdiff); +DLOOP_Offset +PREPEND_PREFIX(Dataloop_stream_size)(DLOOP_Dataloop *dl_p, + DLOOP_Offset (*sizefn)(DLOOP_Type el_type)); +void PREPEND_PREFIX(Dataloop_print)(DLOOP_Dataloop *dataloop, + int depth); + +void PREPEND_PREFIX(Dataloop_alloc)(int kind, + DLOOP_Count count, + DLOOP_Dataloop **new_loop_p, + int *new_loop_sz_p); +void PREPEND_PREFIX(Dataloop_alloc_and_copy)(int kind, + DLOOP_Count count, + DLOOP_Dataloop *old_loop, + int old_loop_sz, + DLOOP_Dataloop **new_loop_p, + int *new_loop_sz_p); +void PREPEND_PREFIX(Dataloop_struct_alloc)(DLOOP_Count count, + int old_loop_sz, + int basic_ct, + DLOOP_Dataloop **old_loop_p, + DLOOP_Dataloop **new_loop_p, + int *new_loop_sz_p); +void PREPEND_PREFIX(Dataloop_dup)(DLOOP_Dataloop *old_loop, + int old_loop_sz, + DLOOP_Dataloop **new_loop_p); + +void PREPEND_PREFIX(Dataloop_free)(DLOOP_Dataloop **dataloop); + +/* Segment functions (segment.c) */ +DLOOP_Segment * PREPEND_PREFIX(Segment_alloc)(void); + +void PREPEND_PREFIX(Segment_free)(DLOOP_Segment *segp); + +int PREPEND_PREFIX(Segment_init)(const DLOOP_Buffer buf, + DLOOP_Count count, + DLOOP_Handle handle, + DLOOP_Segment *segp, + int hetero); + +void +PREPEND_PREFIX(Segment_manipulate)(DLOOP_Segment *segp, + DLOOP_Offset first, + DLOOP_Offset *lastp, + int (*piecefn) (DLOOP_Offset *blocks_p, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer bufp, + void *v_paramp), + int (*vectorfn) (DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count blklen, + DLOOP_Offset stride, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer bufp, + void *v_paramp), + int (*blkidxfn) (DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count blklen, + DLOOP_Offset *offsetarray, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer bufp, + void *v_paramp), + int (*indexfn) (DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count *blockarray, + DLOOP_Offset *offsetarray, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer bufp, + void *v_paramp), + DLOOP_Offset (*sizefn) (DLOOP_Type el_type), + void *pieceparams); + +/* Common segment operations (segment_ops.c) */ +void PREPEND_PREFIX(Segment_count_contig_blocks)(DLOOP_Segment *segp, + DLOOP_Offset first, + DLOOP_Offset *lastp, + DLOOP_Count *countp); +void PREPEND_PREFIX(Segment_mpi_flatten)(DLOOP_Segment *segp, + DLOOP_Offset first, + DLOOP_Offset *lastp, + int *blklens, + MPI_Aint *disps, + int *lengthp); + +#define DLOOP_M2M_TO_USERBUF 0 +#define DLOOP_M2M_FROM_USERBUF 1 + +struct PREPEND_PREFIX(m2m_params) { + int direction; /* M2M_TO_USERBUF or M2M_FROM_USERBUF */ + char *streambuf; + char *userbuf; +}; + +void PREPEND_PREFIX(Segment_pack)(struct DLOOP_Segment *segp, + DLOOP_Offset first, + DLOOP_Offset *lastp, + void *streambuf); +void PREPEND_PREFIX(Segment_unpack)(struct DLOOP_Segment *segp, + DLOOP_Offset first, + DLOOP_Offset *lastp, + void *streambuf); + +/* Segment piece functions that are used in specific cases elsewhere */ +int PREPEND_PREFIX(Segment_contig_m2m)(DLOOP_Offset *blocks_p, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + void *bufp, /* unused */ + void *v_paramp); +int PREPEND_PREFIX(Segment_vector_m2m)(DLOOP_Offset *blocks_p, + DLOOP_Count count, /* unused */ + DLOOP_Count blksz, + DLOOP_Offset stride, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + void *bufp, /* unused */ + void *v_paramp); +int PREPEND_PREFIX(Segment_blkidx_m2m)(DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count blocklen, + DLOOP_Offset *offsetarray, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + void *bufp, /*unused */ + void *v_paramp); +int PREPEND_PREFIX(Segment_index_m2m)(DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count *blockarray, + DLOOP_Offset *offsetarray, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + void *bufp, /*unused */ + void *v_paramp); +#endif + + + diff --git a/ompi/mca/io/romio/romio/common/dataloop/romio_dataloop.c b/ompi/mca/io/romio/romio/common/dataloop/romio_dataloop.c new file mode 100644 index 0000000000..00731bc9ad --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/romio_dataloop.c @@ -0,0 +1,575 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2007 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +/* This file implements functions necessary to store dataloop representations + * as an attribute on the MPI datatype. It also implements functionality to + * store a size, extent, and count of contig blocks in the same way. The + * size and extent are stored as MPI_Offset-sized values, so that overflow + * isn't a factor (as it would be on BG* for example if we used ints or + * MPI_Aints). + * + * Additionally this code puts an attribute on MPI_COMM_WORLD with a delete + * callback to clean up keyvals and any allocated memory (although currently + * memory cleanup relies on the delete functions on the types being called + * as well). + */ + +#include +#include + +#include "romio_dataloop.h" +#include "typesize_support.h" + +typedef struct MPIO_Datatype_s { + int valid, refct; + int dloop_size, dloop_depth; /* size, depth of dloop struct */ + DLOOP_Offset size, extent; /* size and extent of type */ + DLOOP_Offset true_lb, true_extent; + DLOOP_Dataloop *dloop; + DLOOP_Count contig_blks; +} MPIO_Datatype; + +/* valid flags */ +#define MPIO_DATATYPE_VALID_DLOOP_PTR 1 +#define MPIO_DATATYPE_VALID_DLOOP_SIZE 2 +#define MPIO_DATATYPE_VALID_DLOOP_DEPTH 4 +#define MPIO_DATATYPE_VALID_TYPESZEXT 8 +#define MPIO_DATATYPE_VALID_CONTIG_BLKS 16 + +#define MPIO_DATATYPE_VALID_DATALOOP (MPIO_DATATYPE_VALID_DLOOP_PTR | \ + MPIO_DATATYPE_VALID_DLOOP_SIZE | \ + MPIO_DATATYPE_VALID_DLOOP_DEPTH) + +/* MPI attr keyvals used internally */ +static int MPIO_Datatype_keyval = MPI_KEYVAL_INVALID; +static int MPIO_Datatype_finalize_keyval = MPI_KEYVAL_INVALID; + +static MPIO_Datatype *MPIO_Datatype_allocate(MPI_Datatype type); +static void MPIO_Datatype_set_szext(MPI_Datatype type, MPIO_Datatype *dtp); +static int MPIO_Datatype_initialize(void); +static int MPIO_Datatype_finalize(MPI_Comm comm, int comm_keyval, + void *attrval, void *extrastate); +static int MPIO_Datatype_copy_attr_function(MPI_Datatype type, int type_keyval, + void *extra_state, + void *attribute_val_in, + void *attribute_val_out, int *flag); +static int MPIO_Datatype_delete_attr_function(MPI_Datatype type, + int type_keyval, + void *attribute_val, + void *extra_state); + +/* MPIO_Datatype_init_dataloop + * + * Must be called before dataloop is accessed. + * + * Valid to call more than once on the same type. + */ +void MPIO_Datatype_init_dataloop(MPI_Datatype type) +{ + int mpi_errno, attrflag; + MPIO_Datatype *dtp; + + /* trivial types don't get dataloops */ + if (!(MPIO_Datatype_is_nontrivial(type))) return; + + if (MPIO_Datatype_keyval != MPI_KEYVAL_INVALID) { + MPIO_Datatype_initialize(); + } + + mpi_errno = PMPI_Type_get_attr(type, MPIO_Datatype_keyval, &dtp, &attrflag); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + if (!attrflag) { + /* need to allocate structure and create dataloop representation */ + dtp = MPIO_Datatype_allocate(type); + } + if (!(dtp->valid | MPIO_DATATYPE_VALID_DLOOP_PTR)) { + MPIO_Dataloop_create(type, + &dtp->dloop, + &dtp->dloop_size, + &dtp->dloop_depth, + 0); + } + + return; +} + +void MPIO_Datatype_get_size(MPI_Datatype type, MPI_Offset *size_p) +{ + int mpi_errno, attrflag; + MPIO_Datatype *dtp; + + if (MPIO_Datatype_keyval != MPI_KEYVAL_INVALID) { + MPIO_Datatype_initialize(); + } + + mpi_errno = PMPI_Type_get_attr(type, MPIO_Datatype_keyval, &dtp, &attrflag); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + if (!attrflag) { + dtp = MPIO_Datatype_allocate(type); + } + + if (!(dtp->valid & MPIO_DATATYPE_VALID_TYPESZEXT)) { + MPIO_Datatype_set_szext(type, dtp); + } + + *size_p = dtp->size; + return; +} + +void MPIO_Datatype_get_extent(MPI_Datatype type, MPI_Offset *extent_p) +{ + int mpi_errno, attrflag; + MPIO_Datatype *dtp; + + if (MPIO_Datatype_keyval != MPI_KEYVAL_INVALID) { + MPIO_Datatype_initialize(); + } + + mpi_errno = PMPI_Type_get_attr(type, MPIO_Datatype_keyval, &dtp, &attrflag); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + if (!attrflag) { + dtp = MPIO_Datatype_allocate(type); + } + + if (!(dtp->valid & MPIO_DATATYPE_VALID_TYPESZEXT)) { + MPIO_Datatype_set_szext(type, dtp); + } + + *extent_p = dtp->extent; + return; +} + +/* MPIO_Datatype_get_block_info + * + * Parameters: + * type - MPI datatype + * true_lb - true_lb for type (offset to start of data) + * count - count of # of contiguous regions in the type + * n_contig - flag, indicating if N of these types would also form a + * single contiguous block + */ +void MPIO_Datatype_get_block_info(MPI_Datatype type, + MPI_Offset *true_lb_p, + MPI_Offset *count_p, + int *n_contig_p) +{ + int mpi_errno, attrflag; + int nr_ints, nr_aints, nr_types, combiner; + + mpi_errno = PMPI_Type_get_envelope(type, &nr_ints, &nr_aints, + &nr_types, &combiner); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + if (combiner == MPI_COMBINER_NAMED && + (type != MPI_FLOAT_INT && + type != MPI_DOUBLE_INT && + type != MPI_LONG_INT && + type != MPI_SHORT_INT && + type != MPI_LONG_DOUBLE_INT)) + { + *true_lb_p = 0; + *count_p = 1; + *n_contig_p = 1; + } + else { + MPIO_Datatype *dtp; + MPIO_Segment *segp; + MPI_Offset bytes; + + mpi_errno = PMPI_Type_get_attr(type, MPIO_Datatype_keyval, &dtp, + &attrflag); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + if (!attrflag) { + /* need to allocate structure and create dataloop representation */ + dtp = MPIO_Datatype_allocate(type); + } + if (!(dtp->valid | MPIO_DATATYPE_VALID_DLOOP_PTR)) { + MPIO_Dataloop_create(type, + &dtp->dloop, + &dtp->dloop_size, + &dtp->dloop_depth, 0); + DLOOP_Assert(dtp->dloop != NULL); + } + + DLOOP_Assert((dtp->valid | MPIO_DATATYPE_VALID_DLOOP_PTR) && + (dtp->valid | MPIO_DATATYPE_VALID_DLOOP_SIZE) && + (dtp->valid | MPIO_DATATYPE_VALID_DLOOP_DEPTH)); + + segp = MPIO_Segment_alloc(); + DLOOP_Assert(segp != NULL); + + MPIO_Segment_init(NULL, 1, type, segp, 0); + bytes = SEGMENT_IGNORE_LAST; + + MPIO_Segment_count_contig_blocks(segp, 0, &bytes, &dtp->contig_blks); + MPIO_Segment_free(segp); + dtp->valid |= MPIO_DATATYPE_VALID_CONTIG_BLKS; + + if (!(dtp->valid | MPIO_DATATYPE_VALID_TYPESZEXT)) { + MPIO_Datatype_set_szext(type, dtp); + } + *true_lb_p = dtp->true_lb; + *count_p = dtp->contig_blks; + *n_contig_p = (dtp->contig_blks == 1 && + dtp->true_extent == dtp->extent) ? 1 : 0; + } + + return; +} + +void MPIO_Datatype_get_el_type(MPI_Datatype type, + MPI_Datatype *eltype_p, + int flag) +{ + int mpi_errno; + int nr_ints, nr_aints, nr_types, combiner; + + mpi_errno = PMPI_Type_get_envelope(type, &nr_ints, &nr_aints, + &nr_types, &combiner); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + if (combiner == MPI_COMBINER_NAMED) { + if (type == MPI_FLOAT_INT || + type == MPI_DOUBLE_INT || + type == MPI_LONG_INT || + type == MPI_SHORT_INT || + type == MPI_LONG_DOUBLE_INT) + { + *eltype_p = MPI_DATATYPE_NULL; + } + else if (type == MPI_2INT) { + *eltype_p = MPI_INT; + } + else { + /* all the other named types are their own element type */ + *eltype_p = type; + } + } + else { + MPIO_Dataloop *dlp; + MPIO_Datatype_get_loopptr(type, &dlp, flag); + + *eltype_p = dlp->el_type; + } + return; +} + +/* dataloop-related functions used by dataloop code */ +void MPIO_Datatype_get_loopptr(MPI_Datatype type, + MPIO_Dataloop **ptr_p, + int flag) +{ + int mpi_errno, attrflag; + MPIO_Datatype *dtp; + + if (MPIO_Datatype_keyval != MPI_KEYVAL_INVALID) { + MPIO_Datatype_initialize(); + } + + mpi_errno = PMPI_Type_get_attr(type, MPIO_Datatype_keyval, &dtp, &attrflag); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + if (!(dtp->valid & MPIO_DATATYPE_VALID_DLOOP_PTR)) + *ptr_p = NULL; + else + *ptr_p = dtp->dloop; + + return; +} + +void MPIO_Datatype_get_loopsize(MPI_Datatype type, int *size_p, int flag) +{ + int mpi_errno, attrflag; + MPIO_Datatype *dtp; + + if (MPIO_Datatype_keyval != MPI_KEYVAL_INVALID) { + MPIO_Datatype_initialize(); + } + + mpi_errno = PMPI_Type_get_attr(type, MPIO_Datatype_keyval, &dtp, &attrflag); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + if (!(dtp->valid & MPIO_DATATYPE_VALID_DLOOP_SIZE)) + *size_p = -1; + else + *size_p = dtp->dloop_size; + + return; +} + +void MPIO_Datatype_get_loopdepth(MPI_Datatype type, int *depth_p, int flag) +{ + int mpi_errno, attrflag; + MPIO_Datatype *dtp; + + if (MPIO_Datatype_keyval != MPI_KEYVAL_INVALID) { + MPIO_Datatype_initialize(); + } + + mpi_errno = PMPI_Type_get_attr(type, MPIO_Datatype_keyval, &dtp, &attrflag); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + if (!(dtp->valid & MPIO_DATATYPE_VALID_DLOOP_DEPTH)) + *depth_p = -1; + else + *depth_p = dtp->dloop_depth; + + return; +} + +void MPIO_Datatype_set_loopptr(MPI_Datatype type, MPIO_Dataloop *ptr, int flag) +{ + int mpi_errno, attrflag; + MPIO_Datatype *dtp; + + if (MPIO_Datatype_keyval != MPI_KEYVAL_INVALID) { + MPIO_Datatype_initialize(); + } + + mpi_errno = PMPI_Type_get_attr(type, MPIO_Datatype_keyval, &dtp, &attrflag); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + if (!attrflag) { + dtp = MPIO_Datatype_allocate(type); + } + + printf("set loopptr = %x\n", (int) ptr); + + dtp->dloop = ptr; + dtp->valid |= MPIO_DATATYPE_VALID_DLOOP_PTR; + return; +} + +void MPIO_Datatype_set_loopsize(MPI_Datatype type, int size, int flag) +{ + int mpi_errno, attrflag; + MPIO_Datatype *dtp; + + if (MPIO_Datatype_keyval != MPI_KEYVAL_INVALID) { + MPIO_Datatype_initialize(); + } + + mpi_errno = PMPI_Type_get_attr(type, MPIO_Datatype_keyval, &dtp, &attrflag); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + if (!attrflag) { + dtp = MPIO_Datatype_allocate(type); + } + + dtp->dloop_size = size; + dtp->valid |= MPIO_DATATYPE_VALID_DLOOP_SIZE; + return; +} + +void MPIO_Datatype_set_loopdepth(MPI_Datatype type, int depth, int flag) +{ + int mpi_errno, attrflag; + MPIO_Datatype *dtp; + + if (MPIO_Datatype_keyval != MPI_KEYVAL_INVALID) { + MPIO_Datatype_initialize(); + } + + mpi_errno = PMPI_Type_get_attr(type, MPIO_Datatype_keyval, &dtp, &attrflag); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + if (!attrflag) { + dtp = MPIO_Datatype_allocate(type); + } + + dtp->dloop_depth = depth; + dtp->valid |= MPIO_DATATYPE_VALID_DLOOP_DEPTH; + return; +} + +int MPIO_Datatype_is_nontrivial(MPI_Datatype type) +{ + int nr_ints, nr_aints, nr_types, combiner; + + PMPI_Type_get_envelope(type, &nr_ints, &nr_aints, &nr_types, &combiner); + if (combiner != MPI_COMBINER_NAMED || + type == MPI_FLOAT_INT || + type == MPI_DOUBLE_INT || + type == MPI_LONG_INT || + type == MPI_SHORT_INT || + type == MPI_LONG_DOUBLE_INT) return 1; + else return 0; +} + +/* internal functions */ + +static int MPIO_Datatype_initialize(void) +{ + int mpi_errno; + + DLOOP_Assert(MPIO_Datatype_keyval == MPI_KEYVAL_INVALID); + + /* create keyval for dataloop storage */ + mpi_errno = PMPI_Type_create_keyval(MPIO_Datatype_copy_attr_function, + MPIO_Datatype_delete_attr_function, + &MPIO_Datatype_keyval, + NULL); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + /* create keyval to hook to COMM_WORLD for finalize */ + mpi_errno = PMPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, + MPIO_Datatype_finalize, + &MPIO_Datatype_finalize_keyval, + NULL); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + mpi_errno = PMPI_Comm_set_attr(MPI_COMM_WORLD, + MPIO_Datatype_finalize_keyval, + NULL); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + printf("created keyval\n"); + + return 0; +} + +/* MPIO_Datatype_finalize() + */ +static int MPIO_Datatype_finalize(MPI_Comm comm, + int comm_keyval, + void *attrval, + void *extrastate) +{ + int mpi_errno; + + DLOOP_Assert(MPIO_Datatype_keyval != MPI_KEYVAL_INVALID); + + /* remove keyvals */ + mpi_errno = PMPI_Type_free_keyval(&MPIO_Datatype_keyval); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + mpi_errno = PMPI_Type_free_keyval(&MPIO_Datatype_finalize_keyval); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + printf("freed keyvals\n"); + + return MPI_SUCCESS; +} + +static MPIO_Datatype *MPIO_Datatype_allocate(MPI_Datatype type) +{ + int mpi_errno; + MPIO_Datatype *dtp; + + dtp = (MPIO_Datatype *) malloc(sizeof(MPIO_Datatype)); + DLOOP_Assert(dtp != NULL); + dtp->valid = 0; + dtp->refct = 1; + dtp->dloop = NULL; + dtp->dloop_size = -1; + dtp->dloop_depth = -1; + + mpi_errno = PMPI_Type_set_attr(type, MPIO_Datatype_keyval, dtp); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + printf("allocated attr struct\n"); + + return dtp; +} + +/* MPIO_Datatype_set_szext() + * + * Calculates size, extent, true_lb, and true_extent of type, fills in values + * in MPIO_Datatype structure, and sets valid flag. + * + * Note: This code currently checks for compatible variable sizes at + * runtime, while this check could instead be performed at configure + * time to save a few instructions. This seems like micro-optimization, + * so I skipped it for now. -- RobR, 03/22/2007 + */ +static void MPIO_Datatype_set_szext(MPI_Datatype type, MPIO_Datatype *dtp) +{ + int mpi_errno; + + if (sizeof(int) >= sizeof(MPI_Offset) && + sizeof(MPI_Aint) >= sizeof(MPI_Offset)) + { + int size; + MPI_Aint lb, extent, true_lb, true_extent; + + mpi_errno = PMPI_Type_size(type, &size); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + mpi_errno = PMPI_Type_get_extent(type, &lb, &extent); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + mpi_errno = PMPI_Type_get_true_extent(type, &true_lb, &true_extent); + + dtp->size = (MPI_Offset) size; + dtp->extent = (MPI_Offset) extent; + dtp->true_lb = (MPI_Offset) true_lb; + dtp->true_extent = (MPI_Offset) true_extent; + } + else { + MPIO_Type_footprint tfp; + + MPIO_Type_calc_footprint(type, &tfp); + dtp->size = tfp.size; + dtp->extent = tfp.extent; + dtp->true_lb = tfp.true_lb; + dtp->true_extent = tfp.true_ub - tfp.true_lb; + } + + dtp->valid |= MPIO_DATATYPE_VALID_TYPESZEXT; + return; +} + +static int MPIO_Datatype_copy_attr_function(MPI_Datatype type, + int type_keyval, + void *extra_state, + void *attribute_val_in, + void *attribute_val_out, + int *flag) +{ + MPIO_Datatype *dtp = (MPIO_Datatype *) attribute_val_in; + + printf("copy fn. called\n"); + + DLOOP_Assert(dtp->refct); + + dtp->refct++; + + * (MPIO_Datatype **) attribute_val_out = dtp; + *flag = 1; + + printf("inc'd refct.\n"); + + return MPI_SUCCESS; +} + +static int MPIO_Datatype_delete_attr_function(MPI_Datatype type, + int type_keyval, + void *attribute_val, + void *extra_state) +{ + MPIO_Datatype *dtp = (MPIO_Datatype *) attribute_val; + + printf("delete fn. called\n"); + + DLOOP_Assert(dtp->refct); + + printf("dec'd refct\n"); + + dtp->refct--; + if (dtp->refct == 0) { + free(dtp); + printf("freed attr structure\n"); + } + + return MPI_SUCCESS; +} + +/* + * Local variables: + * c-indent-tabs-mode: nil + * End: + */ diff --git a/ompi/mca/io/romio/romio/common/dataloop/romio_dataloop.h b/ompi/mca/io/romio/romio/common/dataloop/romio_dataloop.h new file mode 100644 index 0000000000..610fd4ac5c --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/romio_dataloop.h @@ -0,0 +1,144 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#ifndef ROMIO_DATALOOP_H +#define ROMIO_DATALOOP_H + +#include +#include + +/* romioconf.h must be included *before* mpi.h to avoid some redeclarations */ +#include "mpitypedefs.h" +#include "mpichconf.h" +#include "romioconf.h" + +#include + +/* Note: this is where you define the prefix that will be prepended on + * all externally visible generic dataloop and segment functions. + */ +#define PREPEND_PREFIX(fn) MPIO_ ## fn + +struct MPIO_iovec { + MPI_Offset base; + MPI_Offset len; +}; + +/* These following dataloop-specific types will be used throughout the DLOOP + * instance: + */ +#define DLOOP_Offset MPI_Offset +#define DLOOP_Count MPI_Offset +#define DLOOP_Handle MPI_Datatype +#define DLOOP_Type MPI_Datatype +#define DLOOP_Buffer void * +#define DLOOP_VECTOR struct MPIO_iovec +#define DLOOP_VECTOR_LEN len +#define DLOOP_VECTOR_BUF base + +/* The following accessor functions must also be defined: + * + * DLOOP_Handle_extent() + * DLOOP_Handle_size() + * DLOOP_Handle_loopptr() + * DLOOP_Handle_loopdepth() + * DLOOP_Handle_hasloop() + * + */ + +/* USE THE NOTATION THAT BILL USED IN MPIIMPL.H AND MAKE THESE MACROS */ + +/* NOTE: put get size into mpiimpl.h; the others go here until such time + * as we see that we need them elsewhere. + */ +#define DLOOP_Handle_get_loopdepth_macro(handle_,depth_,flag_) \ + MPIO_Datatype_get_loopdepth(handle_,&(depth_),flag_) + +#define DLOOP_Handle_get_loopsize_macro(handle_,size_,flag_) \ + MPIO_Datatype_get_loopsize(handle_,&(size_),flag_) + +#define DLOOP_Handle_set_loopptr_macro(handle_,lptr_,flag_) \ + MPIO_Datatype_set_loopptr(handle_,lptr_,flag_) + +#define DLOOP_Handle_set_loopdepth_macro(handle_,depth_,flag_) \ + MPIO_Datatype_set_loopdepth(handle_,depth_,flag_) + +#define DLOOP_Handle_set_loopsize_macro(handle_,size_,flag_) \ + MPIO_Datatype_set_loopsize(handle_,size_,flag_) + +#define DLOOP_Handle_get_loopptr_macro(handle_,lptr_,flag_) \ + MPIO_Datatype_get_loopptr(handle_,&(lptr_),flag_) + +#define DLOOP_Handle_get_size_macro(handle_,size_) \ + MPIO_Datatype_get_size(handle_,&(size_)) + +#define DLOOP_Handle_get_basic_type_macro(handle_,eltype_) \ + MPIO_Datatype_get_el_type(handle_, &(eltype_), 0) + +#define DLOOP_Handle_get_extent_macro(handle_,extent_) \ + MPIO_Datatype_get_extent(handle_,&(extent_)) + +#define DLOOP_Handle_hasloop_macro(handle_) \ + (MPIO_Datatype_is_nontrivial(handle_)) + +/* allocate and free functions must also be defined. */ +#define DLOOP_Malloc malloc +#define DLOOP_Free free + +/* debugging output function */ +#define DLOOP_dbg_printf printf + +/* assert function */ +#define DLOOP_Assert assert + +/* contents access functions -- use MPICH2 versions for now. */ +#define MPIO_Type_access_contents MPID_Type_access_contents +#define MPIO_Type_release_contents MPID_Type_release_contents + +/* Include dataloop_parts.h at the end to get the rest of the prototypes + * and defines, in terms of the prefixes and types above. + */ +#include "./dataloop_parts.h" +#include "./dataloop_create.h" + +/* accessor functions */ +void MPIO_Datatype_init_dataloop(MPI_Datatype type); +void MPIO_Datatype_get_size(MPI_Datatype type, MPI_Offset *size_p); +void MPIO_Datatype_get_extent(MPI_Datatype type, MPI_Offset *extent_p); +void MPIO_Datatype_get_block_info(MPI_Datatype type, MPI_Offset *true_lb, + MPI_Offset *count, int *n_contig); +int MPIO_Datatype_is_nontrivial(MPI_Datatype type); +void MPIO_Datatype_get_el_type(MPI_Datatype type, MPI_Datatype *eltype_p, + int flag); + +/* accessor functions used only by dataloop code proper */ +void MPIO_Datatype_get_loopptr(MPI_Datatype type, MPIO_Dataloop **ptr_p, + int flag); +void MPIO_Datatype_get_loopsize(MPI_Datatype type, int *size_p, int flag); +void MPIO_Datatype_get_loopdepth(MPI_Datatype type, int *depth_p, int flag); +void MPIO_Datatype_set_loopptr(MPI_Datatype type, MPIO_Dataloop *ptr, int flag); +void MPIO_Datatype_set_loopsize(MPI_Datatype type, int size, int flag); +void MPIO_Datatype_set_loopdepth(MPI_Datatype type, int depth, int flag); + +/* accessor functions from elsewhere */ +void MPIO_Type_access_contents(MPI_Datatype type, + int **ints_p, + MPI_Aint **aints_p, + MPI_Datatype **types_p); +void MPIO_Type_release_contents(MPI_Datatype type, + int **ints_p, + MPI_Aint **aints_p, + MPI_Datatype **types_p); + +/* These values are defined by DLOOP code. + * + * Note: DLOOP_DATALOOP_ALL_BYTES not currently used in MPICH2. + */ +#define MPID_DATALOOP_HETEROGENEOUS DLOOP_DATALOOP_HETEROGENEOUS +#define MPID_DATALOOP_HOMOGENEOUS DLOOP_DATALOOP_HOMOGENEOUS + +#endif diff --git a/ompi/mca/io/romio/romio/common/dataloop/romio_segment_ops.c b/ompi/mca/io/romio/romio/common/dataloop/romio_segment_ops.c new file mode 100644 index 0000000000..36b0336ef8 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/romio_segment_ops.c @@ -0,0 +1,482 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2007 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#include +#include + +#include "romio_dataloop.h" +#include "typesize_support.h" + +/* Nomenclature: + * ib_ - intermediate buffer + * fv_ - file view + * ub_ - user buffer + * ds_ - data sieving + */ + +/* MPIO_File_ib_params - used when finding "good" data in intermediate buffer + * + * direction - either DLOOP_M2M_TO_USERBUF or DLOOP_M2M_FROM_USERBUF + * iobuf - location of IO buffer in memory + * ib_file_off - location in file corresponding to first byte in buffer + * ib_file_size - size of valid data in IO buffer + * fv_disp - displacement of file view (not kept elsewhere) + * + * ub_segp - segment corresponding to user buffer (with valid bufp) + * + * note: fv_disp is here mainly because the "bufp" value in the segment + * isn't necessarily large enough to hold an MPI_Offset. + */ +struct MPIO_File_ib_params { + int direction; + char *ib_ptr; + MPI_Offset ib_file_off, ib_file_size; + MPI_Offset fv_disp; + + DLOOP_Segment *ub_segp; +}; + +/* MPIO_File_ds_params + * + * Used to drive process of sifting data through intermediate buffer + * + * fv_disp - file view displacement + * ib_size - size of intermediate (data sieving) buffer + * ib_file_off - starting location of next file access going to buffer + * ib_file_size - size of access going to buffer + * ib_fv_off - file view (stream) location corresponding buffer start + * ib_fv_last - stream location one past last one going to buffer + * + * ib - + * ib_segp - + */ +struct MPIO_File_ds_params { + int direction; + MPI_Offset fv_disp; + MPI_Offset ib_size; + + /* cur_file_size helps us track what I/O would be necessary to cover + * the stream region we've processed so far. + */ + MPI_Offset ib_file_off, ib_file_size; + MPI_Offset ib_fv_off, ib_fv_last; + + struct MPIO_File_ib_params ib; + DLOOP_Segment *ib_segp; +}; + +static int MPIO_Segment_contig_file_ds(MPI_Offset *blocks_p, + MPI_Datatype el_type, + MPI_Offset rel_off, + void *unused, + void *v_paramp); + +static int MPIO_Segment_contig_fv2ib(DLOOP_Offset *blocks_p, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + void *unused, + void *v_paramp); +static int MPIO_File_datasieve(struct MPIO_File_ds_params *dsp, + MPI_Offset *fv_lastp); + + +static int MPIO_Segment_contig_m2m_fake(DLOOP_Offset *blocks_p, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + void *unused, + void *v_paramp); + +int MPIO_File_ds_io(MPI_Aint *ub_ptr, + int ub_count, + MPI_Datatype ub_type, + MPI_Offset fv_off, + MPI_Offset fv_disp, + MPI_Datatype fv_type, + int direction, + MPI_Offset ib_size) +{ + /* "b" variables refer to memory buffer, "f" variables to file regions */ + int ub_ncontig, fv_ncontig; + MPI_Offset ub_blkoff, ub_blkct, ub_typesize; + MPI_Offset fv_blkoff, fv_blkct, fv_size, fv_count, fv_counttouch; + + MPI_Offset size; + char *ib_ptr; + struct MPIO_File_ds_params *dsp; + struct MPIO_File_ib_params *ibp; + DLOOP_Segment *ds_segp; + + MPIO_Datatype_get_size(fv_type, &fv_size); + + /* calculate size of access in bytes using buffer type */ + MPIO_Datatype_get_size(ub_type, &ub_typesize); + size = ub_typesize * ub_count; + + /* fv_counttouch is count of file types touched by this particular + * access. used to determine if we're really contiguous in file or not. + */ + fv_counttouch = (((fv_off % fv_size) ? 1 : 0) + + (size - fv_size + (fv_off % fv_size)) / fv_size + + (((size - fv_size + (fv_off % fv_size)) % fv_size) ? 1:0)); + + MPIO_Datatype_get_block_info(ub_type, &ub_blkoff, &ub_blkct, &ub_ncontig); + MPIO_Datatype_get_block_info(fv_type, &fv_blkoff, &fv_blkct, &fv_ncontig); + + /* set up IO buffer */ + DLOOP_Assert(ib_size > 0); + if (size < ib_size) ib_size = size; + ib_ptr = (char *) DLOOP_Malloc(ib_size); + + /* optimizations: use simplest possible types when contiguous */ + if (ub_ncontig || (ub_count == 1 && ub_blkct == 1)) { + /* contiguous in memory -- use very simple contig instead */ + MPI_Datatype eltype; + MPI_Offset eltypesz; + + MPIO_Datatype_get_el_type(ub_type, &eltype, 0); + if (eltype == MPI_DATATYPE_NULL) eltype = MPI_BYTE; + + MPIO_Datatype_get_size(eltype, &eltypesz); + DLOOP_Assert((size / eltypesz) * eltypesz == size); + + /* it's possible that the original ub_type has a true_lb > 0. + * this would be returned in ub_blkoff. we account for this by + * incrementing the ub_ptr prior to processing. + * + * we will do the same thing for the file view below. + */ + ub_ptr += ub_blkoff; + ub_type = eltype; + ub_count = size / eltypesz; + } + + if (fv_ncontig || (fv_counttouch == 1 && fv_blkct == 1)) { + /* contiguous in file -- use very simple contig instead */ + MPI_Datatype eltype; + + MPIO_Datatype_get_el_type(fv_type, &eltype, 0); + if (eltype == MPI_DATATYPE_NULL) eltype = MPI_BYTE; + + MPIO_Datatype_get_size(eltype, &fv_size); + + fv_off += fv_blkoff; + fv_type = eltype; + /* fcount assigned below for all cases */ + } + + /* fv_count is the count necessary to encompass from start of file view + * past end of access. needed for accurate dataloop processing. + */ + fv_count = ((fv_off + size) / fv_size) + + (((fv_off + size) % fv_size) ? 1 : 0); + + /* allocate and populate DS (top-level) param structures */ + dsp = (struct MPIO_File_ds_params *) + DLOOP_Malloc(sizeof(struct MPIO_File_ds_params)); + DLOOP_Assert(dsp != NULL); + + dsp->direction = direction; + dsp->fv_disp = fv_disp; + dsp->ib_size = ib_size; + dsp->ib_file_off = (MPI_Offset) -1; + dsp->ib_file_size = 0; + dsp->ib_fv_off = (MPI_Offset) -1; + dsp->ib_fv_last = fv_off; + + dsp->ib_segp = MPIO_Segment_alloc(); + MPIO_Segment_init(NULL, /* bufp not used */ + fv_count, + fv_type, + dsp->ib_segp, + 0); + + /* allocate and populate IB (mid-level) param structures */ + ibp = (struct MPIO_File_ib_params *) + DLOOP_Malloc(sizeof(struct MPIO_File_ib_params)); + DLOOP_Assert(ibp != NULL); + + ibp->ib_ptr = ib_ptr; + ibp->direction = direction; + ibp->ib_file_off = (MPI_Offset) -1; + ibp->ib_file_size = (MPI_Offset) -1; + + ibp->ub_segp = MPIO_Segment_alloc(); + MPIO_Segment_init(ub_ptr, + ub_count, + ub_type, + ibp->ub_segp, + 0); + + /* allocate segment to drive the whole process */ + ds_segp = MPIO_Segment_alloc(); + MPIO_Segment_init(NULL, /* bufp not used */ + fv_count, + fv_type, + ds_segp, + 0); + + MPIO_Segment_manipulate(ds_segp, + fv_off, + &size, + MPIO_Segment_contig_file_ds, + NULL, NULL, NULL, NULL, 0); + + /* finish any remaining I/O */ + if (dsp->ib_file_off != 0) { + MPIO_File_datasieve(dsp, &size); + } + + /* free resources */ + DLOOP_Free(ib_ptr); + MPIO_Segment_free(dsp->ib_segp); + DLOOP_Free(dsp); + MPIO_Segment_free(ibp->ub_segp); + DLOOP_Free(ibp); + MPIO_Segment_free(ds_segp); + + return 0; +} + +/* MPIO_Segment_contig_file_ds + * + * Make progress on a data sieving I/O operation for a contiguous + * region in the file view. + */ +static int MPIO_Segment_contig_file_ds(MPI_Offset *fv_blocks_p, + MPI_Datatype fv_eltype, + MPI_Offset rel_file_off, + void *unused, + void *v_paramp) +{ + MPI_Offset fv_blocks = *fv_blocks_p, fv_elsize; + struct MPIO_File_ds_params *dsp = v_paramp; + + MPIO_Datatype_get_size(fv_eltype, &fv_elsize); + + while (fv_blocks > 0) { + /* pastlastbyte is 1 more than the last byte location */ + MPI_Offset thisfirstbyte, blocksizebytes, pastlastbyte; + MPI_Offset min_access_size, full_access_size; + MPI_Offset fv_last; + + blocksizebytes = fv_blocks * fv_elsize; + thisfirstbyte = dsp->fv_disp + rel_file_off; + pastlastbyte = dsp->fv_disp + rel_file_off + blocksizebytes; + + /* start building a new access if one isn't in progress */ + if (dsp->ib_file_off == (MPI_Offset) -1) { + dsp->ib_file_off = thisfirstbyte; + dsp->ib_file_size = 0; + dsp->ib_fv_off = dsp->ib_fv_last; + } + + /* monotonically nondecreasing file views guarantee that we don't + * move backward in the file as we move forward in the stream. + */ + DLOOP_Assert(thisfirstbyte >= dsp->ib_file_off); + + /* min_access_size = size needed to get even one element + * full_access_size = size needed to get all elements + */ + min_access_size = (thisfirstbyte - dsp->ib_file_off) + fv_elsize; + full_access_size = pastlastbyte - dsp->ib_file_off; + + if (dsp->ib_size > full_access_size) { + /* the entire region fits in the iobuf with space to spare. + * update current stream offset and file size but wait to + * perform I/O. + */ + dsp->ib_file_size = pastlastbyte - dsp->ib_file_off; + dsp->ib_fv_last = dsp->ib_fv_last + blocksizebytes; + + /* update blocks to account for progress (I/O will be covered in a + * subsequent pass). + * + * note: we don't bother to update rel_file_off because we're done + * with this region. + */ + fv_blocks = 0; + + continue; + } + + if (dsp->ib_size < min_access_size) { + /* iobuf will not fit any of this region; perform I/O + * for previous regions and then deal with this one. + */ + } + else if (dsp->ib_size <= full_access_size) { + /* this I/O can complete some or all of this region */ + MPI_Offset remaining_iobuf, partial_elements; + + remaining_iobuf = dsp->ib_size - (thisfirstbyte - dsp->ib_file_off); + partial_elements = (remaining_iobuf / fv_elsize); + + /* update stored last file view offset (will verify later) */ + dsp->ib_fv_last += partial_elements * fv_elsize; + + /* update blocks and rel_off to account for upcoming progress */ + fv_blocks -= partial_elements; + rel_file_off += partial_elements * fv_elsize; + } + + fv_last = dsp->ib_fv_last; + + /* perform read */ + MPIO_File_datasieve(dsp, &fv_last); + } + + return 0; +} + +/* MPIO_File_datasieve + * + */ +static int MPIO_File_datasieve(struct MPIO_File_ds_params *dsp, + MPI_Offset *fv_lastp) +{ + DLOOP_Assert(*fv_lastp == dsp->ib_fv_last); + + /* perform read */ + printf("READ: buf = %x, off = %d, size = %d\n", + (unsigned int) dsp->ib.ib_ptr, + (int) dsp->ib_file_off, + (int) dsp->ib_file_size); + + /* copy data between ib and ub */ + MPIO_Segment_manipulate(dsp->ib_segp, + dsp->ib_fv_off, + fv_lastp, + MPIO_Segment_contig_fv2ib, + NULL, NULL, NULL, NULL, + &dsp->ib); + + DLOOP_Assert(*fv_lastp == dsp->ib_fv_last); + + if (dsp->direction == DLOOP_M2M_FROM_USERBUF) { + /* perform write */ + printf("WRITE: buf = %x, off = %d, size = %d\n", + (unsigned int) dsp->ib.ib_ptr, + (int) dsp->ib_file_off, + (int) dsp->ib_file_size); + } + + /* mark iobuf values as unused */ + dsp->ib_file_off = (MPI_Offset) -1; + dsp->ib_file_size = 0; + dsp->ib_fv_off = (MPI_Offset) -1; + /* dsp->ib_fv_last remains pointing to next byte to access */ + + return 0; +} + +/* MPIO_Segment_contig_fv2ib + * + * Map contiguous region in file view to a region in the intermediate + * buffer, then use m2m functions (pack/unpack) to transfer into + * user buffer. + * + * Parameters: + * fv_blocks_p - number of eltypes in contiguous fv region + * fv_eltype - element type for region + * rel_file_off - offset into actual file, relative to fv_disp (displacement) + * v_paramp - pointer to a MPIO_File_ib_params structure + * + * Variables: + * ib_off - offset into intermed. buffer for start of "good" data + */ +static int MPIO_Segment_contig_fv2ib(DLOOP_Offset *fv_blocks_p, + DLOOP_Type fv_eltype, + DLOOP_Offset rel_file_off, + void *unused, + void *v_paramp) +{ + MPI_Offset fv_elsize, file_off, ub_first, ub_last; + MPI_Aint ib_off; + struct MPIO_File_ib_params *ibp = v_paramp; + struct MPIO_m2m_params m2m_params; + + MPIO_Datatype_get_size(fv_eltype, &fv_elsize); + + file_off = ibp->fv_disp + rel_file_off; + ib_off = (MPI_Aint) (file_off - ibp->ib_file_off); + + /* we never attempt to process the filetype before the IO buffer or + * beyond the IO buffer. + */ + DLOOP_Assert(ib_off >= 0); + DLOOP_Assert((ib_off + (*fv_blocks_p * fv_elsize)) <= ibp->ib_file_size); + + /* fill in parameters for m2m functions */ + m2m_params.direction = ibp->direction; + m2m_params.streambuf = ibp->ib_ptr + ib_off; + m2m_params.userbuf = ibp->ub_segp->ptr; + + /* we always move through the memory type in order, so the last stored + * stream location (stream_off) is where we want to start next. + */ + ub_first = ibp->ub_segp->stream_off; + ub_last = *fv_blocks_p * fv_elsize; + + /* move to/from user buffer (using m2m fns in segment_ops.c) */ + MPIO_Segment_manipulate(ibp->ub_segp, ub_first, &ub_last, +#if 0 + MPIO_Segment_contig_m2m, + MPIO_Segment_vector_m2m, + MPIO_Segment_blkidx_m2m, + MPIO_Segment_index_m2m, +#else + MPIO_Segment_contig_m2m_fake, + NULL, NULL, NULL, +#endif + NULL, + &m2m_params); + + /* data should always be successfully processed by m2m code */ + DLOOP_Assert(ub_last == *fv_blocks_p * fv_elsize); + + return 0; +} + +static int MPIO_Segment_contig_m2m_fake(DLOOP_Offset *blocks_p, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + void *unused, + void *v_paramp) +{ + DLOOP_Offset el_size; + DLOOP_Offset size; + struct PREPEND_PREFIX(m2m_params) *paramp = v_paramp; + + DLOOP_Handle_get_size_macro(el_type, el_size); + size = *blocks_p * el_size; + + printf("\t[%s: ub_start=%x (%x+%x), ib_start=%x, sz=%d (%d*%d)]\n", + (paramp->direction == DLOOP_M2M_TO_USERBUF) ? "unpack" : "pack", + (unsigned int) (paramp->userbuf + rel_off), + (unsigned) paramp->userbuf, (unsigned) rel_off, + (unsigned) paramp->streambuf, + (int) size, + (int) *blocks_p, (int) el_size); + +#if 0 + if (paramp->direction == DLOOP_M2M_TO_USERBUF) { + memcpy((char *) (paramp->userbuf + rel_off), paramp->streambuf, size); + } + else { + memcpy(paramp->streambuf, (char *) (paramp->userbuf + rel_off), size); + } +#endif + paramp->streambuf += size; + return 0; +} + +/* + * Local variables: + * c-indent-tabs-mode: nil + * End: + */ diff --git a/ompi/mca/io/romio/romio/common/dataloop/segment.c b/ompi/mca/io/romio/romio/common/dataloop/segment.c new file mode 100644 index 0000000000..25c6635228 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/segment.c @@ -0,0 +1,988 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#include +#include + +#include "./dataloop.h" + +#undef DLOOP_DEBUG_MANIPULATE + +#ifndef PREPEND_PREFIX +#error "You must explicitly include a header that sets the PREPEND_PREFIX and includes dataloop_parts.h" +#endif + +/* Notes on functions: + * + * There are a few different sets of functions here: + * - DLOOP_Segment_manipulate() - uses a "piece" function to perform operations + * using segments (piece functions defined elsewhere) + * - PREPEND_PREFIX functions - these define the externally visible interface + * to segment functionality + */ + +static inline DLOOP_Count DLOOP_Stackelm_blocksize(struct DLOOP_Dataloop_stackelm *elmp); +static inline DLOOP_Offset DLOOP_Stackelm_offset(struct DLOOP_Dataloop_stackelm *elmp); +static inline void DLOOP_Stackelm_load(struct DLOOP_Dataloop_stackelm *elmp, + struct DLOOP_Dataloop *dlp, + int branch_flag); +/* Segment_init + * + * buf - datatype buffer location + * count - number of instances of the datatype in the buffer + * handle - handle for datatype (could be derived or not) + * segp - pointer to previously allocated segment structure + * flag - flag indicating which optimizations are valid + * should be one of DLOOP_DATALOOP_HOMOGENEOUS, _HETEROGENEOUS, + * of _ALL_BYTES. + * + * Notes: + * - Assumes that the segment has been allocated. + * - Older MPICH2 code may pass "0" to indicate HETEROGENEOUS or "1" to + * indicate HETEROGENEOUS. + * + */ +int PREPEND_PREFIX(Segment_init)(const DLOOP_Buffer buf, + DLOOP_Count count, + DLOOP_Handle handle, + struct DLOOP_Segment *segp, + int flag) +{ + DLOOP_Offset elmsize = 0; + int i, depth = 0; + int branch_detected = 0; + + struct DLOOP_Dataloop_stackelm *elmp; + struct DLOOP_Dataloop *dlp = 0, *sblp = &segp->builtin_loop; + + DLOOP_Assert(flag == DLOOP_DATALOOP_HETEROGENEOUS || + flag == DLOOP_DATALOOP_HOMOGENEOUS || + flag == DLOOP_DATALOOP_ALL_BYTES); + +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("DLOOP_Segment_init: count = %d, buf = %x\n", + count, + buf); +#endif + + if (!DLOOP_Handle_hasloop_macro(handle)) { + /* simplest case; datatype has no loop (basic) */ + + DLOOP_Handle_get_size_macro(handle, elmsize); + + sblp->kind = DLOOP_KIND_CONTIG | DLOOP_FINAL_MASK; + sblp->loop_params.c_t.count = count; + sblp->loop_params.c_t.dataloop = 0; + sblp->el_size = elmsize; + DLOOP_Handle_get_basic_type_macro(handle, sblp->el_type); + DLOOP_Handle_get_extent_macro(handle, sblp->el_extent); + + dlp = sblp; + depth = 1; + } + else if (count == 0) { + /* only use the builtin */ + sblp->kind = DLOOP_KIND_CONTIG | DLOOP_FINAL_MASK; + sblp->loop_params.c_t.count = 0; + sblp->loop_params.c_t.dataloop = 0; + sblp->el_size = 0; + sblp->el_extent = 0; + + dlp = sblp; + depth = 1; + } + else if (count == 1) { + /* don't use the builtin */ + DLOOP_Handle_get_loopptr_macro(handle, dlp, flag); + DLOOP_Handle_get_loopdepth_macro(handle, depth, flag); + } + else { + /* default: need to use builtin to handle contig; must check + * loop depth first + */ + DLOOP_Dataloop *oldloop; /* loop from original type, before new count */ + DLOOP_Offset type_size, type_extent; + DLOOP_Type el_type; + + DLOOP_Handle_get_loopdepth_macro(handle, depth, flag); + if (depth >= DLOOP_MAX_DATATYPE_DEPTH) return -1; + + DLOOP_Handle_get_loopptr_macro(handle, oldloop, flag); + DLOOP_Assert(oldloop != NULL); + DLOOP_Handle_get_size_macro(handle, type_size); + DLOOP_Handle_get_extent_macro(handle, type_extent); + DLOOP_Handle_get_basic_type_macro(handle, el_type); + + if (depth == 1 && ((oldloop->kind & DLOOP_KIND_MASK) == DLOOP_KIND_CONTIG)) + { + if (type_size == type_extent) + { + /* use a contig */ + sblp->kind = DLOOP_KIND_CONTIG | DLOOP_FINAL_MASK; + sblp->loop_params.c_t.count = count * oldloop->loop_params.c_t.count; + sblp->loop_params.c_t.dataloop = NULL; + sblp->el_size = oldloop->el_size; + sblp->el_extent = oldloop->el_extent; + sblp->el_type = oldloop->el_type; + } + else + { + /* use a vector, with extent of original type becoming the stride */ + sblp->kind = DLOOP_KIND_VECTOR | DLOOP_FINAL_MASK; + sblp->loop_params.v_t.count = count; + sblp->loop_params.v_t.blocksize = oldloop->loop_params.c_t.count; + sblp->loop_params.v_t.stride = type_extent; + sblp->loop_params.v_t.dataloop = NULL; + sblp->el_size = oldloop->el_size; + sblp->el_extent = oldloop->el_extent; + sblp->el_type = oldloop->el_type; + } + } + else + { + /* general case */ + sblp->kind = DLOOP_KIND_CONTIG; + sblp->loop_params.c_t.count = count; + sblp->loop_params.c_t.dataloop = oldloop; + sblp->el_size = type_size; + sblp->el_extent = type_extent; + sblp->el_type = el_type; + + depth++; /* we're adding to the depth with the builtin */ + } + + dlp = sblp; + } + + /* initialize the rest of the segment values */ + segp->handle = handle; + segp->ptr = (DLOOP_Buffer) buf; + segp->stream_off = 0; + segp->cur_sp = 0; + segp->valid_sp = 0; + + /* initialize the first stackelm in its entirety */ + elmp = &(segp->stackelm[0]); + DLOOP_Stackelm_load(elmp, dlp, 0); + branch_detected = elmp->may_require_reloading; + + /* Fill in parameters not set by DLOOP_Stackelm_load */ + elmp->orig_offset = 0; + elmp->curblock = elmp->orig_block; + /* DLOOP_Stackelm_offset assumes correct orig_count, curcount, loop_p */ + elmp->curoffset = /* elmp->orig_offset + */ DLOOP_Stackelm_offset(elmp); + + i = 1; + while(!(dlp->kind & DLOOP_FINAL_MASK)) + { + /* get pointer to next dataloop */ + switch (dlp->kind & DLOOP_KIND_MASK) + { + case DLOOP_KIND_CONTIG: + case DLOOP_KIND_VECTOR: + case DLOOP_KIND_BLOCKINDEXED: + case DLOOP_KIND_INDEXED: + dlp = dlp->loop_params.cm_t.dataloop; + break; + case DLOOP_KIND_STRUCT: + dlp = dlp->loop_params.s_t.dataloop_array[0]; + break; + default: + /* --BEGIN ERROR HANDLING-- */ + DLOOP_Assert(0); + break; + /* --END ERROR HANDLING-- */ + } + + /* loop_p, orig_count, orig_block, and curcount are all filled by us now. + * the rest are filled in at processing time. + */ + elmp = &(segp->stackelm[i]); + + DLOOP_Stackelm_load(elmp, dlp, branch_detected); + branch_detected = elmp->may_require_reloading; + i++; + + } + + segp->valid_sp = depth-1; + + return 0; +} + +/* Segment_alloc + * + */ +struct DLOOP_Segment * PREPEND_PREFIX(Segment_alloc)(void) +{ + return (struct DLOOP_Segment *) DLOOP_Malloc(sizeof(struct DLOOP_Segment)); +} + +/* Segment_free + * + * Input Parameters: + * segp - pointer to segment + */ +void PREPEND_PREFIX(Segment_free)(struct DLOOP_Segment *segp) +{ + DLOOP_Free(segp); + return; +} + +/* DLOOP_Segment_manipulate - do something to a segment + * + * If you think of all the data to be manipulated (packed, unpacked, whatever), + * as a stream of bytes, it's easier to understand how first and last fit in. + * + * This function does all the work, calling the piecefn passed in when it + * encounters a datatype element which falls into the range of first..(last-1). + * + * piecefn can be NULL, in which case this function doesn't do anything when it + * hits a region. This is used internally for repositioning within this stream. + * + * last is a byte offset to the byte just past the last byte in the stream + * to operate on. this makes the calculations all over MUCH cleaner. + * + * stream_off, stream_el_size, first, and last are all working in terms of the + * types and sizes for the stream, which might be different from the local sizes + * (in the heterogeneous case). + * + * This is a horribly long function. Too bad; it's complicated :)! -- Rob + * + * NOTE: THIS IMPLEMENTATION CANNOT HANDLE STRUCT DATALOOPS. + */ +#define DLOOP_SEGMENT_SAVE_LOCAL_VALUES \ +{ \ + segp->cur_sp = cur_sp; \ + segp->valid_sp = valid_sp; \ + segp->stream_off = stream_off; \ + *lastp = stream_off; \ +} + +#define DLOOP_SEGMENT_LOAD_LOCAL_VALUES \ +{ \ + last = *lastp; \ + cur_sp = segp->cur_sp; \ + valid_sp = segp->valid_sp; \ + stream_off = segp->stream_off; \ + cur_elmp = &(segp->stackelm[cur_sp]); \ +} + +#define DLOOP_SEGMENT_RESET_VALUES \ +{ \ + segp->stream_off = 0; \ + segp->cur_sp = 0; \ + cur_elmp = &(segp->stackelm[0]); \ + cur_elmp->curcount = cur_elmp->orig_count; \ + cur_elmp->orig_block = DLOOP_Stackelm_blocksize(cur_elmp); \ + cur_elmp->curblock = cur_elmp->orig_block; \ + cur_elmp->curoffset = cur_elmp->orig_offset + \ + DLOOP_Stackelm_offset(cur_elmp); \ +} + +#define DLOOP_SEGMENT_POP_AND_MAYBE_EXIT \ +{ \ + cur_sp--; \ + if (cur_sp >= 0) cur_elmp = &segp->stackelm[cur_sp]; \ + else { \ + DLOOP_SEGMENT_SAVE_LOCAL_VALUES; \ + return; \ + } \ +} + +#define DLOOP_SEGMENT_PUSH \ +{ \ + cur_sp++; \ + cur_elmp = &segp->stackelm[cur_sp]; \ +} + +#define DLOOP_STACKELM_BLOCKINDEXED_OFFSET(elmp_, curcount_) \ +(elmp_)->loop_p->loop_params.bi_t.offset_array[(curcount_)] + +#define DLOOP_STACKELM_INDEXED_OFFSET(elmp_, curcount_) \ +(elmp_)->loop_p->loop_params.i_t.offset_array[(curcount_)] + +#define DLOOP_STACKELM_INDEXED_BLOCKSIZE(elmp_, curcount_) \ +(elmp_)->loop_p->loop_params.i_t.blocksize_array[(curcount_)] + +#define DLOOP_STACKELM_STRUCT_OFFSET(elmp_, curcount_) \ +(elmp_)->loop_p->loop_params.s_t.offset_array[(curcount_)] + +#define DLOOP_STACKELM_STRUCT_BLOCKSIZE(elmp_, curcount_) \ +(elmp_)->loop_p->loop_params.s_t.blocksize_array[(curcount_)] + +#define DLOOP_STACKELM_STRUCT_EL_EXTENT(elmp_, curcount_) \ +(elmp_)->loop_p->loop_params.s_t.el_extent_array[(curcount_)] + +#define DLOOP_STACKELM_STRUCT_DATALOOP(elmp_, curcount_) \ +(elmp_)->loop_p->loop_params.s_t.dataloop_array[(curcount_)] + +void PREPEND_PREFIX(Segment_manipulate)(struct DLOOP_Segment *segp, + DLOOP_Offset first, + DLOOP_Offset *lastp, + int (*contigfn) (DLOOP_Offset *blocks_p, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer bufp, + void *v_paramp), + int (*vectorfn) (DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count blklen, + DLOOP_Offset stride, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer bufp, + void *v_paramp), + int (*blkidxfn) (DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count blklen, + DLOOP_Offset *offsetarray, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer bufp, + void *v_paramp), + int (*indexfn) (DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count *blockarray, + DLOOP_Offset *offsetarray, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer bufp, + void *v_paramp), + DLOOP_Offset (*sizefn) (DLOOP_Type el_type), + void *pieceparams) +{ + /* these four are the "local values": cur_sp, valid_sp, last, stream_off */ + int cur_sp, valid_sp; + DLOOP_Offset last, stream_off; + + struct DLOOP_Dataloop_stackelm *cur_elmp; + enum { PF_NULL, PF_CONTIG, PF_VECTOR, PF_BLOCKINDEXED, PF_INDEXED } piecefn_type = PF_NULL; + + DLOOP_SEGMENT_LOAD_LOCAL_VALUES; + + if (first == *lastp) { + /* nothing to do */ + DLOOP_dbg_printf("dloop_segment_manipulate: warning: first == last (%d)\n", (int) first); + return; + } + + /* first we ensure that stream_off and first are in the same spot */ + if (first != stream_off) { +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("first=%d; stream_off=%ld; resetting.\n", + first, stream_off); +#endif + + if (first < stream_off) { + DLOOP_SEGMENT_RESET_VALUES; + stream_off = 0; + } + + if (first != stream_off) { + DLOOP_Offset tmp_last = first; + + /* use manipulate function with a NULL piecefn to advance + * stream offset + */ + PREPEND_PREFIX(Segment_manipulate)(segp, + stream_off, + &tmp_last, + NULL, /* contig fn */ + NULL, /* vector fn */ + NULL, /* blkidx fn */ + NULL, /* index fn */ + sizefn, + NULL); + + /* --BEGIN ERROR HANDLING-- */ + /* verify that we're in the right location */ + if (tmp_last != first) DLOOP_Assert(0); + /* --END ERROR HANDLING-- */ + } + + DLOOP_SEGMENT_LOAD_LOCAL_VALUES; + +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("done repositioning stream_off; first=%d, stream_off=%ld, last=%d\n", + first, stream_off, last); +#endif + } + + for (;;) { +#ifdef DLOOP_DEBUG_MANIPULATE +#if 0 + DLOOP_dbg_printf("looptop; cur_sp=%d, cur_elmp=%x\n", + cur_sp, (unsigned) cur_elmp); +#endif +#endif + + if (cur_elmp->loop_p->kind & DLOOP_FINAL_MASK) { + int piecefn_indicated_exit = -1; + DLOOP_Offset myblocks, local_el_size, stream_el_size; + DLOOP_Type el_type; + + /* structs are never finals (leaves) */ + DLOOP_Assert((cur_elmp->loop_p->kind & DLOOP_KIND_MASK) != + DLOOP_KIND_STRUCT); + + /* pop immediately on zero count */ + if (cur_elmp->curcount == 0) DLOOP_SEGMENT_POP_AND_MAYBE_EXIT; + + /* size on this system of the int, double, etc. that is + * the elementary type. + */ + local_el_size = cur_elmp->loop_p->el_size; + el_type = cur_elmp->loop_p->el_type; + stream_el_size = (sizefn) ? sizefn(el_type) : local_el_size; + + /* calculate number of elem. types to work on and function to use. + * default is to use the contig piecefn (if there is one). + */ + myblocks = cur_elmp->curblock; + piecefn_type = (contigfn ? PF_CONTIG : PF_NULL); + + /* check for opportunities to use other piecefns */ + switch (cur_elmp->loop_p->kind & DLOOP_KIND_MASK) { + case DLOOP_KIND_CONTIG: + break; + case DLOOP_KIND_BLOCKINDEXED: + /* only use blkidx piecefn if at start of blkidx type */ + if (blkidxfn && + cur_elmp->orig_block == cur_elmp->curblock && + cur_elmp->orig_count == cur_elmp->curcount) + { + /* TODO: RELAX CONSTRAINTS */ + myblocks = cur_elmp->curblock * cur_elmp->curcount; + piecefn_type = PF_BLOCKINDEXED; + } + break; + case DLOOP_KIND_INDEXED: + /* only use index piecefn if at start of the index type. + * count test checks that we're on first block. + * block test checks that we haven't made progress on first block. + */ + if (indexfn && + cur_elmp->orig_count == cur_elmp->curcount && + cur_elmp->curblock == DLOOP_STACKELM_INDEXED_BLOCKSIZE(cur_elmp, 0)) + { + /* TODO: RELAX CONSTRAINT ON COUNT? */ + myblocks = cur_elmp->loop_p->loop_params.i_t.total_blocks; + piecefn_type = PF_INDEXED; + } + break; + case DLOOP_KIND_VECTOR: + /* only use the vector piecefn if at the start of a + * contiguous block. + */ + if (vectorfn && cur_elmp->orig_block == cur_elmp->curblock) + { + myblocks = cur_elmp->curblock * cur_elmp->curcount; + piecefn_type = PF_VECTOR; + } + break; + default: + /* --BEGIN ERROR HANDLING-- */ + DLOOP_Assert(0); + break; + /* --END ERROR HANDLING-- */ + } + +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("\thit leaf; cur_sp=%d, elmp=%x, piece_sz=%d\n", + cur_sp, + (unsigned) cur_elmp, myblocks * local_el_size); +#endif + + /* enforce the last parameter if necessary by reducing myblocks */ + if (last != SEGMENT_IGNORE_LAST && + (stream_off + (myblocks * stream_el_size) > last)) + { + myblocks = ((last - stream_off) / stream_el_size); +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("\tpartial block count=%d (%d bytes)\n", + myblocks, + (int) myblocks * stream_el_size); +#endif + if (myblocks == 0) { + DLOOP_SEGMENT_SAVE_LOCAL_VALUES; + return; + } + } + + /* call piecefn to perform data manipulation */ + switch (piecefn_type) { + case PF_NULL: + piecefn_indicated_exit = 0; +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("\tNULL piecefn for this piece\n"); +#endif + break; + case PF_CONTIG: + DLOOP_Assert(myblocks <= cur_elmp->curblock); + piecefn_indicated_exit = + contigfn(&myblocks, + el_type, + cur_elmp->curoffset, /* relative to segp->ptr */ + segp->ptr, /* start of buffer (from segment) */ + pieceparams); + break; + case PF_VECTOR: + piecefn_indicated_exit = + vectorfn(&myblocks, + cur_elmp->curcount, + cur_elmp->orig_block, + cur_elmp->loop_p->loop_params.v_t.stride, + el_type, + cur_elmp->curoffset, + segp->ptr, + pieceparams); + break; + case PF_BLOCKINDEXED: + piecefn_indicated_exit = + blkidxfn(&myblocks, + cur_elmp->curcount, + cur_elmp->orig_block, + cur_elmp->loop_p->loop_params.bi_t.offset_array, + el_type, + cur_elmp->orig_offset, /* blkidxfn adds offset */ + segp->ptr, + pieceparams); + break; + case PF_INDEXED: + piecefn_indicated_exit = + indexfn(&myblocks, + cur_elmp->curcount, + cur_elmp->loop_p->loop_params.i_t.blocksize_array, + cur_elmp->loop_p->loop_params.i_t.offset_array, + el_type, + cur_elmp->orig_offset, /* indexfn adds offset value */ + segp->ptr, + pieceparams); + break; + } + + /* update local values based on piecefn returns (myblocks and + * piecefn_indicated_exit) + */ + DLOOP_Assert(piecefn_indicated_exit >= 0); + DLOOP_Assert(myblocks >= 0); + stream_off += myblocks * stream_el_size; + + /* myblocks of 0 or less than cur_elmp->curblock indicates + * that we should stop processing and return. + */ + if (myblocks == 0) { + DLOOP_SEGMENT_SAVE_LOCAL_VALUES; + return; + } + else if (myblocks < cur_elmp->curblock) { + cur_elmp->curoffset += myblocks * local_el_size; + cur_elmp->curblock -= myblocks; + + DLOOP_SEGMENT_SAVE_LOCAL_VALUES; + return; + } + else /* myblocks >= cur_elmp->curblock */ { + int count_index = 0; + + /* this assumes we're either *just* processing the last parts + * of the current block, or we're processing as many blocks as + * we like starting at the beginning of one. + */ + + switch (cur_elmp->loop_p->kind & DLOOP_KIND_MASK) { + case DLOOP_KIND_INDEXED: + while (myblocks > 0 && myblocks >= cur_elmp->curblock) { + myblocks -= cur_elmp->curblock; + cur_elmp->curcount--; + DLOOP_Assert(cur_elmp->curcount >= 0); + + count_index = cur_elmp->orig_count - + cur_elmp->curcount; + cur_elmp->curblock = + DLOOP_STACKELM_INDEXED_BLOCKSIZE(cur_elmp, + count_index); + } + + if (cur_elmp->curcount == 0) { + /* don't bother to fill in values; we're popping anyway */ + DLOOP_Assert(myblocks == 0); + DLOOP_SEGMENT_POP_AND_MAYBE_EXIT; + } + else { + cur_elmp->orig_block = cur_elmp->curblock; + cur_elmp->curoffset = cur_elmp->orig_offset + + DLOOP_STACKELM_INDEXED_OFFSET(cur_elmp, + count_index); + + cur_elmp->curblock -= myblocks; + cur_elmp->curoffset += myblocks * local_el_size; + } + break; + case DLOOP_KIND_VECTOR: + /* this math relies on assertions at top of code block */ + cur_elmp->curcount -= myblocks / cur_elmp->curblock; + if (cur_elmp->curcount == 0) { + DLOOP_Assert(myblocks % cur_elmp->curblock == 0); + DLOOP_SEGMENT_POP_AND_MAYBE_EXIT; + } + else { + /* this math relies on assertions at top of code + * block + */ + cur_elmp->curblock = cur_elmp->orig_block - + (myblocks % cur_elmp->curblock); + /* new offset = original offset + + * stride * whole blocks + + * leftover bytes + */ + cur_elmp->curoffset = cur_elmp->orig_offset + + ((cur_elmp->orig_count - cur_elmp->curcount) * + cur_elmp->loop_p->loop_params.v_t.stride) + + ((cur_elmp->orig_block - cur_elmp->curblock) * + local_el_size); + } + break; + case DLOOP_KIND_CONTIG: + /* contigs that reach this point have always been + * completely processed + */ + DLOOP_Assert(myblocks == cur_elmp->curblock && + cur_elmp->curcount == 1); + DLOOP_SEGMENT_POP_AND_MAYBE_EXIT; + break; + case DLOOP_KIND_BLOCKINDEXED: + while (myblocks > 0 && myblocks >= cur_elmp->curblock) + { + myblocks -= cur_elmp->curblock; + cur_elmp->curcount--; + DLOOP_Assert(cur_elmp->curcount >= 0); + + count_index = cur_elmp->orig_count - + cur_elmp->curcount; + cur_elmp->curblock = cur_elmp->orig_block; + } + if (cur_elmp->curcount == 0) { + /* popping */ + DLOOP_Assert(myblocks == 0); + DLOOP_SEGMENT_POP_AND_MAYBE_EXIT; + } + else { + /* cur_elmp->orig_block = cur_elmp->curblock; */ + cur_elmp->curoffset = cur_elmp->orig_offset + + DLOOP_STACKELM_BLOCKINDEXED_OFFSET(cur_elmp, + count_index); + cur_elmp->curblock -= myblocks; + cur_elmp->curoffset += myblocks * local_el_size; + } + break; + } + } + + if (piecefn_indicated_exit) { + /* piece function indicated that we should quit processing */ + DLOOP_SEGMENT_SAVE_LOCAL_VALUES; + return; + } + } /* end of if leaf */ + else if (cur_elmp->curblock == 0) { +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("\thit end of block; elmp=%x [%d]\n", + (unsigned) cur_elmp, cur_sp); +#endif + cur_elmp->curcount--; + + /* new block. for indexed and struct reset orig_block. + * reset curblock for all types + */ + switch (cur_elmp->loop_p->kind & DLOOP_KIND_MASK) { + case DLOOP_KIND_CONTIG: + case DLOOP_KIND_VECTOR: + case DLOOP_KIND_BLOCKINDEXED: + break; + case DLOOP_KIND_INDEXED: + cur_elmp->orig_block = + DLOOP_STACKELM_INDEXED_BLOCKSIZE(cur_elmp, cur_elmp->curcount ? cur_elmp->orig_count - cur_elmp->curcount : 0); + break; + case DLOOP_KIND_STRUCT: + cur_elmp->orig_block = + DLOOP_STACKELM_STRUCT_BLOCKSIZE(cur_elmp, cur_elmp->curcount ? cur_elmp->orig_count - cur_elmp->curcount : 0); + break; + default: + /* --BEGIN ERROR HANDLING-- */ + DLOOP_Assert(0); + break; + /* --END ERROR HANDLING-- */ + } + cur_elmp->curblock = cur_elmp->orig_block; + + if (cur_elmp->curcount == 0) { +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("\talso hit end of count; elmp=%x [%d]\n", + (unsigned) cur_elmp, cur_sp); +#endif + DLOOP_SEGMENT_POP_AND_MAYBE_EXIT; + } + } + else /* push the stackelm */ { + DLOOP_Dataloop_stackelm *next_elmp; + int count_index, block_index; + + count_index = cur_elmp->orig_count - cur_elmp->curcount; + block_index = cur_elmp->orig_block - cur_elmp->curblock; + + /* reload the next stackelm if necessary */ + next_elmp = &(segp->stackelm[cur_sp + 1]); + if (cur_elmp->may_require_reloading) { + DLOOP_Dataloop *load_dlp = NULL; + switch (cur_elmp->loop_p->kind & DLOOP_KIND_MASK) { + case DLOOP_KIND_CONTIG: + case DLOOP_KIND_VECTOR: + case DLOOP_KIND_BLOCKINDEXED: + case DLOOP_KIND_INDEXED: + load_dlp = cur_elmp->loop_p->loop_params.cm_t.dataloop; + break; + case DLOOP_KIND_STRUCT: + load_dlp = DLOOP_STACKELM_STRUCT_DATALOOP(cur_elmp, + count_index); + break; + default: + /* --BEGIN ERROR HANDLING-- */ + DLOOP_Assert(0); + break; + /* --END ERROR HANDLING-- */ + } + +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("\tloading dlp=%x, elmp=%x [%d]\n", + (unsigned) load_dlp, + (unsigned) next_elmp, + cur_sp+1); +#endif + + DLOOP_Stackelm_load(next_elmp, + load_dlp, + 1); + } + +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("\tpushing type, elmp=%x [%d], count=%d, block=%d\n", + (unsigned) cur_elmp, cur_sp, count_index, + block_index); +#endif + /* set orig_offset and all cur values for new stackelm. + * this is done in two steps: first set orig_offset based on + * current stackelm, then set cur values based on new stackelm. + */ + switch (cur_elmp->loop_p->kind & DLOOP_KIND_MASK) { + case DLOOP_KIND_CONTIG: + next_elmp->orig_offset = cur_elmp->curoffset + + block_index * cur_elmp->loop_p->el_extent; + break; + case DLOOP_KIND_VECTOR: + /* note: stride is in bytes */ + next_elmp->orig_offset = cur_elmp->orig_offset + + count_index * cur_elmp->loop_p->loop_params.v_t.stride + + block_index * cur_elmp->loop_p->el_extent; + break; + case DLOOP_KIND_BLOCKINDEXED: + next_elmp->orig_offset = cur_elmp->orig_offset + + block_index * cur_elmp->loop_p->el_extent + + DLOOP_STACKELM_BLOCKINDEXED_OFFSET(cur_elmp, + count_index); + break; + case DLOOP_KIND_INDEXED: + next_elmp->orig_offset = cur_elmp->orig_offset + + block_index * cur_elmp->loop_p->el_extent + + DLOOP_STACKELM_INDEXED_OFFSET(cur_elmp, count_index); + break; + case DLOOP_KIND_STRUCT: + next_elmp->orig_offset = cur_elmp->orig_offset + + block_index * DLOOP_STACKELM_STRUCT_EL_EXTENT(cur_elmp, count_index) + + DLOOP_STACKELM_STRUCT_OFFSET(cur_elmp, count_index); + break; + default: + /* --BEGIN ERROR HANDLING-- */ + DLOOP_Assert(0); + break; + /* --END ERROR HANDLING-- */ + } + +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("\tstep 1: next orig_offset = %d (0x%x)\n", + next_elmp->orig_offset, + next_elmp->orig_offset); +#endif + + switch (next_elmp->loop_p->kind & DLOOP_KIND_MASK) { + case DLOOP_KIND_CONTIG: + case DLOOP_KIND_VECTOR: + next_elmp->curcount = next_elmp->orig_count; + next_elmp->curblock = next_elmp->orig_block; + next_elmp->curoffset = next_elmp->orig_offset; + break; + case DLOOP_KIND_BLOCKINDEXED: + next_elmp->curcount = next_elmp->orig_count; + next_elmp->curblock = next_elmp->orig_block; + next_elmp->curoffset = next_elmp->orig_offset + + DLOOP_STACKELM_BLOCKINDEXED_OFFSET(next_elmp, 0); + break; + case DLOOP_KIND_INDEXED: + next_elmp->curcount = next_elmp->orig_count; + next_elmp->curblock = + DLOOP_STACKELM_INDEXED_BLOCKSIZE(next_elmp, 0); + next_elmp->curoffset = next_elmp->orig_offset + + DLOOP_STACKELM_INDEXED_OFFSET(next_elmp, 0); + break; + case DLOOP_KIND_STRUCT: + next_elmp->curcount = next_elmp->orig_count; + next_elmp->curblock = + DLOOP_STACKELM_STRUCT_BLOCKSIZE(next_elmp, 0); + next_elmp->curoffset = next_elmp->orig_offset + + DLOOP_STACKELM_STRUCT_OFFSET(next_elmp, 0); + break; + default: + /* --BEGIN ERROR HANDLING-- */ + DLOOP_Assert(0); + break; + /* --END ERROR HANDLING-- */ + } + +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("\tstep 2: next curoffset = %d (0x%x)\n", + next_elmp->curoffset, + next_elmp->curoffset); +#endif + + cur_elmp->curblock--; + DLOOP_SEGMENT_PUSH; + } /* end of else push the stackelm */ + } /* end of for (;;) */ + +#ifdef DLOOP_DEBUG_MANIPULATE + DLOOP_dbg_printf("hit end of datatype\n"); +#endif + + DLOOP_SEGMENT_SAVE_LOCAL_VALUES; + return; +} + +/* DLOOP_Stackelm_blocksize - returns block size for stackelm based on current + * count in stackelm. + * + * NOTE: loop_p, orig_count, and curcount members of stackelm MUST be correct + * before this is called! + * + */ +static inline DLOOP_Count DLOOP_Stackelm_blocksize(struct DLOOP_Dataloop_stackelm *elmp) +{ + struct DLOOP_Dataloop *dlp = elmp->loop_p; + + switch(dlp->kind & DLOOP_KIND_MASK) { + case DLOOP_KIND_CONTIG: + /* NOTE: we're dropping the count into the + * blksize field for contigs, as described + * in the init call. + */ + return dlp->loop_params.c_t.count; + break; + case DLOOP_KIND_VECTOR: + return dlp->loop_params.v_t.blocksize; + break; + case DLOOP_KIND_BLOCKINDEXED: + return dlp->loop_params.bi_t.blocksize; + break; + case DLOOP_KIND_INDEXED: + return dlp->loop_params.i_t.blocksize_array[elmp->orig_count - elmp->curcount]; + break; + case DLOOP_KIND_STRUCT: + return dlp->loop_params.s_t.blocksize_array[elmp->orig_count - elmp->curcount]; + break; + default: + /* --BEGIN ERROR HANDLING-- */ + DLOOP_Assert(0); + break; + /* --END ERROR HANDLING-- */ + } + return -1; +} + +/* DLOOP_Stackelm_offset - returns starting offset (displacement) for stackelm + * based on current count in stackelm. + * + * NOTE: loop_p, orig_count, and curcount members of stackelm MUST be correct + * before this is called! + * + * also, this really is only good at init time for vectors and contigs + * (all the time for indexed) at the moment. + * + */ +static inline DLOOP_Offset DLOOP_Stackelm_offset(struct DLOOP_Dataloop_stackelm *elmp) +{ + struct DLOOP_Dataloop *dlp = elmp->loop_p; + + switch(dlp->kind & DLOOP_KIND_MASK) { + case DLOOP_KIND_VECTOR: + case DLOOP_KIND_CONTIG: + return 0; + break; + case DLOOP_KIND_BLOCKINDEXED: + return dlp->loop_params.bi_t.offset_array[elmp->orig_count - elmp->curcount]; + break; + case DLOOP_KIND_INDEXED: + return dlp->loop_params.i_t.offset_array[elmp->orig_count - elmp->curcount]; + break; + case DLOOP_KIND_STRUCT: + return dlp->loop_params.s_t.offset_array[elmp->orig_count - elmp->curcount]; + break; + default: + /* --BEGIN ERROR HANDLING-- */ + DLOOP_Assert(0); + break; + /* --END ERROR HANDLING-- */ + } + return -1; +} + +/* DLOOP_Stackelm_load + * loop_p, orig_count, orig_block, and curcount are all filled by us now. + * the rest are filled in at processing time. + */ +static inline void DLOOP_Stackelm_load(struct DLOOP_Dataloop_stackelm *elmp, + struct DLOOP_Dataloop *dlp, + int branch_flag) +{ + elmp->loop_p = dlp; + + if ((dlp->kind & DLOOP_KIND_MASK) == DLOOP_KIND_CONTIG) { + elmp->orig_count = 1; /* put in blocksize instead */ + } + else { + elmp->orig_count = dlp->loop_params.count; + } + + if (branch_flag || (dlp->kind & DLOOP_KIND_MASK) == DLOOP_KIND_STRUCT) + { + elmp->may_require_reloading = 1; + } + else { + elmp->may_require_reloading = 0; + } + + /* required by DLOOP_Stackelm_blocksize */ + elmp->curcount = elmp->orig_count; + + elmp->orig_block = DLOOP_Stackelm_blocksize(elmp); + /* TODO: GO AHEAD AND FILL IN CURBLOCK? */ +} + +/* + * Local variables: + * c-indent-tabs-mode: nil + * End: + */ diff --git a/ompi/mca/io/romio/romio/common/dataloop/segment_ops.c b/ompi/mca/io/romio/romio/common/dataloop/segment_ops.c new file mode 100644 index 0000000000..d90c4f5adf --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/segment_ops.c @@ -0,0 +1,816 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ + +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ + +#include +#include +#include +#include + +#include "./dataloop.h" + + + +#ifdef HAVE_ANY_INT64_T_ALIGNEMENT +#define MPIR_ALIGN8_TEST(p1,p2) +#else +#define MPIR_ALIGN8_TEST(p1,p2) && ((((MPI_Aint)p1 | (MPI_Aint)p2) & 0x7) == 0) +#endif + +#ifdef HAVE_ANY_INT32_T_ALIGNEMENT +#define MPIR_ALIGN4_TEST(p1,p2) +#else +#define MPIR_ALIGN4_TEST(p1,p2) && ((((MPI_Aint)p1 | (MPI_Aint)p2) & 0x3) == 0) +#endif + +#define MPIDI_COPY_FROM_VEC(src,dest,stride,type,nelms,count) \ +{ \ + type * l_src = (type *)src, * l_dest = (type *)dest; \ + int i, j; \ + const int l_stride = stride; \ + if (nelms == 1) { \ + for (i=count;i!=0;i--) { \ + *l_dest++ = *l_src; \ + l_src = (type *) ((char *) l_src + l_stride); \ + } \ + } \ + else { \ + for (i=count; i!=0; i--) { \ + for (j=0; ju.unpack.unpack_buffer, + el_size, + (int) *blocks_p); +#endif + + if (paramp->direction == DLOOP_M2M_TO_USERBUF) { + memcpy((char *) (paramp->userbuf + rel_off), paramp->streambuf, size); + } + else { + memcpy(paramp->streambuf, (char *) (paramp->userbuf + rel_off), size); + } + paramp->streambuf += size; + return 0; +} + +/* Segment_vector_m2m + * + * Note: this combines both packing and unpacking functionality. + * + * Note: this is only called when the starting position is at the beginning + * of a whole block in a vector type. + */ +int PREPEND_PREFIX(Segment_vector_m2m)(DLOOP_Offset *blocks_p, + DLOOP_Count unused, + DLOOP_Count blksz, + DLOOP_Offset stride, + DLOOP_Type el_type, + DLOOP_Offset rel_off, /* into buffer */ + DLOOP_Buffer unused2, + void *v_paramp) +{ + DLOOP_Count i, blocks_left, whole_count; + DLOOP_Offset el_size; + struct PREPEND_PREFIX(m2m_params) *paramp = v_paramp; + char *cbufp; + + cbufp = paramp->userbuf + rel_off; + DLOOP_Handle_get_size_macro(el_type, el_size); + + whole_count = (blksz > 0) ? (*blocks_p / blksz) : 0; + blocks_left = (blksz > 0) ? (*blocks_p % blksz) : 0; + + if (paramp->direction == DLOOP_M2M_TO_USERBUF) { + if (el_size == 8 + MPIR_ALIGN8_TEST(paramp->streambuf,cbufp)) + { + MPIDI_COPY_TO_VEC(paramp->streambuf, cbufp, stride, + int64_t, blksz, whole_count); + MPIDI_COPY_TO_VEC(paramp->streambuf, cbufp, 0, + int64_t, blocks_left, 1); + } + else if (el_size == 4 + MPIR_ALIGN4_TEST(paramp->streambuf,cbufp)) + { + MPIDI_COPY_TO_VEC((paramp->streambuf), cbufp, stride, + int32_t, blksz, whole_count); + MPIDI_COPY_TO_VEC(paramp->streambuf, cbufp, 0, + int32_t, blocks_left, 1); + } + else if (el_size == 2) { + MPIDI_COPY_TO_VEC(paramp->streambuf, cbufp, stride, + int16_t, blksz, whole_count); + MPIDI_COPY_TO_VEC(paramp->streambuf, cbufp, 0, + int16_t, blocks_left, 1); + } + else { + for (i=0; i < whole_count; i++) { + memcpy(cbufp, paramp->streambuf, blksz * el_size); + paramp->streambuf += blksz * el_size; + cbufp += stride; + } + if (blocks_left) { + memcpy(cbufp, paramp->streambuf, blocks_left * el_size); + paramp->streambuf += blocks_left * el_size; + } + } + } + else /* M2M_FROM_USERBUF */ { + if (el_size == 8 + MPIR_ALIGN8_TEST(cbufp,paramp->streambuf)) + { + MPIDI_COPY_FROM_VEC(cbufp, paramp->streambuf, stride, + int64_t, blksz, whole_count); + MPIDI_COPY_FROM_VEC(cbufp, paramp->streambuf, 0, + int64_t, blocks_left, 1); + } + else if (el_size == 4 + MPIR_ALIGN4_TEST(cbufp,paramp->streambuf)) + { + MPIDI_COPY_FROM_VEC(cbufp, paramp->streambuf, stride, + int32_t, blksz, whole_count); + MPIDI_COPY_FROM_VEC(cbufp, paramp->streambuf, 0, + int32_t, blocks_left, 1); + } + else if (el_size == 2) { + MPIDI_COPY_FROM_VEC(cbufp, paramp->streambuf, stride, + int16_t, blksz, whole_count); + MPIDI_COPY_FROM_VEC(cbufp, paramp->streambuf, 0, + int16_t, blocks_left, 1); + } + else { + for (i=0; i < whole_count; i++) { + memcpy(paramp->streambuf, cbufp, blksz * el_size); + paramp->streambuf += blksz * el_size; + cbufp += stride; + } + if (blocks_left) { + memcpy(paramp->streambuf, cbufp, blocks_left * el_size); + paramp->streambuf += blocks_left * el_size; + } + } + } + + return 0; +} + +/* MPID_Segment_blkidx_m2m + */ +int PREPEND_PREFIX(Segment_blkidx_m2m)(DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count blocklen, + DLOOP_Offset *offsetarray, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer unused, + void *v_paramp) +{ + DLOOP_Count curblock = 0; + DLOOP_Offset el_size; + DLOOP_Offset blocks_left = *blocks_p; + char *cbufp; + struct PREPEND_PREFIX(m2m_params) *paramp = v_paramp; + + DLOOP_Handle_get_size_macro(el_type, el_size); + + while (blocks_left) { + char *src, *dest; + + DLOOP_Assert(curblock < count); + + cbufp = paramp->userbuf + rel_off + offsetarray[curblock]; + + if (blocklen > blocks_left) blocklen = blocks_left; + + if (paramp->direction == DLOOP_M2M_TO_USERBUF) { + src = paramp->streambuf; + dest = cbufp; + } + else { + src = cbufp; + dest = paramp->streambuf; + } + + /* note: macro modifies dest buffer ptr, so we must reset */ + if (el_size == 8 + MPIR_ALIGN8_TEST(src, dest)) + { + MPIDI_COPY_FROM_VEC(src, dest, 0, int64_t, blocklen, 1); + } + else if (el_size == 4 + MPIR_ALIGN4_TEST(src,dest)) + { + MPIDI_COPY_FROM_VEC(src, dest, 0, int32_t, blocklen, 1); + } + else if (el_size == 2) { + MPIDI_COPY_FROM_VEC(src, dest, 0, int16_t, blocklen, 1); + } + else { + memcpy(dest, src, blocklen * el_size); + } + + paramp->streambuf += blocklen * el_size; + blocks_left -= blocklen; + curblock++; + } + + return 0; +} + +/* MPID_Segment_index_m2m + */ +int PREPEND_PREFIX(Segment_index_m2m)(DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count *blockarray, + DLOOP_Offset *offsetarray, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer unused, + void *v_paramp) +{ + int curblock = 0; + DLOOP_Offset el_size; + DLOOP_Offset cur_block_sz, blocks_left = *blocks_p; + char *cbufp; + struct PREPEND_PREFIX(m2m_params) *paramp = v_paramp; + + DLOOP_Handle_get_size_macro(el_type, el_size); + + while (blocks_left) { + char *src, *dest; + + DLOOP_Assert(curblock < count); + cur_block_sz = blockarray[curblock]; + + cbufp = paramp->userbuf + rel_off + offsetarray[curblock]; + + if (cur_block_sz > blocks_left) cur_block_sz = blocks_left; + + if (paramp->direction == DLOOP_M2M_TO_USERBUF) { + src = paramp->streambuf; + dest = cbufp; + } + else { + src = cbufp; + dest = paramp->streambuf; + } + + /* note: macro modifies dest buffer ptr, so we must reset */ + if (el_size == 8 + MPIR_ALIGN8_TEST(src, dest)) + { + MPIDI_COPY_FROM_VEC(src, dest, 0, int64_t, cur_block_sz, 1); + } + else if (el_size == 4 + MPIR_ALIGN4_TEST(src,dest)) + { + MPIDI_COPY_FROM_VEC(src, dest, 0, int32_t, cur_block_sz, 1); + } + else if (el_size == 2) { + MPIDI_COPY_FROM_VEC(src, dest, 0, int16_t, cur_block_sz, 1); + } + else { + memcpy(dest, src, cur_block_sz * el_size); + } + + paramp->streambuf += cur_block_sz * el_size; + blocks_left -= cur_block_sz; + curblock++; + } + + return 0; +} + +void PREPEND_PREFIX(Segment_pack)(DLOOP_Segment *segp, + DLOOP_Offset first, + DLOOP_Offset *lastp, + void *streambuf) +{ + struct PREPEND_PREFIX(m2m_params) params; + + /* experimenting with discarding buf value in the segment, keeping in + * per-use structure instead. would require moving the parameters around a + * bit. same applies to Segment_unpack below. + */ + params.userbuf = segp->ptr; + params.streambuf = streambuf; + params.direction = DLOOP_M2M_FROM_USERBUF; + + PREPEND_PREFIX(Segment_manipulate)(segp, first, lastp, + PREPEND_PREFIX(Segment_contig_m2m), + PREPEND_PREFIX(Segment_vector_m2m), + PREPEND_PREFIX(Segment_blkidx_m2m), + PREPEND_PREFIX(Segment_index_m2m), + NULL, /* size fn */ + ¶ms); + return; +} + +void PREPEND_PREFIX(Segment_unpack)(DLOOP_Segment *segp, + DLOOP_Offset first, + DLOOP_Offset *lastp, + void *streambuf) +{ + struct PREPEND_PREFIX(m2m_params) params; + + params.userbuf = segp->ptr; + params.streambuf = streambuf; + params.direction = DLOOP_M2M_TO_USERBUF; + + PREPEND_PREFIX(Segment_manipulate)(segp, first, lastp, + PREPEND_PREFIX(Segment_contig_m2m), + PREPEND_PREFIX(Segment_vector_m2m), + PREPEND_PREFIX(Segment_blkidx_m2m), + PREPEND_PREFIX(Segment_index_m2m), + NULL, /* size fn */ + ¶ms); + return; +} + +struct PREPEND_PREFIX(contig_blocks_params) { + DLOOP_Count count; + DLOOP_Offset last_loc; +}; + +/* MPID_Segment_contig_count_block + * + * Note: because bufp is just an offset, we can ignore it in our + * calculations of # of contig regions. + */ +static int DLOOP_Segment_contig_count_block(DLOOP_Offset *blocks_p, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer unused, + void *v_paramp) +{ + DLOOP_Offset size, el_size; + struct PREPEND_PREFIX(contig_blocks_params) *paramp = v_paramp; + + DLOOP_Assert(*blocks_p > 0); + + DLOOP_Handle_get_size_macro(el_type, el_size); + size = *blocks_p * el_size; + +#ifdef MPID_SP_VERBOSE + MPIU_dbg_printf("contig count block: count = %d, buf+off = %d, lastloc = %d\n", + (int) paramp->count, + (int) ((char *) bufp + rel_off), + (int) paramp->last_loc); +#endif + + if (paramp->count > 0 && rel_off == paramp->last_loc) + { + /* this region is adjacent to the last */ + paramp->last_loc += size; + } + else { + /* new region */ + paramp->last_loc = rel_off + size; + paramp->count++; + } + return 0; +} + +/* DLOOP_Segment_vector_count_block + * + * Input Parameters: + * blocks_p - [inout] pointer to a count of blocks (total, for all noncontiguous pieces) + * count - # of noncontiguous regions + * blksz - size of each noncontiguous region + * stride - distance in bytes from start of one region to start of next + * el_type - elemental type (e.g. MPI_INT) + * ... + * + * Note: this is only called when the starting position is at the beginning + * of a whole block in a vector type. + */ +static int DLOOP_Segment_vector_count_block(DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count blksz, + DLOOP_Offset stride, + DLOOP_Type el_type, + DLOOP_Offset rel_off, /* into buffer */ + DLOOP_Buffer unused, + void *v_paramp) +{ + DLOOP_Count new_blk_count; + DLOOP_Offset size, el_size; + struct PREPEND_PREFIX(contig_blocks_params) *paramp = v_paramp; + + DLOOP_Assert(count > 0 && blksz > 0 && *blocks_p > 0); + + DLOOP_Handle_get_size_macro(el_type, el_size); + size = el_size * blksz; + new_blk_count = count; + + /* if size == stride, then blocks are adjacent to one another */ + if (size == stride) new_blk_count = 1; + + if (paramp->count > 0 && rel_off == paramp->last_loc) + { + /* first block sits at end of last block */ + new_blk_count--; + } + + paramp->last_loc = rel_off + (count-1) * stride + size; + paramp->count += new_blk_count; + return 0; +} + +/* DLOOP_Segment_blkidx_count_block + * + * Note: this is only called when the starting position is at the + * beginning of a whole block in a blockindexed type. + */ +static int DLOOP_Segment_blkidx_count_block(DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count blksz, + DLOOP_Offset *offsetarray, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer unused, + void *v_paramp) +{ + DLOOP_Count i, new_blk_count; + DLOOP_Offset size, el_size, last_loc; + struct PREPEND_PREFIX(contig_blocks_params) *paramp = v_paramp; + + DLOOP_Assert(count > 0 && blksz > 0 && *blocks_p > 0); + + DLOOP_Handle_get_size_macro(el_type, el_size); + size = el_size * blksz; + new_blk_count = count; + + if (paramp->count > 0 && rel_off == paramp->last_loc) + { + /* first block sits at end of last block */ + new_blk_count--; + } + + last_loc = rel_off + offsetarray[0] + size; + for (i=1; i < count; i++) { + if (last_loc == rel_off + offsetarray[i]) new_blk_count--; + + last_loc = rel_off + offsetarray[i] + size; + } + + paramp->last_loc = last_loc; + paramp->count += new_blk_count; + return 0; +} + +/* DLOOP_Segment_index_count_block + * + * Note: this is only called when the starting position is at the + * beginning of a whole block in an indexed type. + */ +static int DLOOP_Segment_index_count_block(DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count *blockarray, + DLOOP_Offset *offsetarray, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer unused, + void *v_paramp) +{ + DLOOP_Count new_blk_count; + DLOOP_Offset el_size, last_loc; + struct PREPEND_PREFIX(contig_blocks_params) *paramp = v_paramp; + + DLOOP_Assert(count > 0 && *blocks_p > 0); + + DLOOP_Handle_get_size_macro(el_type, el_size); + new_blk_count = count; + + if (paramp->count > 0 && rel_off == paramp->last_loc) + { + /* first block sits at end of last block */ + new_blk_count--; + } + + /* Note: when we build an indexed type we combine adjacent regions, + * so we're not going to go through and check every piece + * separately here. if someone else were building indexed + * dataloops by hand, then the loop here might be necessary. + * DLOOP_Count i and DLOOP_Offset size would need to be + * declared above. + */ +#if 0 + last_loc = rel_off * offsetarray[0] + blockarray[0] * el_size; + for (i=1; i < count; i++) { + if (last_loc == rel_off + offsetarray[i]) new_blk_count--; + + last_loc = rel_off + offsetarray[i] + blockarray[i] * el_size; + } +#else + last_loc = rel_off + offsetarray[count-1] + blockarray[count-1] * el_size; +#endif + + paramp->last_loc = last_loc; + paramp->count += new_blk_count; + return 0; +} + +/* DLOOP_Segment_count_contig_blocks() + * + * Count number of contiguous regions in segment between first and last. + */ +void PREPEND_PREFIX(Segment_count_contig_blocks)(DLOOP_Segment *segp, + DLOOP_Offset first, + DLOOP_Offset *lastp, + DLOOP_Count *countp) +{ + struct PREPEND_PREFIX(contig_blocks_params) params; + + params.count = 0; + params.last_loc = 0; + + PREPEND_PREFIX(Segment_manipulate)(segp, + first, + lastp, + DLOOP_Segment_contig_count_block, + DLOOP_Segment_vector_count_block, + DLOOP_Segment_blkidx_count_block, + DLOOP_Segment_index_count_block, + NULL, /* size fn */ + (void *) ¶ms); + + *countp = params.count; + return; +} + +/********** FUNCTIONS FOR FLATTENING INTO MPI OFFSETS AND BLKLENS **********/ + +/* Segment_mpi_flatten + * + * Flattens into a set of blocklengths and displacements, as in an + * MPI hindexed type. Note that we use appropriately-sized variables + * in the associated params structure for this reason. + * + * NOTE: blocks will be in units of bytes when returned. + * + * WARNING: there's potential for overflow here as we convert from + * various types into an index of bytes. + */ +struct PREPEND_PREFIX(mpi_flatten_params) { + int index, length; + MPI_Aint last_end; + int *blklens; + MPI_Aint *disps; +}; + +/* DLOOP_Segment_contig_mpi_flatten + * + */ +static int DLOOP_Segment_contig_mpi_flatten(DLOOP_Offset *blocks_p, + DLOOP_Type el_type, + DLOOP_Offset rel_off, + DLOOP_Buffer bufp, + void *v_paramp) +{ + int last_idx, size; + DLOOP_Offset el_size; + char *last_end = NULL; + struct PREPEND_PREFIX(mpi_flatten_params) *paramp = v_paramp; + + DLOOP_Handle_get_size_macro(el_type, el_size); + size = *blocks_p * (int) el_size; + + last_idx = paramp->index - 1; + if (last_idx >= 0) { + last_end = ((char *) paramp->disps[last_idx]) + + paramp->blklens[last_idx]; + } + + if ((last_idx == paramp->length-1) && + (last_end != ((char *) bufp + rel_off))) + { + /* we have used up all our entries, and this region doesn't fit on + * the end of the last one. setting blocks to 0 tells manipulation + * function that we are done (and that we didn't process any blocks). + */ + *blocks_p = 0; + return 1; + } + else if (last_idx >= 0 && (last_end == ((char *) bufp + rel_off))) + { + /* add this size to the last vector rather than using up another one */ + paramp->blklens[last_idx] += size; + } + else { + paramp->disps[last_idx+1] = (MPI_Aint) ((char *) bufp + rel_off); + paramp->blklens[last_idx+1] = size; + paramp->index++; + } + return 0; +} + +/* DLOOP_Segment_vector_mpi_flatten + * + * Input Parameters: + * blocks_p - [inout] pointer to a count of blocks (total, for all noncontiguous pieces) + * count - # of noncontiguous regions + * blksz - size of each noncontiguous region + * stride - distance in bytes from start of one region to start of next + * el_type - elemental type (e.g. MPI_INT) + * ... + * + * Note: this is only called when the starting position is at the beginning + * of a whole block in a vector type. + * + * TODO: MAKE THIS CODE SMARTER, USING THE SAME GENERAL APPROACH AS IN THE + * COUNT BLOCK CODE ABOVE. + */ +static int DLOOP_Segment_vector_mpi_flatten(DLOOP_Offset *blocks_p, + DLOOP_Count count, + DLOOP_Count blksz, + DLOOP_Offset stride, + DLOOP_Type el_type, + DLOOP_Offset rel_off, /* into buffer */ + DLOOP_Buffer bufp, /* start of buffer */ + void *v_paramp) +{ + int i, size, blocks_left; + DLOOP_Offset el_size; + struct PREPEND_PREFIX(mpi_flatten_params) *paramp = v_paramp; + + DLOOP_Handle_get_size_macro(el_type, el_size); + blocks_left = *blocks_p; + +#if 0 + MPIU_DBG_MSG_FMT(DATATYPE,VERBOSE,(MPIU_DBG_FDEST, + "\t[vector to vec: do=%d, dp=%x, len=%d, ind=%d, ct=%d, blksz=%d, str=%d, blks=%d]\n", + (unsigned) rel_off, + (unsigned) (MPI_Aint)bufp, + paramp->u.pack_vector.length, + paramp->u.pack_vector.index, + count, + blksz, + stride, + (int) *blocks_p)); +#endif + + for (i=0; i < count && blocks_left > 0; i++) { + int last_idx; + char *last_end = NULL; + + if (blocks_left > blksz) { + size = blksz * (int) el_size; + blocks_left -= blksz; + } + else { + /* last pass */ + size = blocks_left * (int) el_size; + blocks_left = 0; + } + + last_idx = paramp->index - 1; + if (last_idx >= 0) { + last_end = ((char *) paramp->disps[last_idx]) + + paramp->blklens[last_idx]; + } + + if ((last_idx == paramp->length-1) && + (last_end != ((char *) bufp + rel_off))) + { + /* we have used up all our entries, and this one doesn't fit on + * the end of the last one. + */ + *blocks_p -= (blocks_left + (size / (int) el_size)); +#ifdef MPID_SP_VERBOSE + MPIU_dbg_printf("\t[vector to vec exiting (1): next ind = %d, %d blocks processed.\n", + paramp->u.pack_vector.index, + (int) *blocks_p); +#endif + return 1; + } + else if (last_idx >= 0 && (last_end == ((char *) bufp + rel_off))) + { + /* add this size to the last vector rather than using up new one */ + paramp->blklens[last_idx] += size; + } + else { + paramp->disps[last_idx+1] = (MPI_Aint) ((char *) bufp + rel_off); + paramp->blklens[last_idx+1] = size; + paramp->index++; + } + + rel_off += stride; + } + +#ifdef MPID_SP_VERBOSE + MPIU_dbg_printf("\t[vector to vec exiting (2): next ind = %d, %d blocks processed.\n", + paramp->u.pack_vector.index, + (int) *blocks_p); +#endif + + /* if we get here then we processed ALL the blocks; don't need to update + * blocks_p + */ + + DLOOP_Assert(blocks_left == 0); + return 0; +} + +/* MPID_Segment_mpi_flatten - flatten a type into a representation + * appropriate for passing to hindexed create. + * + * Parameters: + * segp - pointer to segment structure + * first - first byte in segment to pack + * lastp - in/out parameter describing last byte to pack (and afterwards + * the last byte _actually_ packed) + * NOTE: actually returns index of byte _after_ last one packed + * blklens, disps - the usual blocklength and displacement arrays for MPI + * lengthp - in/out parameter describing length of array (and afterwards + * the amount of the array that has actual data) + */ +void PREPEND_PREFIX(Segment_mpi_flatten)(DLOOP_Segment *segp, + DLOOP_Offset first, + DLOOP_Offset *lastp, + int *blklens, + MPI_Aint *disps, + int *lengthp) +{ + struct PREPEND_PREFIX(mpi_flatten_params) params; + + DLOOP_Assert(*lengthp > 0); + + params.index = 0; + params.length = *lengthp; + params.blklens = blklens; + params.disps = disps; + + PREPEND_PREFIX(Segment_manipulate)(segp, + first, + lastp, + DLOOP_Segment_contig_mpi_flatten, + DLOOP_Segment_vector_mpi_flatten, + NULL, /* blkidx fn */ + NULL, /* index fn */ + NULL, + ¶ms); + + /* last value already handled by MPID_Segment_manipulate */ + *lengthp = params.index; + return; +} + +/* + * Local variables: + * c-indent-tabs-mode: nil + * End: + */ diff --git a/ompi/mca/io/romio/romio/common/dataloop/subarray_support.c b/ompi/mca/io/romio/romio/common/dataloop/subarray_support.c new file mode 100644 index 0000000000..becce18349 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/subarray_support.c @@ -0,0 +1,99 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * + * Copyright (C) 1997 University of Chicago. + * See COPYRIGHT notice in top-level directory. + * + * Note: This code originally appeared in ROMIO. + */ + +#include "dataloop.h" + +int PREPEND_PREFIX(Type_convert_subarray)(int ndims, + int *array_of_sizes, + int *array_of_subsizes, + int *array_of_starts, + int order, + MPI_Datatype oldtype, + MPI_Datatype *newtype) +{ + MPI_Aint extent, disps[3], size; + int i, blklens[3]; + MPI_Datatype tmp1, tmp2, types[3]; + + PMPI_Type_extent(oldtype, &extent); + + if (order == MPI_ORDER_FORTRAN) { + /* dimension 0 changes fastest */ + if (ndims == 1) { + PMPI_Type_contiguous(array_of_subsizes[0], oldtype, &tmp1); + } + else { + PMPI_Type_vector(array_of_subsizes[1], + array_of_subsizes[0], + array_of_sizes[0], oldtype, &tmp1); + + size = array_of_sizes[0]*extent; + for (i=2; i=0; i--) { + size *= array_of_sizes[i+1]; + PMPI_Type_hvector(array_of_subsizes[i], 1, size, tmp1, &tmp2); + PMPI_Type_free(&tmp1); + tmp1 = tmp2; + } + } + + /* add displacement and UB */ + disps[1] = array_of_starts[ndims-1]; + size = 1; + for (i=ndims-2; i>=0; i--) { + size *= array_of_sizes[i+1]; + disps[1] += size*array_of_starts[i]; + } + } + + disps[1] *= extent; + + disps[2] = extent; + for (i=0; i= old_lb_) { \ + lb_ = old_lb_; \ + ub_ = old_ub_ + (old_extent_) * (cnt_ - 1); \ + } \ + else /* negative extent */ { \ + lb_ = old_lb_ + (old_extent_) * (cnt_ - 1); \ + ub_ = old_ub_; \ + } \ + } + +/* DLOOP_DATATYPE_VECTOR_LB_UB() + * + * Determines the new LB and UB for a vector of blocks of old types + * given the old type's LB, UB, and extent, and a count, stride, and + * blocklen describing the vectorization. + */ +#define DLOOP_DATATYPE_VECTOR_LB_UB(cnt_, \ + stride_, \ + blklen_, \ + old_lb_, \ + old_ub_, \ + old_extent_, \ + lb_, \ + ub_) \ + { \ + if (cnt_ == 0 || blklen_ == 0) { \ + lb_ = old_lb_; \ + ub_ = old_ub_; \ + } \ + else if (stride_ >= 0 && (old_extent_) >= 0) { \ + lb_ = old_lb_; \ + ub_ = old_ub_ + (old_extent_) * ((blklen_) - 1) + \ + (stride_) * ((cnt_) - 1); \ + } \ + else if (stride_ < 0 && (old_extent_) >= 0) { \ + lb_ = old_lb_ + (stride_) * ((cnt_) - 1); \ + ub_ = old_ub_ + (old_extent_) * ((blklen_) - 1); \ + } \ + else if (stride_ >= 0 && (old_extent_) < 0) { \ + lb_ = old_lb_ + (old_extent_) * ((blklen_) - 1); \ + ub_ = old_ub_ + (stride_) * ((cnt_) - 1); \ + } \ + else { \ + lb_ = old_lb_ + (old_extent_) * ((blklen_) - 1) + \ + (stride_) * ((cnt_) - 1); \ + ub_ = old_ub_; \ + } \ + } + +/* DLOOP_DATATYPE_BLOCK_LB_UB() + * + * Determines the new LB and UB for a block of old types given the LB, + * UB, and extent of the old type as well as a new displacement and count + * of types. + * + * Note: we need the extent here in addition to the lb and ub because the + * extent might have some padding in it that we need to take into account. + */ +#define DLOOP_DATATYPE_BLOCK_LB_UB(cnt_, \ + disp_, \ + old_lb_, \ + old_ub_, \ + old_extent_, \ + lb_, \ + ub_) \ + { \ + if (cnt_ == 0) { \ + lb_ = old_lb_ + (disp_); \ + ub_ = old_ub_ + (disp_); \ + } \ + else if (old_ub_ >= old_lb_) { \ + lb_ = old_lb_ + (disp_); \ + ub_ = old_ub_ + (disp_) + (old_extent_) * ((cnt_) - 1); \ + } \ + else /* negative extent */ { \ + lb_ = old_lb_ + (disp_) + (old_extent_) * ((cnt_) - 1); \ + ub_ = old_ub_ + (disp_); \ + } \ + } + +void PREPEND_PREFIX(Type_calc_footprint)(MPI_Datatype type, + DLOOP_Type_footprint *tfp) +{ + int mpi_errno; + int nr_ints, nr_aints, nr_types, combiner; + int *ints; + MPI_Aint *aints; + MPI_Datatype *types; + + /* used to store parameters for constituent types */ + DLOOP_Offset size = 0, lb = 0, ub = 0, true_lb = 0, true_ub = 0; + DLOOP_Offset extent = 0, alignsz; + int has_sticky_lb, has_sticky_ub; + + /* used for vector/hvector/hvector_integer calculations */ + DLOOP_Offset stride; + + /* used for indexed/hindexed calculations */ + DLOOP_Offset disp; + + /* used for calculations on types with more than one block of data */ + DLOOP_Offset i, min_lb, max_ub, ntypes, tmp_lb, tmp_ub; + + /* used for processing subarray and darray types */ + int ndims; + MPI_Datatype tmptype; + + mpi_errno = PMPI_Type_get_envelope(type, &nr_ints, &nr_aints, + &nr_types, &combiner); + DLOOP_Assert(mpi_errno == MPI_SUCCESS); + + if (combiner == MPI_COMBINER_NAMED) { + int mpisize; + MPI_Aint mpiextent; + + PMPI_Type_size(type, &mpisize); + PMPI_Type_extent(type, &mpiextent); + tfp->size = (DLOOP_Offset) mpisize; + tfp->lb = 0; + tfp->ub = (DLOOP_Offset) mpiextent; + tfp->true_lb = 0; + tfp->true_ub = (DLOOP_Offset) mpiextent; + tfp->extent = (DLOOP_Offset) mpiextent; + tfp->alignsz = DLOOP_Named_type_alignsize(type, (MPI_Aint) 0); + tfp->has_sticky_lb = (type == MPI_LB) ? 1 : 0; + tfp->has_sticky_ub = (type == MPI_UB) ? 1 : 0; + + goto clean_exit; + } + + /* get access to contents; need it immediately to check for zero count */ + PREPEND_PREFIX(Type_access_contents)(type, &ints, &aints, &types); + + /* knock out all the zero count cases */ + if ((combiner == MPI_COMBINER_CONTIGUOUS || + combiner == MPI_COMBINER_VECTOR || + combiner == MPI_COMBINER_HVECTOR_INTEGER || + combiner == MPI_COMBINER_HVECTOR || + combiner == MPI_COMBINER_INDEXED_BLOCK || + combiner == MPI_COMBINER_INDEXED || + combiner == MPI_COMBINER_HINDEXED_INTEGER || + combiner == MPI_COMBINER_STRUCT_INTEGER || + combiner == MPI_COMBINER_STRUCT) && ints[0] == 0) + { + tfp->size = tfp->lb = tfp->ub = tfp->extent = tfp->alignsz = 0; + tfp->true_lb = tfp->true_ub = 0; + tfp->has_sticky_lb = tfp->has_sticky_ub = 0; + goto clean_exit; + } + + if (combiner != MPI_COMBINER_STRUCT && + combiner != MPI_COMBINER_STRUCT_INTEGER) + { + DLOOP_Type_footprint cfp; + + PREPEND_PREFIX(Type_calc_footprint)(types[0], &cfp); + size = cfp.size; + lb = cfp.lb; + ub = cfp.ub; + true_lb = cfp.true_lb; + true_ub = cfp.true_ub; + extent = cfp.extent; + alignsz = cfp.alignsz; + has_sticky_lb = cfp.has_sticky_lb; + has_sticky_ub = cfp.has_sticky_ub; + + /* initialize some common values so we don't have to assign + * them in every case below. + */ + tfp->alignsz = alignsz; + tfp->has_sticky_lb = has_sticky_lb; + tfp->has_sticky_ub = has_sticky_ub; + + } + + switch(combiner) + { + case MPI_COMBINER_DUP: + tfp->size = size; + tfp->lb = lb; + tfp->ub = ub; + tfp->true_lb = true_lb; + tfp->true_ub = true_ub; + tfp->extent = extent; + break; + case MPI_COMBINER_RESIZED: + tfp->size = size; + tfp->lb = aints[0]; /* lb */ + tfp->ub = aints[0] + aints[1]; + tfp->true_lb = true_lb; + tfp->true_ub = true_ub; + tfp->extent = aints[1]; /* extent */ + tfp->has_sticky_lb = 1; + tfp->has_sticky_ub = 1; + break; + case MPI_COMBINER_CONTIGUOUS: + DLOOP_DATATYPE_CONTIG_LB_UB(ints[0] /* count */, + lb, ub, extent, + tfp->lb, tfp->ub); + tfp->true_lb = tfp->lb + (true_lb - lb); + tfp->true_ub = tfp->ub + (true_ub - ub); + tfp->size = ints[0] * size; + tfp->extent = tfp->ub - tfp->lb; + break; + case MPI_COMBINER_VECTOR: + case MPI_COMBINER_HVECTOR: + case MPI_COMBINER_HVECTOR_INTEGER: + if (combiner == MPI_COMBINER_VECTOR) stride = ints[2] * extent; + else if (combiner == MPI_COMBINER_HVECTOR) stride = aints[0]; + else /* HVECTOR_INTEGER */ stride = ints[2]; + + DLOOP_DATATYPE_VECTOR_LB_UB(ints[0] /* count */, + stride /* stride in bytes */, + ints[1] /* blklen */, + lb, ub, extent, + tfp->lb, tfp->ub); + tfp->true_lb = tfp->lb + (true_lb - lb); + tfp->true_ub = tfp->ub + (true_ub - ub); + tfp->size = ints[0] * ints[1] * size; + tfp->extent = tfp->ub - tfp->lb; + break; + case MPI_COMBINER_INDEXED_BLOCK: + /* prime min_lb and max_ub */ + DLOOP_DATATYPE_BLOCK_LB_UB(ints[1] /* blklen */, + ints[2] * extent /* disp */, + lb, ub, extent, + min_lb, max_ub); + + for (i=1; i < ints[0]; i++) { + DLOOP_DATATYPE_BLOCK_LB_UB(ints[1] /* blklen */, + ints[i+2] * extent /* disp */, + lb, ub, extent, + tmp_lb, tmp_ub); + if (tmp_lb < min_lb) min_lb = tmp_lb; + if (tmp_ub > max_ub) max_ub = tmp_ub; + } + tfp->size = ints[0] * ints[1] * size; + tfp->lb = min_lb; + tfp->ub = max_ub; + tfp->true_lb = min_lb + (true_lb - lb); + tfp->true_ub = max_ub + (true_ub - ub); + tfp->extent = tfp->ub - tfp->lb; + break; + case MPI_COMBINER_INDEXED: + case MPI_COMBINER_HINDEXED_INTEGER: + case MPI_COMBINER_HINDEXED: + /* find first non-zero blocklength element */ + for (i=0; i < ints[0] && ints[i+1] == 0; i++); + if (i == ints[0]) { + /* all zero blocklengths */ + tfp->size = tfp->lb = tfp->ub = tfp->extent = tfp->alignsz = 0; + tfp->has_sticky_lb = tfp->has_sticky_ub = 0; + } + else { + /* prime min_lb, max_ub, count */ + ntypes = ints[i+1]; + if (combiner == MPI_COMBINER_INDEXED) + disp = ints[ints[0]+i+1] * extent; + else if (combiner == MPI_COMBINER_HINDEXED_INTEGER) + disp = ints[ints[0]+i+1]; + else /* MPI_COMBINER_HINDEXED */ + disp = aints[i]; + + DLOOP_DATATYPE_BLOCK_LB_UB(ints[i+1] /* blklen */, + disp, + lb, ub, extent, + min_lb, max_ub); + + for (i++; i < ints[0]; i++) { + /* skip zero blocklength elements */ + if (ints[i+1] == 0) continue; + + ntypes += ints[i+1]; + if (combiner == MPI_COMBINER_INDEXED) + disp = ints[ints[0]+i+1] * extent; + else if (combiner == MPI_COMBINER_HINDEXED_INTEGER) + disp = ints[ints[0]+i+1]; + else /* MPI_COMBINER_HINDEXED */ + disp = aints[i]; + + DLOOP_DATATYPE_BLOCK_LB_UB(ints[i+1], + disp, + lb, ub, extent, + tmp_lb, tmp_ub); + if (tmp_lb < min_lb) min_lb = tmp_lb; + if (tmp_ub > max_ub) max_ub = tmp_ub; + } + tfp->size = ntypes * size; + tfp->lb = min_lb; + tfp->ub = max_ub; + tfp->true_lb = min_lb + (true_lb - lb); + tfp->true_ub = max_ub + (true_ub - ub); + tfp->extent = tfp->ub - tfp->lb; + } + break; + case MPI_COMBINER_STRUCT_INTEGER: + DLOOP_Assert(combiner != MPI_COMBINER_STRUCT_INTEGER); + break; + case MPI_COMBINER_STRUCT: + /* sufficiently complicated to pull out into separate fn */ + DLOOP_Type_calc_footprint_struct(type, + combiner, ints, aints, types, + tfp); + break; + case MPI_COMBINER_SUBARRAY: + ndims = ints[0]; + PREPEND_PREFIX(Type_convert_subarray)(ndims, + &ints[1] /* sizes */, + &ints[1+ndims] /* subsz */, + &ints[1+2*ndims] /* strts */, + ints[1+3*ndims] /* order */, + types[0], + &tmptype); + PREPEND_PREFIX(Type_calc_footprint)(tmptype, tfp); + PMPI_Type_free(&tmptype); + break; + case MPI_COMBINER_DARRAY: + ndims = ints[2]; + + PREPEND_PREFIX(Type_convert_darray)(ints[0] /* size */, + ints[1] /* rank */, + ndims, + &ints[3] /* gsizes */, + &ints[3+ndims] /*distribs */, + &ints[3+2*ndims] /* dargs */, + &ints[3+3*ndims] /* psizes */, + ints[3+4*ndims] /* order */, + types[0], + &tmptype); + + PREPEND_PREFIX(Type_calc_footprint)(tmptype, tfp); + PMPI_Type_free(&tmptype); + break; + case MPI_COMBINER_F90_REAL: + case MPI_COMBINER_F90_COMPLEX: + case MPI_COMBINER_F90_INTEGER: + default: + DLOOP_Assert(0); + break; + } + + clean_exit: + PREPEND_PREFIX(Type_release_contents)(type, &ints, &aints, &types); + return; +} + +/* + DLOOP_Type_calc_footprint_struct - calculate size, lb, ub, extent, + and alignsize for a struct type +*/ +static void DLOOP_Type_calc_footprint_struct(MPI_Datatype type, + int struct_combiner, + int *ints, + MPI_Aint *aints, + MPI_Datatype *types, + DLOOP_Type_footprint *tfp) +{ + int i, found_sticky_lb = 0, found_sticky_ub = 0, first_iter = 1; + DLOOP_Offset tmp_lb, tmp_ub, tmp_extent, tmp_true_lb, tmp_true_ub; + DLOOP_Offset max_alignsz = 0, tmp_size = 0, min_lb = 0, max_ub = 0; + DLOOP_Offset min_true_lb = 0, max_true_ub = 0; + + int nr_ints, nr_aints, nr_types, combiner; + + /* used to store parameters for constituent types */ + DLOOP_Type_footprint cfp; + DLOOP_Offset size, lb, ub, true_lb, true_ub, extent, alignsz; + int sticky_lb, sticky_ub; + + /* find first non-zero blocklength element */ + for (i=0; i < ints[0] && ints[i+1] == 0; i++); + + if (i == ints[0]) /* all zero-length blocks */ { + tfp->size = tfp->lb = tfp->ub = tfp->extent = tfp->alignsz = 0; + tfp->has_sticky_lb = tfp->has_sticky_ub = 0; + return; + } + + for (; i < ints[0]; i++) { + /* skip zero blocklength elements */ + if (ints[i+1] == 0) continue; + + PMPI_Type_get_envelope(types[i], &nr_ints, &nr_aints, &nr_types, + &combiner); + + /* opt: could just inline assignments for combiner == NAMED case */ + + PREPEND_PREFIX(Type_calc_footprint)(types[i], &cfp); + size = cfp.size; + lb = cfp.lb; + ub = cfp.ub; + true_lb = cfp.true_lb; + true_ub = cfp.true_ub; + extent = cfp.extent; + alignsz = cfp.alignsz; + sticky_lb = cfp.has_sticky_lb; + sticky_ub = cfp.has_sticky_ub; + + DLOOP_DATATYPE_BLOCK_LB_UB(ints[i+1] /* blklen */, + aints[i] /* disp */, + lb, ub, extent, + tmp_lb, tmp_ub); + + tmp_true_lb = tmp_lb + (true_lb - lb); + tmp_true_ub = tmp_ub + (true_ub - ub); + tmp_size += size * ints[i+1]; + + if (combiner == MPI_COMBINER_NAMED) { + /* NOTE: This is a special case. If a user creates a struct + * with a named type at a non-zero displacement, the + * alignment may be different than expected due to + * special compiler rules for this case. Thus we must + * over-ride the value that we obtained from + * Type_calc_footprint() above. + */ + alignsz = DLOOP_Named_type_alignsize(types[i], aints[i]); + } + + if (max_alignsz < alignsz) max_alignsz = alignsz; + + /* We save this LB if: + * (1) this is our first iteration where we saw a nonzero blklen, + * (2) we haven't found a sticky LB and this LB is lower than + * any we have previously seen, + * (3) we haven't found a sticky LB and this one is sticky, or + * (4) this sticky LB is lower than any we have previously seen. + */ + if ((first_iter) || + (!found_sticky_lb && min_lb > tmp_lb) || + (!found_sticky_lb && sticky_lb) || + (sticky_lb && min_lb > tmp_lb)) + { + min_lb = tmp_lb; + if (sticky_lb) found_sticky_lb = 1; + } + + if ((first_iter) || + (!found_sticky_ub && max_ub < tmp_ub) || + (!found_sticky_ub && sticky_ub) || + (sticky_ub && max_ub < tmp_ub)) + { + max_ub = tmp_ub; + if (sticky_ub) found_sticky_ub = 1; + } + + if ((first_iter) || + (tmp_true_lb > min_true_lb)) + { + min_true_lb = tmp_true_lb; + } + + if ((first_iter) || + (tmp_true_ub < max_true_ub)) + { + max_true_ub = tmp_true_ub; + } + + first_iter = 0; + } + + /* calculate extent, not including potential padding */ + tmp_extent = max_ub - min_lb; + + /* account for padding if no sticky LB/UB is found */ + if ((!found_sticky_lb) && (!found_sticky_ub)) { + DLOOP_Offset epsilon; + + epsilon = (max_alignsz > 0) ? tmp_extent % max_alignsz : 0; + + if (epsilon) { + max_ub += (max_alignsz - epsilon); + tmp_extent = max_ub - min_lb; + } + } + +#if 0 + printf("size = %d, extent = %d\n", (int) tmp_size, (int) tmp_extent); +#endif + + tfp->size = tmp_size; + tfp->lb = min_lb; + tfp->ub = max_ub; + tfp->true_lb = min_true_lb; + tfp->true_ub = max_true_ub; + tfp->extent = tmp_extent; + tfp->alignsz = max_alignsz; + tfp->has_sticky_lb = found_sticky_lb; + tfp->has_sticky_ub = found_sticky_ub; + return; +} + +/* + DLOOP_Named_type_alignsize - calculate alignment in bytes for a struct + based on constituent elements. + + Returns alignment in bytes. +*/ +static int DLOOP_Named_type_alignsize(MPI_Datatype type, MPI_Aint disp) +{ + int alignsize = 0; + + static int havent_tested_align_rules = 1; + static int max_intalign = 0, max_fpalign = 0; + static int have_double_pos_align = 0, have_llint_pos_align = 0; + static int max_doublealign = 0, max_longdoublealign = 0; + + if (havent_tested_align_rules) { + max_intalign = DLOOP_Structalign_integer_max(); + max_fpalign = DLOOP_Structalign_float_max(); + max_doublealign = DLOOP_Structalign_double_max(); + max_longdoublealign = DLOOP_Structalign_long_double_max(); + have_double_pos_align = DLOOP_Structalign_double_position(); + have_llint_pos_align = DLOOP_Structalign_llint_position(); + + havent_tested_align_rules = 0; + } + + /* skip LBs, UBs, and elements with zero block length */ + if (type == MPI_LB || type == MPI_UB) + return 0; + + PMPI_Type_size(type, &alignsize); + + switch(type) + { + case MPI_FLOAT: + if (alignsize > max_fpalign) + alignsize = max_fpalign; + break; + case MPI_DOUBLE: + if (alignsize > max_doublealign) + alignsize = max_doublealign; + + if (have_double_pos_align && disp != (MPI_Aint) 0) + alignsize = 4; /* would be better to test */ + break; + case MPI_LONG_DOUBLE: + if (alignsize > max_longdoublealign) + alignsize = max_longdoublealign; + break; + default: + if (alignsize > max_intalign) + alignsize = max_intalign; + + if (have_llint_pos_align && + type == MPI_LONG_LONG_INT && + disp != (MPI_Aint) 0) + { + alignsize = 4; /* would be better to test */ + } + break; + } + + return alignsize; +} + + +/* INTERNAL STRUCT ALIGNMENT TESTS BELOW */ + +/* from MPICH2 PAC_C_MAX_INTEGER_ALIGN test: + * + * Tests for max C struct integer alignment. Note that this is for *all* + * integer types. + * + * Return value is 1, 2, 4, or 8. + */ +static int DLOOP_Structalign_integer_max() +{ + int is_packed = 1; + int is_two = 1; + int is_four = 1; + int is_eight = 1; + + int size, extent; + + struct { char a; int b; } char_int; + struct { char a; short b; } char_short; + struct { char a; long b; } char_long; + struct { char a; int b; char c; } char_int_char; + struct { char a; short b; char c; } char_short_char; +#ifdef HAVE_LONG_LONG_INT + struct { long long int a; char b; } lli_c; + struct { char a; long long int b; } c_lli; + int extent2; +#endif + + /* assume max integer alignment isn't 8 if we don't have + * an eight-byte value. + */ +#ifdef HAVE_LONG_LONG_INT + if (sizeof(int) < 8 && sizeof(long) < 8 && sizeof(long long int) < 8) + is_eight = 0; +#else + if (sizeof(int) < 8 && sizeof(long) < 8) is_eight = 0; +#endif + + size = sizeof(char) + sizeof(int); + extent = sizeof(char_int); + if (size != extent) is_packed = 0; + if ( (extent % 2) != 0) is_two = 0; + if ( (extent % 4) != 0) is_four = 0; + if (sizeof(int) == 8 && (extent % 8) != 0) is_eight = 0; + + size = sizeof(char) + sizeof(short); + extent = sizeof(char_short); + if (size != extent) is_packed = 0; + if ( (extent % 2) != 0) is_two = 0; + if (sizeof(short) == 4 && (extent % 4) != 0) is_four = 0; + if (sizeof(short) == 8 && (extent % 8) != 0) is_eight = 0; + + size = sizeof(char) + sizeof(long); + extent = sizeof(char_long); + if (size != extent) is_packed = 0; + if ( (extent % 2) != 0) is_two = 0; + if ( (extent % 4) != 0) is_four = 0; + if (sizeof(long) == 8 && (extent % 8) != 0) is_eight = 0; + +#ifdef HAVE_LONG_LONG_INT + size = sizeof(char) + sizeof(long long int); + extent = sizeof(lli_c); + extent2 = sizeof(c_lli); + if (size != extent) is_packed = 0; + if ( (extent % 2) != 0 && (extent2 % 2) != 0) is_two = 0; + if ( (extent % 4) != 0 && (extent2 % 4) != 0) is_four = 0; + if (sizeof(long long int) >= 8 && (extent % 8) != 0 && (extent2 % 8) != 0) + is_eight = 0; +#endif + + size = sizeof(char) + sizeof(int) + sizeof(char); + extent = sizeof(char_int_char); + if (size != extent) is_packed = 0; + if ( (extent % 2) != 0) is_two = 0; + if ( (extent % 4) != 0) is_four = 0; + if (sizeof(int) == 8 && (extent % 8) != 0) is_eight = 0; + + size = sizeof(char) + sizeof(short) + sizeof(char); + extent = sizeof(char_short_char); + if (size != extent) is_packed = 0; + if ( (extent % 2) != 0) is_two = 0; + if (sizeof(short) == 4 && (extent % 4) != 0) is_four = 0; + if (sizeof(short) == 8 && (extent % 8) != 0) is_eight = 0; + + if (is_eight) { is_four = 0; is_two = 0; } + if (is_four) is_two = 0; + + DLOOP_Assert(is_packed + is_two + is_four + is_eight == 1); + + if (is_packed) return 1; + if (is_two) return 2; + if (is_four) return 4; + return 8; +} + +/* from MPICH2 PAC_C_MAX_FP_ALIGN test: + * + * Checks for max C struct floating point alignment. Note that + * in this test we are *only* testing float types, whereas in + * the original test we were testing double and long double also. + * + * Return value is 1, 2, 4, 8, or 16. + */ +static int DLOOP_Structalign_float_max() +{ + int is_packed = 1; + int is_two = 1; + int is_four = 1; + int is_eight = 1; + int is_sixteen = 1; + struct { char a; float b; } char_float; + struct { float b; char a; } float_char; + int size, extent1, extent2; + + size = sizeof(char) + sizeof(float); + extent1 = sizeof(char_float); + extent2 = sizeof(float_char); + if (size != extent1) is_packed = 0; + if ( (extent1 % 2) != 0 && (extent2 % 2) != 0) is_two = 0; + if ( (extent1 % 4) != 0 && (extent2 % 4) != 0) is_four = 0; + if (sizeof(float) == 8 && (extent1 % 8) != 0 && (extent2 % 8) != 0) + is_eight = 0; + + if (is_sixteen) { is_eight = 0; is_four = 0; is_two = 0; } + if (is_eight) { is_four = 0; is_two = 0; } + if (is_four) is_two = 0; + + DLOOP_Assert(is_packed + is_two + is_four + is_eight + is_sixteen == 1); + + if (is_packed) return 1; + if (is_two) return 2; + if (is_four) return 4; + if (is_eight) return 8; + return 16; +} + +/* from MPICH2 PAC_C_MAX_DOUBLE_FP_ALIGN test: + * + * Determines maximum struct alignment with floats and doubles. + * + * Return value is 1, 2, 4, or 8. + */ +static int DLOOP_Structalign_double_max() +{ + int is_packed = 1; + int is_two = 1; + int is_four = 1; + int is_eight = 1; + struct { char a; double b; } char_double; + struct { double b; char a; } double_char; + int size, extent1, extent2; + + size = sizeof(char) + sizeof(double); + extent1 = sizeof(char_double); + extent2 = sizeof(double_char); + if (size != extent1) is_packed = 0; + if ( (extent1 % 2) != 0 && (extent2 % 2) != 0) is_two = 0; + if ( (extent1 % 4) != 0 && (extent2 % 4) != 0) is_four = 0; + if (sizeof(double) == 8 && (extent1 % 8) != 0 && (extent2 % 8) != 0) + is_eight = 0; + + if (is_eight) { is_four = 0; is_two = 0; } + if (is_four) is_two = 0; + + DLOOP_Assert(is_packed + is_two + is_four + is_eight == 1); + + if (is_packed) return 1; + if (is_two) return 2; + if (is_four) return 4; + return 8; +} + +/* from MPICH2 PAC_C_MAX_LONGDOUBLE_FP_ALIGN test: + * + * Determines maximum alignment of structs with long doubles. + * + * Return value is 1, 2, 4, 8, or 16. + */ +static int DLOOP_Structalign_long_double_max() +{ + int is_packed = 1; + int is_two = 1; + int is_four = 1; + int is_eight = 1; + int is_sixteen = 1; + struct { char a; long double b; } char_long_double; + struct { long double b; char a; } long_double_char; + struct { long double a; int b; char c; } long_double_int_char; + int size, extent1, extent2; + + size = sizeof(char) + sizeof(long double); + extent1 = sizeof(char_long_double); + extent2 = sizeof(long_double_char); + if (size != extent1) is_packed = 0; + if ( (extent1 % 2) != 0 && (extent2 % 2) != 0) is_two = 0; + if ( (extent1 % 4) != 0 && (extent2 % 4) != 0) is_four = 0; + if (sizeof(long double) >= 8 && (extent1 % 8) != 0 && (extent2 % 8) != 0) + is_eight = 0; + if (sizeof(long double) > 8 && (extent1 % 16) != 0 + && (extent2 % 16) != 0) is_sixteen = 0; + + extent1 = sizeof(long_double_int_char); + if ( (extent1 % 2) != 0) is_two = 0; + if ( (extent1 % 4) != 0) is_four = 0; + if (sizeof(long double) >= 8 && (extent1 % 8) != 0) is_eight = 0; + if (sizeof(long double) > 8 && (extent1 % 16) != 0) is_sixteen = 0; + + if (is_sixteen) { is_eight = 0; is_four = 0; is_two = 0; } + if (is_eight) { is_four = 0; is_two = 0; } + if (is_four) is_two = 0; + + DLOOP_Assert(is_packed + is_two + is_four + is_eight + is_sixteen == 1); + + if (is_packed) return 1; + if (is_two) return 2; + if (is_four) return 4; + if (is_eight) return 8; + return 16; +} + + +/* from MPICH2 PAC_C_DOUBLE_POS_ALIGN test: + * + * Test for odd struct alignment rule that only applies max. padding when + * double value is at front of type. + * + * Search for "Power alignment mode" for more details. + * + * Return value is 1 or 0. + */ +static int DLOOP_Structalign_double_position() +{ + int padding_varies_by_pos = 0; + struct { char a; double b; } char_double; + struct { double b; char a; } double_char; + int extent1, extent2; + + extent1 = sizeof(char_double); + extent2 = sizeof(double_char); + if (extent1 != extent2) padding_varies_by_pos = 1; + + if (padding_varies_by_pos) return 1; + else return 0; +} + +/* from MPICH2 PAC_C_LLINT_POS_ALIGN test: + * Test for odd struct alignment rule that only applies max. + * padding when long long int value is at front of type. + * + * Search for "Power alignment mode" for more details. + * + * Return value is 1 or 0. + */ +static int DLOOP_Structalign_llint_position() +{ + int padding_varies_by_pos = 0; +#ifdef HAVE_LONG_LONG_INT + struct { char a; long long int b; } char_llint; + struct { long long int b; char a; } llint_char; + int extent1, extent2; + + extent1 = sizeof(char_llint); + extent2 = sizeof(llint_char); + if (extent1 != extent2) padding_varies_by_pos = 1; +#endif + + if (padding_varies_by_pos) return 1; + else return 0; +} + +#if 0 +/* from MPICH2 PAC_C_DOUBLE_ALIGNMENT_EXCEPTION test: + * + * Other tests assume that there is potentially a maximum alignment + * and that if there is no maximum alignment, or a type is smaller than + * that value, then we align on the size of the value, with the exception + * of the "position-based alignment" rules we test for separately. + * + * It turns out that these assumptions have fallen short in at least one + * case, on MacBook Pros, where doubles are aligned on 4-byte boundaries + * even when long doubles are aligned on 16-byte boundaries. So this test + * is here specifically to handle this case. + * + * Return value is 4 or 0. +*/ +static int double_align_exception() +{ + struct { char a; double b; } char_double; + struct { double b; char a; } double_char; + int extent1, extent2, align_4 = 0; + + extent1 = sizeof(char_double); + extent2 = sizeof(double_char); + + /* we're interested in the largest value, will let separate test + * deal with position-based issues. + */ + if (extent1 < extent2) extent1 = extent2; + if ((sizeof(double) == 8) && (extent1 % 8) != 0) { + if (extent1 % 4 == 0) { +#ifdef HAVE_MAX_FP_ALIGNMENT + if (HAVE_MAX_FP_ALIGNMENT >= 8) align_4 = 1; +#else + align_4 = 1; +#endif + } + } + + if (align_4) return 4; + else return 0; +} +#endif diff --git a/ompi/mca/io/romio/romio/common/dataloop/typesize_support.h b/ompi/mca/io/romio/romio/common/dataloop/typesize_support.h new file mode 100644 index 0000000000..9b4bdee814 --- /dev/null +++ b/ompi/mca/io/romio/romio/common/dataloop/typesize_support.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- */ +/* + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ +#ifndef TYPESIZE_SUPPORT_H +#define TYPESIZE_SUPPORT_H + +#include "dataloop.h" + +#define DLOOP_Type_footprint PREPEND_PREFIX(Type_footprint) + +typedef struct PREPEND_PREFIX(Type_footprint_s) { + DLOOP_Offset size, extent; + + /* these are only needed for calculating footprint of types + * built using this type. no reason to expose these. + */ + DLOOP_Offset lb, ub, alignsz; + DLOOP_Offset true_lb, true_ub; + int has_sticky_lb; + int has_sticky_ub; +} DLOOP_Type_footprint; + +void PREPEND_PREFIX(Type_calc_footprint)(MPI_Datatype type, + DLOOP_Type_footprint *tfp); + +#endif diff --git a/ompi/mca/io/romio/romio/configure.in b/ompi/mca/io/romio/romio/configure.in index c6e6fc456e..cade4bb5e1 100644 --- a/ompi/mca/io/romio/romio/configure.in +++ b/ompi/mca/io/romio/romio/configure.in @@ -25,6 +25,13 @@ if test -n "$CONFIGURE_ARGS" ; then echo "Configuring with args $CONFIGURE_ARGS" fi +if test "$FROM_MPICH2" = "yes" ; then + CFLAGS="$MPICH2_INTERNAL_CFLAGS" + CXXFLAGS="$MPICH2_INTERNAL_CXXFLAGS" + FFLAGS="$MPICH2_INTERNAL_FFLAGS" + F90FLAGS="$MPICH2_INTERNAL_F90FLAGS" +fi + AC_CONFIG_HEADER(adio/include/romioconf.h) # Open MPI: added AH_TOP @@ -136,7 +143,7 @@ MPIO_REQ_REAL_POBJECTS="_iotest.o _iowait.o _iowaitall.o _iowaitany.o _iotestall # have_aio=no # -known_filesystems="nfs ufs pfs pvfs pvfs2 testfs xfs panfs gridftp" +known_filesystems="nfs ufs pfs pvfs pvfs2 testfs xfs panfs gridftp lustre bgl bglockless" # Open MPI: added "open_mpi_mpi" known_mpi_impls="mpich2_mpi mpich_mpi sgi_mpi hp_mpi cray_mpi lam_mpi open_mpi_mpi" # @@ -180,6 +187,11 @@ if test "$enable_weak_symbols" = "no" ; then TRY_WEAK_SYMBOLS=0 fi MPI=$with_mpi +# Open MPI: No! +#if test -n "$with_mpi"; then +# CC=$MPI/bin/mpicc +#fi + FILE_SYSTEM=$with_file_system # dnl AC_CONFIG_AUX_DIR(../../../confdb) @@ -206,7 +218,7 @@ if test -z "$ARCH" -a -x $srcdir/util/tarch ; then ARCH=`$srcdir/util/tarch | sed s/-/_/g` if test -z "$ARCH" ; then AC_MSG_RESULT(Unknown!) - AC_MSG_ERROR([Error: Couldn\'t guess target architecture, you must + AC_MSG_ERROR([Error: Could not guess target architecture, you must set an architecture type with the environment variable ARCH]) fi eval "arch_$ARCH=1" @@ -377,7 +389,7 @@ fi # C_DEBUG_FLAG="-g" F77_DEBUG_FLAG="-g" -C_OPT_FLAG=${CFLAGS:-"-O"} +# C_OPT_FLAG=${CFLAGS:-"-O"} # MPICH1 uses OPTFLAGS and OPTFLAGSC to specify separate optimization # flags for the C compiler (this is better that adding it to the # undifferentiated CFLAGS, at least on input). @@ -465,6 +477,7 @@ case $ARCH in esac AC_PROG_CC + if test "$NOF77" != 1 ; then # Grrr. The autoconf test for F77 will abort the configure # if no compiler is found. We'd prefer to simply turn off @@ -480,7 +493,6 @@ if test $DEBUG = "yes" ; then #else # CFLAGS="$CFLAGS $C_OPT_FLAG" fi - # --------------------------------------------------------------------------- # Here go the rest of the tests # --------------------------------------------------------------------------- @@ -511,6 +523,7 @@ if test -n "$arch_rs6000"; then longlongsize=${longlongsize:-8} MPI_OFFSET_KIND1=" INTEGER MPI_OFFSET_KIND" MPI_OFFSET_KIND2=" PARAMETER (MPI_OFFSET_KIND=8)" + MPI_OFFSET_KIND_VAL=8 fi # @@ -556,6 +569,7 @@ if test -n "$arch_SX4" ; then fi MPI_OFFSET_KIND1=" INTEGER MPI_OFFSET_KIND" MPI_OFFSET_KIND2=" PARAMETER (MPI_OFFSET_KIND=8)" + MPI_OFFSET_KIND_VAL=8 fi # if test -n "$arch_hpux" || test -n "$arch_sppux" ; then @@ -599,6 +613,7 @@ if test -n "$arch_hpux" || test -n "$arch_sppux" ; then MPI_OFFSET_KIND1=" INTEGER MPI_OFFSET_KIND" MPI_OFFSET_KIND2=" PARAMETER (MPI_OFFSET_KIND=8)" + MPI_OFFSET_KIND_VAL=8 if test "$CC" != "gcc" ; then ROMIO_TCFLAGS="-Ae" fi @@ -615,6 +630,7 @@ if test -n "$arch_alpha" || test -n "$arch_ALPHA" ; then MPI_OFFSET_KIND1=" INTEGER MPI_OFFSET_KIND" MPI_OFFSET_KIND2=" PARAMETER (MPI_OFFSET_KIND=8)" + MPI_OFFSET_KIND_VAL=8 ROMIO_LIBLIST="$ROMIO_LIBLIST -laio" fi # @@ -640,6 +656,7 @@ if test -n "$arch_CRAY" ; then MPI_OFFSET_KIND1=" INTEGER MPI_OFFSET_KIND" MPI_OFFSET_KIND2=" PARAMETER (MPI_OFFSET_KIND=8)" + MPI_OFFSET_KIND_VAL=8 fi # if test -n "$arch_sgi" ; then @@ -687,7 +704,10 @@ if test -n "$arch_IRIX"; then MPI=mpich2 mpi_mpich2=1 elif test -f "/usr/include/mpi.h" ; then - AC_MSG_WARN([assuming that you want to use ROMIO with SGI\'s MPI]) + # removed use of escaped single quotes in messages + # because they confuse Emacs, making it hard to + # read the files (with emacs :) ) + AC_MSG_WARN([assuming that you want to use ROMIO with the SGI MPI]) MPI=sgi mpi_sgi=1 else @@ -728,6 +748,7 @@ if test -n "$arch_IRIX"; then AC_DEFINE(IRIX,1,[Define if IRIX]) MPI_OFFSET_KIND1=" INTEGER MPI_OFFSET_KIND" MPI_OFFSET_KIND2=" PARAMETER (MPI_OFFSET_KIND=8)" + MPI_OFFSET_KIND_VAL=8 fi AC_HAVE_FUNCS(memalign) @@ -836,6 +857,7 @@ fi if test -n "$OFFSET_KIND" -a "A$MPI_OFFSET_KIND1" = "A!" ; then MPI_OFFSET_KIND1=" INTEGER MPI_OFFSET_KIND" MPI_OFFSET_KIND2=" PARAMETER (MPI_OFFSET_KIND=$OFFSET_KIND)" + MPI_OFFSET_KIND_VAL=$OFFSET_KIND else if test "$FORTRAN_MPI_OFFSET" = "integer*8" && test "A$MPI_OFFSET_KIND2" = "A!" && test $NOF77 = 0 && test $NOF90 = 0 ; then PAC_MPI_OFFSET_KIND @@ -864,13 +886,13 @@ EOF rm -f conftest* AC_MSG_RESULT($ac_cv_f77_offset_type_works) - if test "$ac_cv_f77_offset_type_works" != "yes" ; then + if test "$ac_cv_f77_offset_type_works" != "yes" -a -n "$MPI_OFFSET_KIND_VAL"; then AC_MSG_CHECKING([whether we can use KIND with the selected F77 compiler $F77]) ac_cv_f77_allows_offset_kind=no rm -f conftest* cat >conftest.f <>config.log 2>&1 && test -x conftest ; then @@ -879,7 +901,7 @@ EOF rm -f conftest* AC_MSG_RESULT($ac_cv_f77_allows_offset_kind) if test "$ac_cv_f77_allows_offset_kind" ; then - FORTRAN_MPI_OFFSET="integer (kind=$MPI_OFFSET_KIND)" + FORTRAN_MPI_OFFSET="integer (kind=$MPI_OFFSET_KIND_VAL)" else AC_MSG_WARN([Could not find a way to declare an integer type corresponding to MPI_Offset in Fortran.]) fi @@ -1097,14 +1119,20 @@ FILE_SYSTEM="testfs $FILE_SYSTEM" # - if we can find 'pvfs2-config' in our path, we can use it to set CFLAGS, # LIBS, and LDFLAGS accordingly # - as a fallback, use CFLAGS, LIBS, and LDFLAGS passed in by the user +# - don't do any of this if --with-file-system was given and did not include +# 'pvfs2': i.e. don't surprise the user with pvfs support. -AC_PATH_PROG(PVFS2_CONFIG, pvfs2-config, notfound, [$PATH:${with_pvfs2}/bin]) +AC_PATH_PROG(PVFS2_CONFIG, pvfs2-config, notfound, [${with_pvfs2}/bin:$PATH]) if test $PVFS2_CONFIG != "notfound" ; then - CFLAGS="$CFLAGS $( $PVFS2_CONFIG --cflags)" - LIBS="$LIBS $( $PVFS2_CONFIG --libs)" - ROMIO_LIBLIST="$ROMIO_LIBLIST $LIBS" - FILE_SYSTEM="pvfs2 $FILE_SYSTEM" - file_system_pvfs2=1 + if test -n "${with_pvfs2}" -o -n "${file_system_pvfs2}" ; then + # the user either told us where pvfs is or asked for it in + # --with-file-system (or both) + CFLAGS="$CFLAGS $( $PVFS2_CONFIG --cflags)" + LIBS="$LIBS $( $PVFS2_CONFIG --libs)" + ROMIO_LIBLIST="$ROMIO_LIBLIST $LIBS" + FILE_SYSTEM="pvfs2 $FILE_SYSTEM" + file_system_pvfs2=1 + fi fi if test "$PVFS2_CONFIG" = "notfound" -a -n "$with_pvfs2" ; then @@ -1131,6 +1159,15 @@ fi if test -n "$file_system_ufs"; then AC_DEFINE(ROMIO_UFS,1,[Define for ROMIO with UFS]) fi +if test -n "$file_system_bgl"; then + AC_DEFINE(ROMIO_BGL,1,[Define for ROMIO with BGL]) +fi +if test -n "$file_system_bglockless"; then + if test x"$file_system_bgl" = x ; then + AC_MSG_ERROR("bglockless requested without bgl") + fi + AC_DEFINE(ROMIO_BGLOCKLESS,1,[Define for lock-free ROMIO with BGL]) +fi if test -n "$file_system_hfs"; then AC_DEFINE(ROMIO_HFS,1,[Define for ROMIO with HFS]) fi @@ -1145,6 +1182,9 @@ fi if test -n "$file_system_testfs"; then AC_DEFINE(ROMIO_TESTFS,1,[Define for ROMIO with TESTFS]) fi +if test -n "$file_system_lustre"; then + AC_DEFINE(ROMIO_LUSTRE,1,[Define for ROMIO with LUSTRE]) +fi if test -n "$file_system_xfs"; then AC_DEFINE(ROMIO_XFS,1,[Define for ROMIO with XFS]) @@ -1155,11 +1195,11 @@ if test -n "$file_system_xfs"; then rm -f confmemalignval rm -f /tmp/romio_tmp.bin AC_TRY_RUN([ - #include - #include - #include - #include - main() { +#include +#include +#include +#include +int main(int argc, char **argv) { struct dioattr st; int fd = open("/tmp/romio_tmp.bin", O_RDWR | O_CREAT, 0644); FILE *f=fopen("confmemalignval","w"); @@ -1198,7 +1238,7 @@ if test -n "$file_system_pvfs"; then # selected) AC_MSG_CHECKING([that pvfs.h can be compiled]) AC_TRY_COMPILE([ - #include +#include ],[ ],pvfs_header_ok=yes,pvfs_header_ok=no ) @@ -1208,7 +1248,7 @@ if test -n "$file_system_pvfs"; then # Try again, but with int64_t AC_TRY_COMPILE([ typedef long long int int64_t; - #include +#include ],[ ], pvfs_header_ok="yes with int64 definition" @@ -1240,10 +1280,32 @@ if test -n "$file_system_pvfs2"; then ) fi +# layout change after pvfs-2.6.3: +if test -n "$file_system_pvfs2"; then + AC_COMPILE_IFELSE( + [ +#include +#include "pvfs2.h" + int main(int argc, char **argv) { + PVFS_object_ref ref; + PVFS_sys_attr attr; + PVFS_sys_create(NULL, ref, attr, NULL, NULL, NULL, NULL); + return 0; } + ], + , AC_DEFINE(HAVE_PVFS2_CREATE_WITHOUT_LAYOUT, 1, + [Define if PVFS_sys_create does not have layout parameter]) + ) +fi + + if test -n "$file_system_gridftp"; then AC_DEFINE(ROMIO_GRIDFTP, 1, [Define for ROMIO with gridftp]) fi +if test -n "$file_system_bgl"; then + SYSDEP_INC=-I${prefix}/include +else + SYSDEP_INC= # # Check for presence and characteristics of async. I/O calls if # not disabled. @@ -1321,6 +1383,7 @@ if test "x$disable_aio" = "xno" -a -n "$aio_write_found" ; then # We try to compile it, not just read it. AC_MSG_CHECKING([if aio.h exists and can be compiled]) AC_TRY_COMPILE([ +#include #include ], [],have_aio_h=yes,have_aio_h=no) AC_MSG_RESULT($have_aio_h) @@ -1330,15 +1393,19 @@ if test "x$disable_aio" = "xno" -a -n "$aio_write_found" ; then AC_MSG_CHECKING([if sys/aio.h exists and can be compiled]) AC_TRY_COMPILE([ +#include #include ], [],have_sys_aio_h=yes,have_sys_aio_h=no) AC_MSG_RESULT($have_sys_aio_h) if test "$have_sys_aio_h" = yes ; then AC_DEFINE(HAVE_SYS_AIO_H,1,[Define if sys/aio.h exists and can be compiled]) fi + if test "$have_aio_h" = "no" -a "$have_sys_aio_h" = "no" ; then + disable_aio=yes + fi fi -if test "$have_aio_h" = "yes" -o "$have_sys_aio_h" = "yes" ; then +if test "$have_aio_h" = "yes" -o "$have_sys_aio_h" = "yes" -o "x$disable_aio" = "xno"; then # Check that aio is available (many systems appear to have aio # either installed improperly or turned off). @@ -1372,6 +1439,8 @@ if test "$have_aio_h" = "yes" -o "$have_sys_aio_h" = "yes" ; then AC_MSG_RESULT(yes)], [aio_runs=no AC_MSG_RESULT(no)] + [aio_runs=no + AC_MSG_RESULT(no: aio routines disabled when cross compiling)] ) if test "$aio_runs" != "no" ; then AC_DEFINE(ROMIO_HAVE_WORKING_AIO, 1, Define if AIO calls seem to work) @@ -1409,6 +1478,8 @@ if test "$have_aio_h" = "yes" -o "$have_sys_aio_h" = "yes" ; then AC_MSG_RESULT(yes)], [aio_two_arg_write=no AC_MSG_RESULT(no)] + [aio_two_arg_write=no + AC_MSG_RESULT(no: cannot test when cross-compiling)] ) if test "$aio_two_arg_write" != "no" -a "$aio_runs" != "yes" ; then @@ -1437,6 +1508,8 @@ if test "$have_aio_h" = "yes" -o "$have_sys_aio_h" = "yes" ; then AC_MSG_RESULT(yes)], [aio_two_arg_suspend=no AC_MSG_RESULT(no)] + [aio_two_arg_suspend=no + AC_MSG_RESULT(no: cannot test when cross compiling)] ) if test "$aio_two_arg_suspend" != "no" -a "$aio_runs" != "yes" ; then @@ -1447,15 +1520,18 @@ if test "$have_aio_h" = "yes" -o "$have_sys_aio_h" = "yes" ; then AC_MSG_CHECKING([for aio_fildes member of aiocb structure]) AC_TRY_COMPILE([ - #ifdef HAVE_SIGNAL_H - #include - #endif - #ifdef HAVE_AIO_H - #include - #endif - #ifdef HAVE_SYS_AIO_H - #include - #endif +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_AIO_H +#include +#endif +#ifdef HAVE_SYS_AIO_H +#include +#endif ],[ struct aiocb a; a.aio_fildes = 0; @@ -1466,17 +1542,20 @@ if test "$have_aio_h" = "yes" -o "$have_sys_aio_h" = "yes" ; then ) AC_MSG_CHECKING(for aio_whence member of aiocb structure) AC_TRY_COMPILE([ - #ifdef HAVE_SIGNAL_H - #include - #endif - #ifdef HAVE_AIO_H - #include - #endif - #ifdef HAVE_SYS_AIO_H - #include - #endif - #include - #include +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_AIO_H +#include +#endif +#ifdef HAVE_SYS_AIO_H +#include +#endif +#include +#include ],[ struct aiocb a; a.aio_whence = SEEK_SET; @@ -1487,15 +1566,18 @@ if test "$have_aio_h" = "yes" -o "$have_sys_aio_h" = "yes" ; then ) AC_MSG_CHECKING(for aio_handle member of aiocb structure) AC_TRY_COMPILE([ - #ifdef HAVE_SIGNAL_H - #include - #endif - #ifdef HAVE_AIO_H - #include - #endif - #ifdef HAVE_SYS_AIO_H - #include - #endif +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_AIO_H +#include +#endif +#ifdef HAVE_SYS_AIO_H +#include +#endif ],[ struct aiocb a; aio_handle_t h; @@ -1508,15 +1590,18 @@ if test "$have_aio_h" = "yes" -o "$have_sys_aio_h" = "yes" ; then ) AC_MSG_CHECKING(for aio_reqprio member of aiocb structure) AC_TRY_COMPILE([ - #ifdef HAVE_SIGNAL_H - #include - #endif - #ifdef HAVE_AIO_H - #include - #endif - #ifdef HAVE_SYS_AIO_H - #include - #endif +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_AIO_H +#include +#endif +#ifdef HAVE_SYS_AIO_H +#include +#endif ],[ struct aiocb a; @@ -1528,15 +1613,18 @@ if test "$have_aio_h" = "yes" -o "$have_sys_aio_h" = "yes" ; then ) AC_MSG_CHECKING(for aio_sigevent member of aiocb structure) AC_TRY_COMPILE([ - #ifdef HAVE_SIGNAL_H - #include - #endif - #ifdef HAVE_AIO_H - #include - #endif - #ifdef HAVE_SYS_AIO_H - #include - #endif +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_AIO_H +#include +#endif +#ifdef HAVE_SYS_AIO_H +#include +#endif ],[ struct aiocb a; @@ -1547,6 +1635,7 @@ if test "$have_aio_h" = "yes" -o "$have_sys_aio_h" = "yes" ; then AC_MSG_RESULT(no) ) +fi fi # End of aio-related tests @@ -1556,18 +1645,18 @@ fi AC_CHECK_HEADERS(sys/vfs.h sys/param.h sys/mount.h sys/statvfs.h) AC_MSG_CHECKING([whether struct statfs properly defined]) AC_TRY_COMPILE([ - #ifdef HAVE_SYS_VFS_H - #include - #endif - #ifdef HAVE_SYS_STATVFS_H - #include - #endif - #ifdef HAVE_SYS_PARAM_H - #include - #endif - #ifdef HAVE_SYS_MOUNT_H - #include - #endif +#ifdef HAVE_SYS_VFS_H +#include +#endif +#ifdef HAVE_SYS_STATVFS_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif ],[ struct statfs f; ], @@ -1584,9 +1673,9 @@ fi AC_MSG_CHECKING([for f_fstypename member of statfs structure]) AC_TRY_COMPILE([ - #include - #include - #include +#include +#include +#include ],[ struct statfs f; memset(&f, 0, sizeof(f)); @@ -1608,15 +1697,15 @@ AC_CHECK_FUNCS(stat, [AC_DEFINE(HAVE_STAT, 1, Define if stat function is present) AC_MSG_CHECKING([for st_fstype member of stat structure]) AC_TRY_COMPILE([ - #ifdef HAVE_SYS_TYPES_H - #include - #endif - #ifdef HAVE_SYS_STAT_H - #include - #endif - #ifdef HAVE_UNISTD_H - #include - #endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif ],[ struct stat s; @@ -1636,20 +1725,20 @@ AC_CHECK_FUNCS(statvfs, [AC_DEFINE(HAVE_STATVFS, 1, Define if statvfs function is present) AC_MSG_CHECKING([for f_basetype member of statvfs structure]) AC_TRY_COMPILE([ - #ifdef HAVE_SYS_TYPES_H - #include - #endif - #ifdef HAVE_SYS_VFS_H - #include - #endif - #ifdef HAVE_SYS_STATVFS_H - #include - #endif - ], [ +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_VFS_H +#include +#endif +#ifdef HAVE_SYS_STATVFS_H +#include +#endif + ], [[ struct statvfs foo; - foo.f_basetype = NULL; - ], + foo.f_basetype[0] = 'a'; + ]], AC_MSG_RESULT(yes) AC_DEFINE(ROMIO_HAVE_STRUCT_STATVFS_WITH_F_BASETYPE, 1, defined if struct statvfs has a f_basetype member), AC_MSG_RESULT(no) @@ -1667,9 +1756,9 @@ if test "$ac_cv_header_unistd_h" = "yes" ; then AC_TRY_COMPILE([ #include ], [ - #ifndef _LFS64_LARGEFILE - #error no largefile defines - #endif +#ifndef _LFS64_LARGEFILE +#error no largefile defines +#endif ],ac_cv_has_large_file_defines=yes,ac_cv_has_large_file_defines=no)]) if test "$ac_cv_has_large_file_defines" = "yes" ; then # See if we can use them @@ -1833,6 +1922,7 @@ if test "$MPI" = "open_mpi" ; then HAVE_MPI_DARRAY_SUBARRAY="#define HAVE_MPI_DARRAY_SUBARRAY" # Open MPI: see comments in mpi-io/mpioprof.h AC_DEFINE(MPIO_BUILD_PROFILING, 1, [hack to make ROMIO build without profiling]) + DEFINE_HAVE_MPI_GREQUEST="#define HAVE_MPI_GREQUEST" # # if MPICH, use mpicc in test programs @@ -1909,6 +1999,8 @@ elif test $FROM_MPICH2 = yes ; then EXTRA_DIRS="" AC_DEFINE(HAVE_STATUS_SET_BYTES,1,[Define if status_set_bytes available]) DEFINE_HAVE_MPI_GREQUEST="#define HAVE_MPI_GREQUEST" + # Add the MPICH2_INCLUDE_FLAGS to CPPFLAGS + CPPFLAGS="$CPPFLAGS $MPICH2_INCLUDE_FLAGS" fi # # @@ -2002,6 +2094,23 @@ else F77MPIOINC="include 'mpiof.h'" fi +AC_CHECK_HEADERS(unistd.h, + AC_MSG_CHECKING([for large file defines]) + AC_TRY_COMPILE([ + #include + ], [ + #ifndef _LFS64_LARGEFILE + #error no largefile defines + #endif + ], + CFLAGS="${CFLAGS} -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64" + AC_MSG_RESULT(yes), + AC_MSG_RESULT(none) ) + ) + +echo "setting SYSDEP_INC to $SYSDEP_INC" +AC_SUBST(SYSDEP_INC) + # Open MPI: use the exact same restrict test that we use in the # upper-level Open MPI configure script so that we always get the same # #define for "restrict" (there are a small number of files that will @@ -2018,7 +2127,7 @@ AM_PROG_LIBTOOL # Open MPI: setup the AM_CONDITIONALs to build the different adio devices m4_foreach([my_fs], - [gridftp, hfs, nfs, panfs, pfs, piofs, pvfs, pvfs2, sfs, testfs, ufs, xfs], + [bgl, bglockless, gridftp, lustre, nfs, panfs, pfs, pvfs, pvfs2, sfs, testfs, ufs, xfs], [AM_CONDITIONAL(BUILD_[]AS_TR_CPP(my_fs), [test -n "$file_system_]my_fs["])]) echo "setting CC to $CC" @@ -2114,9 +2223,10 @@ dnl #AC_SUBST(ENABLE_SHLIB) #AC_SUBST(CC_SHL) #AC_SUBST(LIBTOOL) +# Open MPI: This is no longer necessary with modern versions of autotools # Remove the .a from the library file name (so that we can use .so or # other appropriate suffix) -#SHLIBNAME=`echo $LIBNAME | sed 's/\.a//'` +#SHLIBNAME=`echo $LIBNAME | sed 's/\.a$//'` #AC_SUBST(SHLIBNAME) #dnl # Open MPI: This is no longer necessary with modern versions of autotools @@ -2140,13 +2250,14 @@ AC_CONFIG_FILES([ localdefs adio/Makefile + adio/ad_bgl/Makefile + adio/ad_bglockless/Makefile adio/ad_gridftp/Makefile - adio/ad_hfs/Makefile + adio/ad_lustre/Makefile adio/ad_nfs/Makefile - adio/ad_ntfs/Makefile + adio/ad_ntfs/Makefile adio/ad_panfs/Makefile adio/ad_pfs/Makefile - adio/ad_piofs/Makefile adio/ad_pvfs/Makefile adio/ad_pvfs2/Makefile adio/ad_sfs/Makefile @@ -2167,6 +2278,10 @@ AC_CONFIG_FILES([ test/Makefile test/misc.c test/large_file.c + test/fmisc.f + test/fcoll_test.f + test/pfcoll_test.f + test/fperf.f test/runtests util/romioinstall @@ -2175,6 +2290,19 @@ AC_CONFIG_FILES([ util/Makefile ]) +# Open MPI: intentionally skip the following: +# mpi2-other/info/Makefile +# mpi2-other/info/fortran/Makefile +# mpi2-other/array/Makefile +# mpi2-other/array/fortran/Makefile +# mpi-io/fortran/Makefile +# mpi-io/glue/mpich2/Makefile +# mpi-io/glue/mpich1/Makefile +# mpi-io/glue/default/Makefile +# Open MPI: according to romio-maint@mcs.anl.gov: +# adio/ad_hfs/Makefile: old and no longer used +# adio/ad_ntfs/Makefile: not built with GNU Autotools +# adio/ad_piofs/Makefile: old and no longer used AC_OUTPUT # # Open MPI - don't need to remove this... diff --git a/ompi/mca/io/romio/romio/doc/Makefile.am b/ompi/mca/io/romio/romio/doc/Makefile.am index b6005c0c27..9a71567242 100644 --- a/ompi/mca/io/romio/romio/doc/Makefile.am +++ b/ompi/mca/io/romio/romio/doc/Makefile.am @@ -9,6 +9,7 @@ # University of Stuttgart. All rights reserved. # Copyright (c) 2004-2005 The Regents of the University of California. # All rights reserved. +# Copyright (c) 2008 Cisco Systems, Inc. All rights reserved. # $COPYRIGHT$ # # Additional copyrights may follow @@ -18,7 +19,7 @@ # Portions taken from original ROMIO Makefile* # -EXTRA_DIST = users-guide.bbl users-guide.ps.gz users-guide.tex source-guide.tex README +EXTRA_DIST = users-guide.pdf users-guide.tex source-guide.tex README romio.bib all: latex users-guide.tex diff --git a/ompi/mca/io/romio/romio/doc/romio.bib b/ompi/mca/io/romio/romio/doc/romio.bib new file mode 100644 index 0000000000..bb94a2d2ea --- /dev/null +++ b/ompi/mca/io/romio/romio/doc/romio.bib @@ -0,0 +1,92 @@ +@Book{grop99a, + author = {William Gropp and Ewing Lusk and Rajeev Thakur}, + title = {{Using MPI-2: Advanced Features of the Message-Passing Interface}}, + publisher = {MIT Press}, + year = 1999, + address = {Cambridge, MA} +} + +@Misc{mpi97a, + author = "{Message Passing Interface Forum}", + title = "{{MPI-2}: Extensions to the Message-Passing Interface}", + note = "{\tt http://www.mpi-forum.org/docs/docs.html}", + year = {July 1997} +} + +@InProceedings{thak96e, + author = {Rajeev Thakur and William Gropp and Ewing Lusk}, + title = {An Abstract-Device Interface for Implementing Portable + Parallel-{I/O} Interfaces}, + booktitle = {Proceedings of the 6th Symposium on the Frontiers of + Massively Parallel Computation}, + publisher = {IEEE Computer Society Press}, + pages = {180--187}, + month = {October}, + year = {1996} +} + +@InProceedings{thak99b, + author = {Rajeev Thakur and William Gropp and Ewing Lusk}, + title = {On Implementing {MPI-IO} Portably and with High Performance}, + booktitle = {Proceedings of the 6th Workshop on I/O in Parallel and + Distributed Systems}, + pages = {23--32}, + year = {1999}, + month = {May}, + publisher = {ACM Press} +} + +@InProceedings{bordawekar:primitives, + author = {Rajesh Bordawekar and Juan Miguel {del Rosario} and Alok + Choudhary}, + title = {Design and Evaluation of Primitives for Parallel {I/O}}, + booktitle = {Proceedings of Supercomputing '93}, + year = {1993}, + pages = {452--461}, + publisher = {IEEE Computer Society Press}, + address = {Portland, OR}, + URL = {ftp://erc.cat.syr.edu/ece/choudhary/PASSION/sc93.ps.Z}, + keywords = {parallel I/O, pario-bib}, + abstract = {In this paper, we show that the performance of parallel file + systems can vary greatly as a function of the selected data distributions, + and that some data distributions can not be supported. Also, we describe how + the parallel language extensions, though simplifying the programming, do not + address the performance problems found in parallel file systems. \par We have + devised an alternative scheme for conducting parallel I/O - the Two-Phase + Access Strategy - which guarantees higher and more consistent performance + over a wider spectrum of data distributions. We have designed and implemented + runtime primitives that make use of the two-phase access strategy to conduct + parallel I/O, and facilitate the programming of parallel I/O operations. We + describe these primitives in detail and provide performance results which + show that I/O access rates are improved by up to several orders of magnitude. + Further, we show that the variation in performance over various data + distributions is restricted to within a factor of 2 of the best access + rate.}, + comment = {Much of this is the same as delrosario:two-phase, except for + section~4 where they describe their actual run-time library of primitives, + with a little bit about how it works. It's not clear, for example, how their + meta-data structures are distributed across the machine. They also do not + describe their methods for the data redistribution.} +} + +@TechReport{choudhary:passion, + author = {Alok Choudhary and Rajesh Bordawekar and Michael Harry and Rakesh + Krishnaiyer and Ravi Ponnusamy and Tarvinder Singh and Rajeev Thakur}, + title = {{PASSION:} Parallel And Scalable Software for Input-Output}, + year = {1994}, + month = {September}, + number = {SCCS-636}, + institution = {ECE Dept., NPAC and CASE Center, Syracuse University}, + later = {thakur:jpassion}, + URL = {ftp://erc.cat.syr.edu/ece/choudhary/PASSION/passion_report.ps.Z}, + keywords = {parallel I/O, out-of-core, pario-bib}, + comment = {This TR overviews the PASSION project, and all its components: + two-phase access, out-of-core support for structured and unstructured + problems, data sieving, prefetching, caching, compiler and language support, + file system support, virtual parallel file system, and parallel pipes. They + reference many of their related papers in an extensive bibliography. See also + singh:adopt, jadav:ioschedule, thakur:passion, thakur:runtime, + bordawekar:efficient, thakur:out-of-core, delrosario:prospects, + delrosario:two-phase, bordawekar:primitives, bordawekar:delta-fs.} +} + diff --git a/ompi/mca/io/romio/romio/doc/users-guide.bbl b/ompi/mca/io/romio/romio/doc/users-guide.bbl deleted file mode 100644 index f4eff7a802..0000000000 --- a/ompi/mca/io/romio/romio/doc/users-guide.bbl +++ /dev/null @@ -1,41 +0,0 @@ -\begin{thebibliography}{1} - -\bibitem{bordawekar:primitives} -R. Bordawekar, J.~M. del Rosario, and A. Choudhary. -\newblock {Design and Evaluation of Primitives for Parallel {I/O}}. -\newblock In \emph{Proceedings of Supercomputing '93}, pages 452--461. -IEEE Computer Society Press, 1993. - -\bibitem{choudhary:passion} -A. Choudhary, R. Bordawekar, M. Harry, -R. Krishnaiyer, R. Ponnusamy, T. Singh, R. Thakur. -\newblock {{PASSION:} Parallel And Scalable Software for Input-Output}. -\newblock Technical Report SCCS-636, September 1994, ECE -Dept., NPAC and CASE Center, Syracuse University. - -\bibitem{grop99a} -William Gropp, Ewing Lusk, and Rajeev Thakur. -\newblock {\em {Using MPI-2: Advanced Features of the Message-Passing - Interface}}. -\newblock MIT Press, Cambridge, MA, 1999. - -\bibitem{mpi97a} -{Message Passing Interface Forum}. -\newblock {{MPI-2}: Extensions to the Message-Passing Interface}, July 1997. -\newblock {\tt http://www.mpi-forum.org/docs/docs.html}. - -\bibitem{thak96e} -Rajeev Thakur, William Gropp, and Ewing Lusk. -\newblock {An Abstract-Device Interface for Implementing Portable Parallel-I/O - Interfaces}. -\newblock In {\em Proceedings of the 6th Symposium on the Frontiers of - Massively Parallel Computation}, pages 180--187. IEEE Computer Society Press, - October 1996. - -\bibitem{thak99b} -Rajeev Thakur, William Gropp, and Ewing Lusk. -\newblock {On Implementing MPI-IO Portably and with High Performance}. -\newblock In {\em Proceedings of the 6th Workshop on I/O in Parallel and - Distributed Systems}, pages 23--32. ACM Press, May 1999. - -\end{thebibliography} diff --git a/ompi/mca/io/romio/romio/doc/users-guide.pdf b/ompi/mca/io/romio/romio/doc/users-guide.pdf new file mode 100644 index 0000000000000000000000000000000000000000..35bb792ee88edc11985cbcfe6c8e692b0bab095a GIT binary patch literal 157904 zcmeFZ2|Uza+dpoJ7_u*sELlQojA4u=WsS1$k~A0$W68djtwa=BP!f?PC8;b~QYmFG zDf=!(L`W+7ozLepmA>EmdG4P3Szf>2|Np$0GiSNZbf~S)M z*%wV9qP@VsFPY%$idNS~8-af$;Op=0?E}g{6N4Pd1P}CKe=k2G$m2tDarHxc`g=GK zeSAqSo}dbVrw`hj=;KKA^g|OofX^4OM{*$K-UJ{9GSS%&4f~Ktg0COZhve&y_9pxL zq8+^;@wf(iyAnMiW+X2sG*XigqK_Ba%ae%q3-m(!x%v=^XlE~fA0QIYK3`H0+7~p` z6HP=aMgoM6UXToZi5?`xhcv;-D-ci}_9u`*)rh`+Bri`m>PG-g@I$MkHPD)9EwnaT z2fYWai`GN$MejrFqYco8Xd|>S+5~NiHba}EEzp)|E3`G*2E88$n&9Y81RVmJ_d`l> zfRcc;){*4n=RBMth(=(Ozh8 z^kK9Q+86DI_D2Vx1JOa~U~~wY=;;J?8_@&)a`pFgA^7-vkO}^NXg?C!iHLS0f#K&% z@M*;V6tz#QxtfB!9Ha4yC4s0#bnHA4Yb~MEo%ag`wGLN|~5C zUY>p^w3)wyA2dy&xr#xd4GA7Zv<~n4C9*f<9UE@=P zQ;>8l9=}6L7;z!m-_qcGFdYg?`_m7?d990Zl~aU2G-=QcB(P43z9T;H+3`BwO!^tTKD`9Yybc7?47fDyd;-DM~ z3V<7hKU5z0U}WVW;lRa%APxhuMk)+7@-IHruQ0+)US58OJcTjh$I3#5zygAcLvm1J z;j)kzN>@NMh)7{-1(*V>gaJ*#DnYy{9kM1BX#hfrxRhFe`eI~}-uc-}&`|u({Xw#Y zD~qDY8m<-&_fy(Pf59V(B3sxGl}f299v*c_6JQrxlYIVtzIIf;=2?7(dPIVtz-H@C6T0FzR~8UvC4^I3_N zmxBfh9wV%TRotNom6U+Y@o@d{pw?i{V)0<8D#|JXYCM>ySYf=P5@?+~MhR{q9?WAr z3a6+5W-LY)rwHa4LVySJ2TWod9UJfwDQG7AeT%pw$F0g~km04F(KW7%Ptj6Ah6Y6bt7;QDT*#G=vM%D{wxT zH|&Gc%gmvH!n3&O(P$GIU!0-vjOc9%%okbgX}Ppk1q3{@gq<&;HF{^dlA!TojjG*RDgVghzgbG<*^Jt-#zJor`wX9}<{z&>y~1mE(M53~%yCr0p% z1?LDT4PKgleg8Zu{UsizgXn*JAU;26;Nzkkyr+OuGqm%8a^O7>2hOeXC>*jMg5?w} zzAznD5u%39tbkep1MNnz9}mYPG!R!fKXM#}(-lA*;EtkDz`T^!=0R#fsV7vwA8q_m zZzxV4aDhHQ{2&eWBm9F73d?~HBuA*!A8m#6B4-hZJG52AAB6%Bb*iuu>_T;b!~}Bxwp&43+K=<44v7qwZdTNn}kI%*Z!rQk#5-7Pgva%^!<-I;ou z`vdnV4~%=9-1?}4x7lf%QDTsn9fPE(fJThDfp0-k;}Ma; zcL}v7hx|qaB={D%w|#1KBFC_#3%O@&Sjuf)r%}|jQva%5=?o)7`enQs?@>%u`rwTf z^2|1mRnc5#2lBg}Z-le56qis_ujq#pIFb|6ZaQsYh`xElqhzpaHnz6QM)i1@y`rCW zu<}sTvfr{wcdpp#ZM?wBo|oP9D&}o_A;#%3t-< zi_9BecG_3BQNusj>Ls_%@>8nawhhg%_qtANczSQ~awlq`EgQM#d^r zFKD$tM8y0Hjnwdk&GQqr58@)*`Ce|y_fnPfW>L&RPV$C%*_D5&z2dg zLbdr@(0U)+E2t*s^DQ;RnG;;N;*%yXPV_%N-@a5P{>IokmtdFqwG~IZtNa+_PlzlEyb&YUf6pKsExEenxV6#@i55w8H5lI@&xRxo{hq?NP6}jieLG#jbuiVKY=) zdZT621^%)voU0bs>7*JyWhid;WGqnmm>-6@Y+j~RJ#4F3vDn#qi{7oWNXqMsSJhOj ztMXtS{lWP9%jWCtPp|Ig*+$cTprtM?X+MtT;Ww?53FYrveDLPlR#dMR4rz!;&`X~w z*0MQDvqIf2L__l`X~)RmcJnvQpRAX@Ulr`FxzRJ6GIInYO{MmYQFUgHuH%G|-p8PG zJS4WgnJgB=%W-Tc*Vkt>ZS3#pm3c8M{WVZcc31F6f!^lbbhF>&MY673WDDD6__oLW zaP~xgcWxQA_V<&4gz(mPo7<^0Y|OQp9v|e{eKAn8G3w%K?YgY{A$s!n8?L$S9!Nhi zy!5e`_2}SY2xRM_Oks-EM-H=g1;7A32ZX`)s3Rwgfs{|kiv?yY*!#;^1Z{;s)(vAH zFGn+?pDh|#bkXL-AV0L82f>A?2^J4%2Ni~kFkNG5l3Sbm=f?bpmL~bX?aN@<`(3Nm zH`dWGGe)%9KeIJKyDSdY$(0l!YmNd~ei0uYF=t?uz)~tN4{3^s3sJ01!@|}AEJTNb z(*E=V=K%T|#2=~!!czgL8Nl)byKoE&kAo=iKnnp%C>F9W;qlOZ44on%>kt;WO0x2x zeoA2Zmq#o|aNU(;F(4-nHqKyV;V(b}7{Z1qJY-@*{K$R-9Rif#?G#dp6+s$QFa`tL z*(kMuqZDCzAvK3(heG^N%h$F_K>?f^V5SPNnTp~ECL^Hvf-?m$M1dSg><>A>HI@Zu z4+>ADFDN;GQsW>BV2^{^F0TN)a1SALNGc*L#1FeDq!v(_7!0%{0?&`nAMEa6sQu;d zH=Py{^V*?LW)0+k34O^mEOWT*pX33y1Y8Q_4=+S7sD+5_}|mo7{2@$m}u_eR6tlHD>-@qyF=LODx#hr}< zxV}Mdntr>mA^19yNXV@X+TYU&TxvOb`4EvCo`3nu1}cGwTn>RdGdT3)G7OaVms^{) zUHD&jbMybki2X+zF&0{6er(%6H(q#}|A9uV@b{hfua`Uqy2ctdMvz8q{)e|bkkayB z^g;a>@mX7+|4;M$%SQJ5#`fE`_HR-Csl=iDfcC#rnrk;M>~DwKe^0Fic3s)u^xA3b zUQeLchJUbnYeFNf^XOuIgD1b29sXk7E1tL0>ISZRj%{C`(l`BSN~K!&yr>JOuty|5 z{K`&=wj>wlr%EgwDPj+#OCEh4vO9vyruKdql!T_$31Y(v(?osiz5Hn2gB!zOJAGW@ zi^BI+@?t+>GQ9>(6<+S0T6!(Ojt5mVKd{*J1T z&BeH?qoX-7u`36y?(KNR610DN`C?v4XoAg_-IeEDIJ@R)Qe^wYWw%slZV9rM^?bvA zZn$%N0cC~dKiFSBp7*V3WOyLxp$5m}Eqa2RD@=_Hf{C==*JMv+9?`szf7#fmCvc;B z?hOwCQojE;UY54DSn4+OBrz3zV>dG{JS*?zifaaF2 z6%Q(Kn)&mj&za@EZ(V0Q83C( z@XcS2pL`W=9aikwe7!2fcKIuZm&>7>5fKZ|1x7{%-26Ls+-l5a`)=>uwgsouEPeP} zwnCiKWzwyqdAFNm9xn2xzBW4j&^Nyyl)&7|`j znHxl>nOQ{>Ez@_hzZ1N6@}0=by2)%F-eua{vz!>My=GUQ=+e5!%CEe0IE(Fing8x+ zI`;dKi*CK?3hEC-G=JJBncBPCwsRyI}Z*v=j{G+ zEZ*;K(bun`ByI*ZYwT;x^lI2_QcHZu_{D2_l{gM@$H}{v`TQLp-t}~#1y+J{wN9MA z=!jD2>U*kYTV^@MsJ?Nk{@_SwCYGfxmsVxabEfHT>`3N;=*psN1JpbrWHepIVLLA? z|67=8cL}_oAj{an$r2aIr#*&A>34g_*_S0lmR*_q&#t8F^Gh%IYFRqBwl(CH#6<{x z{8+2>kVf@Mln;Y%`01%z&#(7p#s^k)VkDK1r)=)TjJj3$`m&)L{q4BjeO(VF1xT)s zePGbDh_!oZasOmy$orYGIscb8LeG2OFJ2TH516>&Tk}fW@s06QD%EeP?mXA4*{yFs z$xa#&;TawfvApU3R6hcHk5$%`x!`okC~w#G%vYjCCGnA`Hc5#k)FzaS#V|UK9^F|Qkc#WD2bRB4M`%S%FdujX6lw-KKwYjlt58Xfa=s&eu1Ng*Wtk&4STCL6Z znOnd>3}ChXL+u#oS>V1A7#blB6b2pu)d8rD6hD-Tg=ypg76NEwu#2c5h~k5!ViiDM zV9Z3gA!vwy-KFq^xKVf_Wl>ZgSl2{i6oDQMZhK{6g%<lQS!nukZ~B? zQN#VP=c6KX9t zgyvs!5k9a>;fd5l0a&oW`UAU2j3THT1b>nPtxyEO3}{_~<_Uze0mv(WU4X1)&{aJ` zi3QyR088=;(B-`x)L!Vu3Ruhlq6^s2U~Ux7Fb9es3>tuSQIZ8xLQ0{O3)4fkG}PJ% z9DsOWDPlteuvRc@8b=W}!We;*t?{F@AU!l_7u5K0O?3~xjD0JsZ;Rh5Zl*B#Z;i0_Z@bL9k2V2#;oD4E-j^HEmeiDx?D`odDCU zEf1lD3~#_*CJXNH@F=9pYkKy#dA?Th_;)R49wbkI*+pQw5Rw((Or6jk{$xLrH#r#n z1KI0A@N)$q)BqBUMJ0Fuz%3jF0e%qp$?WLm z9qi+W_VOlrI{1^xL_aj;3xIw}0R-P*4*>1-BRN90BoHPO0WuryLxeE7zWxrrfc{4S zGz*oVGH*E;16KVFl)rPh}fJUb0fuK^f${RY}s+}0y2kvo?bqFu0+rmNYK~I zpAw{05~&?xxO4#G0`TP@fi)!n$oEh4rUMzaUjCr^OHqLUQ@y4rI0!WtR0BY*0qWY@ zhXla3Bu{6OC&@1u4eVeZ5Mms_vVld<&xb^SZKgjwB+me}CqTbLnGy4x2f^Fd&kGpi z0Pq_?%*nvKN&;{lPd^gylf7I>js&tN7!5?w&%sdtnK}b=n;(FR>;3S6yJ0W@h<;w) z(BK6F#fjwT4|fP!b$X@goUq42qCxkQdrP&$w+B}mBw znH~u~pl6Zpg8DJwXWt{CUq;bDXk@yAj&$;O1T%yHBnHF{<{Bl&li&c}6hQ39l%-wFCu6# ztiePFwr#Mmz@rfo3^0*m(En2l6AFUD|L@`y2%eBY>kvFJ;r{q-KUs?={CRGahjy7i zFxWz8!QVmo@{}vxp8#O>{g#@r!Pd;?50$~+pfYgamxDm9e}xfTI^zC{g?$LxT@Jd1 zgTUViFdK34$WZ#hAl0xYLV?Re0B#ggnj%oSfQkp8OyH0Jjt<}; zs0d#*!bo*RaE%I1SU?*9H=O{>3l2K4i$mbx0IrL}D?wYnoUAO+-0*U;5CfnA0*Eg- zpMbkp7`2ZFw~e3>1Ue5GfRiUUTjBvm3Q-DRO&Qkoa6prU-UvVkJGcp`wGIBw51(U^ zMj-g{zq>ymKx^MVzyrFcksvS{5j8 zN1p13dvU0zm92tYH=uKaAi& zfapjul!9S;j2u`5e<^0~rT$@GAQEexyw0e>i3T$utM+ zHas~gk_RghMan?OQb0kV2nas|cP!GS@GzuwFYJf1BVptn6lA{xT?xAwSzz9Rwhs6U z7S8WtT(gR;J!|p2(2@H{3s(M1RmLu(Y5u z{s-?<71o+H{#}@`99BW$xBJu^*86-%E7)r%tlpkAw&2kpdC<^I(s2wuTiQ^3&gFLD zqM$2dS%qe@bjnB3`)VsxQhUz^luIkIljScYK6%|Qg#UQ#!|BXD3l;p~wY%=W%&Ftm zlf8E|N?j~W=|aKojcj4donH_3UK2Djm#Hc8D7%AyIbdcpygPVTqQy}EK(ptrlGd%6 z-!JUEDG`Ww+175C|M+2M;?b(_6S~?D>()nE7sRMh?SHYYNFuy>dvA5+MFZz@)yx}; zm3F-}1`4Dz=jiq;-DeI^YZxjOG|1(OlQ?5i;iAWYLu1^X_`7hS(J6is^ z1U;+G8N5`q&`_p4%4O^U0(YBg^}wrLy8NM}TVoIImxifEjt8GF zw(+q@j%+!;^JR{fmt)=6~G8)Is zi7r}Be<=E~hFh?Kz9Do1lgvioYN(>k>hx$=AEdi9HqLp`ot1&edhm9sOy(t(ZW`X= z7Y2^oTuQpOpJjDa*PVUveYaS3=U47~*Ux@DFRpZ_tBfVfI%JtWDr}t1#8n)^XQuDgBig!N1J&}#azPGvsIaloqvhl) zsSmQ-$lE`Ak4d&`kd!y`F~&_WL?z*P6A1aHq1cF99~8JlCPSO1Mvc5s}(@aceK zKlQF%xN@TSr}?L3FOX!$$9+akDHzF9_qITUb3jwnN^j zc_Bf&w*5(ZwCGjM>WWy2oIN*18HuRfD#>y6J@*Cm3hI>Ku3L6x33oN(*OT*Cs;b48 zTzyn4a*5%_`r{#{F0QK|ygJkwgpEd@Dkbj5_i=xweXDXyid$<7Dx_AD??j!zmlvs- zeP>@7xB3sBKmJf~19>sG_0__cfFqc$G}HDyVXv7A-`+G`H@5Hi?8BVLPIX%YmX5y6 z@NJAJqTafhSIF~o?B@$?ekCWB<7Ztyf2j_C6I1y9f?UeUnUpi*h0?E8!d_UQU`9F8NOYFe5ylWdyaj2EiOI&nMmyB$>-M%L8Z(B!eQX#YEhEiR zC=%ezk<_HAvUB)do$I0WdlSj$Z|SngEIN_j-fiSpaZ+||T1Y?TGyT4ihiW&(m@h?aLq_DU^#qc_#n7kYb(an6$$YPy`0`=)wG%V zCJFBu7?{&El;+K3@Ci0L&c-k%XV;IUHYQQen#+~|Vd0~W?*>~Cp9c9)PQTjJuMtjS9 zoZf6NziDTw$JN5KmwE!C7K(ezcv{;B*9X)ktTtIPXG(p!yVs8Q*prdZrI&@p-x69C zj_gys>3aWkkGNozmIi4sSnY-kE0u&%uRudKEv|J3Pg${2zHT>NjdKnE$X>?dLOw!% zb=j+n`u+e>$DFN*O-n_Wn%%oX~YwNonVOO&kPEQjhU1$b* zpAU$499FXK9AFLgk9ud`Q(@1#!X>6WUYuEm7b_O=V!F?0bz5sO`NhE7g$LT4>&#n* zyT22tZ+4b>2i938ABbA&%e%WtZaO+LC){M;*WEQol~!akH*RcG8!Y+qZN{;;ROzhJ z&B{mRclKUxqT%Kk4X^Q4@0&j17&PZhm>|)GNHA9~cP31&c%0kma%>^|vU9FtKlhAv z=+fkf=(ZzOX&(=*HfgE!bKqhWlyC=EY7g4JrDn3+pOfe@;1pE5U{8gzi1T1}v9mk< z%%18+cZp(jw9B`)k4=xy>aZQ(9%Ufzv9yjo=B;Rv|CNWhZ=sRJw7c9g^BkkQ&)dB= zX!zzx7FX$=PVH72S5h3>XV|BlTQim5eJ4EUD#7+p&!q~DSRHBiU2XS`Q!R`=Cw7)< z7~C&vRxKW!dQ$9enRh_F@R8=*{uWR4go|NU>^l9*IJW9tm(Pz0e6(?>Z&q(Dx;M_yIWYc_p4GIdqEDO;HKK5zG?wm{qZZJRu9)dYp#&nVxOPT%hT zY@<-S5!YNPg8no7>@YyA=?{thC)X9Gbq$vCbNcqPo z6r3;&8-cJXYu;@@xBK9r2*rXIVw87}IzJIBXdMVVPay#BK45L;Hz>+l>!a~MdF26D zb8T(_vG}DN{kw-o@H$WNw?pIA{rk?0mO+Qc0-qP=)9v*qPxp=BH+fxq??2rtKE4=v z1<&CoZYom9*R~q6#3Ux>hPxHc(M5G{Q$IK~Mp%+k@{0Q7kF4JP@@e>Dx!ctjx#mT& z4d)Cw9W9r1#939|ZCwomM8e4Ssb(%(Pj(7({PICF8SuVW$nweRg2 z*QaHCLHeDZS4WLE(3;KrcPjS1y*Srb-tx(9wjyJv`*+?S7~8MxiU_lX3zAO$+W)ys_WLfVyK;X`P&sAb$tz4 zP%jpu!Fqr4=!nZ8*DQvcowhTo_K}xT!m=K0n%8}Iz*^I#;r^yFnxjbu!bk8l5mfX~ z>l8&#_IRTME&ThLqX*YLIUOEYox;Slbp|iX5uvj*J(HL6{-J!ppskO6+j-KF8>fa6 z6lX1VN0c6v*?M`*X==5Pp1pglrI`E7&K%pSRjkgu)4b!cm%OX4RpTk@88SG|DJ4wQ z!oX}S=P=(5?0$ZgIpGki^`Dkrv0f(jh?+^aI~i>fP%B=L5Vh{UDi9Jr-?hjQAk-fC z;b_oOrsz_)kXF1*>yz5Rj*;s!Gj6fg2QSxjQH#8d^-a7sZDV?OVVKM{D^6bL-twiP zTI8mW80@{3#|qG z$Wd+CqqJp@50V-;(H+0DInZmT_p`c(*JW8#vSJBYlg-kfnJ?US-F(tgQh9W)kW91j zuxGi!(Vz$IH3Of>Pt)xrme=1P3FL7HsOcPgQO9S7mZ&-FXd9!Xf4A$Y<2fu#g*?-> zp=XCxHC#7*J7&x5TRT{~y5q%Q%NgfI9}}aBfGu+GQ|0g1h^9T*^y#~(OCa_`$j0w2 zwu(Y$kLj5*3Akc6^liSBvse6;-ja{cd8c$g!OKU!_Z?84>JUop75ZM$*D!Z{NY&)n z!d4EAR`JS{9^X2$3uWo=Sa>g(+mcD2L~tytZ({jpu^*H)3XGBatfhlf)q1S7 zr`Nm=)hZnGpl^K-P1mRG-;a>T-ak66%5!Z?wylzWYr}Dxz%Y-K*DpGy>N3~~-q@X` z8#5Rl_&&h9-G7~n@D16Ht%HHv86{th&c540;B@!f_pZI3t@xh4{HRzg+RZ`J5@X2{#lnI@#yo9zdam!(39%lDkS|d^qB>e}YCZeQM>@RE2#sCd zJ3Lp8wi%=u9TA?9L!}Ok-f~nJW~EX}rQN-u`=cb!&g13}*6)1Z{B``zu`rY9iyp;6 zQ#}F#N0m2+wFE?%TR6qDvwa$N{=j}>!7+!vP>CaI+2`=i0aBF#)A)!viB%ZnN*&sO-38kS8s0MlR!% z&}qTcQ=(S9W&!9I{>Q4W82i2DeJ({mEp=1#>r2Pd*$h^;AsR`?2&PLO;+r3ST<36& zA6uU(b%*zsOBqMY89L&8l>Wh1%ZHeQMiBOC*sUc)#r-;@(Lngd#CDzhqkP+Ao(y#tQ!UMe>q~5sR{gT)VNm-Rrxhmcn21qe z?s~y_)S-c7eVvtrJ|7y5WQK(O8e&)LsZaI$SU)H!<4YFJFI~lQPI;zP$~gKOD(zum zuRFDl`39boG1Ymf_1cUI&8GG?20H#+bv07Nr_%F}`afDeD%>b*{6H{GSuZ*Mv_#y> z+X9EL4B8fROC!s zI$eXTsnnvmi%dQ35jFAt@1wOT3nJzY?expn9ZSL8XmX!*$R5>8y5GbJnTjBx+fY9Fspbyj+JYTJX}x9%+ebmVwJ?k3IVvqb?#od@oY zuD72x**oQw@xH!R<-m6I!vy_9*w~invwRir%w^q`t!9tfzBFiJl1HEaI`K%_;QeIq zRf{XPx!qF60{jn(*J}=A@l{siRN6@Rlz1gd`a^(a?~0-|1Xc zeZdKfgT|W!pGj{)6+Jjg;=x-_<%ZqOYd&LqY6tm>Jl9!eQ;l@44MA76^|=)?Z(8PD zH!PKV<8(XCe~8QEzQZPq0kwVa~7eaB#h$8d^oIy zd^d|#hJfGWPtVi!uG@IW7l^FGn1%Tp9L}q-7T_w~5gP7KJv)p#5~@C(}iXN%X7z@c3;VyQKG_&47|C=UdQONtxhy)Yo-3N{n`506PJ#L zXP(JTZHj!ig=Uxj6?>zEgm1^`Q@U`%?G+qbWh-Uu&qdlD(wdvAtlBmGPWp+{w=mzD z*1oJ)ruU_<+k1GvV$GddpcEdF3dsU|#mJhJCmh|+~#00BHBw05Hg&(|i$xp6H2EV!frn+c3*s)06i3*!p`^u6Jp3J2Exzuk&lr zrBzwJcQ`OHd8{Z`_s#tJb z>uG6kWSX0k|H5OeDONpwuk*7idi6_l&39Q})Tqh`mukhwALOB9&}OB5qV@XSM!W5- zX~JeLk-SCEF1yw$?%VoI;k>$$(G*{rY=0=-OcQLoHf`f&HHk8Rvwf~V{f>LDCmYYHkbzLSlqS5{y;<_a$3whZ(G0r_ zKgV(?&a^kLGssY7uTqtx{y4NK>*3@-ZsIA@@4yf2Y zkvI^v&D@VyQt4ywYxdGt+2of3rXo$Gl9_`vhlh+RIbL5lEQib65HFN)=GHNOO^w?6 zXUh0Arzd#6Skq4bV0!^m3BwrXa;+!y-p>53FG75SJ$vPiWU@FWs2GJa9+Q;A*5fy^ z8$1iU_v!hE;vnh^lDAK!9ur-@(5cU9`1#=MSGG;L*J;R^%3s*rPcWblS~ULyi|}9D14e9@C8zh^O(rp{4bWpWkpZH`jcWscXUG_$`x{ z>mI$cV#{gt(YSS6qH;?_*i}6(ZUY`QX=aCxEf@-%G@xLF;14jc9A^G$Z$6~ENa&8arK<9G1P z&daTgAIrV3nk7VT;fSa&qwU_b*>OMy8`4NL*RGHZc0}P21(joNiUJK99r&{M#Vk z0kOkrwu(DF)E?08yr7zUX|6;fVGj>(11HuI^CISP%ec2>uhd1nDIp~>VdUv?6V=f% z_G1=SEBim58=Y&EX7m*gT%C3+GTrp8{iRhSeRyw%a;}*AQJKR@a!mYo(J?_^Th8r` zZm4}ZwIr&t z!(6e_$Lu#tFxGs0bSvg{`~LNT-dmU&KegYPxK>LXseQ{Nz!xO~1PX&zQutgf8oNqdgbyCm*9()aeMOfK0kOnjbSiE99oOv>RcFO}iV3f7v4t60C&&}#1e`aEu5V-9 zdRp@O>U~-FT25!79@OAl+uiODRoU4k8Vh7>b#3wqnXG9B4vz~~dQ**y9yM35yX)aw zd_l@OMYS2rb%`ZW^w1vBCHGwger9I(ZhsYaMaC0Tte4U7~^2N1wsjE2ru67`P6J8Sh?K+T0U6n+H{rPw9>$aS26!;GKteIHp%K_hu+&$ABsE| zt0Q-3Y?vr-q_XTiSI&T4Iy>@0(IsNe^8r%^*JsSYu`MTFun+H*;))QV9+E#u8^$v_ zbTp{7DrC8JIilainzT?5O618uwqoUspN%zNFoT)q_bdmmnR;|H&m~+Q& z^S0hQj-$e6X)91?z8ru1MDbf|%f}loZkrrlMQvZ;cF!v`I$gau z=qZ>moT|{XE%@ctMkMKi@KivW;p?FngXt| zLwYQ(vN&_+G$wvkhYLH?vm#=R)ry*9NUAR5?8iDoqEb%PvL2=z_!4W9z1hq9T0QQz;cM%C-A9@6EQ_Y-Xe# zI^|ktP_eM*U|WNv?sHV`hf#ms%GS^4+3?q@9(WmIB{5$tElpxl%x^7UF1NqAlruKYJ;y|5oEo0C5e~a_XNh0;-M5oBbr1z{|kJLT|Cbmi&nJjDionMw$7JZ&(5vJ!S*rh9+Svq&(;hVw) zm8EE3{Rgob^Gy8F20LQhywtiuS z*C`hqzu=wnHn&c@NwBoOKC*8|=5bu8jhrXdEA1M4dDDij+aoK9> z{&2~_1TRc#afX%f$Y*{j*8`Wfx14@pb#KEFZuy{nyV_Rl!X*wel#V`dX$+ghD!d#^ zBJ3Q+uE&iC=9B41>fXe~(=UwE5zkEpyUPS})IS%vrF#)?aBh=f-64Ya1zoM~(GOcp znqxn4?YJ4*B7<_A8XcIwAgypjGy0ADLF}=lEBD{yrq6hG96$W=p`gzB>s1CSc|P`o z2aKP%JLEpQ(5o*~#$w-$XH+{UzR&PWx~o=y@r{{+Nk^5Kb@I1sMKnxr%Tn#Et&69l z(&FCzTI&|qD350HMU}Uww4|O)@FWp8Zq~smog2sE1Wk4h=5LA-Ay($4L<>Ad2j*W1 zxOy|SVMAQciE8f4$rFU!`=>i(_l}0L7;3&T%a5!#--oK5l&8my2<1UbawOh#pk>p% zO~Fn>%&Riz*2a9)%})f8Z+J$t`+65#jO9&Q>2h-JygYpV%Z5{@q()xPt5(z==c*;! z#z%YmJdOMQ)VIOJ=cxM@-qwhx!_4&V!_2*jt)^c;m6}!-sRyT!HePxWd-A!>a`uK% zlkp3}6C*CE$}CwO>Txr7NMA`4#T$F{HrzdYcmG%ZLrEv6=nSi5%-SZGJ~C%r9r!Y? z&?&cJda&w~`PfCZb7l1Bug*V`NgSEnbo7DW=in;A&Zy8i>5BSbF6Ii6t1a# zE-~z48|AHS2#B4I*7O2{lNC=HIwU^WTQwKeI_q|EeA@err8aLbI8yX3^UaOKT%{*urw zKHc0hrye^M{N7xlwAB5Dt`6kEnh_J5WFOZF?|KWax8d<2TUjGE_7Wm*&29{miQMpl zx0`#6E=BhwZ{1zH?x-v7nF%kl?e=_m@*qePb-*pwVc6m&jo>6z*kcdI*`_mTHC!nt zS86pjJ1WKW4jv5yFtJOTd+Fye7^OU(Lj@*Uw`>_&O*bpV$#&F*))cXBxZl<`RwWp0 zI_eTxdAV!E@%_V=I}8MNQN~Zz?6!8&|mWR%G9kj2?bY(hPMo?;I8!+v5M{!K}^ zzOyUY6M6?K-sF{Uc)Ci|7q@w{`iLs#R4q}2bVO*ta-pexeQSuTUE71}4lUoe#+8)k z&)zl7Wc;FPT9E0!xxw{Pil~uigoP!GTW+QOxitNZRgDyfypzjDjJ%$xhr?S%>6B$kz; zrv2|vH}+McP8WRK8h$KF`k9j`nhy2AE1C14nn}!by9nK4Wr0NGRI1W_yM`uvF3Zzp64Ef-6d0FsE6`)GNuc(1$Kmg(&DR2Y8 z+fRrKg%&9p?tIEG>HodI{tXx(m<90PAHew#DI-;;NEH+b9tnVc{3&_3C*V}@KpgHz zxEG)@kgx&>gV6~!2Ye82h>M8zr`Udo1a1~Zkgy-=B8s??m^CFKEmwlyMSx!h0~LcS zu%`dkB3^O7Sv3B=Usl0_U$gtoqL9enTRd7eS7Se1zGx%h{`lgKe2<6$T=$iCJk!)) zk~r7xw=Fx?l_L7AyFW{4u+rp;Sm>Gcnqx@;fc3Go68t6%yJLB2s zgyK8KtzW+O?Y8upE%qwTT zy zzOdjQmm1igZ!)=i#V7L$Z*!CAdR~5s_rsE#+;^sDKeszU8b5Dy&++Xo*Fd44@Wgwy|PX z>x}iJY4W`t4$OYmjvCKni)|D`S4&qnjKqJHO*?i{S%~50kklkW>*iEmag*kAt6jT3 z^Eunyay}bdI&OoWvftZBn`%``eIWM%X}4|G^31r%mC!=5#;sSH>H{YCU31@ZjzMAT z8z;k0FSX*5ku~2e5{=Kw zvYpZ*P6dY2cWyYU$@k3hP3H#H)7}GkzNPf-?rv^x3E|KEhcE$mNbwh2m0USDONCn5 z;p7BQ&}FxJ9>h5A)zEBW&pv!69KSmxsik}A#QLqh#`Ch1n-$v6z7S-(5&7V>#nG|n ztJaLyMTt*d74)d4nv~*&t$5WoYKz{>;r%YTv{I@5Ir825zm7Dh$0iGmm#q^j-<~sGx!S!j&N=_Z)+x9%nJ3(s(NH_NBHBkKECt zHy&)1zbji=5NI~Kzq9J&Lx+QASu~9yW`fLiaoeBPO9kbfFQv0BeYQ8L^}OvpVE6I4ia5T(2wN{aV_U7mOK9?PYypUcGlz%r!R51mLp`odPo+yB!l& zmU(`XYVVb6X7+Z5a+No&XR>TMv04HLMNfz+5_V2ISw0f3h-prqFa5M7*E59PgDH#Q zQT#Eo(Za;ynf-fB1V~=gq}K`Wo&5_cTP8NYXN)?PyL#b$|J`%qv6?4Y_-Lragf99Qz!FI+H80WG z==qN3Qr(;NPlz4X>m}GNOWZcopW1Zs!eVbMGuiZHz`B}p|J=!_(07kk6JLjYpRQWk za@Iwb#|1M;*JJ6mq$MBizA=tW)${V;SOix~cJUD@?Y$?D5NPYwaX_73q{SRTNIJ>-LTAi^I}RC%J0YpQPnLD>WWri%{>=YiCQ`%x|`^T}-jF@N?ax zr|D&tf~QpTmqKZ;@7W`pzmwx}VQ9S~?zHJ+y>m@TPHARc)I)kayF1&R7eRr376v#Rs5}Rl<$~~R6rGq`#K&IX3w*7 zcYAQsobqW!^_>RVMx?=GnIjesK2NK*lY6#1G9TV}Satuw_h&Ef@HXUlY<Ffn2^?bJyKVorQ*9tL*m|H@@|uRX!d&@*Y0Y`P zcUw7k)gFmYeayR=Z!0I*6nk@aE(_=oNXC(+2U-{$D}|+k&rWjot=^G|n8csr*px}q znZ<70+|v7T=Kj|O-RX91*hRam>MV-R`o^-t zt&LI^Jhfq}&LIIOt{ZDhJWkn8yPo~6YE5J=+uM^f1dP%pu8!~fwAtP3KRY@~iXUnw z_1!SNruU(<=$wH`l~?`>sxR075X}qKMuGDuu$s?GsKOGvGzs8`u3T27bJASu@`eK> z(j7NtiI(-IALSRXoz+zni7WDG6C;S8etrHWPo%nVOn<6B-)6}eqXpgO{f(zO_KGsg zhd#SO<`B1+Vcn12#=1q=N&b@;Mz5b}dR|*+u&AYecX+Oq=Vv7mL(xlLYqz>l`6+Ev zzg=Z=b%X!>cqE6wL}88>{p2g}8ys!+uWWVsPSOs>ZewQSF-Zg zu5W0(si&Q6dKqr1pI`8@k6DXDag< zdVDHo9CW)sw@7G4Pf|a+ofw<9@!EhzOoE{F9w(cqBY~&L5iCzVr_`fvN-s@LYL%Kw z%C(de&oXb!^%gpP&RW(GbBOkg(KT(afzcwJvYAsYi&|*;U}J;W);q1oAD)Tgw@ZDN z=EEK*2@b5+=}54j)OD@|^n zXF6*VQr`Z$pw8tG<}R;IJqdAFBzGiQpSxbD?{v;_Pl@owz3r@hA{U>_gz3l` z5e1yZ1c#pS>=rMr*jF}8$oC4--#DAAp2&2W>*MEFXQJXg&c0bXE9Gl)o_>8NUqR*t zA^Dg_buTGS=k_!1n=TDh1Tb)um#X7e9&S7LFd@z@S8QuzYmw%4l~dpRvs6}Y6{Jcu zWCzw5h;1@!pT2IS=Hc{z*n7ty%hoLmG;Q0qZ96M%+m*ImY1>w%ZQIVQv~63j`u2-+ z>%8u`w>zSLzSt4_&tB{MR;;!6o@0(V#+(l`PpwA~i3@j2QLlR#Zt~ho&&T1>9FsaW z-D}sxdrYwet!@1d$OUm&)deR}?k-sX6iclZya_yYYy+UZ*2%Q7pz^&g(_V1Xc$+mZ zW=oQfh%ZlmILNi`{z&5#?BnXPcwi%v&allw5A4VKpnMC;fK6_@LefmX-e|p%PAhy# zwr8gUVp5?AI=F02Z5eGf!nM`WLzXsL7Q`gK#Stu06!L=v03)Eh;{oFy7eUFlCI(DU zmH|-wv~jepD1-1t3dMSe=Y=}E|6I(tvjG5oA-~_QdG}k$Xt+5*sv9RL{V1R4% zm6n3SV@J8#wW=#8O8rr`{tEl}p2VMIJQBS%u2AF^2frv;*eJ{z^Wm#1b}=Q=UP^`k z-VVU69hxWP5oHjoe2A?FQ?jPMH@b6U&^Sff@}}3d1F48g(@vkC0L43xL;WTEV4FuN zHrwq@Fo7CWM?}>9#@~wk7Wdz(;~4?B##K$AQ~k$L#NIXp}gOE_k8(T(e^go&#${x(o17bV~ACajidAa0Em%3 zc-QxnqTqo`BNJ^%Z0z~c{sxfmH|L0~(n&k5L`N4j=VZKBM=@Q!{JWMq&v(a{pr1oF z)?jtt%$(#8dQfK8V<5_mviO=J+ z`>U7}Z(L@Pe}l3ar3tpmUso!V+f-*k>+9hgpZ(A8+KclqYO3Opq5QLMUh%NfO730f zV|a(dHFXAkdq!KjWB9s+i#rzIi@%UtJKs(&xG!y>^6;T>uDO-no8GUA?s(uy<)|Lt zn%2?1=UsSqTd`97i&*nF;!K`?Bk@=pMwwc+NxrT<0VLQ zIMY}aU|{B8JW7I`;~3`>DkDj#Q_Qqn>a)yIDamsP+>FQZ9`abrk+ChsNL(LL&s2sG zh@RWq8**1vRe_R|bRMl8MFKSW@XKJUxa^v3LWwWIUrjX{`gnRo1_+$p3wNTa4{SF- zbc$1C#v?aN-8+2WWLGtF;iI0O@IME{;U=c2q7~8W-qTl3UVr=RR5H_H(MkO( zNPfjB0Zu{wZb_?_ z1D=li)#d#Wonkd!@?F>gjEWN4=R_b-(O=$bwHP?ATG9F>9?@CrGJL%DNS2i6nXk^F zjz-bKT(C_}M=x8S$Q=KI#v6!+4u2mNzJi{Y-K9{>JQu4~WmJA~5;eY5qJ$`4g{iq4 za?=T~n4Xj6(b#%4oQMEk^h%4AeV=3D5^nOKB}oQux7S}mmuUhh;qhiVa_X69ABVkE*_*ynLSnOsI-fz67EubJqu zoeQ<=yp$eE!C>5$l7tf?)Q8dX5U~3q*FCg6- zNG7i3(U(AmVvg5)ey{HPfm1LS7ZW(nzlV$vH2qqafgKDIH8CK{yKZ84vZnct98|+5 znig6>xkerdvdk+;VhI(ZsWJLI-N=)REYSNF1o441$>Qhz12b z{~~Lh_;PlW)&g~n9`X5m&Iud>>x;gw%j@A7wl1^I2~AYIr1TSQ2c*UJhh$uHL8fo5 zATy3yD?wEmi62)ptmyh;G7d^41mC1veUplo&A@S(Q2@8gWx3J=^#LqBfYHkkia+Hh z2dqEG=C>XrL$4+5uDBrw%W*@`ae7iRKfUByy*l9^iLE!W8gwB)z{Y_NiS;5KXbQy% zubJ3E@uWLpJ2=MX=z*rA)b^Qs#^bL5vx6$ELGaGc*sftrvHjuS#kOUVDjnH4>GP~d zCX!$mCO@dXfoJV(0zQj6k}{5Gwo{+E$iT*O?TNExGelV6U=1Uw*ni0=!%+(!oa8zj zZz*%GOCNO^T2{Q)jj4R{QVnNCQ-v_EtG1G2cKxhWu%MYmZXz23gWTWWNr{Yl7!t#5 z)u&2AfIHG|i>Evuh$A3|Jm^;;#(8;7rsXjBc9XCGj>@1Ej>Di}y2bbx z;17>@P$zs*EEYv8N*1xt?9fn<-MuWQ^3NV*C^9DoH0I|YC_QIm_xzkM{U_~NitKruf;|xWflX~zp59k4iFlk+QMbT#x z532L6M`b9>g^r{;G4n~>GECgla!hdS!2+`@8zw&hiYXy50s>e_H#Wuw zy;5VelrGL*(5;E(l0>}d?3vUaV}spQ>YF zMNGOutfvQ!-1|rgsNS4^cKlld(H; z@$4Q^4hd@57j*~85jQ4kU$sY&DIUZ|K4F!eiwj3$ zV1O(H!Ed=QC&d1d2s&Gw;tNioKkEKTfM(>3a3J}>=Z+v6I1&@Trlb-mV+l=%1r$c? zX7!=%NChOgp^L{^8hoP1TRYp^W_Z;)`WlRg6Qui;#CBb|3)nIBUp7~;QX1HTJCe_-u@Q>&#H`^y{nPqfbVrvmifVf}w> z_h;Fb{ZIAjztoR1F>?If!k4737xUY~=X+Jcuo^PrSDEzFR}~Ma(7d`C&;gxf?J-Y{ zL_4f0G(jXV-lblE-^+|cSfk0y;#EHs2QGxUKWz)9GpvtdHIwb~u6Z&i2XAvQg9B>Z z?apbP#Gq}3+&;DUoc;?c8>?}HJPiVC!&VeA5?FR`J7FaJ7cHrviMixywi@Y$#RCIU z1m|WP5zxvK3kG%(Ev#OX2G)p+qHxQGnF7jtC^w(a*K^!2J?FJKbF769PkkMyYH`}N zvw3s2pY{tM7R%OOANLz%tLJ+p6HI0Y%c&;|09x@NW|p$5F2Iw8Dl8nD2VxcsSos)< zA9bi90vhF(hY6P>?3oZu*v%|u&>OY(c*&5sS#&sFp#cT8St2U%j2D4d5)n5^TYj#O ze31_VH1T}rcR{fko%@a$ZaKi?gGby1i8akffzxZ9%rnqesGDPi@Li;1Imb#nkWmeF zSYhb*r&+ykL4%ts=Tr@ST-L}i`7Wm(SZ7_ol5iFUHWRW7&QQ|oh@avqdun0gjNuT~ zXG%7ou`{Pd3EdgKYa(oxvB(5Ijg?iu*H%Qms<<4fdPg1dbd1xL0uhqbG0mIWTwi#B(ao)oD(U=*ZJoWy;BoP$t5U$mo)J8&?yfk#r&LXF&UrdNjS~A)3V*Pj zFF40f52%}kEQfA8qyY5&n<uz+wKMwH`Ia^QQ1~0&|sJCOC(T%tkPR9NtpH09+beCGlQN+T;rAK>@-y zqFVFq%fX~$=_F`*5-E5=I6?Y#ntWTcoKZ8@g+nn@Iur-hasam{ht5hQ2GM=tM3IBV zYd!2-6L&6m$jbhtjEJ&xZ^Q6%{FTzO0`-~Gx9S{6s`T7=?&k?9#Z2*Zf|efNR*UJ97JL7 zh@x2f7{o9A?TzlKiwdaY|3fJM^vANkqx(F~T-0cEL@*MpQtVpU2?{}Kn z>kEOrfZ0FADjGF#@C>7?bavwU#AQyC*MFrsL1?s;1$41 zoGnuXzJrAlL=qS80l@P9>fgiLh%^$IMT16N=U}7eRPKZip9D71L}(C}4juJb&(~-u zglF!aUD-Eq%+|D6YF;RXvIfj}@EhBY^x*K*vfBu)%3`1EXu$v)cN2b2y2s2_EKeUc z(6+lXTm>G3t^JLQk_Lp&uO4vtmIbF9}_^A{bhnE-ReGaEBn%aaTcH2(7j?=al`l z*^yac*LQ(HEuLs80Lh^dqs5pDEYUsr#EIEFHL?Lj@|lCO59#_b0Y6pZL$K35)}EV%2;OSUv}6vRs}*LwEQhffD^LAUi1{19^%5w&%Pcvvh!m8kw*C(?<%UwkKOp9fhz(ZR^8>(Li1~12+ z^yKr_6u%;bb;R%L1a!11Pz#*_zrvRUcPze~2`i)mC+3WvIg%yLQIRZr+a8e9@{&jd z+5^pYNXCtb7kGJh;U2VC{;{Hh!|h)+&yC3EX94M_&39|lHzmGawJ~uq6p%bNOf5gu6azkn}E+(;8H(QZ#M%@67;dN86})S;-@Oj zc0VOe#6jakZ>*XBmylI3$CyU56d#Hqn|6MPIdek# zJ)t<_DwPXm$EGO&5a=kPlN~H&cLEJk07s5qjL0E&MGF4m7fcPX^l06Q=OEl+Az5C4)mG}4F?Q^nO)o)* zUwPA`Wp@VyD!JJI-|jQD5N}y*Ft04AIytp zcwrQ@&CluKUB+X$jH6$Khtb{L=OlB(kaDPbmP8m#O~dX@v< z0|_n-K{jLIt5stP(BCGgXL@`(EF0>_z!9>xD*~}%90uPD!-@SK+ zyS%HtjHi7`+-Z-$DAM(lbw0)i7Dp)^?c-0yuYPC@f;(UCR{L%R)etwdMR6w9wxy^v zBb2BMKT4vI1tF*2efkfzZREV74~)w|z_ zJscm7`ll0&nPl$V*pSiAr{jV)5{qrwvNrF8u3RaV*jLrqEsqwzR}^z7 z)z4H=46K@vi=!i}qK`Eu4&mM{t5y}8NQ$OdcvR(6i{f$z|Vr8bk{ zlNf1s`87ATV!@e7Ca>`2^J>z3mzwl3v)Xv#BFD^L_~;~PwkSyVL)nn9^OLLI%s#V? z0)*kTFzZ21Q?lW63$M)y=%Z6rrG%Z^s9WlXvR>#9KVWppeLf4WSDNuU>AWK71>d_a zj@~gAodL@2Z%vUYk<{*)!`t%dXeReJRB{FF@Xf9>axY9VKJLEsD6FtZPvTm63r&`l zFIozg@p1RYAV5OTRFeG1ehC^#)=5S}JLFM#__)U&gB>&b5_h5np@G;V$#GQ^Mr>N< zu3b5Hgt57oe0nAk60V^pr1vsw?5tN8o^e%4==0=K4qa@Q$n~mL_8TtgnDStgv&)kQ zDq;9{q{boLmCtLGVPJ0RiO-d*8(p)yMi#)_r=_ils&)G!cc13&Yid4nb1i&qsj>1Z@bk67I;RI=_K(Wx7EX z`Kcbnl0hpO4iW6t0t5~9#jPtyzXwZKcWe{nkR!9!ZikVx$Gm3d4(1H5YpyFuMNefB z$gYZ}u#tbkJF}yM$Awt4eGLT4np+Zz5VWqf9IJcGsi+_w$n+sCq3j36yjeI>f=~}P^=BlI?_Kj ze)SG4EQsxOCeN1CjedQsy%tOq&S@(BW4Fcx+{4e7TVl}OrEnH6xvG>5{3X0 z<^+&yiRKzHjbj=@E3wB?uXgd|pz}ETes5k(^oNM8<;D$)fTc%h90aNVy)NLGQ?ja~ zgFi`y*VEgOa(ISwn|kr=)x90sV61L_lvhq9U!ytVMW9*B3J z(}OI)0s&iglz}s#7?Pf}Q}hvh~42oANJ=Pv}e`Odf)=2P9bD?hiFEcE>V1F>a&TUCA`m zK=>LCvHb*Wj|30?Z({YI^u?d$(7&jTzYXL1tM}0#Vf)X5>c1sG{$sm82@sAy36Q^Z z>;K~huS~x?Fa5p2E64x00lS8ZqKJ~zKN`F;{>S0}gAT91$GZHUO7nm7{(I=r|KV!? zc~$@an*Uc%=l}SA{=d?70*3#Ac>S+qfBz!>`TCdNmVfiy|6`SZdH#R>^xv%bzg+FV z-pxOi?Z534e{ik;QrXVH^mnea@ei+jn6ilhB#_s^A;+VKqL0x=zE%5Pt65Ot*rTIJ;} z%ZBj#)642Fb!=4YCe09+kGmm|vuS)=KHDa;+Ov*L>reNmX8cWyDmfj6p}2~Z_rFc8R!e~GhJONc0x6NfG-F0oM*GGG3ca-I^&B;#yzPh?Pa*> zuzRoE6n@4&!5qS%yxfckY+aAdwmAG0R2z-25=tnAnZoe@z7v|qI!1C!L5@=+PfJM^ zX{1=35bw8H7lsnTNvXDoLPiB+QvLKK>T|iCcMv-JT1wlkI@!7h%-@`Jd~mwc7?orn zT*p80Yd-EtX*rsRjizzY~)#FEH)OtHVempW% zvGX?guR##sb9;Zx$)x|J(6-)_Sb?}4HwNTN<{1xi*TtvTmwnJaf%p z(azD5cY%g#&RS4oOrP|N zoudeEX?Py=v$f69|A?;#W%<*XRA8VbJQxnPXQ`|lFZx#CjRCEY$LXMZiqsOg)@~#D zrD|_!(g%;)sw{u&KA8Z!lN8XFFwaXLf%6&6reBR>nMub0LJeaaRg)enAWf}fvTidk zfdFVKCO*KDdr}cqWE|2DN54E!9hB0<59TfiI2eT?-=HlO7=|=K0nJS<->i;D*;ohO zuC`7#$hbW?5HK4(aVZxrJ4D=PI$T<89HgS`K&ynWCJ5QbtBP6zTZa3moolNEF=p{0 z^sGhkoJJ<|HhrpJG*fYm5^xu(fWH*(?Q7g4u-|hK`%^dpvnK4e$&})?WHt7^Y_=1> z&zgVK5_fRUZIfBUid=F_GTRmV_J;yme2_cdkZCiuy{V3UKqDb0=Evq1yiG*Rc@s)F zs|Bv0CI+A;N7|uf`s&R>bq0jQ#9kWD*7sfJ`~1_5fmbIHYSOj1NhNI`X%(rnjaYixze=;bKoVxxdzCnB*5DN!6B<|lmLMC`LUcM-)Mq&N@?Ix zHKbt1If8@2`+*O=^|G@)eC%?MaY8B_=f=`Z2m325xtjfPmGEWT^wh>m^^Ep^bzNMR zU5oq8scfVMVo+Oh1@2E&*Kk=|=5_t`g zq`LBIu=V_4OFGCSvK(C_wD4GwR~D0meyF2cEJ)8{mrZi1@RoZ`hm@WDDvVwZJ<+g> zf_{DO5=?%OzK;$wl>*@|+K!aQVMjGZIPwe^g~%GUijDOAfnU6@`2C2~u*TiodhHbE`%& z=hlA&lZMHi0YsY_M4Q_F*GRG5{R9izrld4o^rFde(MG7&fu6+tt__j$1a3E|_qEJP z>i4;ue7_#y4%?T8cn7Uz;%7U-BQnD^)<*56dIlH8JQRsqJycVPxn-W~3FiEaD2);8 zwGHwN5en4dqSs+yVqF;Y%%?jLprKS+G6ZCDWv?KFDrTBenfy{Ow4Rq0zW{M#DZUJi zvTiXAgn-KIQLGukUxr!9TqPIfN?~lEAa|^K+@4q*YzhH!xj5K{cWlMB%Zncv@bqJC z+|9;#Xs7ue5CygMf*Cb;+XC~c>Zq8e`#leCl@tM~fZD(3jis*STYU&*1VD1rMI7f|kMM=yH_*{a^$&Az-Khi~k0Q<9{==KodvUAIv&dClXNnkN=x#{uBAhcs9bq zM6YkAn{L`{+Bk;e0-K%J5OqC*HNn2)ZRS&bUS5oR2|Z>pk^m<4R8dD>eP+!=8vTo* z@1ChwqED!FTt6_5J*scM@}A0SZw}e@AXA<78pG$le)%}(2dkb1QNW`rG}~U0#VZCz z_9BBGNN{Z>g>WgbcXwrJyrCTss$pfc-VK$JL9^t>(Bw^(eG4AWs~=Mj&CoioDWaO- zVT>C}|4w8E04~T3JAmn&1I`(y&?`L!5bY4WC-=SKI6uTdhm9VKz*bbxyC|9z+YEZA zrL4%y5jM>H=PJRj+Ln~uHkW@vSyE>qydUHvmNw~u zx!R{asnUi{Tvq)F2WJaw^>~`C6MN2w`0;N6*Z%km22kM682THxeZnjLENCK#wx#Mg_2-VGrNZF%B4B%WoeOkm9*1Xc6LlhF)`1Tng!#C5Tln8 z3M#V4FlXiABFC;R5H4NVWx8ibA!u2J3cmdw0sh&Gk3dh-0K>@95DYe3qU{aZm4 z`j&tiU0ZD6zmPl}79>wAK>ziw4-88VbcI}UKEAPjV_TCgLU27BL;Wo`xTz{zMqeeiBJtdFNNbU2$usTxFrR})ri5a?G)-HX1TEw!&r3J%=wwAU~N!0V!hr$g$)Sd z$j^@RSSF+sP?zP?n1F}LE1d`ra^@p@-#`E@gvB27L{QB75leBW?-OA$fkCw{H@1#P z;V|pV7Vyj|_ieW8_#SQidQbCu`&Ji&TT79`=jx@qV|Nvp22(%2ZGK4g?X`aD`j?C7 zH?9Hs5^v*`lqnD>@cNkTZ@mjgY;UhE@xnLV`I-T&q;(6;PqoJ#9%$KPzPf6)xT$p@z21H^tO4vN21_dg`s zfAm=XOQQWxPJ-i4S;xQQB>u7W-#CfCaU%aJQDI_dWBi*1H%mh!W^Dw~XSy2Uy$;1? zLD=O^WEqP9Im!fG)nGXz6X_x%c`o^)9pU= zuI^>JpD#6TUvA#mv_N{sy}S1Hv9_BDyc}OK)Q^k2>ua zw{1rXn-wx48FDX6MUkDil46j)h#u~)uTGT6R~fn~htwN(+~*VS>(FM5k6v6b8@C=5 zZw4-x90-!%M}Dm1qqI#zPnO+6WPEHT{C(% zXEmRZoRME?8T(N-C&TIc-m!*viUgPJ(eF@{c$=MVdK)D~Xhht+80;z(v}#^|r95Ln zqDXzr9w3S-L2A5x`Qdg#DU7&WIkj*b=?ijY9r~I-@5$kjN_n+1!ah;hOvnC`#{}I2 zS@fgBBfC|6ZaU|ZkNKhaqD5FyG(dqiZ>#%JSR;e}tchKldJidx$Ya2E2=2Ob>68j6RJ=@4>cGgw!N)?2i>!OqN?sVCQ~bP1hBEJT5Vzkua& zL54U$jh(&rPh}84azT_(b=p%=hzpJiGhaYW%dDktOKn+E`jYF6n*%i-l|R!Jy%?|8X^Sz;+7cod{_5MAFC<0N9; zbCC$=?fWD)vL*|OpPK12K95Z2r@evgWj8F>>68xg{3%TmbxIK$aSEA_tM-GwJ%RbJ z!YW<2Kk$UXhg9C#sYy54e1%H5tbc}Nv~~J1QHQ{8?QfV;aPtk{394CoDB>Kf$6^(# z!a`-gi`i72?a--tEzU~Y3qS=u&B*ZYs|?~>5JOv|O@igZ2hzc**vQ)78j3hC^k4cy z2YPYoc&;OXcW-U2Z+=`{Jmg(fl{bx)Tf$+O--O{Icn<6EC2oCVc7XHiSL;A^0l*$F zIrt)+`?bEW2Yy!b)iayJ)0u|jXONujv6Y1V(HH2o?UD}{SVfP=4zSH7Y%8Ll1UiLm z%EyOP<`9MmNB27u*Gl3PWt4IN7x9Q0>+t~(8P<_}^_^lQxWH;D2M2NG=i92=Qt%T5 zkH+?5fCJ!;mW>`a(TIvel?&fbgp;omE#2VF!^5YNS`hO$Xmbo2{Zh(^x~sb>FMrb+$CMtR)l6(^j$lcnNf+18?Qa?#!cuf){i}ZX?J1kv z9z{9r9rLC*0X5i`muvR%yGMwY1kY44V04qt2#ll9l}jJ{$TQO8hcEiEppAjQzrA>_ z*3N{W$7{lBvGIM@ZSjVPGlAT5(=)n!j&wOSO;RpaW?ZNOnuZT|hLLo=I}<`qhH{Kp zxIkS-`;+G<%qDu&W_mGFULU{V-yZ8zty-H#3RqR%Fq#$7A_Z#gTUTOPVTrP603eoV zfcNIqfh!hT8c!B{{vso^8`$r8O$;+6B;YMcC74TL*%cv;^ zuuNtqQ#p-n&Nt?6pTm!+Fm1bAW-C5}1uVlbpqb{EJm6hV1rdmr{`&=5jga;wK*KBA zKHF5xw*vw=a}ML+DR;aGMBKKNDiHcs)9@oiwkf47k*v8g{C%$2VQOK};uMJ>2ou@x z)`LWP<5r`ZkA4)Y8atU&Lj>+jF7vl+xI|Xsdk$NW&;?c0q6_i5WY)8O=5J;`Zv=3R zs}J3JCb16Ew;=Rmq6nchhSQm3L?F~jn5Ib*AZdFJs35T^pG>Dn{US)1*M3vtEu}!z zUcGsP5Sby;E&G?1L#~;4;1l8j?BnM2q-xU``9XFUN*(~fZn446A_fQ|R56}G1hi9V z?Zp+TzsxnJMDeP6_lPwXj(#nDw?;MA#kAZHsd38ZEhC$BZ zJ!MT+;i`|wHzlh-7S`f*T|1c0Ir^n}N0IS2yWynM>^i}cJw9x2G3;T!m&0b9(3O@< znhoaF3-q3R-#rM)s|1vX=B}j!zlP2fEg^|d6Q|2~$LxWPpxU1O3ZD@*mlP0^^()8w zAB6z-=(2@1JrTRbwrBnsNh?OFE=dAhusMTH#%w0n+ zhHwPLv%glckIa06x>iE_NOb8jVSNf-q^1W(sSvW*#U~WOkes8Dn|&yb2+gNYa&2Qc z15yjpqRr{WFpM$#+SycGH^sz1#;_!~(R zn7Omzx=fkGKwN#6a@uu0{^-64qs_<7#zzP?3XJU)3IoHri*13I-x8&#nTzqu^$Yz| z_xeOR#t8=T%8bLf2d7wM#Soj;`{)m^3pM10#`S=rN@+^wd(rq-vE6ii5FSjxTF*Ln z41|1va*}2kubCC$RN!>&CwEsGp7BRU;zi4ZU2fi)ig#@o^K_#V>Tvi=uZD;;&n zq=^D!5iWssO{>Wkf=USpEi4N@IJ^Rg zwFxf+esc3BnlbwgoYjXpQ2gY|Y0ayWCO(t;Ru34+Yo(nL8(T`Sox@Fm{TR)NDgd>V z)F3%7?VS*41hMGZysMNJAjhZ9w)$(-Nk>+xvp{CF$6)`gQReNOT#o6{QE6nC12VbU zi#@?VwFAWq1y!1!i;^lzAlK>?WrSRM0|NqmSj#|U!*MMsmEJxrk`63Kup*#2R6-~b zX&IhKI@V3Q3k4vC+ywsS9Xw1{q1N83mL!kF`fsgSt@lkv;W3gIq=EKV%0~+N^5uc1S!;?TN&giuhIbQ%E+REywVK0hw}mE3LPb{{x*F$?N{V!gJ35s4e{E zN&EwR{{hMW!r_0k-2DTR|EbUUcaZ!a+x-d2IseoK{Tp=;$KO4Xjp`e=Yhp0ITY7{j zz+}L+L?^O(z^}2Uk_iSDfdlMFp@T>Uq0{gq$@Uaa2xXq9o+HapCfyt z4!BE=c(hoXnzp7TDP0E^;ue-oHHih4rMHU@;hmRvG*C;Ef$p5$SiHI0nl;LD3*^~EgQc8*0W-B+ z)0N06ql$i(eBzSU#G_$bR=js06nkgqpp!Aor864PkRD-GuUX>Ew+!y0IZx6f50eQZ z#byFMuDrVSCfvHixv$yLBtBU~*s@eX~!GhdC+Whssp1ney_@PbLXfsx;+(L_jAJCSG6c_G#eT z^J8FMo>-p>p*!4^b8?8N8d?;Pw&M=}QSU-X{0tR>0OpZDO7tq66}lmjhplM9@V7$d)?0#kaqO}OzdZS z=eHwpT?)|ll=~-A8@sosd}XA9IZZ zuzwbxdtyV`Re0FaBR%!3gwr|eI`rl5D~_;PID%ls?Y#oWDL7FuIm8#XbHXMKX;&DZ z3tOd6YKEKAX1cxlU9L27ir49f9>utCNI-N_*uC3f^mn(!2N*-Vza-%R6p(iyqiyDe z<}+oV-hm~WmanF{6FqyyLlfl!%K% zCW&cxRNaZfsZD3D1j;m0#0nG3q=A)_Kq{z_VspOu%?olCN#T{WJOoe*Ud9c?FE0gX zC^=hKT7V$z)%j(%VghPA1H|NjyRiU0_r!aeD{*)6gIx<+ae zm5@q&2?A^iU&W;Gi`_Wh!UJq@f5y~jB+QBu+wkq$I-ftj!1>aDnT61I_wRw@^%jc_ zZ*Ic{#+a;{Q8?nUiLNlYhdl2;iVs(@bYj^w4pkVlHg^vcf`Pk>V`uh645g2LwN!Vr z_)~vw$ys)~>b>SQaiYhmx_p}GplQq|Yh`=V@Xe!Mg#a!=8&@ec;8S)~TkMzSURB1f zIS)ukor$pM3bwSId4x)$LWg-H2S;`AR1QtvU&c+kDEKoKJT*6s@8-##bOLKX6F4gR z-_6%;6Ommc0yMG|as-ylcrBUZb^r(VoTLZm@UOaV-~*o-nzlwaWz+CWW-` zk119;bZod-!Z?W08Ug7%uZ9;v+#7UOR(KsU@cCd`qToAc*R zx^pP)e(Txxr;*qHEk<_?p*Oybf$|{VGusH0fK0(C}H9NmMnE3~%L+OD6G zqez2OwuyGqq6+4Ld3AxlaLWq>p59vv%dX857b3^nu$wFvTidnM7sR=i!f3Vfz_}4- zu!OfnUjV0f@2wh{y{U=9mw+2|6}W%FlkCE;rKudul>uH6(2Z2pd$HlOQVPjBlykwQc(mhar-P^1C%VNx_}%(-U~9KV{ingQ;~l zi+Fgm3(!MdV0(`vVOSBMU_tLPvBM53i`R1J5Asn;e1yW>n_mV`;IodTTfVR8d540x z|4sD%Q)Kh!{IWly_g^vnpCOy`f78Z*^FK+tKhFMB7xXVfHWNDw+uvBUN(~LiH8CWg zExq1VbbpmY24>)v3O)#QFp&y!Z20{29Cm?-+kX*@(;ot5#9KY-a2H~ zI;b)ZzKM;Mv)y|wA0*$6Ga+VIVaE!J%|vwQeY@P~>TK2d{JgFnU%Cl+quF_8!ehdu zQw7qknmEG~rH}}ZDfCl+HvL@60{h(wMtA9$Rbkq_otTO?h3Hn-uBWrvm3?6HDMaY1 zF|B|$R$%ecNrvcxuJQYho*F-*##u>6?OflUG=r?M$(KrDWtAQvZULCgnQDLA$6&Zr zF;M+2nC=E^eWzq|eZ!`UXmVGNhk z5Edar!NH9-?QJA&R0c7@-w`vWkAbO-MeuN~7Fk|Ogc2gFES&@-Oq0@d&klXb{a~Kl z@kXF{Y=N-186yzK!YfjT;Kq5G88t8YwRLp1wx*+0x$0}NzqirMM{S%)+Kox%X1qXe z?tv~~+DuTnVjehTrcw9^E0F*{t!nZCH zGqf)@Xx+vuA<3H2^(50CU1q&b-dfP7AEU>t!#mGw6=kYxSc>fgE7L0=x+xFxI^IPG z8?%24*5=A*gx$=`4fbmDSve`Ah4^ zuQ2at36u?Tgrvpm%hzz_bzf~zvU?H)RRka08>x$Y_)EMTjYj_c!%c&0EMc^Djf=jq zys9SWHrqpU#@Vyy??Hw>=B}XM*~9p|*HR%}-`pWhldG(qRwouHuYcS~=%7q4rlDkt zOavPlRp01WlS!W{ss8k!=E+mHJ8XZ}pC0LfSJQm_zJWcY8^(bKD}h%4XeCUAChO4Q z_At5+P)W9wsnk~9d=z=UhBMJGJHHdlTCIJkgyeb!bbFo}kD%s8hII#UiJhMpDnJ#0 zG06CQlWh>Vkcur@c=|YeMy!LOFn^*1o1Qe|T(Yc*g9H zL0UxBl!E2Sj1-D$iORB`U@5G56{h`8Tvlc;^XK0)hYS+aI6f`eqeOXXg+aW+7<`ABi!Q+^1i`nv z1BCx>wM26wl2gF^LPzGVF=qANz)8^xj zQR$U4uc=sFT>ZUQSIs*L?CF|MmfPpEz&Kk;le2?fy2a(-gsiRt_~DxoAiU#Xf8e>% zW3V?dnJ*b6cAn@<4>GDW+EXWSqoOZ72AveYYgIx?@Ks&DOh01FAT45w$NaRv#HL&YcYs~m#B5}=ONx0_%m?=g(HuV~*eekGLH zDGYXUP09YpwIfe%-hd7#M>&BXQ&`?>O07WLzzHMKJ|iurf{!p^=lUuD-Asj-Vo*kF z30J*__Fy7r*zSo5`|=jHM*ui_stgcN3@2ha{~$A2eZ4)7 zs4U^A)Y{p5j%MS9T(W32^l0Rt*a2@!(>+AYX)6<(us^&SIYeo&#qF9z@pBiF3WP%e z9D)waPz;Y+H$z8E8F0z{EjUq!!$qVN8W2{|p6?;P_@}9`I|TL|HhE3IL*o0UKw+vF zawCRSD0qCJy?wLlED$y0UBaDriRbW`RduFv^p}T|^6GByl&s98*xH?NoAYc96?BE?>4|L0*tQ+lnmm%gzGS=TrFU zoWQ%NetN&E|5Vm8tPeXv#>SbERen(Dn|hzF;p2BhgE2TFQ327A{BiIzcZB-|$nmx- zi_DW!u3NPK{@$t!<3OR+(DFj#)PyPfZk%9n|8eN!EdBOM1uykPx1to5E@npWJXYCs z6E58l`_$XAM}kYxV$Ia8Gs=?bK#x+TP3D)wXPK@15~e{aB}q?isihe0CEP;@?l{^# zK0&Pxo^y_j-`K*5;4|dTYexa=n~O2MqrAT>L-MX8$E%sF6p?=hS+soJrh?Ju7?N!$WSuL z)v;gsC_`~O^>Yd^Sue~mTnVs{`RjQ-3XEK%;l8X@jSuN%=GvwwBC+VypdEtuTjRsY z(S8&y%}9X8<_gV!bWp;GY8sWb+f!ofkYc<=if5(Y;iubNTa`r<-Gcj_V)x_tH0yU) z%LJ#A7e$&^3m4@^um?CXmsJyx_f=XC(=lU0-v z`e#zwFsE$x*FuVg0}Ta#xRSZN=%vGs#W0xqA2N)1(rNFwnCd4<3D2=SV7(0Wf(yNW zzMZ*d-W`N0{u14WGr>|#i+0M$amql^OTmurcXZKqb^B#+&xO0{eVJx%(PWKBtJPk; zmZr>*#L&Ahv!F;5KVC-f{MkU|hRCD~b~vJxJrQ*AT2*Fg07+c}KAyKjNx-OLhL|Je zZAYEza1E#c5$6WY0YxOpi?yCs*5pT)(D}$C7MZh@OWqT54g0d9iI|@X0qCtPqwF<~ zk`ty;*AVF+UjpC|(jvRlTWU@Sx(@{YHR|dj+n|~2vctvL>Db#4n(Jlw zpv<=_>peivK+Yhvc@fvM>HU}(?ic}rT{+1RBzQWZp?$2!Q6U;(Fc2=H&S|7mGH%F| zph2n%Gt+g6|D`M4C+yj^Mo|AMfY>6(tj^{c9U^uMQ}R~i;m41%o{PfZd1MpK`C0IF z^iV5Vq-{l$V&ULWgk|TJSh3~nY84RBFc%-+3EvP3h`Xx3fzbByQ1kSbXcZ8^lGmgO z>nqPc(N1X;bq8lay#ztl7lw^ZOjUwG?ojSyvu(_UZLv-}_wBk>d&RP^reeYFX(Y?m zBX4|IWl%5y5<9MCh&>#^fuVTmEH)Mw0B~1`uk`k_wP!B3>r+$b!Utd-wJZmv!0Uv_ zfG?9sJGkgc3Z&ig)KC$)f`@kWi`l;Z0{Up74%^E@(4M>3RgcL$c&>4%6hDH+5i<4b zJQ~QPNs}l8r6u{gHrLVR)KtwwjXRng-{t#Y=fo-&L7amx*0(B$>wcuJ4E)A`l9=Ff zK*la7rROSzzmNJRtf)I3+9^qBbn57selu_ z?b)mJla6J@NGzK)W32ku^{k|w#$aO(GDq%I_<=TA-HrLez}V`VQ-h$45NAlVFuN)w z^3o4?f;ee6(!pkFZDyB;^7}%tSJAQVyyjG6mh2-b$}y{O22E7Pb)O3R$rV|VL+{!G z7`T1{=&d-@xoz;ZLXtUTDy*{6Z!R3bELhy$ZT~Ggon-fPD_rS%og<6}&>L`W=(o)A zA{8Mz?dv{OSzQ4UvHO!>5Mr zr;B-4N_D5p^hhQ2x%y$h_citlmMb}Bk>>zNrH2B_O|+jg$aqBZ`mL}C@CEDH&Q4rS z2~{>_6-bE&zRzHv zP_~AA*#iYZ!l}K^4{H$sEQTG3-M+1tX$}`w+i4 z7@g*vw+irMK1BEcWp%4q<5A#US;MsbbQ1`so=2C^7(!Be;x_%Lr2;L^A#0Oj7)ncZ zJ%#GW&d)wPJpaUi%g^7oX6Zd;2T#B|v(R>C!ny*kstlXSuEK*O&;7hyTCD_peiHj( z#JmPW15qW-9Iw(W2nG9e-*AI^MV1`=gVjvm@76fGVCWf|oUKeBe<32qZaZ5Jg@)Jp!qiNH@n^9ahvDz^yr=~P>J5Eei<;qb z$PCsc1xYYiGO7sIZ(F8~Lezq<^R~eche9X(z0W3R_&9*&k?@PN?I}aGdqt3J%M`)F zL`)3oRHa<_Y?K!bgkyOeqe^03n7Okt1eB#PtY)lUL~Hx!UZ=R&SN71 zFJY22{{_V~Vq)r|skq{WP$wtl=RTbJ9$ydXvZTN~kEQEQd~KaNMDF~9o){q7F*8JW zcKIG1h}zrf=X0(F@HKdtl*9T@Qi+Xk=5xJoh*^-RICxkBr@IX4AohvC=sLDRESbgC zcPxd{K=t(*4?+7VIajv=cqX*oW2} z*X(0|Acfv@nPl-Pg&M(ZFs~~#H^1Nk<{$YSB}!S{X0cSuz1p&36wuM`!AyA~h09ry z-+Cu@+542eVQA4P!kPCBMIt)sj>M20%ZkL1e!RdO2t+cARu{W4P*A`;(wVspCJZ_H z(xnI!ILD7f{XJeT>Zzy%Ri*x4awu=tlZV5r+HObYw3(PY-ZgjX+nevzITDSr=@M!) z?`m{{t|(i=*Ke1o76;qIxT=_Xmh1Yz3XqxVq%R(CC70)D>7xo#N%|Il+T7YBbOP;? z>71Whwv4^s|9n7a;3g82jIcU1W9#}&q+sOu#O7dt;oQKwFNsT5fs+jtJghn1w5txd zw#!Sy+qJUqSxxdAA-Xtvn%et-^rU>7R&Wj4cHG9wAs8*u>&4X>b3_g|KL(*S5is=S*dZ` zP**}mCx~wiOi5Gi<3X1q_^wRaljJ3JaQ0ueZE=W;Y@E3O<4F~T>(rDh_9>fGYeV-J z`@_FH+uz=v-mn_u6_7{IT)OA_Dp)kag{o|}KS<*xN*_fejPqR&Jrd4NQkpbvosxyB z;f?S0etnfDcGb~7+7^_Mr6 zd~a$RQtMcO*ZySRDbG&U1G4U1iDjdizN+3_ZIy|<-gfb8T{rIm|3btfj+mH{c2)-t z{XvjDa)d9Vv{TnMp7?Fh2Ao8Ro3>3+TxEIbvJ$hlW-Az`US8tlz*YU4o6O7z!RzDc zs60F5`|F4Z{)8=dx|HoK=e-xV*JEg4Hl$Ia;Q4ySM&xL=$w_Jl$gQTF0}*$ab@@6i z=(9sb-3V(->C!!&%6Gn|^`6k_$*15FCje)CxvjMrCmD=e1G-#UKQ#wP^}RzzO36hz z!bCX;sQ7b6`77Ao+VB#tQv6-xOOj*9yBZjgAH25!z7)7zCW2)3#*s}a`jNZp*Hc8< zk>6;H@v&OdukOLAMfyUS8C}ycPR7V9(U$lHmjl2X>Egj&Z6+4C9l1rZSb6nju70@W zEX$b0I!8rO&W%c(O8bhe324+~LUu}Onm}q18LUm*4&ZBQH+CFo9WS!?qjEpetJO&4 zQFK&0#P-&(wzIs;9mMlv=PDvUT(kUZOU)-5r>4@^-o6~Jw}Yv?m4)>F2DIGl6uJT{ zFhuXq$lv&WEt@HZ+2-B+9~R@28e0rW|5h4#$kfc0+2HB$dNhgaL&h%4JlSbjw%a{!JC#a(U~QbA3)PxF zm}Nkt;7lQv(_MnI2%mA7q81l6LEYeT-g}x4$yjfOery;5nW_^s(a_335NSJIhv%hw z4yg4C?lu7L4bAON^&w!8P|z!YCY2A>(miJ3`Bd~w?u^;pNuk62HHCODp~C#0O!SO& z_ka75)QX<0}(n@~@6AP)e0 z0KI2c1epk8&t#`1#z@4WpWS9rBWdms=V2b$4r(lvJXx+Ta43lU>n-`LStM6*$U>^$uzd>M*2hXRsH-K@DTtEwsU!TPM%U1A}6 zs%!Xm%}ZU67yz15wrwc!tIXrqppJb!vtRqX3}>RDrw2Be%8-=VXgj_`LvA`HG2C-r z;b#~lX3mB#h{WkNki>PFx8-@8l@;fk7&;z_)>HZACOP8>OgKc4S<}rfT&eoE{zAnU zn?)Bp&(e}A6jP2kH!ASU44yS0UgC@Iw=)4+ z;mCwEDv$yeUN8jYH=&R!gj_89E0Dtba2uvui!~r}07xWlTCk3AUQuN4PWI0h_K9iQ z@C>my6k0K?@wA4~O-wHCQO6$kDeBpIZsb9t=%3oAReTEt_4@l(vI;eIE%+A%m1#o= zKn>LX34*U9ae(bVpyv_84LNV>&RX<0^TrPDTe#XCpq%l9?yc~yQqpntHj*d_erbXsDrZcqlldF2iy11gbu|(e!amcH4rdhSN0!%|(lu0pegminliLeBB z`_jRq0IPij25P|ll$~*}xmiK16LgX!fy~8nWT$ajNwYaNztnHI8^K9=*@Aj~{TSDE zieZDWe9yZa11Ei&@~&z^(^Lxvj47%{VsPw&?!>`z>1Q#XYjUE)k*H%Ehtye4mhmb9~5mzD%DiqfK6tMMC%uh6v=DT7eX_CK)SFR z_9NBZ;3LT4*Y-v~uiF<#YyP3Bmp*6jSAODtAh4O4W~qOw$6+xS8R;o^r8zfKx&!p1 z&fn4D#88gTY&FE)#=nfqQY#LS6me(LIvO$xCrTY5wNtcedV)StWvsawycu3Q3T=N= zD;&aUDt~8$DqKD1atlfc5Eq0R{$cD;Z**KgJfMpVVz*f zce#eBYd_xl{F{5eYqO!g2EB=cB;Y|Oj6}@NL>bI#l<;4?jYOn%u0$usFU2xD7ov&7 z=sZZGWE|p5kLM=q0o&=o^Fu;8S&oN4s~MNv6V6V&t~C6rbS-#~fv zPWhG_RKIjpywgSi?XbRp(q-Wqkq{;g6gV5~L2 z8TC|Dl^&=_bJ@#}=Fff#P$z)UgOWD6scHzdk9hdqik_|{vdaAaH&={j|=)evFn3b)OBf+9rz~{}iR|P(_ z-_6Df_%qaVg_riC_(=#b7{?`e!fFCz?;5wFpva>v$|#rUj-lrtLoK&ZFg|V1S-;1J zI|fT#=|H*g7{l7Ou0p%AOjZ<;>?mC?dn4D`K;EC?+z~Il1iE7=pRI zrj!Wb9+Nbn(2f=J|B!M6W z+p4TD8}6*BCtgr50{eL}B%`|bOtG!cYhB^Npsu(%N84xQ^?u7?V1VsbRGup1AKEYh zvI<%XTy$6E?Gq}&j$vUVFIRNeaXok4B9Gt^?Z*WHs9`4rG<>lsvl&e71Jfgg? z>F6?}PP2RfhxEU^x1%r=j09=0ySAotf%nK6Xtx2r9TMq z-pN@m>;@)o*CQI)9lJ`v+J#mQ;KCi6VjjPJ5ZpuYd9Iq@RCzbv^^2B1=nA%ZQnsGn zU(^GzQGh%gmg_hYK=J#r=bV3ZU$WM%+i)&oilI`U7DvYJZ$7qJh4~_l$cZhmmTKqe z0K9U|4?c0PC_WuQ^O*^Fv$lNsTGzQ$W50p+?wq*_DoAw*LdHdJRzw1=~W{CfP zam;^03IA$#{&Rcsj|2a;J^8<1m|~*;Z|#Y?<{xz?>Yw(6fC`E#D4ocGt$!TVU94XE zB45UTjk-?l8kkxhl@*!XPKwF z<(4Be$Zw=?_0j_slrY-V8yO0A`}-xuG+Rp5YH}l{;o<6-;Wpb2+oO~G9Gql(bMo8k z{eG{JCo=(5U`(A))N{JNK(j%Nkn?pJZzq!NVM%)Y7&YhavWkL+M3h8BC~xzydc3R$ zr9Q`I_oqcuj`)r`g#k{o^{!?ZoBj6U1z0u*32x{0;FY~~*Y&PXCkv8GKR$}MNj0kL zeQR|<&-*b~R7bIC)w8j3E!|nZ+HI-j%ksrhmsF>LdR0!SX^T#{(@{v=Ogh{p3ur|Y zp>2vQ7Vhko7*6rl_t#snsBve-n{w~Hs{*OAzS~jl#O0MVJwh%_qL?9R?_t{*Ua_rm zoLFXJhPEopW6#v7I|afVPP5gS^Xp9WpwcqFMacy;PlPjUfT{DPsGS>fedxj{Y*+Hy z?RQ_d#zV1o?iZ>IO$pTq9rACNw~Tj+3AtbK4d8qAYY7~WcVkRk2Hbn4^@&8>cQ9vQ z;OAR7Jy3!e2T>dJHN&JkC;I|Gzb!#Tem zhCJbZWnY-X%2r^ydYGs7b^mUJ$IonxVMEy6(eD{n`m^VD`rAH|rpw#s@$AmlBpghm z^dcg8Pt=^m->Jf>-fCQXc*x=SaOgo(1n0#&eLVG(HeP2-gVw$VB>R?f?0EBWkyEG6 zCWBh`LNYe8(bC}6h9)(`X#h5w&vlMqFe~y9!|cbEemBO003;OAAo8o|7lW+khshyx zlD6aVB`YXH6dpT`Tc+jZp>fi-DyTuE5-N)QpqH)+rAL}lTq7s`^;+=9_LLYKNVN@Y zDOjfTke_O}z)IdGm>f{`(WCXetFbn^2QQbAQ2{M=v6_%M^t|fkAS7AZUAK!633s+b z@I0u(rQV8<{q>@js0M5E!GL1S0_0@<=as9_tE0NXtPjFp)@ zF#Y&7r;Eyn4x*DmVRvQbk=rd9TxWdJYb^eXD>U(mRG5_YK8gbZ!_rP0_zSl-GeN=vOv4M>k_X`X|3pkm(<&sXMJ_ z9eP5+D;TY&8v8o;OI2j_x_)`nocbZ7&d>~fB2%iSUX;$SM_U_F3=Yaiawos49{_Y9 znX`4hF6FhDSv_zUri~f5^EEvg+-7PS4_fklAVDZ95TB|_%v8Sb#?H*Ois~Q2Gwh86 zNW{L}H?{a?pl)}5JTCr>?&-md%MqtW%9|IoNIMznBiZX|I$^nJM74hcNxBr>=jyIw z6Vx9doDj0Ir!sm2JqRfaMr#R;_h?#?BQW0CudqNapCHDka4rQ1_C@D^yYb{(_&pLY z?+pt&i&6v(_ks`>;S2{k&`_aB`1Y}HkT=Z@C$pG%mcIq73)J$fw}AA4VG52J7*t_= z&51oDk_XRtUjFwD6(=0%Q-(v>vsGpxV8y~p8Q7E6pX zv2ImE14LHtojKxFhyr9rh0C^dH$0L3JA+dB_+ zE9$!p-k+0&6J(I|yV2HtQGvdUn?h}bUW4OgRzOK(uq1#>5de(TBg~_kdT)A-HNKYV z2QwD-@3bTlz{@TG3@-q-!27^kPtJ=6<>vV4%A!zx&Ezib&>R)n0Cvv#qqry_cOm|W zV(9v?24Sr~d$XgT3yNa}V4!dv8KTG6_G*bIcb7{=*tdT5DVP`c+7K(F)GHA*ejXk^ zf-2H%-_zFUle5}Z$%tDK*IZx9RtK~97WfU2BUBY9189f1CovZEW%mM)ZKZ^zk4{B1OVeDKv3)!W_*UwB8sp36Cl7?y`wK|xQuc0m-%^>ibF z5Gd}!`DqsNK;!5RUXCyJ470)DF%Koq+Z}9r-Yi1u795z6O`RJ|Cs5io9JVgWkT9qo zYA~U5l&nNa;q%YjK&Ul<3Q+aA;je6+`eBC2IS|?xt3!oLYQNDjyo_nL|`$`Q>Sm&QA(#yv6z$Xie02bc1jX9?uwTb9Mz%7rsM}d9cX^_29-yCS&TU1hyuL##w zf+|cg+UTKWfd`Hf0R>-TSWpw;UZZrTc||b8|9b{{zyG5)obwD|XR+4Qn5}0i_R9|7 z8wn8=nv()TR!e@ec!{i4YE9Mwp;6j(*)Q@bk3guYUs` z|0kFI&%K_1Cq?-8-1fg+@u8>tTkh~5apeEv(f^{!>HqJyeE#I2{|n6>r7CH=|35u^ zPZX4B2t_;U>J9(86ZaGgax* znOH{m{ZC&SDX}{%>{d}^h4&#Ed0VEJTkf?~f=p+*kF(Y-V?z1~^+~ESt|F{7>%<~= zZ|CqD5qs5axz|AzZz=B-?dciS^A!y0$nrA9%$e5~7v~A%q}o=M+{q!2JMVI#(MY^w zfYjLCZxbni&Em#C>n1|F<+Yt&Z%AkQHzlWOJ zH%)BxgKk{dJ$IzJH+UVh;u~D$wm8_+_qGjJk+GT`PW&8$c@%f*hbrHc0Efqv2FhQ$ z$TqUjB1G9J*ZEn;Wm|}aD|0?~tIR%k1r>RjU%#X{{4lwsN{Y8q9+|f~wo)$BD@?CG+YrD`~GmA8uY(& zqN9%Ilek4A1{Gn|M8d8HPxTctLq-F!QhZ9>_$e7Gf3?)!mtAcT2YNCw%z2dTZeU`P zUT@B{PgYv+CTFh~yVS0#Y`c#|ID6bfszP}MR{K#+UZL2dN<)WsS9q9b@^79xZ)vnM z^z6)-q@tOh$ck@@toyT-5z-(GzI+s~!b3D^2bpl7(MKUk$a&YKtp zEQa#bxg~V znPNohQP-|O6^MgTMDXAY;G#9_ z`y@I}Cmg*9rqKlVG0PFc1`;&dv~_8*@HV&>?ufbuCCfMNJHFTGyndKg23ZN%X$C=p zE{~2Dju8Gj)We`A%AI!&4Mf3ei!P9LR~c+UX8Zx4dtq_g&!w7+{+@6?9Omi^3vaYH z(_%)SL|*8Th8we9DcM>>{{sRMko5!^Jd@FY0YCr-b&y%gIj*d*|YCfBN@ssdeVVJf|Wi2rKja&L9m7i0U+VpQaQ^hRHiXpHm81>+DRm@O{( zpeJ-)m*bwfNGH2HX=3m38#>8){)3KFk{R$z__T={rn!!Ws04rFOtR_lg=7zjji@&%M%fzNRsusJM8li5rBa%2b{u-Qyg3=2#ChMV@7$i&5sk8k8 zPgII*|93_6Z@NU}FDc(&1L1#`(!a~w-!tX^Tps`XLJd9r|Mf-%78*(rt$fVdL^ z@9QCtF5Ia+-O2N`HEqKiimRHZ)VW$mD!2$u3~#uVvFT$dyUJi$=aAj_d0j8tjOP|3 zsy{Z%a0UwGGB|aG^}PkJlwL00gCyw1l zy!E^Ngj6kkR%IrG$s*$Q!aj$TGwlvTWKz52=1Sw`#nRyV6L6klQw63P453QK5HcuO zsP6tc_zeY$>3%%G9I0>1o_;!jjN6Ifd<=W}4gIjFizk&;s{kzkuF%2Oy!2)aQ8KgW zy@j?d2OaTe&Tr2IxGLe1qGzyBNWMZAN>{*8rNOEL%j$i zVnLZm_=#HC#fa79Ke1v&h%{ie#eNWalCxCaAhR^9hlpOwQ|!#CuLnC-gs6uCH+DhF zVn~z#y{n!EwjdjgB2YX93KZlZLRFHwM9*#L1t%U{(9UaNKMS}|ihsKru6HoGTo+D` z{+_Y2oF%z4OZwTxQmOvDTWnWs#JUX0B$#F1)TD*aLvd&Y)jGa=gRaG)P^Oe=0lIQp zVbbcp$^_s%#_SfPcvU=q+kfm@$YT%hiWg&kTue7DEFxmvctQ7qBVvw8>-;eoL``7n z7s1IfZQppxhhTuptc_1ZED1dvpaw*tepv@Iz~AdD9nx=}TOyG zF{?-zuqf+J#tq-SmR}RAt>-^q!V?3mpM0CTg>oKL1!~vKA0lnj7U?E((((evd=t% z0jzd4V^*ct1kP%ZgL&XPi9m@?e;xM|FSi;{va_*EbkleP+n)}66;mWYbojK&_3DCp z=@CSM_L+6FKH4fxGe??rmlWSqodHDX%h;y@L{g5#`a&r0=f-fVSTDrGrs6V!UVPNo zZEu^Gmp0AN%Z;$fG&^PLn>1za=eYV?l@Y8n&;)|+LfHys^K@RYI-cwfaXyG=i7&2# zc%mx%!+wh`#j6m|vbI`;o!Vu2lqp*mt(Uo|?Rs(6l>vBl7Sn!&ap!a`y;bLgn(zK_ zG0a3wrT?55Be`RlCf4P<>tV~Y9FR&;iiXLM!v zNGU%{JIKdULpXS(-7AE754Ce7^YOSaJ{;7>bzv=i4*EOFv$JN^!54?&(gV}G^Ygnz zgo_5l!YQ~5^5;xpSss;y?gdL0@Aw8m4BW`Xe;J_KO=CHzzU_mrImwi8huvJ1V{fQW zG#)^XUyfB<_?Y6~V`|t1H})iyYas#j**Z#7?4>J>H4~_@RqgIsE_O7yEAOW`F$t2- z>b4p^fyKS&niJH@=w{&U%H~d zYscR#|9`F>|6K_FH$kuDZfE@8T+^Xw1@-O3jLl8WobdmVDNW1oXlQKnzn%*j|JMaI z1KpoH|FByZ5U_RAq-J2I$N%#{_eT&#$Bxg=!mj;acV+agjcFx?1o$OI|G2Iz(EoXp zxRbt>xgo!esns9nNhn%HCu3_Bd=~cqdi@_+x-tHLePLP=D}7T(e5QZ2cE&bF|7cAA z<)`~Uc(KvrvoieoXenbGQzx@OdM##FX4e0G$%xO)$oRi6@ab6?>HoEPylgd$CD~k} z)@a&XG+q};vCfd?HePSCUUx~Eq;Xwss=UAIymqzOxZdC;o$AJ7Wh643WEYKQcc?(Y zpd=g(Ls32nlDdfW+jD7}dYFKj;1VKq7@Lq1nt+j#>a#mIAdKcCGKJ>UlqWL9heu#! zWrdDon|aCUQrk3(e{bDF{kyWkg7Vca;q=;ae4HnNs3{HPYq3{CWt;S*KOg#^aNW@L;D1MNv0K{qqehaP^R zRaR&!b`i?_7SCu|`Boi8IXQWVp&=~up)4YK6Di;D_s9R3Easjqdl2jf5-T}=bPvHs>%0)D}>cHl#T zM$7otNpfJRYXX2+O;-mFOZ`fyvqNmQ15YQ@U;~~AlZvXy^8KCywwC^C5B14>Zw3*; zkk==`fL!~QI`;_=7eQ^BD#LyHPI%Hi@fn`@cKwLqjz+x_;*k1&8UKL4$+_X;49yA)3rom&+*bFEC?J|ydflen&0+njURl_~^YP_##Rr(u6&8;^^S6MFfE>>a z_ouMiEuiAUkClzOA5Zt8eyZWNUT)96vscpV9E6(6_z1wM9NanRdi(q}CYv6?LuW`~ z(;IRvWl{NykbdP}1V=~i3nQuW%bPjik_cR9D;A>BuvpuR5C3PhY(_2ksqlP=Tt7Qi zhOo|QK^#Sgv>%M3y?Sgek@sy^1zc_-jT9o&>^29rMLkbzcx(0}4ldO-jpgG+5?mJr zSPb8V0puCT&!bDr4PIM&_{B-ECeyaZ-tucCjQ&!`d*ga?w)4cAN;x9y(E8dGaV%4Q zNiXoB{V1b0l=l1x`*%WNtD&nBB^N@@jxk@)pJ;UZ%P5Xw(>sfM_%Ksh4{KKesoZF%s7Vrj`Iy}15uESm-0WJ?O!)7xjH+b=VE_(&H)HxZwgPA* zH~WE_rW)wRzA020!cZ7n4e-bU_J*ve2q6C19zU>AhcW@moZi$LmzLdvIBYQYPe}uf z5OP?%R~Dg(GF0D>{$PGv8MYou<0ez7ke5g>V{yXDRx;Rswe$3LmXkJ4c zRFF-K{)R^O-*t&dRD#bGPOY+GFBBV^@JiLE;NR~YpjXrC7g)_^^dFeNr$bh$KSk~M ze=tE^uZQRrm33(p6cyP4$dC_GI*T3FSzp#%G3Kq1klU2_q5$~gQMr-}c>FT%2a#}m zD#)%7N>hhb5N0(B$anMua6yL*J(9iT$l-AchaluI^IRC8H`1!`NNN!N5>5b1i zYz(76&7dY{;v8`dC615a%65UG5x^qWX!Jw!dxAB*$t`CwC-rc(27?v1m$1ovi~J0x zYO0>4QRtpztklKnp&Cwkz_uF3C-9mw%YVZDfVY%Tr#%CYRr{yOOi?D#N!+YuHVOOM-ANKDF5B`Am)qu}k4PS@F z2vBJ|tNMUq&yAnCXd`Zl-KER~vtp_$&k;G{ye&<&j;YX)4WtUr>qKBt2Mg54^SLNu z1&UCq&?v8rkO(ED`8w;#1`m>U;&6`179%(N6O?%!X%`7bP0(b23Lf=vw(eHig zMO%cmCvjv0k!IUu2>-49T&S}(QfxDee{iQuCu6AoYxq+E%^uab0_(&l`~;E{+xYXLlL)pRM3 zX;8rdO{qWm`9}}|Ngaf=pLiWHVC<2lt{L~1toCyZ{2mu0(oKBU+?#^4XZbr3Gic%% zt7L|ii~e0}z~Q4zfF=a`)NcI7EPR0>8MWb9WwpA|ayz6y0|=U22Db9x5~9p(Ys+eZle*t8+o#`tJbK)AGR+9R~Ya z)Ak7V)>{IvMNdeD4(h_r2rsLO@(7?XU4fTv`Oxh~nsCovjT6!m>%mRppeJwl+tynv zZ-H1OKmZmRVDgElL=THPwdLI-{PR007Ai6Qq;ul-EvArT(+-GbXvJ`Nn_DxuCKjN= zHUxo($F;n#(YgNkoFT5K%8?wjvgR8+yfMRYFDbDG%Z{$E zm3c9mBaAWfM`uI7MV*s<%SL_$kA!<<)8Rrn8;;3V9FtHeo^S_CRBfUEwKJd2OV&~AE?7T zmBuJxXo+On$6rAe*($?O>J_HwN_en01tI90 z{Pot*{v5G%NG63TZq3V#EYm6_+w$YUK~?O&7g) zeLrB%z^#8@q3S{ruFffJqPg~;q#|(TUMCP;3??6M#a|vCh+e6VEzVBr6*go`?`yOP zR4#s*e^N}y3+!D3#-Rp#1tWAhP6))H#IB9IIj`ZcFJIaWTilu2c}(2P(f)-%gk3*Z z&SS$+$!?`U@cHB_8^jf=32G%7LmLB2K{Zw949~m|-e<*t)=AF5tcgM@MQ`ntJfaa|3!jT{NmE+Kf5H9;aI{~@I^+(s6aMP0lp*~Y zEP4KEJ$Fw{6Y?$PMy3|MK-~HgfJrHuDQvc@SI`Rql2lc{YKYHf6U~3DAK=Do7KF?6 zVkV+@)94ybR0JHTt>Wh(EGQbiS2Dym2wB$lXoL9`U9j&?f`lk+jJ~<(Mnn;gY^}`Y?%8~j88|< z8+I^$N+S9uuT^4lRrKn`3EQ2DWCUhn&e*$NPiC&eBSLW~8h3kgkkq!Xrb|oD>2hVU zMUY#cP?g|Ez)x*4{;Fsc&UB5tyhJ?*@BS8WC~Zkpr_*>NLYZK6!m;P5JPVYdd7mhy ze9u?uIut0Husc2F(qb@T!X(k6&v+75^a*+JrO9=X+a^Z!eqk~@)M2x(%|P9 z9c0rs+oI%1ku$7A62#aYlN4hz5?5SS)b#mg2U4W`AHiE3pU4BIy;DS)#-RcB_6^Dg z4I@g@O-+_XM9}~RyIUpdj(bTV%`Y>rro`FX9Rq#h`-+6L8qn%%KZ|bC zoYYV8h40l+HQ!orE=Gmdd@cA^V?d5tl{3PGAzgt+AD=L04CmC$fZA>fv2x@5V;z@m z_A?Yo;?m5G zs}h;FMH&&!Z=qiK+1e2cC^TWMKlZEfMbLnMbhbkRW5*&~C^#B^!#92&*m?0bJ{05r z5WeWU4!G}jI6It==N{841^@zsN-i9Gc6zZs>M;}HkaX2{eQ5`_Z>b7usx`CCSc$RnIQC>J8OuQ6PRqEiQMjF zJGw_(UUo;EKv5drQ!tsvxp}(mYOkN$3$1}sHf4(uij5JU6+%e$!@0K}4g^MZY#68r zw&x}>6W%)K*nh7+XDOXz$yZ+Z0a6+jx=-a|%(Bm`)l-4mNsQ9d z=u+d|0o40)aI0PeBr8zft>EX9DyC8oksOItcTO9A`D2Pj@iUhw8q=+vIY%}LRho4G zcLmQUweNb0i47nnYV+EB$~9d}%KrGu-IAEw6{S@Nn`=Rfjn58dG7?PI?J}_FNeFWY z`l(2y1;ybmbty%&*Kaf7Itv(~`xf&tWp=k0ffm;4+)biF>MnL1xnLv61Wx=xvKn8h ztJ(~T#aeCa3u8B2#V;id-SXDrkG3Uv)H;0wPAC~fSE-ldsYvBF+C4?9+`#97{r#FI z(X#_Ol;#qeRZo^AwEh9(m7!-n5hcUC1OfYZ+tP>IN}ezf455pZLf-CtXjj~!X86;G z4V6;^4+5UvzY*O(?5|73|I~Jg(Lz&n{!4G%Q`_yzfsimF>AJD!$?_6yn&Be?SlHys z6I^$4v>h!(vI;Gdoh*2AIvC*?z-@9QjyPw)Lb+K4UP(d1y>kkw&7bZ23u8&&u0|DKx}{Ud_ph^2AZ`oO_DyuR8heKZ18c{qhLdkyoVz0y8YLG zsMC>_4$R9Ti1x4PGnQA7@F>j2cto7MFN1x2g3`i8&$Q&|Da3a2c+Np`wbxvJo0;iAvLX~S+(`x z{ntJ)HR|(fTX?wey@}TgO!+WPDlO2~4nt)8#{*+@VJ^RTOq$dY*l-ar@K;5HIML)z zZH~m&Q=Z(8%g!&GFyWyE?ZNc!EJ5CqQ#5cO9~rwoya9wivmUcM{6>Rvxdf6p67e$% zEOjxYmr!gF6!Df8hfDqJ0<~n>VT0yYneJVn+PA3U(Cx7%VG%x1#S6ej!HH`ZBv0ru z6kx(Ust(rwUy<$F{RF|hSJrSoDJhr<$LhZn@3%U>c&TWOi&ohd@adP_V)+#k#C%#{-~_NuG{(%Jb?SpPJ-_djRo$l}Kt)QMNGxMl z-Du|Q2N3f?l1RGI-9%^EXo*nt>5VYEsyt&1OwKvNiLFTWw6o&W?@sw&v_38@)rXwx zq}h)e8vwX-J3-i|U=V5fu_Ob>y*4%aoaP1S{@ma@45;Xs^5-$3Yv(m=O^BfT8p!oQ zdH!-~rx$vq`D6+|YxF6X`(*`0xDQaFCDMXYPSY}*^KpI0xE^OOr43i%@vRGTv=lO% zb~6%0T;m!zOnd*404}O*y}|5io*&`M`uu-6ApLjz6DrjsBM|oq(%p5vJHhSkX8yi7gvr3M9!yfU>>xs9) zRJq3oq6k*A&uSu9C#ux=4RzLm=)h~3Hu84^8;fano_awtNFAj>s{LmxDnnzuk3^fq zOKI<3An(#`b#yw^ge@UPlW&(@TdUD!R1xE*r)5PqJ|0qDnrU<^zCI00yf8{Ro@CovNiZn#OD)RJbqa)a}|wq zE${`Hnu&?r{vvq|iiI?P^j})27~Wo;^%H>9b?>Nf3rgYFQDrR4_?%JR(E3cmiuIwh zbqr7?wM1Vf^8#@07LW_UcXOSdZiIr*b@#0zCkqR*6IV20krY|FBbZ>_f*^Ld#k9v@ zu`4*~T4|(eQ_DM@koDE;BQ0(|;~)G%&}ldMWnad_?H$vvqdr;sM8%#FZ)YBd^C2c2 z^6E;&3=j3}DZM`&6(f05;Lk~V0^n`4YmPhBX9ZR`*_@B`!@Nx=ptcA%reCcd9VJW= zSq63P`0fySM0~WJr*l^i@K(G#kGVqQUzN~_C-Wp@J~UpDkURJ2mZ9f^9J+?ybo*;8 zQ{L@&{4`$1FA-M3cO(_$U#j4dAGGB;eqVlOpBn1wzwkoUmXr5V_1xg6>J%?%Wmhuj1p~8Z4A& zUPW zW+N8ShHuVN=5C)jl5G_hW2%E0nqAhN0;^-be+2^nvS`ot14>R9DNQex0ag4OdrL9= zFjx#|N$$9DKln^1D%xrg`=0Tg&8r7yV@y*OY_?o+j}h>`2H(@4TEg3VI>!J|NZKYZ zxgz4uV0S9+)$Fw}pxFNM&TVor``vI572f*I(q}Cz@CWtio?BQ#J@%yR9N)mMFE3oz z6~Mq;mvFVP#g8CwO;yoLSKb+Jtom~OAu>%v_A9_=5#iCb_Np~@;_}ByNS|LST5sLW zqfe__U?*M9<87zWKLozBhJqD6d;EB|IjnJ-l^QZ@xOglg2aoSIW*(7ozSwJU%5NX= z6{Y$PB?!o0RB<~ETq9cExA%*0E-<2MCYwuq{zX+F_Qj*XUO(TxkXn`a*T`&j zkBBAuz&nSlNPK~;7B$=gxoT2Wg5#=8N;-i+uQ+}26Y1_78RhFs6a%;rRh_cb5M##s zf~CFl#RU`kbb)H#nOc$y(OX@W&-YPcMSdjmCG_NT23j{yAe1Ytf> z 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

-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 if ((fh)->shared_fp_fd != ADIO_FILE_NULL) { + MPI_File *mpi_fh_shared = &(fh->shared_fp_fd); ADIO_Close((fh)->shared_fp_fd, &error_code); + MPIO_File_free(mpi_fh_shared); /* --BEGIN ERROR HANDLING-- */ if (error_code != MPI_SUCCESS) goto fn_fail; /* --END ERROR HANDLING-- */ diff --git a/ompi/mca/io/romio/romio/mpi-io/get_amode.c b/ompi/mca/io/romio/romio/mpi-io/get_amode.c index 35f2e938d4..dbe4374482 100644 --- a/ompi/mca/io/romio/romio/mpi-io/get_amode.c +++ b/ompi/mca/io/romio/romio/mpi-io/get_amode.c @@ -36,7 +36,7 @@ Output Parameters: @*/ int MPI_File_get_amode(MPI_File mpi_fh, int *amode) { - int error_code; + int error_code=MPI_SUCCESS; static char myname[] = "MPI_FILE_GET_AMODE"; ADIO_File fh; @@ -49,5 +49,5 @@ int MPI_File_get_amode(MPI_File mpi_fh, int *amode) *amode = fh->access_mode; fn_exit: - return MPI_SUCCESS; + return error_code; } diff --git a/ompi/mca/io/romio/romio/mpi-io/glue/default/mpio_file.c b/ompi/mca/io/romio/romio/mpi-io/glue/default/mpio_file.c index 1121e342c7..5dd2ea238a 100644 --- a/ompi/mca/io/romio/romio/mpi-io/glue/default/mpio_file.c +++ b/ompi/mca/io/romio/romio/mpi-io/glue/default/mpio_file.c @@ -36,6 +36,10 @@ void MPIO_File_free(MPI_File *mpi_fh) *mpi_fh = MPI_FILE_NULL; } +extern ADIO_File *ADIOI_Ftable; +extern int ADIOI_Ftable_ptr; +extern int ADIOI_Ftable_max; + MPI_File MPIO_File_f2c(MPI_Fint fh) { #ifndef INT_LT_POINTER diff --git a/ompi/mca/io/romio/romio/mpi-io/iread.c b/ompi/mca/io/romio/romio/mpi-io/iread.c index 436380185b..3f64dc92fa 100644 --- a/ompi/mca/io/romio/romio/mpi-io/iread.c +++ b/ompi/mca/io/romio/romio/mpi-io/iread.c @@ -38,103 +38,12 @@ Output Parameters: @*/ #ifdef HAVE_MPI_GREQUEST #include "mpiu_greq.h" - -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) -typedef struct iread_args -{ - MPI_File file; - void *buf; - int count; - MPI_Datatype datatype; - MPIO_Request request; - MPI_Status *status; -} iread_args; - -static DWORD WINAPI iread_thread(LPVOID lpParameter) -{ - int error_code; - iread_args *args = (iread_args *)lpParameter; - - error_code = MPI_File_read(args->file, args->buf, args->count, args->datatype, args->status); - /* ROMIO-1 doesn't do anything with status.MPI_ERROR */ - args->status->MPI_ERROR = error_code; - - MPI_Grequest_complete(args->request); - ADIOI_Free(args); - return 0; -} #endif int MPI_File_iread(MPI_File mpi_fh, void *buf, int count, - MPI_Datatype datatype, MPIO_Request *request) + MPI_Datatype datatype, MPI_Request *request) { int error_code=MPI_SUCCESS; - MPI_Status *status; -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) - iread_args *args; - HANDLE hThread; -#endif - - MPIU_THREAD_SINGLE_CS_ENTER("io"); - MPIR_Nest_incr(); - - status = (MPI_Status *) ADIOI_Malloc(sizeof(MPI_Status)); - -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) - /* kick off the request */ - MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, - MPIU_Greq_cancel_fn, status, request); - - args = (iread_args*) ADIOI_Malloc(sizeof(iread_args)); - args->file = mpi_fh; - args->buf = buf; - args->count = count; - args->datatype = datatype; - args->status = status; - args->request = *request; - hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)iread_thread, args, 0, NULL); - if (hThread == NULL) - { - error_code = GetLastError(); - error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, - "MPI_File_iread", __LINE__, MPI_ERR_OTHER, - "**fail", "**fail %d", error_code); - error_code = MPIO_Err_return_file(args->file, error_code); - return error_code; - } - CloseHandle(hThread); - -#else - - /* for now, no threads or anything fancy. - * just call the blocking version */ - error_code = MPI_File_read(mpi_fh, buf, count, datatype, status); - /* ROMIO-1 doesn't do anything with status.MPI_ERROR */ - status->MPI_ERROR = error_code; - - /* --BEGIN ERROR HANDLING-- */ - if (error_code != MPI_SUCCESS) - error_code = MPIO_Err_return_file(mpi_fh, error_code); - /* --END ERROR HANDLING-- */ - - /* kick off the request */ - MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, - MPIU_Greq_cancel_fn, status, request); - /* but we did all the work already */ - MPI_Grequest_complete(*request); - - /* passed the buck to the blocking version...*/ -#endif - - MPIR_Nest_decr(); - MPIU_THREAD_SINGLE_CS_EXIT("io"); - return error_code; -} -#else -int MPI_File_iread(MPI_File mpi_fh, void *buf, int count, - MPI_Datatype datatype, MPIO_Request *request) -{ - int error_code; static char myname[] = "MPI_FILE_IREAD"; #ifdef MPI_hpux int fl_xmpi; @@ -143,6 +52,8 @@ int MPI_File_iread(MPI_File mpi_fh, void *buf, int count, count); #endif /* MPI_hpux */ + MPIU_THREAD_SINGLE_CS_ENTER("io"); + MPIR_Nest_incr(); error_code = MPIOI_File_iread(mpi_fh, (MPI_Offset) 0, ADIO_INDIVIDUAL, buf, count, datatype, myname, request); @@ -159,9 +70,7 @@ int MPI_File_iread(MPI_File mpi_fh, void *buf, int count, return error_code; } -#endif -#ifndef HAVE_MPI_GREQUEST /* prevent multiple definitions of this routine */ #ifdef MPIO_BUILD_PROFILING int MPIOI_File_iread(MPI_File mpi_fh, @@ -171,13 +80,14 @@ int MPIOI_File_iread(MPI_File mpi_fh, int count, MPI_Datatype datatype, char *myname, - MPIO_Request *request) + MPI_Request *request) { int error_code, bufsize, buftype_is_contig, filetype_is_contig; int datatype_size; ADIO_Status status; ADIO_File fh; ADIO_Offset off; + MPI_Offset nbytes=0; MPIU_THREAD_SINGLE_CS_ENTER("io"); MPIR_Nest_incr(); @@ -227,14 +137,6 @@ int MPIOI_File_iread(MPI_File mpi_fh, else { /* to maintain strict atomicity semantics with other concurrent operations, lock (exclusive) and call blocking routine */ - - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_READ; - (*request)->fd = fh; - (*request)->datatype = datatype; - (*request)->queued = 0; - (*request)->handle = 0; - if ((fh->file_system != ADIO_PIOFS) && (fh->file_system != ADIO_NFS) && (fh->file_system != ADIO_PVFS) && (fh->file_system != ADIO_PVFS2)) @@ -251,10 +153,10 @@ int MPIOI_File_iread(MPI_File mpi_fh, { ADIOI_UNLOCK(fh, off, SEEK_SET, bufsize); } - - fh->async_count++; - /* status info. must be linked to the request structure, so that it - can be accessed later from a wait */ + if (error_code == MPI_SUCCESS) { + nbytes = count*datatype_size; + } + MPIO_Completed_request_create(&fh, nbytes, &error_code, request); } } else ADIO_IreadStrided(fh, buf, count, datatype, file_ptr_type, @@ -267,4 +169,3 @@ fn_exit: return error_code; } #endif -#endif diff --git a/ompi/mca/io/romio/romio/mpi-io/iread_at.c b/ompi/mca/io/romio/romio/mpi-io/iread_at.c index 476010ce3f..8e04c2f48d 100644 --- a/ompi/mca/io/romio/romio/mpi-io/iread_at.c +++ b/ompi/mca/io/romio/romio/mpi-io/iread_at.c @@ -40,103 +40,9 @@ Output Parameters: @*/ #ifdef HAVE_MPI_GREQUEST #include "mpiu_greq.h" - -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) -typedef struct iread_at_args -{ - MPI_File file; - MPI_Offset offset; - void *buf; - int count; - MPI_Datatype datatype; - MPIO_Request request; - MPI_Status *status; -} iread_at_args; - -static DWORD WINAPI iread_at_thread(LPVOID lpParameter) -{ - int error_code; - iread_at_args *args = (iread_at_args *)lpParameter; - - error_code = MPI_File_read_at(args->file, args->offset, args->buf, args->count, args->datatype, args->status); - /* ROMIO-1 doesn't do anything with status.MPI_ERROR */ - args->status->MPI_ERROR = error_code; - - MPI_Grequest_complete(args->request); - ADIOI_Free(args); - return 0; -} #endif -int MPI_File_iread_at(MPI_File mpi_fh, MPI_Offset offset, void *buf, - int count, MPI_Datatype datatype, - MPIO_Request *request) -{ - int error_code=MPI_SUCCESS; - MPI_Status *status; -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) - iread_at_args *args; - HANDLE hThread; -#endif - MPIU_THREAD_SINGLE_CS_ENTER("io"); - MPIR_Nest_incr(); - - status = (MPI_Status *) ADIOI_Malloc(sizeof(MPI_Status)); - -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) - /* kick off the request */ - MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, - MPIU_Greq_cancel_fn, status, request); - - args = (iread_at_args*) ADIOI_Malloc(sizeof(iread_at_args)); - args->file = mpi_fh; - args->offset = offset; - args->buf = buf; - args->count = count; - args->datatype = datatype; - args->status = status; - args->request = *request; - hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)iread_at_thread, args, 0, NULL); - if (hThread == NULL) - { - error_code = GetLastError(); - error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, - "MPI_File_iread_at", __LINE__, MPI_ERR_OTHER, - "**fail", "**fail %d", error_code); - error_code = MPIO_Err_return_file(args->file, error_code); - return error_code; - } - CloseHandle(hThread); - -#else - - /* for now, no threads or anything fancy. - * just call the blocking version */ - error_code = MPI_File_read_at(mpi_fh, offset, buf, count, datatype, - status); - /* ROMIO-1 doesn't do anything with status.MPI_ERROR */ - status->MPI_ERROR = error_code; - - /* --BEGIN ERROR HANDLING-- */ - if (error_code != MPI_SUCCESS) - error_code = MPIO_Err_return_file(mpi_fh, error_code); - /* --END ERROR HANDLING-- */ - - /* kick off the request */ - MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, - MPIU_Greq_cancel_fn, status, request); - /* but we did all the work already */ - MPI_Grequest_complete(*request); - /* passed the buck to the blocking version...*/ -#endif - - MPIR_Nest_decr(); - MPIU_THREAD_SINGLE_CS_EXIT("io"); - - return error_code; -} -#else int MPI_File_iread_at(MPI_File mpi_fh, MPI_Offset offset, void *buf, int count, MPI_Datatype datatype, MPIO_Request *request) @@ -166,4 +72,3 @@ int MPI_File_iread_at(MPI_File mpi_fh, MPI_Offset offset, void *buf, return error_code; } -#endif diff --git a/ompi/mca/io/romio/romio/mpi-io/iread_sh.c b/ompi/mca/io/romio/romio/mpi-io/iread_sh.c index 262dffb362..d467511a56 100644 --- a/ompi/mca/io/romio/romio/mpi-io/iread_sh.c +++ b/ompi/mca/io/romio/romio/mpi-io/iread_sh.c @@ -40,111 +40,17 @@ Output Parameters: #ifdef HAVE_MPI_GREQUEST #include "mpiu_greq.h" -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) -typedef struct iread_shared_args -{ - MPI_File file; - void *buf; - int count; - MPI_Datatype datatype; - MPIO_Request request; - MPI_Status *status; -} iread_shared_args; - -static DWORD WINAPI iread_shared_thread(LPVOID lpParameter) -{ - int error_code; - iread_shared_args *args = (iread_shared_args *)lpParameter; - - error_code = MPI_File_read_shared(args->file, args->buf, args->count, args->datatype, args->status); - /* ROMIO-1 doesn't do anything with status.MPI_ERROR */ - args->status->MPI_ERROR = error_code; - - MPI_Grequest_complete(args->request); - ADIOI_Free(args); - return 0; -} -#endif int MPI_File_iread_shared(MPI_File mpi_fh, void *buf, int count, - MPI_Datatype datatype, MPIO_Request *request) -{ - int error_code=MPI_SUCCESS; - ADIO_File fh; - MPI_Status *status; -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) - iread_shared_args *args; - HANDLE hThread; -#endif - - MPIU_THREAD_SINGLE_CS_ENTER("io"); - MPIR_Nest_incr(); - - fh = MPIO_File_resolve(mpi_fh); - - status = (MPI_Status *) ADIOI_Malloc(sizeof(MPI_Status)); - -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) - /* kick off the request */ - MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, - MPIU_Greq_cancel_fn, status, request); - - args = (iread_shared_args*) ADIOI_Malloc(sizeof(iread_shared_args)); - args->file = mpi_fh; - args->buf = buf; - args->count = count; - args->datatype = datatype; - args->status = status; - args->request = *request; - hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)iread_shared_thread, args, 0, NULL); - if (hThread == NULL) - { - error_code = GetLastError(); - error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, - "MPI_File_iread_shared", __LINE__, MPI_ERR_OTHER, - "**fail", "**fail %d", error_code); - error_code = MPIO_Err_return_file(fh, error_code); - return error_code; - } - CloseHandle(hThread); - -#else - - /* for now, no threads or anything fancy. - * just call the blocking version */ - error_code = MPI_File_read_shared(mpi_fh, buf, count, datatype, - status); - /* ROMIO-1 doesn't do anything with status.MPI_ERROR */ - status->MPI_ERROR = error_code; - - /* --BEGIN ERROR HANDLING-- */ - if (error_code != MPI_SUCCESS) - error_code = MPIO_Err_return_file(mpi_fh, error_code); - /* --END ERROR HANDLING-- */ - - /* kick off the request */ - MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, - MPIU_Greq_cancel_fn, status, request); - - /* but we did all the work already */ - MPI_Grequest_complete(*request); - /* passed the buck to the blocking version...*/ -#endif - - MPIR_Nest_decr(); - MPIU_THREAD_SINGLE_CS_EXIT("io"); - return error_code; -} -#else -int MPI_File_iread_shared(MPI_File mpi_fh, void *buf, int count, - MPI_Datatype datatype, MPIO_Request *request) + MPI_Datatype datatype, MPI_Request *request) { int error_code, bufsize, buftype_is_contig, filetype_is_contig; ADIO_File fh; static char myname[] = "MPI_FILE_IREAD_SHARED"; int datatype_size, incr; - ADIO_Status status; + MPI_Status status; ADIO_Offset off, shared_fp; + MPI_Offset nbytes=0; MPIU_THREAD_SINGLE_CS_ENTER("io"); MPIR_Nest_incr(); @@ -195,13 +101,6 @@ int MPI_File_iread_shared(MPI_File mpi_fh, void *buf, int count, /* to maintain strict atomicity semantics with other concurrent operations, lock (exclusive) and call blocking routine */ - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_READ; - (*request)->fd = fh; - (*request)->datatype = datatype; - (*request)->queued = 0; - (*request)->handle = 0; - if (fh->file_system != ADIO_NFS) { ADIOI_WRITE_LOCK(fh, off, SEEK_SET, bufsize); @@ -214,10 +113,10 @@ int MPI_File_iread_shared(MPI_File mpi_fh, void *buf, int count, { ADIOI_UNLOCK(fh, off, SEEK_SET, bufsize); } - - fh->async_count++; - /* status info. must be linked to the request structure, so that - it can be accessed later from a wait */ + if (error_code == MPI_SUCCESS){ + nbytes = count * datatype_size; + } + MPIO_Completed_request_create(&fh, nbytes, &error_code, request); } } else diff --git a/ompi/mca/io/romio/romio/mpi-io/iwrite.c b/ompi/mca/io/romio/romio/mpi-io/iwrite.c index 6ebf76c81c..dd859624e6 100644 --- a/ompi/mca/io/romio/romio/mpi-io/iwrite.c +++ b/ompi/mca/io/romio/romio/mpi-io/iwrite.c @@ -39,106 +39,13 @@ Output Parameters: @*/ #ifdef HAVE_MPI_GREQUEST #include "mpiu_greq.h" - -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) -typedef struct iwrite_args -{ - MPI_File file; - void *buf; - int count; - MPI_Datatype datatype; - MPIO_Request request; - MPI_Status *status; -} iwrite_args; - -static DWORD WINAPI iwrite_thread(LPVOID lpParameter) -{ - int error_code; - iwrite_args *args = (iwrite_args *)lpParameter; - - error_code = MPI_File_write(args->file, args->buf, args->count, args->datatype, args->status); - /* ROMIO-1 doesn't do anything with status.MPI_ERROR */ - args->status->MPI_ERROR = error_code; - - MPI_Grequest_complete(args->request); - ADIOI_Free(args); - return 0; -} #endif int MPI_File_iwrite(MPI_File mpi_fh, void *buf, int count, - MPI_Datatype datatype, MPIO_Request *request) + MPI_Datatype datatype, MPI_Request *request) { int error_code=MPI_SUCCESS; - MPI_Status *status; -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) - iwrite_args *args; - HANDLE hThread; -#endif - MPIU_THREAD_SINGLE_CS_ENTER("io"); - MPIR_Nest_incr(); - - status = (MPI_Status *) ADIOI_Malloc(sizeof(MPI_Status)); - -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) - /* kick off the request */ - MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, - MPIU_Greq_cancel_fn, status, request); - - args = (iwrite_args*) ADIOI_Malloc(sizeof(iwrite_args)); - args->file = mpi_fh; - args->buf = buf; - args->count = count; - args->datatype = datatype; - args->status = status; - args->request = *request; - hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)iwrite_thread, args, 0, NULL); - if (hThread == NULL) - { - error_code = GetLastError(); - error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, - "MPI_File_iwrite", __LINE__, MPI_ERR_OTHER, - "**fail", "**fail %d", error_code); - error_code = MPIO_Err_return_file(args->file, error_code); - return error_code; - } - CloseHandle(hThread); - -#else - - /* for now, no threads or anything fancy. - * just call the blocking version */ - error_code = MPI_File_write(mpi_fh, buf, count, datatype, status); - /* ROMIO-1 doesn't do anything with status.MPI_ERROR */ - status->MPI_ERROR = error_code; - - /* --BEGIN ERROR HANDLING-- */ - if (error_code != MPI_SUCCESS) - error_code = MPIO_Err_return_file(mpi_fh, error_code); - /* --END ERROR HANDLING-- */ - - /* kick off the request */ - MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, - MPIU_Greq_cancel_fn, status, request); - - /* but we did all the work already */ - MPI_Grequest_complete(*request); -#endif - - /* FIXME: Shouldn't status be freed before leaving this function */ - - MPIR_Nest_decr(); - MPIU_THREAD_SINGLE_CS_EXIT("io"); - - /* passed the buck to the blocking version...*/ - return error_code; -} -#else -int MPI_File_iwrite(MPI_File mpi_fh, void *buf, int count, - MPI_Datatype datatype, MPIO_Request *request) -{ - int error_code; static char myname[] = "MPI_FILE_IWRITE"; #ifdef MPI_hpux @@ -148,6 +55,9 @@ int MPI_File_iwrite(MPI_File mpi_fh, void *buf, int count, count); #endif /* MPI_hpux */ + MPIU_THREAD_SINGLE_CS_ENTER("io"); + MPIR_Nest_incr(); + error_code = MPIOI_File_iwrite(mpi_fh, (MPI_Offset) 0, ADIO_INDIVIDUAL, buf, count, datatype, myname, request); @@ -162,10 +72,7 @@ int MPI_File_iwrite(MPI_File mpi_fh, void *buf, int count, return error_code; } -#endif - -#ifndef HAVE_MPI_GREQUEST /* prevent multiple definitions of this routine */ #ifdef MPIO_BUILD_PROFILING int MPIOI_File_iwrite(MPI_File mpi_fh, @@ -175,13 +82,14 @@ int MPIOI_File_iwrite(MPI_File mpi_fh, int count, MPI_Datatype datatype, char *myname, - MPIO_Request *request) + MPI_Request *request) { int error_code, bufsize, buftype_is_contig, filetype_is_contig; int datatype_size; ADIO_Status status; ADIO_Offset off; ADIO_File fh; + MPI_Offset nbytes=0; MPIU_THREAD_SINGLE_CS_ENTER("io"); MPIR_Nest_incr(); @@ -232,14 +140,6 @@ int MPIOI_File_iwrite(MPI_File mpi_fh, else { /* to maintain strict atomicity semantics with other concurrent operations, lock (exclusive) and call blocking routine */ - - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_WRITE; - (*request)->fd = fh; - (*request)->datatype = datatype; - (*request)->queued = 0; - (*request)->handle = 0; - if ((fh->file_system != ADIO_PIOFS) && (fh->file_system != ADIO_NFS) && (fh->file_system != ADIO_PVFS) && (fh->file_system != ADIO_PVFS2)) @@ -256,11 +156,11 @@ int MPIOI_File_iwrite(MPI_File mpi_fh, { ADIOI_UNLOCK(fh, off, SEEK_SET, bufsize); } - - fh->async_count++; - /* status info. must be linked to the request structure, so that it - can be accessed later from a wait */ - + if (error_code == MPI_SUCCESS) { + nbytes = count * datatype_size; + } + + MPIO_Completed_request_create(&fh, nbytes, &error_code, request); } } else { @@ -274,4 +174,3 @@ fn_exit: return error_code; } #endif -#endif diff --git a/ompi/mca/io/romio/romio/mpi-io/iwrite_at.c b/ompi/mca/io/romio/romio/mpi-io/iwrite_at.c index 8bbca39d88..321a55398a 100644 --- a/ompi/mca/io/romio/romio/mpi-io/iwrite_at.c +++ b/ompi/mca/io/romio/romio/mpi-io/iwrite_at.c @@ -40,104 +40,8 @@ Output Parameters: @*/ #ifdef HAVE_MPI_GREQUEST #include "mpiu_greq.h" - -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) -typedef struct iwrite_at_args -{ - MPI_File file; - MPI_Offset offset; - void *buf; - int count; - MPI_Datatype datatype; - MPIO_Request request; - MPI_Status *status; -} iwrite_at_args; - -static DWORD WINAPI iwrite_at_thread(LPVOID lpParameter) -{ - int error_code; - iwrite_at_args *args = (iwrite_at_args *)lpParameter; - - error_code = MPI_File_write_at(args->file, args->offset, args->buf, args->count, args->datatype, args->status); - /* ROMIO-1 doesn't do anything with status.MPI_ERROR */ - args->status->MPI_ERROR = error_code; - - MPI_Grequest_complete(args->request); - ADIOI_Free(args); - return 0; -} #endif -int MPI_File_iwrite_at(MPI_File mpi_fh, MPI_Offset offset, void *buf, - int count, MPI_Datatype datatype, - MPIO_Request *request) -{ - int error_code=MPI_SUCCESS; - MPI_Status *status; -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) - iwrite_at_args *args; - HANDLE hThread; -#endif - - MPIU_THREAD_SINGLE_CS_ENTER("io"); - MPIR_Nest_incr(); - - status = (MPI_Status *) ADIOI_Malloc(sizeof(MPI_Status)); - -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) - /* kick off the request */ - MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, - MPIU_Greq_cancel_fn, status, request); - - args = (iwrite_at_args*) ADIOI_Malloc(sizeof(iwrite_at_args)); - args->file = mpi_fh; - args->offset = offset; - args->buf = buf; - args->count = count; - args->datatype = datatype; - args->status = status; - args->request = *request; - hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)iwrite_at_thread, args, 0, NULL); - if (hThread == NULL) - { - error_code = GetLastError(); - error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, - "MPI_File_iwrite_at", __LINE__, MPI_ERR_OTHER, - "**fail", "**fail %d", error_code); - error_code = MPIO_Err_return_file(args->file, error_code); - return error_code; - } - CloseHandle(hThread); - -#else - - /* for now, no threads or anything fancy. - * just call the blocking version */ - error_code = MPI_File_write_at(mpi_fh, offset, buf, count, datatype, - status); - /* ROMIO-1 doesn't do anything with status.MPI_ERROR */ - status->MPI_ERROR = error_code; - - /* --BEGIN ERROR HANDLING-- */ - if (error_code != MPI_SUCCESS) - error_code = MPIO_Err_return_file(mpi_fh, error_code); - /* --END ERROR HANDLING-- */ - - /* kick off the request */ - MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, - MPIU_Greq_cancel_fn, status, request); - - /* but we did all the work already */ - MPI_Grequest_complete(*request); -#endif - - MPIR_Nest_decr(); - MPIU_THREAD_SINGLE_CS_EXIT("io"); - - /* passed the buck to the blocking version...*/ - return error_code; -} -#else int MPI_File_iwrite_at(MPI_File mpi_fh, MPI_Offset offset, void *buf, int count, MPI_Datatype datatype, MPIO_Request *request) @@ -170,4 +74,3 @@ int MPI_File_iwrite_at(MPI_File mpi_fh, MPI_Offset offset, void *buf, return error_code; } -#endif diff --git a/ompi/mca/io/romio/romio/mpi-io/iwrite_sh.c b/ompi/mca/io/romio/romio/mpi-io/iwrite_sh.c index 76e888684e..5c60df7053 100644 --- a/ompi/mca/io/romio/romio/mpi-io/iwrite_sh.c +++ b/ompi/mca/io/romio/romio/mpi-io/iwrite_sh.c @@ -39,96 +39,8 @@ Output Parameters: @*/ #ifdef HAVE_MPI_GREQUEST #include "mpiu_greq.h" - -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) -typedef struct iwrite_shared_args -{ - MPI_File file; - void *buf; - int count; - MPI_Datatype datatype; - MPIO_Request request; - MPI_Status *status; -} iwrite_shared_args; - -static DWORD WINAPI iwrite_shared_thread(LPVOID lpParameter) -{ - int error_code; - iwrite_shared_args *args = (iwrite_shared_args *)lpParameter; - - error_code = MPI_File_write_shared(args->file, args->buf, args->count, args->datatype, args->status); - /* ROMIO-1 doesn't do anything with status.MPI_ERROR */ - args->status->MPI_ERROR = error_code; - - MPI_Grequest_complete(args->request); - ADIOI_Free(args); - return 0; -} #endif -int MPI_File_iwrite_shared(MPI_File mpi_fh, void *buf, int count, - MPI_Datatype datatype, MPIO_Request *request) -{ - int error_code; - MPI_Status *status; -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) - iwrite_shared_args *args; - HANDLE hThread; -#endif - - MPIU_THREAD_SINGLE_CS_ENTER("io"); - MPIR_Nest_incr(); - - status = (MPI_Status *) ADIOI_Malloc(sizeof(MPI_Status)); - -#if defined(HAVE_WINDOWS_H) && defined(USE_WIN_THREADED_IO) - /* kick off the request */ - MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, - MPIU_Greq_cancel_fn, status, request); - - args = (iwrite_shared_args*) ADIOI_Malloc(sizeof(iwrite_shared_args)); - args->file = mpi_fh; - args->buf = buf; - args->count = count; - args->datatype = datatype; - args->status = status; - args->request = *request; - hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)iwrite_shared_thread, args, 0, NULL); - if (hThread == NULL) - { - error_code = GetLastError(); - error_code = MPIO_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, - "MPI_File_iwrite_shared", __LINE__, MPI_ERR_OTHER, - "**fail", "**fail %d", error_code); - error_code = MPIO_Err_return_file(args->file, error_code); - return error_code; - } - CloseHandle(hThread); - -#else - - /* for now, no threads or anything fancy. - * just call the blocking version */ - error_code = MPI_File_write_shared(mpi_fh, buf, count, - datatype, status); - /* ROMIO-1 doesn't do anything with status.MPI_ERROR */ - status->MPI_ERROR = error_code; - - /* kick off the request */ - MPI_Grequest_start(MPIU_Greq_query_fn, MPIU_Greq_free_fn, - MPIU_Greq_cancel_fn, status, request); - - /* but we did all the work already */ - MPI_Grequest_complete(*request); -#endif - - MPIR_Nest_decr(); - MPIU_THREAD_SINGLE_CS_EXIT("io"); - - /* passed the buck to the blocking version...*/ - return MPI_SUCCESS; -} -#else int MPI_File_iwrite_shared(MPI_File mpi_fh, void *buf, int count, MPI_Datatype datatype, MPIO_Request *request) { @@ -181,13 +93,6 @@ int MPI_File_iwrite_shared(MPI_File mpi_fh, void *buf, int count, /* to maintain strict atomicity semantics with other concurrent operations, lock (exclusive) and call blocking routine */ - *request = ADIOI_Malloc_request(); - (*request)->optype = ADIOI_WRITE; - (*request)->fd = fh; - (*request)->datatype = datatype; - (*request)->queued = 0; - (*request)->handle = 0; - if (fh->file_system != ADIO_NFS) ADIOI_WRITE_LOCK(fh, off, SEEK_SET, bufsize); @@ -197,9 +102,7 @@ int MPI_File_iwrite_shared(MPI_File mpi_fh, void *buf, int count, if (fh->file_system != ADIO_NFS) ADIOI_UNLOCK(fh, off, SEEK_SET, bufsize); - fh->async_count++; - /* status info. must be linked to the request structure, so that it - can be accessed later from a wait */ + MPIO_Completed_request_create(&fh, bufsize, &error_code, request); } } else @@ -212,4 +115,3 @@ fn_exit: return error_code; } -#endif diff --git a/ompi/mca/io/romio/romio/mpi-io/mpioprof.h b/ompi/mca/io/romio/romio/mpi-io/mpioprof.h index 5f827ed80f..45c78d44a2 100644 --- a/ompi/mca/io/romio/romio/mpi-io/mpioprof.h +++ b/ompi/mca/io/romio/romio/mpi-io/mpioprof.h @@ -206,5 +206,10 @@ #undef MPI_Status_set_cancelled #define MPI_Status_set_cancelled PMPI_Status_set_cancelled +#undef MPIX_Grequest_start +#define MPIX_Grequest_start PMPIX_Grequest_start +#undef MPIX_Grequest_class_create +#define MPIX_Grequest_class_create PMPIX_Grequest_class_create + #endif #endif diff --git a/ompi/mca/io/romio/romio/mpi-io/mpiu_greq.c b/ompi/mca/io/romio/romio/mpi-io/mpiu_greq.c index 205115d17b..bdd98fabf5 100644 --- a/ompi/mca/io/romio/romio/mpi-io/mpiu_greq.c +++ b/ompi/mca/io/romio/romio/mpi-io/mpiu_greq.c @@ -31,14 +31,14 @@ int MPIU_Greq_query_fn(void *extra_state, MPI_Status *status) MPI_Status_set_cancelled(status, 0); MPIR_Nest_decr(); - /* the MPI_Status structure is a convienent place to stash the return - * code of the blocking operation */ - return ((MPI_Status*)extra_state)->MPI_ERROR; + return MPI_SUCCESS; } int MPIU_Greq_free_fn(void *extra_state) { + /* frees the memory allocated in MPIO_Completed_request_create */ ADIOI_Free(extra_state); + return MPI_SUCCESS; } int MPIU_Greq_cancel_fn(void *extra_state, int complete) diff --git a/ompi/mca/io/romio/romio/test/Makefile.in b/ompi/mca/io/romio/romio/test/Makefile.in index c282d884d2..079f983b27 100644 --- a/ompi/mca/io/romio/romio/test/Makefile.in +++ b/ompi/mca/io/romio/romio/test/Makefile.in @@ -8,7 +8,8 @@ USER_CFLAGS = @CPPFLAGS@ @USER_CFLAGS@ $(INCLUDE_DIR) USER_FFLAGS = @CPPFLAGS@ @USER_FFLAGS@ $(INCLUDE_DIR) CTESTS = simple perf async coll_test coll_perf misc file_info excl large_array \ atomicity noncontig i_noncontig noncontig_coll split_coll shared_fp \ - large_file psimple error status noncontig_coll2 + large_file psimple error status noncontig_coll2 aggregation1 aggregation2 \ + async-multiple ordered_fp FTESTS = fcoll_test fperf fmisc pfcoll_test srcdir=@srcdir@ diff --git a/ompi/mca/io/romio/romio/test/aggregation1.c b/ompi/mca/io/romio/romio/test/aggregation1.c new file mode 100644 index 0000000000..8c6204ad00 --- /dev/null +++ b/ompi/mca/io/romio/romio/test/aggregation1.c @@ -0,0 +1,251 @@ +/* Test case from John Bent (ROMIO req #835) + * Aggregation code was not handling certain access patterns when collective + * buffering forced */ +#include +#include +#include +#include +#include + +#define NUM_OBJS 4 +#define OBJ_SIZE 1048576 + +extern char *optarg; +extern int optind, opterr, optopt; + + +char *prog = NULL; +int debug = 0; + +static void +Usage( int line ) { + int rank; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if ( rank == 0 ) { + fprintf( stderr, + "Usage (line %d): %s [-d] [-h] -f filename\n" + "\t-d for debugging\n" + "\t-h to turn on the hints to force collective aggregation\n", + line, prog ); + } + exit( 0 ); +} + +static void +fatal_error( int mpi_ret, MPI_Status *mpi_stat, char *msg ) { + fprintf( stderr, "Fatal error %s: %d\n", msg, mpi_ret ); + MPI_Abort( MPI_COMM_WORLD, -1 ); +} + +static void +print_hints( int rank, MPI_File *mfh ) { + MPI_Info info; + int nkeys; + int i, dummy_int; + char key[1024]; + char value[1024]; + + MPI_Barrier( MPI_COMM_WORLD ); + if ( rank == 0 ) { + MPI_File_get_info( *mfh, &info ); + MPI_Info_get_nkeys( info, &nkeys ); + + printf( "HINTS:\n" ); + for( i = 0; i < nkeys; i++ ) { + MPI_Info_get_nthkey( info, i, key ); + printf( "%35s -> ", key ); + MPI_Info_get( info, key, 1024, value, &dummy_int ); + printf( "%s\n", value ); + } + } + MPI_Barrier( MPI_COMM_WORLD ); +} + +static void +fill_buffer( char *buffer, int bufsize, int rank, MPI_Offset offset ) { + memset( (void*)buffer, 0, bufsize ); + snprintf( buffer, bufsize, "Hello from %d at %lld\n", rank, offset ); +} + +static MPI_Offset +get_offset( int rank, int num_objs, int obj_size, int which_obj ) { + MPI_Offset offset; + offset = (MPI_Offset)rank * num_objs * obj_size + which_obj * obj_size; + return offset; +} + +static void +write_file( char *target, int rank, MPI_Info *info ) { + MPI_File wfh; + MPI_Status mpi_stat; + int mpi_ret; + int i; + char buffer[OBJ_SIZE]; + + if ( debug ) printf( "%d writing file %s\n", rank, target ); + + if( (mpi_ret = MPI_File_open(MPI_COMM_WORLD, target, + MPI_MODE_WRONLY | MPI_MODE_CREATE, *info, &wfh ) ) + != MPI_SUCCESS ) + { + fatal_error( mpi_ret, NULL, "open for write" ); + } + + for( i = 0; i < NUM_OBJS; i++ ) { + MPI_Offset offset = get_offset( rank, NUM_OBJS, OBJ_SIZE, i ); + fill_buffer( buffer, OBJ_SIZE, rank, offset ); + if ( debug ) printf( "%s", buffer ); + if ( (mpi_ret = MPI_File_write_at_all( wfh, offset, buffer, OBJ_SIZE, + MPI_CHAR, &mpi_stat ) ) != MPI_SUCCESS ) + { + fatal_error( mpi_ret, &mpi_stat, "write" ); + } + } + + if ( debug ) print_hints( rank, &wfh ); + + if( (mpi_ret = MPI_File_close( &wfh ) ) != MPI_SUCCESS ) { + fatal_error( mpi_ret, NULL, "close for write" ); + } + if ( debug ) printf( "%d wrote file %s\n", rank, target ); +} + +static int +reduce_corruptions( int corrupt_blocks ) { + int mpi_ret; + int sum; + if ( ( mpi_ret = MPI_Reduce( &corrupt_blocks, &sum, 1, + MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD ) ) != MPI_SUCCESS ) + { + fatal_error( mpi_ret, NULL, "MPI_Reduce" ); + } + return sum; +} + +static void +read_file( char *target, int rank, MPI_Info *info, int *corrupt_blocks ) { + MPI_File rfh; + MPI_Status mpi_stat; + int mpi_ret; + int i; + char buffer[OBJ_SIZE]; + char *verify_buf = NULL; + verify_buf = (char *)malloc(OBJ_SIZE); + + if ( debug ) printf( "%d reading file %s\n", rank, target ); + + if( (mpi_ret = MPI_File_open(MPI_COMM_WORLD, target, + MPI_MODE_RDONLY, *info, &rfh ) ) != MPI_SUCCESS ) + { + fatal_error( mpi_ret, NULL, "open for read" ); + } + + for( i = 0; i < NUM_OBJS; i++ ) { + MPI_Offset offset = get_offset( rank, NUM_OBJS, OBJ_SIZE, i ); + fill_buffer( verify_buf, OBJ_SIZE, rank, offset ); + if ( debug ) printf( "Expecting %s", buffer ); + if ( (mpi_ret = MPI_File_read_at_all( rfh, offset, buffer, OBJ_SIZE, + MPI_CHAR, &mpi_stat ) ) != MPI_SUCCESS ) + { + fatal_error( mpi_ret, &mpi_stat, "read" ); + } + if ( memcmp( verify_buf, buffer, OBJ_SIZE ) != 0 ) { + (*corrupt_blocks)++; + printf( "Corruption at %lld\n", offset ); + if ( debug ) { + printf( "\tExpecting %s\n" + "\tRecieved %s\n", + verify_buf, buffer ); + } + } + } + + if( (mpi_ret = MPI_File_close( &rfh ) ) != MPI_SUCCESS ) { + fatal_error( mpi_ret, NULL, "close for read" ); + } + +} + +static void +set_hints( MPI_Info *info ) { + MPI_Info_set( *info, "romio_cb_write", "enable" ); + MPI_Info_set( *info, "romio_no_indep_rw", "1" ); + MPI_Info_set( *info, "cb_nodes", "1" ); + MPI_Info_set( *info, "cb_buffer_size", "4194304" ); +} + +/* +void +set_hints( MPI_Info *info, char *hints ) { + char *delimiter = " "; + char *hints_cp = strdup( hints ); + char *key = strtok( hints_cp, delimiter ); + char *val; + while( key ) { + val = strtok( NULL, delimiter ); + if ( debug ) printf( "HINT: %s = %s\n", key, val ); + if ( ! val ) { + Usage( __LINE__ ); + } + MPI_Info_set( *info, key, val ); + key = strtok( NULL, delimiter ); + } + free( hints_cp ); +} +*/ + +int +main( int argc, char *argv[] ) { + int nproc = 1, rank = 0; + char *target = NULL; + int c; + MPI_Info info; + int mpi_ret; + int corrupt_blocks = 0; + + MPI_Init( &argc, &argv ); + MPI_Comm_size(MPI_COMM_WORLD, &nproc); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + if( (mpi_ret = MPI_Info_create(&info)) != MPI_SUCCESS) { + if(rank == 0) fatal_error( mpi_ret, NULL, "MPI_info_create.\n"); + } + + prog = strdup( argv[0] ); + + while( ( c = getopt( argc, argv, "df:h" ) ) != EOF ) { + switch( c ) { + case 'd': + debug = 1; + break; + case 'f': + target = strdup( optarg ); + break; + case 'h': + set_hints( &info ); + break; + default: + Usage( __LINE__ ); + } + } + if ( ! target ) { + Usage( __LINE__ ); + } + + write_file( target, rank, &info ); + read_file( target, rank, &info, &corrupt_blocks ); + + corrupt_blocks = reduce_corruptions( corrupt_blocks ); + if ( rank == 0 ) { + if (corrupt_blocks == 0) { + fprintf(stdout, " No Errors\n"); + } else { + fprintf(stdout, "%d/%d blocks corrupt\n", + corrupt_blocks, nproc * NUM_OBJS ); + } + } + + MPI_Finalize(); + + exit( 0 ); +} diff --git a/ompi/mca/io/romio/romio/test/aggregation2.c b/ompi/mca/io/romio/romio/test/aggregation2.c new file mode 100644 index 0000000000..b5a8c8ce67 --- /dev/null +++ b/ompi/mca/io/romio/romio/test/aggregation2.c @@ -0,0 +1,82 @@ +/* Look for regressions in aggregator code. A more simple access pattern than + * aggregation1 */ + +#include + +#include +#include +#include + +#include +#include + +#include + +#define BUFSIZE 512 + +static void handle_error(int errcode, char *str) +{ + char msg[MPI_MAX_ERROR_STRING]; + int resultlen; + MPI_Error_string(errcode, msg, &resultlen); + fprintf(stderr, "%s: %s\n", str, msg); + MPI_Abort(MPI_COMM_WORLD, 1); +} + +int main(int argc, char ** argv) +{ + MPI_Info info = MPI_INFO_NULL; + MPI_File fh; + MPI_Offset off=0; + MPI_Status status; + int errcode; + int i, rank, errs=0, toterrs, buffer[BUFSIZE], buf2[BUFSIZE]; + + MPI_Init(&argc, &argv); + + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + MPI_Info_create(&info); + MPI_Info_set(info, "romio_cb_write", "enable"); + MPI_Info_set(info, "cb_nodes", "1"); + + for (i=0; i 0) { + fprintf( stderr, "Found %d errors\n", toterrs ); + } + else { + fprintf( stdout, " No Errors\n" ); + } + } + MPI_Finalize(); + + return 0; +} diff --git a/ompi/mca/io/romio/romio/test/async-multiple.c b/ompi/mca/io/romio/romio/test/async-multiple.c new file mode 100644 index 0000000000..6d4ee63f7b --- /dev/null +++ b/ompi/mca/io/romio/romio/test/async-multiple.c @@ -0,0 +1,139 @@ +/* -*- Mode: C; c-basic-offset:4 ; -*- + * vim: ts=8 sts=4 sw=4 noexpandtab + * + * (C) 2001 by Argonne National Laboratory. + * See COPYRIGHT in top-level directory. + */ +#include "mpi.h" +#include +#include +#include + +#define SIZE (65536) +#define NR_NBOPS (32) + +/* Uses asynchronous I/O. Each process writes to separate files and + reads them back. The file name is taken as a command-line argument, + and the process rank is appended to it.*/ + +void handle_error(int errcode, char *str); + +void handle_error(int errcode, char *str) +{ + char msg[MPI_MAX_ERROR_STRING]; + int resultlen; + MPI_Error_string(errcode, msg, &resultlen); + fprintf(stderr, "%s: %s\n", str, msg); + MPI_Abort(MPI_COMM_WORLD, 1); +} +int main(int argc, char **argv) +{ + int *buf, i, rank, nints, len; + char *filename, *tmp; + int errs=0, toterrs; + MPI_File fh; + MPI_Status status[NR_NBOPS]; + MPI_Request request[NR_NBOPS]; + int errcode = 0; + + MPI_Init(&argc,&argv); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + +/* process 0 takes the file name as a command-line argument and + broadcasts it to other processes */ + if (!rank) { + i = 1; + while ((i < argc) && strcmp("-fname", *argv)) { + i++; + argv++; + } + if (i >= argc) { + fprintf(stderr, "\n*# Usage: async -fname filename\n\n"); + MPI_Abort(MPI_COMM_WORLD, 1); + } + argv++; + len = strlen(*argv); + filename = (char *) malloc(len+10); + strcpy(filename, *argv); + MPI_Bcast(&len, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(filename, len+10, MPI_CHAR, 0, MPI_COMM_WORLD); + } + else { + MPI_Bcast(&len, 1, MPI_INT, 0, MPI_COMM_WORLD); + filename = (char *) malloc(len+10); + MPI_Bcast(filename, len+10, MPI_CHAR, 0, MPI_COMM_WORLD); + } + + + buf = (int *) malloc(SIZE); + nints = SIZE/sizeof(int); + for (i=0; i 0) { + fprintf( stderr, "Found %d errors\n", toterrs ); + } + else { + fprintf( stdout, " No Errors\n" ); + } + } + + free(buf); + free(filename); + free(tmp); + + MPI_Finalize(); + return 0; +} diff --git a/ompi/mca/io/romio/romio/test/async.c b/ompi/mca/io/romio/romio/test/async.c index f03c0c88dc..fc26ea7b0e 100644 --- a/ompi/mca/io/romio/romio/test/async.c +++ b/ompi/mca/io/romio/romio/test/async.c @@ -14,6 +14,16 @@ reads them back. The file name is taken as a command-line argument, and the process rank is appended to it.*/ +void handle_error(int errcode, char *str); + +void handle_error(int errcode, char *str) +{ + char msg[MPI_MAX_ERROR_STRING]; + int resultlen; + MPI_Error_string(errcode, msg, &resultlen); + fprintf(stderr, "%s: %s\n", str, msg); + MPI_Abort(MPI_COMM_WORLD, 1); +} int main(int argc, char **argv) { int *buf, i, rank, nints, len; @@ -22,6 +32,7 @@ int main(int argc, char **argv) MPI_File fh; MPI_Status status; MPIO_Request request; + int errcode = 0; MPI_Init(&argc,&argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); @@ -61,10 +72,16 @@ int main(int argc, char **argv) strcpy(tmp, filename); sprintf(filename, "%s.%d", tmp, rank); - MPI_File_open(MPI_COMM_SELF, filename, MPI_MODE_CREATE | MPI_MODE_RDWR, - MPI_INFO_NULL, &fh); + errcode = MPI_File_open(MPI_COMM_SELF, filename, + MPI_MODE_CREATE | MPI_MODE_RDWR, MPI_INFO_NULL, &fh); + if (errcode != MPI_SUCCESS) { + handle_error(errcode, "MPI_File_open"); + } MPI_File_set_view(fh, 0, MPI_INT, MPI_INT, "native", MPI_INFO_NULL); - MPI_File_iwrite(fh, buf, nints, MPI_INT, &request); + errcode = MPI_File_iwrite(fh, buf, nints, MPI_INT, &request); + if (errcode != MPI_SUCCESS) { + handle_error(errcode, "MPI_File_iwrite"); + } #ifdef MPIO_USES_MPI_REQUEST MPI_Wait( &request, &status ); #else @@ -75,10 +92,17 @@ int main(int argc, char **argv) /* reopen the file and read the data back */ for (i=0; i #include +/* #undef INFO_DEBUG */ + /* Set verbose to 0 only if you want no information about any failure */ static int verbose = 1; @@ -62,7 +64,7 @@ int main(int argc, char **argv) for (i=0; i +#include +#include + +#define COUNT (200) +#undef TIMING + +void handle_error(int errcode, char *str); + +void handle_error(int errcode, char *str) +{ + char msg[MPI_MAX_ERROR_STRING]; + int resultlen; + MPI_Error_string(errcode, msg, &resultlen); + fprintf(stderr, "%s: %s\n", str, msg); + MPI_Abort(MPI_COMM_WORLD, 1); +} + +/* tests shared file pointer functions */ + +int main(int argc, char **argv) +{ + int *buf, i, rank, nprocs, len, sum; + int global_sum; + int errs=0, toterrs, errcode; + char *filename; + MPI_File fh; + MPI_Status status; + + MPI_Init(&argc,&argv); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + double wr_stime, wr_etime, wr_time, wr_sumtime; + double rd_stime, rd_etime, rd_time, rd_sumtime; + +/* process 0 takes the file name as a command-line argument and + broadcasts it to other processes */ + if (!rank) { + i = 1; + while ((i < argc) && strcmp("-fname", *argv)) { + i++; + argv++; + } + if (i >= argc) { + fprintf(stderr, "\n*# Usage: shared_fp -fname filename\n\n"); + MPI_Abort(MPI_COMM_WORLD, 1); + } + argv++; + len = strlen(*argv); + filename = (char *) malloc(len+10); + strcpy(filename, *argv); + MPI_Bcast(&len, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(filename, len+10, MPI_CHAR, 0, MPI_COMM_WORLD); + } + else { + MPI_Bcast(&len, 1, MPI_INT, 0, MPI_COMM_WORLD); + filename = (char *) malloc(len+10); + MPI_Bcast(filename, len+10, MPI_CHAR, 0, MPI_COMM_WORLD); + } + + buf = (int *) malloc(COUNT * sizeof(int)); + + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + + for (i=0; i 0) { + fprintf( stderr, "Found %d errors\n", toterrs ); + } + else { + fprintf( stdout, " No Errors\n" ); +#ifdef TIMING + fprintf( stderr, "nprocs: %d bytes: %d write: %f read %f\n", + nprocs, COUNT*sizeof(int), wr_sumtime, rd_sumtime); +#endif + } + } + + MPI_Finalize(); + return 0; +} diff --git a/ompi/mca/io/romio/romio/test/runtests.in b/ompi/mca/io/romio/romio/test/runtests.in index 35505f0604..2c893defef 100644 --- a/ompi/mca/io/romio/romio/test/runtests.in +++ b/ompi/mca/io/romio/romio/test/runtests.in @@ -181,6 +181,16 @@ $mpirun -np 4 ./async -fname $FILENAME CleanExe async # OutTime +testfiles="$testfiles async-multi.out" +\rm -f async-multi.out +MakeExe async-multiple +\rm -f $FILENAME* +echo '**** Testing async-multiple.c ****' +$mpirun -np 4 ./async-multiple -fname $FILENAME +# CheckOutput async-multiple +CleanExe async-multiple +# +OutTime if [ $subset_only -eq 0 ] ; then testfiles="$testfiles atomicity.out" \rm -f atomicity.out @@ -263,7 +273,22 @@ echo '**** Testing noncontig_coll2.c ****' $mpirun -np 4 ./noncontig_coll2 -fname $FILENAME # CheckOutput noncontig_coll2 CleanExe noncontig_coll2 +echo '**** Testing aggregation1 ****' +$mpirun -np 4 ./aggregation1 -h -fname $FILENAME +# CheckOutput aggregation1 +CleanExe aggregation1 +echo '**** Testing aggregation2 ****' +$mpirun -np 4 ./aggregation2 $FILENAME +# CheckOutput aggregation2 +CleanExe aggregation2 # +#echo '**** Testing write_all_test (run 1)****' +#$mpirun -np 4 ./write_all_test -nzp 2 -zplace 2 -nzw 2 -naw 2 -size 100 \ +# -offm 8 -hints romio_cb_write enable -fname $FILENAME +#echo '**** Testing write_all_test (run 2)****' +#$mpirun -np 4 ./write_all_test -nzp 2 -zplace 2 -nzw 2 -naw 2 -size 100 \ +# -offm 4 -hints romio_cb_write enable -fname $FILENAME +#CleanExe write_all_test OutTime if [ $subset_only -eq 0 ] ; then testfiles="$testfiles misc.out" @@ -288,6 +313,15 @@ if [ $subset_only -eq 0 ] ; then CleanExe shared_fp # OutTime + testfiles="$testfiles ordered_fp.out" + \rm -f ordered_fp.out + MakeExe ordered_fp + \rm -f $FILENAME* + echo '**** Testing ordered_fp.c ****' + $mpirun -np 4 ./ordered_fp -fname $FILENAME + CleanExe ordered_fp + # + OutTime fi testfiles="$testfiles split_coll.out" \rm -f split_coll.out diff --git a/ompi/mca/io/romio/romio/test/simple.c b/ompi/mca/io/romio/romio/test/simple.c index d3d4f4b767..7182a4c01f 100644 --- a/ompi/mca/io/romio/romio/test/simple.c +++ b/ompi/mca/io/romio/romio/test/simple.c @@ -10,6 +10,14 @@ #define SIZE (65536) +static void handle_error(int errcode, char *str) +{ + char msg[MPI_MAX_ERROR_STRING]; + int resultlen; + MPI_Error_string(errcode, msg, &resultlen); + fprintf(stderr, "%s: %s\n", str, msg); + MPI_Abort(MPI_COMM_WORLD, 1); +} /* Each process writes to separate files and reads them back. The file name is taken as a command-line argument, and the process rank is appended to it. */ @@ -18,7 +26,7 @@ int main(int argc, char **argv) { int *buf, i, rank, nints, len; char *filename, *tmp; - int errs = 0, toterrs; + int errs = 0, toterrs, errcode; MPI_File fh; MPI_Status status; @@ -60,18 +68,28 @@ int main(int argc, char **argv) strcpy(tmp, filename); sprintf(filename, "%s.%d", tmp, rank); - MPI_File_open(MPI_COMM_SELF, filename, MPI_MODE_CREATE | MPI_MODE_RDWR, - MPI_INFO_NULL, &fh); - MPI_File_write(fh, buf, nints, MPI_INT, &status); - MPI_File_close(&fh); + errcode = MPI_File_open(MPI_COMM_SELF, filename, + MPI_MODE_CREATE | MPI_MODE_RDWR, MPI_INFO_NULL, &fh); + if (errcode != MPI_SUCCESS) handle_error(errcode, "MPI_File_open(1)"); + + errcode = MPI_File_write(fh, buf, nints, MPI_INT, &status); + if (errcode != MPI_SUCCESS) handle_error(errcode, "MPI_File_write"); + + errcode = MPI_File_close(&fh); + if (errcode != MPI_SUCCESS) handle_error(errcode, "MPI_File_clode (1)"); /* reopen the file and read the data back */ for (i=0; iromio_rq; - ret = ROMIO_PREFIX(MPIO_Test)(&romio_rq, &flag, - &(((ompi_request_t *) item)->req_status)); + ret = MPI_Test(&romio_rq, &flag, + &(((ompi_request_t *) item)->req_status)); if ((0 != ret) || (0 != flag)) { ioreq->super.req_status.MPI_ERROR = ret; ++count;

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%0A=B)cKBjy~{$Vjx7>XN6P4m09BBpgWSk! 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 literal 0 HcmV?d00001 diff --git a/ompi/mca/io/romio/romio/doc/users-guide.ps.gz b/ompi/mca/io/romio/romio/doc/users-guide.ps.gz deleted file mode 100644 index dc43c91ff6c6989d8e37ef0e5885760f15603c4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 126177 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/^y?ao&ghZ+ygvsx?W1FG%hUDLfa?@sRrXhMx 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 z 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