Feature #975: Add new OFI LRTS machine. 59/2759/17
authoryohann <yohann.burette@intel.com>
Tue, 9 May 2017 16:12:40 +0000 (09:12 -0700)
committerPhil Miller <mille121@illinois.edu>
Mon, 11 Sep 2017 18:13:54 +0000 (13:13 -0500)
For this commit, both the machine specific metadata cache and the mempool
are disabled. This commit is also missing a Zero copy API implementation
of the OFI layer, which will be added as a separate commit in the future
Change-Id: Iaa547751e94bf2fce77f0008398de1fde49b7074

36 files changed:
README
build
doc/charm++/install.tex
doc/faq/ports.tex
smart-build.pl
src/arch/ofi-linux-x86_64/conv-mach-slurmpmi.h [new file with mode: 0644]
src/arch/ofi-linux-x86_64/conv-mach-slurmpmi.sh [new file with mode: 0644]
src/arch/ofi-linux-x86_64/conv-mach-slurmpmi2.h [new file with mode: 0644]
src/arch/ofi-linux-x86_64/conv-mach-slurmpmi2.sh [new file with mode: 0644]
src/arch/ofi-linux-x86_64/conv-mach-smp.h [new file with mode: 0644]
src/arch/ofi-linux-x86_64/conv-mach-smp.sh [new file with mode: 0644]
src/arch/ofi-linux-x86_64/conv-mach.h [new file with mode: 0644]
src/arch/ofi-linux-x86_64/conv-mach.sh [new file with mode: 0644]
src/arch/ofi/LICENSE [new file with mode: 0644]
src/arch/ofi/LICENSE-SandiaOpenSHMEM [new file with mode: 0644]
src/arch/ofi/Makefile.machine [new file with mode: 0644]
src/arch/ofi/README [new file with mode: 0644]
src/arch/ofi/charmrun [new file with mode: 0755]
src/arch/ofi/conv-common.h [new file with mode: 0644]
src/arch/ofi/conv-common.sh [new file with mode: 0644]
src/arch/ofi/machine.c [new file with mode: 0644]
src/arch/ofi/request.h [new file with mode: 0644]
src/arch/ofi/runtime-codec.h [new file with mode: 0644]
src/arch/ofi/runtime-pmi.c [new file with mode: 0644]
src/arch/ofi/runtime-pmi2.c [new file with mode: 0644]
src/arch/ofi/runtime.h [new file with mode: 0644]
src/arch/ofi/simple_pmi/LICENSE-MPICH [new file with mode: 0644]
src/arch/ofi/simple_pmi/README [new file with mode: 0644]
src/arch/ofi/simple_pmi/mpl.h [new file with mode: 0644]
src/arch/ofi/simple_pmi/pmi.h [new file with mode: 0644]
src/arch/ofi/simple_pmi/simple_pmi.c [new file with mode: 0644]
src/arch/ofi/simple_pmi/simple_pmiutil.c [new file with mode: 0644]
src/arch/ofi/simple_pmi/simple_pmiutil.h [new file with mode: 0644]
src/conv-core/convcore.c
src/scripts/configure
src/scripts/configure.in

diff --git a/README b/README
index cf6a07bfa3a4211f09c2661e9e27f65ee73f062d..43cd154f334e1beb8794c583fe1569089ce23db0 100644 (file)
--- a/README
+++ b/README
@@ -101,6 +101,7 @@ mpi-darwin-x86_64          MacOS X          MPI            Clang C++ compiler
 pamilrts-bluegeneq         CNK              PAMI           XLC
 gni-crayxe                 Linux            GNI            GNU compiler
 verbs-linux-x86_64         Linux            IB Verbs       GNU compiler
+ofi-linux-x86_64           Linux            OFI            GNU compiler
 net-win64                  Windows          UDP            MS Visual C++
 net-cygwin                 Cygwin           UDP            GNU compiler
 
@@ -114,7 +115,7 @@ To choose <version>, your choice is determined by two options:
 option for networks of workstations, clusters, or single-machine
 development and testing.
 
-        "gni-", "pamilrts-", "verbs-" Charm++
+        "gni-", "pamilrts-", "verbs-", "ofi-" Charm++
 communicates using direct calls to the machine's communication primitives.
 Use these versions on machines that support them for best performance.
 
diff --git a/build b/build
index e01ca2dd1c548115c306b2adf4b5671d9afd838d..f256b521b85bb32ce8f96f147b81f8ce0844e4cf 100755 (executable)
--- a/build
+++ b/build
@@ -33,7 +33,7 @@ syntax() {
   echo ''
   fi
   echo '<versions>: ' 
-  ( cd $src ; ls -1 | egrep -v '(^shmem$)|(^mpi$)|(^sim$)|(^net(lrts)?$)|(^multicore$)|(^util$)|(^common$)|(^uth$)|(^conv-mach-fix.sh$)|(^win64$)|(^(gemini_)?gni$)|(^pami(lrts)?$)|(^verbs$)|(^template$)|(^cuda$)' | pr -3 -t )
+  ( cd $src ; ls -1 | egrep -v '(^shmem$)|(^mpi$)|(^sim$)|(^net(lrts)?$)|(^multicore$)|(^util$)|(^common$)|(^uth$)|(^conv-mach-fix.sh$)|(^win64$)|(^(gemini_)?gni$)|(^pami(lrts)?$)|(^verbs$)|(^ofi$)|(^template$)|(^cuda$)' | pr -3 -t )
   echo ''
   echo '<options>: compiler and platform specific options'
   echo 'icc iccstatic xlc xlc64 gcc clang pgcc cc mpicxx'
@@ -466,9 +466,9 @@ fi
 
 [ "x$VERSION" = "x" ] && syntax && exit 1
 
-#Check if building verbs on OmniPath 
+#Check if building verbs on Omni-Path 
 if [ "x_$ARCH" = "x_verbs" ] && type /usr/sbin/opafabricinfo >/dev/null 2>&1; then
-  echo "WARNING: Verbs on OmniPath architectures is not well supported, please use an MPI build instead.";
+  echo "WARNING: Verbs on Omni-Path architectures is not well supported, please use an OFI build instead.";
   exit 1;
 fi 
 
index 8d4e96b816a49a13bfc122c8ec2fa01129d7af73..fbd8e89f0ec4e212b07d1cc6283cfd6f2c8c2bf8 100644 (file)
@@ -72,6 +72,8 @@ MPI with 64 bit Linux (mpicxx wrappers) & \verb|./build charm++ mpi-linux-x86_64
 \\\hline
 IBVERBS with 64 bit Linux & \verb|./build charm++ verbs-linux-x86_64 --with-production -j8|
 \\\hline
+OFI with 64 bit Linux & \verb|./build charm++ ofi-linux-x86_64 --with-production -j8|
+\\\hline
 Net with 64 bit Windows & \verb|./build charm++ netlrts-win-x86_64 --with-production -j8|
 \\\hline
 MPI with 64 bit Windows & \verb|./build charm++ mpi-win-x86_64 --with-production -j8|
index 5eb938f60a9a3b575d53adc343d61849f73d56da..49602a9768de58af573321e5f3b74af24e04f2f9 100644 (file)
@@ -12,7 +12,7 @@ used from Charm++.
 
 Depends. Hopefully, the porting only involves fixing compiler compatibility
 issues.  The LRTS abstraction layer was designed to simplify this process and has been used for the
-MPI, Verbs, uGNI, and PAMI layers.  User level threads and Isomalloc support may require special
+MPI, Verbs, uGNI, PAMI and OFI layers.  User level threads and Isomalloc support may require special
 platform specific support.  Otherwise Charm++ is generally platform independent.
 
 \subsection{If the source is available how feasible would it be for us to do ports
index c43c813ed46167996752c0007f032f176238b347..271b2bb00c8ab5e0fffc3eedfbba9903b8d7b658 100755 (executable)
@@ -142,6 +142,7 @@ Choose an interconnect from below: [1-10]
         3) Cray XE, XK
         4) Cray XC
         5) Blue Gene/Q
+        6) Intel Omni-Path (ofi)
 
 EOF
        
@@ -162,6 +163,9 @@ EOF
          } elsif($line eq "5"){
                $arch = "pamilrts-bluegeneq";
                last;
+         } elsif($line eq "6"){
+               $converse_network_type = "ofi";
+               last;
          } else {
                print "Invalid option, please try again :P\n"
          }
diff --git a/src/arch/ofi-linux-x86_64/conv-mach-slurmpmi.h b/src/arch/ofi-linux-x86_64/conv-mach-slurmpmi.h
new file mode 100644 (file)
index 0000000..45c8759
--- /dev/null
@@ -0,0 +1,4 @@
+#undef CMK_OFI_USE_PMI
+#undef CMK_OFI_USE_PMI2
+#undef CMK_OFI_USE_SIMPLEPMI
+#define CMK_OFI_USE_PMI                                     1
diff --git a/src/arch/ofi-linux-x86_64/conv-mach-slurmpmi.sh b/src/arch/ofi-linux-x86_64/conv-mach-slurmpmi.sh
new file mode 100644 (file)
index 0000000..512c4e7
--- /dev/null
@@ -0,0 +1,2 @@
+CMK_INCDIR="$CMK_INCDIR -I/usr/include/slurm/"
+CMK_LIBS="$CMK_LIBS -lpmi"
diff --git a/src/arch/ofi-linux-x86_64/conv-mach-slurmpmi2.h b/src/arch/ofi-linux-x86_64/conv-mach-slurmpmi2.h
new file mode 100644 (file)
index 0000000..a19f1fd
--- /dev/null
@@ -0,0 +1,4 @@
+#undef CMK_OFI_USE_PMI
+#undef CMK_OFI_USE_PMI2
+#undef CMK_OFI_USE_SIMPLEPMI
+#define CMK_OFI_USE_PMI2                                    1
diff --git a/src/arch/ofi-linux-x86_64/conv-mach-slurmpmi2.sh b/src/arch/ofi-linux-x86_64/conv-mach-slurmpmi2.sh
new file mode 100644 (file)
index 0000000..bb61bd8
--- /dev/null
@@ -0,0 +1,2 @@
+CMK_INCDIR="$CMK_INCDIR -I/usr/include/slurm/"
+CMK_LIBS="$CMK_LIBS -lpmi2"
diff --git a/src/arch/ofi-linux-x86_64/conv-mach-smp.h b/src/arch/ofi-linux-x86_64/conv-mach-smp.h
new file mode 100644 (file)
index 0000000..f72decc
--- /dev/null
@@ -0,0 +1,12 @@
+#define CMK_SMP                                                   1
+
+
+#undef CMK_SHARED_VARS_UNAVAILABLE
+#undef CMK_SHARED_VARS_POSIX_THREADS_SMP
+#define CMK_SHARED_VARS_UNAVAILABLE                        0
+#define CMK_SHARED_VARS_POSIX_THREADS_SMP                  1
+
+#undef CMK_TIMER_USE_GETRUSAGE
+#undef CMK_TIMER_USE_SPECIAL
+#define CMK_TIMER_USE_GETRUSAGE                            1
+#define CMK_TIMER_USE_SPECIAL                              0
diff --git a/src/arch/ofi-linux-x86_64/conv-mach-smp.sh b/src/arch/ofi-linux-x86_64/conv-mach-smp.sh
new file mode 100644 (file)
index 0000000..c5b2de4
--- /dev/null
@@ -0,0 +1,3 @@
+CMK_DEFS=' -D_REENTRANT '
+CMK_LIBS=" -lpthread $CMK_LIBS "
+CMK_SMP='1'
diff --git a/src/arch/ofi-linux-x86_64/conv-mach.h b/src/arch/ofi-linux-x86_64/conv-mach.h
new file mode 100644 (file)
index 0000000..c6189ec
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef _CONV_MACH_H
+#define _CONV_MACH_H
+
+#define CMK_OFI 1
+
+/* define the default linker, together with its options */
+#define CMK_DLL_CC   "g++ -shared -O3 -o "
+
+/* 1 if the machine has a function called "getpagesize()", 0 otherwise .
+   used in the memory files of converse */
+#define CMK_GETPAGESIZE_AVAILABLE                          1
+
+/* defines which version of memory handlers should be used.
+   used in conv-core/machine.c */
+#define CMK_MALLOC_USE_GNU_MALLOC                          0
+#define CMK_MALLOC_USE_OS_BUILTIN                          1
+
+#define CMK_MEMORY_PAGESIZE                                4096
+#define CMK_MEMORY_PROTECTABLE                             1
+
+/* the following definitions set the type of shared variables to be used. only
+   one of them must be 1, all the others 0. The different implementations are in
+   convserve.h Typically used are UNAVAILABLE for non SMP versions and
+   POSIX_THREADS_SMP for SMP versions. The others are used only in special
+   cases: UNIPROCESSOR in sim and uth, PTHREADS in origin,
+   and NT_THREADS in windows. */
+#define CMK_SHARED_VARS_UNAVAILABLE                        1 /* non SMP versions */
+#define CMK_SHARED_VARS_POSIX_THREADS_SMP                  0 /* SMP versions */
+#define CMK_SHARED_VARS_UNIPROCESSOR                       0
+#define CMK_SHARED_VARS_NT_THREADS                         0
+
+/* the following define if signal handlers should be used, both equal to zero
+   means that signals will not be used. only one of the following can be 1, the
+   other must be 0. they differ in the fact that the second (_WITH_RESTART)
+   enables retry on interrupt (a function is recalled upon interrupt and does
+   not return EINTR as in the first case) */
+#define CMK_SIGNAL_USE_SIGACTION                           0
+#define CMK_SIGNAL_USE_SIGACTION_WITH_RESTART              1
+
+/* specifies whether the CthCpv variables should be defined as Cpv (0) or
+   directly as normal c variables (1) */
+#define CMK_THREADS_REQUIRE_NO_CPV                         0
+
+/* decide which is the default implementation of the threads (see threads.c)
+   Only one of the following can be 1. If none of them is selected, qthreads
+   will be used as default. This default can be overwritten at compile time
+   using -DCMK_THREADS_BUILD_"type"=1 */
+#define CMK_THREADS_USE_CONTEXT                            1
+#define CMK_THREADS_USE_JCONTEXT                           0
+#define CMK_THREADS_USE_PTHREADS                           0
+
+/* Specifies what kind of timer to use, and the correspondent headers will be
+   included in convcore.c. If none is selected, then the machine.c file needs to
+   implement the timer primitives. */
+#define CMK_TIMER_USE_RTC                                  0
+#define CMK_TIMER_USE_RDTSC                                0
+#define CMK_TIMER_USE_GETRUSAGE                            1
+#define CMK_TIMER_USE_SPECIAL                              0
+#define CMK_TIMER_USE_TIMES                                0
+#define CMK_TIMER_USE_BLUEGENEL                            0
+
+/* Specifies what the processor will do when it is idle, either sleep (1) or go
+   into busy waiting mode (0). In convcore.c there are a few files included if
+   sleeping mode, but the real distinct implementation is in the machine.c
+   file. */
+#define CMK_WHEN_PROCESSOR_IDLE_USLEEP                     0
+
+/* specifies whether there is a web server collecting utilization statistics (1)
+   or not (0) */
+#define CMK_WEB_MODE                                       1
+
+#define CMK_DEBUG_MODE                                     0
+
+/* enables the load balancer framework. set to 1 for almost all the machines */
+#define CMK_LBDB_ON                                       1
+
+#define CMK_64BIT                      1
+#define CMK_AMD64                      1
+
+/* Other possible definitions:
+
+In fault tolerant architectures, CK_MEM_CHECKPOINT can be set. In this case the
+extended header must contain also another field called "pn" (phase number).
+
+*/
+
+/*
+ * Specifies which version of PMI to use.
+ * See src/arch/ofi/machine.c
+ */
+#define CMK_OFI_USE_PMI                                     1
+#define CMK_OFI_USE_PMI2                                    0
+
+/*
+ * Use Simple client-side implementation of PMI.
+ * Valid only for CMK_OFI_USE_PMI.
+ * Optional in an SLURM environment.
+ * See src/arch/ofi/simple_pmi/
+ */
+#define CMK_OFI_USE_SIMPLEPMI                               1
+
+#endif
diff --git a/src/arch/ofi-linux-x86_64/conv-mach.sh b/src/arch/ofi-linux-x86_64/conv-mach.sh
new file mode 100644 (file)
index 0000000..86fd235
--- /dev/null
@@ -0,0 +1,28 @@
+. $CHARMINC/cc-gcc.sh
+
+CMK_CF90=`which f95 2>/dev/null`
+if test -n "$CMK_CF90"
+then
+    . $CHARMINC/conv-mach-gfortran.sh
+else
+    CMK_CF77='g77 '
+    CMK_CF90='f90 '
+    CMK_CF90_FIXED="$CMK_CF90 -W132 "
+    CMK_F90LIBS='-L/usr/absoft/lib -L/opt/absoft/lib -lf90math -lfio -lU77 -lf77math '
+    CMK_F77LIBS='-lg2c '
+    CMK_F90_USE_MODDIR=1
+    CMK_F90_MODINC='-p'
+fi
+
+# For libfabric
+#If the user doesn't pass --basedir, use defaults for libfabric headers and library
+if test -z "$USER_OPTS_LD"
+then
+    CMK_INCDIR="-I/usr/include/"
+    CMK_LIBDIR="-L/usr/lib64/"
+fi
+
+CMK_LIBS="$CMK_LIBS -lfabric"
+
+# For runtime
+CMK_INCDIR="$CMK_INCDIR -I./simple_pmi/"
diff --git a/src/arch/ofi/LICENSE b/src/arch/ofi/LICENSE
new file mode 100644 (file)
index 0000000..f93dd4f
--- /dev/null
@@ -0,0 +1,25 @@
+Copyright (c) 2017, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright 
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of Intel Corporation nor the names of its contributors
+      may be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/arch/ofi/LICENSE-SandiaOpenSHMEM b/src/arch/ofi/LICENSE-SandiaOpenSHMEM
new file mode 100644 (file)
index 0000000..d30d92c
--- /dev/null
@@ -0,0 +1,147 @@
+Copyright 2011 Sandia Corporation. Under the terms of Contract
+DE-AC04-94AL85000 with Sandia Corporation, the U.S.  Government
+retains certain rights in this software.
+
+Copyright (c) 2015 Intel Corporation. All rights reserved.
+This software is available to you under the BSD license.
+
+COPYRIGHT
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+- Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer listed
+  in this license in the documentation and/or other materials
+  provided with the distribution.
+
+- Neither the name of the copyright holders nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+The copyright holders provide no reassurances that the source code
+provided does not infringe any patent, copyright, or any other
+intellectual property rights of third parties.  The copyright holders
+disclaim any liability to any recipient for claims brought against
+recipient by any third party for infringement of that parties
+intellectual property rights.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Additional copyrights may follow
+
+======================================================================
+
+Doug Lea's memory allocator is included in this work at src/malloc.c
+under the following license terms:
+
+This is a version (aka dlmalloc) of malloc/free/realloc written by
+Doug Lea and released to the public domain, as explained at
+http://creativecommons.org/licenses/publicdomain.  Send questions,
+comments, complaints, performance data, etc to dl@cs.oswego.edu
+
+
+======================================================================
+
+Some autoconf macros used in building Sandia OpenSHMEM, residing in
+config/, are included from the Open MPI project under the following
+license terms:
+
+Copyright (c) 2004-2010 The Trustees of Indiana University and Indiana
+                        University Research and Technology
+                        Corporation.  All rights reserved.
+Copyright (c) 2004-2010 The University of Tennessee and The University
+                        of Tennessee Research Foundation.  All rights
+                        reserved.
+Copyright (c) 2004-2010 High Performance Computing Center Stuttgart, 
+                        University of Stuttgart.  All rights reserved.
+Copyright (c) 2004-2008 The Regents of the University of California.
+                        All rights reserved.
+Copyright (c) 2006-2016 Los Alamos National Security, LLC.  All rights
+                        reserved. 
+Copyright (c) 2006-2010 Cisco Systems, Inc.  All rights reserved.
+Copyright (c) 2006-2010 Voltaire, Inc. All rights reserved.
+Copyright (c) 2006-2010 Sandia National Laboratories. All rights reserved.
+Copyright (c) 2006-2010 Sun Microsystems, Inc.  All rights reserved.
+                        Use is subject to license terms.
+Copyright (c) 2006-2010 The University of Houston. All rights reserved.
+Copyright (c) 2006-2009 Myricom, Inc.  All rights reserved.
+Copyright (c) 2007-2008 UT-Battelle, LLC. All rights reserved.
+Copyright (c) 2007-2010 IBM Corporation.  All rights reserved.
+Copyright (c) 1998-2005 Forschungszentrum Juelich, Juelich Supercomputing 
+                        Centre, Federal Republic of Germany
+Copyright (c) 2005-2008 ZIH, TU Dresden, Federal Republic of Germany
+Copyright (c) 2007      Evergrid, Inc. All rights reserved.
+Copyright (c) 2008      Chelsio, Inc.  All rights reserved.
+Copyright (c) 2008-2009 Institut National de Recherche en
+                        Informatique.  All rights reserved.
+Copyright (c) 2007      Lawrence Livermore National Security, LLC.
+                        All rights reserved.
+Copyright (c) 2007-2009 Mellanox Technologies.  All rights reserved.
+Copyright (c) 2006-2010 QLogic Corporation.  All rights reserved.
+Copyright (c) 2008-2010 Oak Ridge National Labs.  All rights reserved.
+Copyright (c) 2006-2010 Oracle and/or its affiliates.  All rights reserved.
+Copyright (c) 2009      Bull SAS.  All rights reserved.
+Copyright (c) 2010      ARM ltd.  All rights reserved.
+
+
+======================================================================
+
+    SHMEM-PMI implementation is derived from MPICH
+
+
+  COPYRIGHT
+
+The following is a notice of limited availability of the code, and disclaimer
+which must be included in the prologue of the code and in all source listings
+of the code.
+
+Copyright Notice
++ 2002 University of Chicago
+
+Permission is hereby granted to use, reproduce, prepare derivative works, and
+to redistribute to others.  This software was authored by:
+
+Mathematics and Computer Science Division
+Argonne National Laboratory, Argonne IL 60439
+
+(and)
+
+Department of Computer Science
+University of Illinois at Urbana-Champaign
+
+
+                             GOVERNMENT LICENSE
+
+Portions of this material resulted from work developed under a U.S.
+Government Contract and are subject to the following license: the Government
+is granted for itself and others acting on its behalf a paid-up, nonexclusive,
+irrevocable worldwide license in this computer software to reproduce, prepare
+derivative works, and perform publicly and display publicly.
+
+                                 DISCLAIMER
+
+This computer code material was prepared, in part, as an account of work
+sponsored by an agency of the United States Government.  Neither the United
+States, nor the University of Chicago, nor any of their employees, makes any
+warranty express or implied, or assumes any legal liability or responsibility
+for the accuracy, completeness, or usefulness of any information, apparatus,
+product, or process disclosed, or represents that its use would not infringe
+privately owned rights.
+
+======================================================================
+
diff --git a/src/arch/ofi/Makefile.machine b/src/arch/ofi/Makefile.machine
new file mode 100644 (file)
index 0000000..18f7a14
--- /dev/null
@@ -0,0 +1,12 @@
+BUILD_GPU_MANAGER=$(shell CHARMINC=.; if test -f ./conv-config.sh; then . ./conv-config.sh; echo $$BUILD_CUDA; fi )
+
+ifeq "$(BUILD_GPU_MANAGER)" "1"
+hybridAPI:
+          cd hybridAPI && make install
+
+charm++: hybridAPI
+
+.PHONY: hybridAPI
+endif
+
+$(L)/libconv-cplus-n.a: machine.h machine.c machine-common-core.c machine-broadcast.c machine-lrts.h machine-commthd-util.c request.h
diff --git a/src/arch/ofi/README b/src/arch/ofi/README
new file mode 100644 (file)
index 0000000..671bf99
--- /dev/null
@@ -0,0 +1,112 @@
+OFI LRTS machine
+----------------
+
+This is an implementation of the Converse LRTS machine over the Open Fabrics
+Interfaces (OFI) API.  More information about OFI can be found at
+
+    https://ofiwg.github.io/libfabric/
+
+
+PMI-based launcher
+------------------
+
+This implementation requires a PMI-based launcher to run jobs. The application
+(client) uses the PMI protocol to communicate with the PMI server. Typically,
+the server side is provided by the launcher -- e.g. mpiexec.hydra, srun.  The
+client side needs to be built in the application.
+
+On some systems, it is possible to re-use an implementation of the client side
+provided by SLURM.
+
+On others, we provide, for convenience, an implementation of the client side
+called "simple PMI" that was written by the MPICH developers.
+
+By default, we build simple PMI in the application as it works for all cases.
+However, it is also possible to use SLURM's implementation of the client side
+instead. See the "Build the OFI LRTS machine" section for more information.
+
+
+Build the OFI LRTS machine
+--------------------------
+
+To build the OFI LRTS machine with simple PMI:
+
+    ./build charm++ ofi-linux-x84_64 smp icc
+
+(Note: the resulting binary will work with mpiexec.hydra or srun)
+
+
+To build the OFI LRTS machine with SLURM PMI:
+
+    ./build charm++ ofi-linux-x84_64 slurmpmi smp icc
+
+
+To build the OFI LRTS machine with SLURM PMI2:
+
+    ./build charm++ ofi-linux-x84_64 slurmpmi2 smp icc
+
+
+Launch a job
+------------
+
+To launch an application with mpiexec.hydra:
+
+    mpiexec.hydra -hosts host1,host2 -n 2 ./hello
+
+
+Runtime options
+---------------
+
+Several options can be used at runtime:
+    +ofi_eager_maxsize: (default: 65536) Threshold between buffered and RMA
+                        paths.
+    +ofi_cq_entries_count: (default: 8) Maximum number of entries to read from
+                           the completion queue with each call to fi_cq_read().
+    +ofi_use_inject: (default: 1) Whether to use buffered send.
+    +ofi_num_recvs: (default: 8) Number of pre-posted receive buffers.
+    +ofi_runtime_tcp: (default: off) During the initialization phase, the
+                      OFI EP names need to be exchanged among all nodes. By
+                      default, the exchange is done with both PMI and OFI. If
+                      this flag is set then the exchange is done with PMI only.
+
+    +ofi_mempool_init_size_mb: (default: 8) The initial size of memory pool in MBytes.
+    +ofi_mempool_expand_size_mb: (default: 4) The size of expanding chunk in MBytes.
+    +ofi_mempool_max_size_mb: (default: 512) The limit for total size
+                              of memory pool in MBytes.
+    +ofi_mempool_lb_size: (default: 1024) The left border size in bytes
+                          from which the memory pool is used.
+    +ofi_mempool_rb_size: (default: 67108864) The right border size in bytes
+                          to which the memory pool is used.
+
+
+Known issue(s)
+--------------
+
+1) There is a known issue with versions of PSM2 older than 10.2.85 where
+non-MPI applications fail to start with the following error:
+
+    hfi_userinit: assign_context command failed: Invalid argument
+    PSM2 can't open hfi unit: -1 (err=23)
+
+The solution is to either:
+    - install a newer version of PSM2 (>= 10.2.85), or
+    - set the environment variable PSM2_SHAREDCONTEXTS to 0
+
+        e.g. mpiexec.hydra -genv PSM2_SHAREDCONTEXTS=0 -hosts ...
+
+2) There is another known issue with libfabric 1.3.0 which may cause a message
+corruption under certain conditions. The solution is to use a newer version of
+libfabric (>=1.4.0).
+
+
+Licensing
+---------
+
+The code for "simple PMI" (under simple_pmi/) was written by the MPICH
+developers. Copyright information can be found in LICENSE-MPICH.
+
+The code used to encode/decode binary so it can be sent via PMI (in
+runtime-codec.h) was written by the Sandia OpenSHMEM developers. Copyright
+information can be found in LICENSE-SandiaOpenSHMEM.
+
+Copyright information for the OFI LRTS machine code can be found in LICENSE.
diff --git a/src/arch/ofi/charmrun b/src/arch/ofi/charmrun
new file mode 100755 (executable)
index 0000000..9eb662d
--- /dev/null
@@ -0,0 +1,309 @@
+#!/bin/sh
+#
+# Conv-host for MPI:
+#  Translates +pN-style conv-host options into 
+# mpirun -npN options.
+
+args=""
+pes=1
+ppn=1
+machinefile=""
+QUIET=0
+
+while [ $# -gt 0 ]
+do
+       case $1 in
+       +ppn|++ppn)
+               args=$args" +ppn "$2
+               ppn=$2
+               shift
+               ;;
+       +ppn[0-9]*)
+               args=$args" "$1
+               ppn=`echo $1 | awk '{print substr($1,5)}'`
+               ;;
+       ++ppn[0-9]*)
+               args=$args" "$1
+               ppn=`echo $1 | awk '{print substr($1,6)}'`
+               ;;
+       +p)
+               pes=$2
+               shift
+               ;;
+       +pemap)
+               args=$args" "$1" "$2
+               shift
+               ;;
+       +p[0-9]*)
+               pes=`echo $1 | awk '{print substr($1,3)}'`
+               ;;
+        -machinefile)
+               machinefile=$2
+               args=" "$1" "$2" "$args
+               shift
+               ;;
+       ++quiet)
+               QUIET=1
+               args=$args" "$1
+               ;;
+       *) 
+               args=$args" "$1
+               ;;
+       esac
+       shift
+done
+
+rem=`expr $pes % $ppn`
+quot=`expr $pes / $ppn`
+if [ $rem -ne 0 ];
+then
+  printf "p = $pes should be a multiple of ppn = $ppn\n"
+  exit 1
+else
+  pes=$quot
+fi 
+
+test $QUIET -eq 0 && printf "\nRunning on $pes processors: $args\n"
+
+
+if [ -n "$PBS_NODEFILE" ]
+then
+# we are in a job shell
+  aprun=`which aprun 2>/dev/null`
+  if test -n "$aprun"
+  then
+    test $QUIET -eq 0 && echo aprun -n $pes $args
+    $aprun -n $pes $args
+  else
+    mpirun_cmd=`which mpirun 2>/dev/null`
+    if test -n "$mpirun_cmd"
+    then
+      if echo $mpirun_cmd | grep 'mvapich2'  > /dev/null 2>/dev/null
+      then
+        # if daemon not started, start it
+        if ! mpdtrace > /dev/null 2>/dev/null
+        then
+          mvapich2-start-mpd
+        fi
+        mpirun -np $pes $args
+        #    mpdallexit
+      else   # normal case
+        test -z "$machinefile" && args=-machinefile" "$PBS_NODEFILE" "$args
+        test $QUIET -eq 0 && echo mpirun -np $pes $args
+        mpirun -np $pes $args
+      fi
+    else
+      echo "Charmrun> can not locate mpirun in order to run the program."
+      exit 1
+    fi
+  fi
+elif [ -n "$LSB_HOSTS" ]
+then
+# Tungsten
+  test $QUIET -eq 0 && echo cmpirun -lsf -poll -no_smp -gm_long 200000 $args
+  cmpirun -lsf -poll -no_smp -gm_long 200000 $args 
+elif [ -n "$PBS_QUEUE" -o -n "$LSF_QUEUE" ]
+then
+# Interactive mode: create, and submit a batch job
+        script="charmrun_script.$$.sh"
+        indir=`pwd`
+        output="$indir/charmrun_script.$$.stdout"
+        result="$indir/charmrun_script.$$.result"
+       rm -f $result
+# Some machine specific 
+       USE_LSF=0
+# 10 minutes   
+       walllimit=10
+       queue_stat=qstat
+       queue_qsub=qsub
+       queue_kill=qdel
+       hostname=`hostname`
+       case "$hostname" in
+       turing*.turing.uiuc.edu) 
+               ppn='#PBS -l nodes='$pes':ppn=1'
+               extra='-machinefile $PBS_NODEFILE'
+               ;;
+       tg-login*|honest*.ncsa.uiuc.edu)
+               # always ppn=2
+               nodes=`expr \( $pes + 1 \) / 2`
+               test $pes -eq 1 && ppns=1 || ppns=2
+               ppn='#PBS -l nodes='$nodes':ppn='$ppns
+               extra='-machinefile $PBS_NODEFILE'
+               ;;
+       co-login*.ncsa.uiuc.edu)
+               mem='#PBS -l mem=500mb'
+               ncpus="#PBS -l ncpus=$pes"
+               ;;
+       tun*)
+               USE_LSF=1
+               queue_stat=bjobs
+               queue_qsub=bsub
+               queue_kill=bkill
+               ;;
+       abe*)
+               # always ppn=2
+               nodes=`expr \( $pes + 1 \) / 2`
+               test $pes -eq 1 && ppns=1 || ppns=2
+               ppn='#PBS -l nodes='$nodes':ppn='$ppns
+               extra='-machinefile $PBS_NODEFILE'
+               ;;
+        kraken*)
+                ncores=`expr \( $pes + 11 \) / 12 \* 12`
+               ncpus="#PBS -l size=$ncores"
+               ppn=''
+               ;;
+       *)
+               ncpus="#PBS -l ncpus=$pes"
+               ;;
+       esac
+       if test $USE_LSF -eq 0
+       then
+          mpirun=`which aprun 2>/dev/null`
+          npcmd="-n "
+          if test -z "$mpirun"
+          then
+           mpirun=`which mpirun 2>/dev/null`
+            npcmd="-np "
+          fi
+          cat > $script << EOF
+#!/bin/sh
+# This is a charmrun-generated PBS batch job script.
+# The lines starting with #PBS are queuing system flags:
+#
+$ppn
+#
+$ncpus
+#
+#PBS -l walltime=$walllimit:00
+#
+$mem
+#
+#PBS -q $PBS_QUEUE
+#
+#PBS -N autobuild
+#
+#PBS -j oe
+#
+#PBS -o $output
+
+cd $indir
+
+cat \$PBS_NODEFILE
+echo $mpirun $npcmd $pes $extra $args
+$mpirun $npcmd $pes $extra $args
+
+# Save mpirun exit status
+status=\$?
+echo \$status > $result
+EOF
+       else
+#  use LSF
+         mpirun="cmpirun -lsf -poll -no_smp -gm_long 200000"
+          cat > $script << EOF
+#!/bin/sh
+# This is a charmrun-generated PBS batch job script.
+# The lines starting with #PBS are queuing system flags:
+#
+#BSUB -J autobuild
+#BSUB -W 0:$walllimit
+#BSUB -n $pes
+#BSUB -o $output
+
+cd $indir
+echo \$LSB_MCPU_HOSTS
+$mpirun $args
+# Save mpirun exit status
+status=\$?
+echo \$status > $result
+EOF
+       fi
+
+End() {
+       echo "Charmrun> $queue_kill $jobid ..."
+       $queue_kill $jobid
+       rm -f $script
+       exit $1
+}
+
+        echo "Submitting batch job for> $mpirun -np $pes $args"
+        echo " using the command> $queue_qsub $script"
+        chmod 755 $script
+       while [ -z "$jobid" ]
+       do
+         [ $USE_LSF = 0 ] && jobid=`$queue_qsub $script|tail -1`
+         [ $USE_LSF = 1 ] && jobid=`$queue_qsub < $script|tail -1|sed -e 's/[^0-9]*//g'`
+       done
+       echo "Job enqueued under job ID $jobid"
+# kill job if interrupted
+       trap 'End 1' 2 3
+       retry=0
+# Wait for the job to complete, by checking its status
+        while [ true ]
+        do
+                $queue_stat $jobid > tmp.$$
+               exitstatus=$?
+                if test -f $output
+                then
+# The job is done-- print its output
+                        rm tmp.$$
+# When job hangs, result file does not exist
+                       test -f $result && status=`cat $result` || status=1
+                       test $status -eq 0 && status=`grep 'End of program' $output > /dev/null 2>&1`
+                       cat $output
+                       rm -f $result
+                       test -f $status && rm -f $script $output
+                       exit $status
+                fi
+# The job is still queued or running-- print status and wait
+                tail -1 tmp.$$
+                rm tmp.$$
+# Job ID may not exist now
+               if test $exitstatus -ne 0
+               then
+# retry a few times when error occurs
+                       retry=`expr $retry + 1`
+                       if test $retry -gt 6
+                       then
+                               echo "Charmrun> too many errors, abort!"
+                               exit 1
+                       else
+                               sleep 15
+                       fi
+               else
+# job still in queue
+                       retry=0
+                       sleep 20
+               fi
+        done
+else
+  mpirun_cmd=`which mpirun 2>/dev/null`
+  if test -n "$mpirun_cmd"
+  then
+    [ -n "$MPI_MACHINEFILE" ] && args=" -machinefile $MPI_MACHINEFILE $args"
+    setarch_cmd=`which setarch 2>/dev/null`
+    if [ -n "$setarch_cmd" -a -x "$setarch_cmd" ]
+    then
+      # Disables randomization of the virtual address  space  (turns  on
+      #          ADDR_NO_RANDOMIZE).
+      cur_arch=`uname -m`
+      test $QUIET -eq 0 && echo "charmrun>  $setarch_cmd $cur_arch -R  mpirun -np $pes $args"
+      $setarch_cmd $cur_arch -R  mpirun -np $pes $args
+    else
+      test $QUIET -eq 0 && echo "charmrun> mpirun -np $pes $args"
+      mpirun -np $pes $args
+    fi
+  else
+    mpiexec_cmd=`which mpiexec 2>/dev/null`
+    if test -n "$mpiexec_cmd"
+    then
+      test $QUIET -eq 0 && echo "charmrun> $mpiexec_cmd -n $pes $args"
+      test $QUIET -eq 0 && echo
+      "$mpiexec_cmd" -n $pes $args
+    else
+      echo "Don't know how to run MPI program."
+      exit 1
+    fi
+  fi
+fi
+
+
diff --git a/src/arch/ofi/conv-common.h b/src/arch/ofi/conv-common.h
new file mode 100644 (file)
index 0000000..ed2c58b
--- /dev/null
@@ -0,0 +1,79 @@
+#define CMK_USE_LRTS                                        1
+
+/* CMK_HAS_PARTITION requires the 'root' field to be present in CMK_MSG_HEADER_UNIQUE */
+#define CMK_HAS_PARTITION                                1
+
+/* if set to 1 it uses the default scheduler (Csd) defined in convcore.c,
+   otherwise machine.c has to provide its own scheduler. Should be 1 in almost
+   every machine. */
+#define CMK_CMIDELIVERS_USE_COMMON_CODE                    1
+
+/* specifies if the functions CmiPrintf, CmiError and CmiScanf are implemented
+   in machine.c (1), or if the standard definitions in convcore.c should be used
+   (0). */
+#define CMK_CMIPRINTF_IS_A_BUILTIN                         0
+
+/* define the converse headers. For most of the purposes, only the UNIQUE header
+   needs to be modified, the others will follow. BLUEGENE may need to be
+   adapted.
+
+   In particular, the fields "hdl", "xhdl" and "info" must be always present in
+   the extended header, since they are directly accessed in converse.h */
+/* - root is needed by CMK_HAS_PARTITION
+ * - startid, redID
+ * - rank is needed by broadcast
+ */
+#define CMK_MSG_HEADER_UNIQUE    CmiUInt4 size; CmiUInt2 rank,hdl,xhdl,info,stratid,redID; CmiInt4 root;
+
+#define CMK_MSG_HEADER_BASIC  CMK_MSG_HEADER_EXT
+#define CMK_MSG_HEADER_EXT            { CMK_MSG_HEADER_UNIQUE }
+#define CMK_MSG_HEADER_BIGSIM_ {CMK_MSG_HEADER_UNIQUE CMK_BIGSIM_FIELDS}
+
+/* defines different parameters of groups of processors. (next 4 definitions)
+   used in converse.h (the first) and convcore.c (the others). a value of 1
+   means that convcore.c defines the methods, otherwise it is up to machine.c to
+   define them */
+
+/* basic structure of a CmiGroup (defined in converse.h) */
+#define CMK_MULTICAST_GROUP_TYPE                struct { unsigned pe, id; }
+/* definitions of establishment and lookup of groups */
+#define CMK_MULTICAST_DEF_USE_COMMON_CODE                  1
+/* definitions of List sending functions */
+#define CMK_MULTICAST_LIST_USE_COMMON_CODE                 1
+/* definitions of Multicast sending functions */
+#define CMK_MULTICAST_GROUP_USE_COMMON_CODE                1
+
+/* define the entity of the spanning tree used (it is 4 in all configurations)
+   definese also if the code in converse.h will be used (1) or not and
+   implemented in machine.c (0). At the momement all configurations use the
+   common code. */
+#define CMK_SPANTREE_MAXSPAN                               4
+#define CMK_SPANTREE_USE_COMMON_CODE                       1
+
+/* Specifies if the routines which send multiple messages (vectors of messages)
+   to a processors are implemented in convcore.c (1) or in machine.c (1). */
+#define CMK_VECTOR_SEND_USES_COMMON_CODE                   1
+
+/* Enable the CCS protocol if set to 1. */
+#define CMK_CCS_AVAILABLE                                  1
+
+/* Defines if there is a "charmrun" program running on the system, which
+   interacts with possible connecting clients (0), or if there is no such
+   program, and processor 0 does the job (1). Currently only net- versions have
+   this set to 0, all the others have it to 1. */
+#define NODE_0_IS_CONVHOST                                 1
+
+/* Enables the persistent communication protocol if set to 1. */
+#define CMK_PERSISTENT_COMM                                0
+
+/* Enables support for immediate messages if set to 1. */
+#define CMK_IMMEDIATE_MSG                                 0
+
+/* This is needed to be 1 if the machine layer is used in some architectures
+   where there is no coprocessor, and to pull messages out of the network there
+   is the need of the processor intervention (like in BlueGene/L). 0 otherwise.
+ */
+#define CMK_MACHINE_PROGRESS_DEFINED                       1
+
+/* This is required to define LrtsLock/LrtsUnlock */
+#define CMK_USE_COMMON_LOCK                                1
diff --git a/src/arch/ofi/conv-common.sh b/src/arch/ofi/conv-common.sh
new file mode 100644 (file)
index 0000000..35e4562
--- /dev/null
@@ -0,0 +1,2 @@
+
+CMK_BUILD_OFI=1
diff --git a/src/arch/ofi/machine.c b/src/arch/ofi/machine.c
new file mode 100644 (file)
index 0000000..a33c16f
--- /dev/null
@@ -0,0 +1,1752 @@
+/** @file
+ * OFI LRTS machine layer
+ *
+ * Copyright (c) 2017, Intel Corporation. All rights reserved.
+ * See LICENSE in this directory.
+ *
+ * Authors: Yohann Burette <yohann.burette@intel.com>
+ *          Mikhail Shiryaev <mikhail.shiryaev@intel.com>
+ *          Marat Shamshetdinov <marat.shamshetdinov@intel.com>
+ * Date:    2017-06-23
+ *
+ * 10,000ft view:
+ *  - Each Charm++ node opens an OFI RDM endpoint
+ *  - For small (enough) messages, the sender sends the data directly
+ *  - For long messages,
+ *      1) the sender sends a OFIRmaHeader describing the data,
+ *      2) the receiver retrieves the data with RMA Read,
+ *      3) once done, the receiver sends an OFIRmaAck back.
+ *  - The tag associated with each communication helps the receiver
+ *    parse the data (i.e. short, long or ack).
+ *  - The receiver uses a OFILongMsg structure to keep track of an
+ *    ongoing long message retrieval.
+ *
+ * Runtime options:
+ *  +ofi_eager_maxsize: (default: 65536) Threshold between buffered and RMA
+ *                      paths.
+ *  +ofi_cq_entries_count: (default: 8) Maximum number of entries to read from
+ *                         the completion queue.
+ *  +ofi_use_inject: (default: 1) Whether use buffered send.
+ *  +ofi_num_recvs: (default: 8) Number of pre-posted receive buffers.
+ *  +ofi_runtime_tcp: (default: off) During the initialization phase, the
+ *                    OFI EP names need to be exchanged among all nodes. By
+ *                    default, the exchange is done with both PMI and OFI. If
+ *                    this flag is set then the exchange is done with PMI only.
+ *
+ *  Memory pool specific options:
+ *  +ofi_mempool_init_size_mb: (default: 8) The initial size of memory pool in MBytes.
+ *  +ofi_mempool_expand_size_mb: (default: 4) The size of expanding chunk in MBytes.
+ *  +ofi_mempool_max_size_mb: (default: 512) The limit for total size
+ *                            of memory pool in MBytes.
+ *  +ofi_mempool_lb_size: (default: 1024) The left border size in bytes
+ *                        from which the memory pool is used.
+ *  +ofi_mempool_rb_size: (default: 67108864) The right border size in bytes
+ *                        to which the memory pool is used.
+ * @ingroup Machine
+ */
+/*@{*/
+
+#include <stdio.h>
+#include <errno.h>
+#include "converse.h"
+
+/*Support for ++debug: */
+#include <unistd.h> /*For getpid()*/
+#include <stdlib.h> /*For sleep()*/
+
+#include "machine.h"
+
+/* TODO: macros regarding redefining locks that will affect pcqueue.h*/
+#include "pcqueue.h"
+
+/* =======Beginning of Definitions of Performance-Specific Macros =======*/
+/* TODO: add any that are related */
+/* =======End of Definitions of Performance-Specific Macros =======*/
+
+
+/* =====Beginning of Definitions of Message-Corruption Related Macros=====*/
+/* TODO: add any that are related */
+/* =====End of Definitions of Message-Corruption Related Macros=====*/
+
+
+/* =====Beginning of Declarations of Machine Specific Variables===== */
+/* TODO: add any that are related */
+/* =====End of Declarations of Machine Specific Variables===== */
+
+#include "machine-lrts.h"
+#include "machine-common-core.c"
+
+/* Libfabric headers */
+#include <rdma/fabric.h>
+#include <rdma/fi_cm.h>
+#include <rdma/fi_errno.h>
+#include <rdma/fi_endpoint.h>
+#include <rdma/fi_domain.h>
+#include <rdma/fi_tagged.h>
+#include <rdma/fi_rma.h>
+
+#define USE_OFIREQUEST_CACHE 0
+
+/* Definition of OFIRequest + request cache */
+#include "request.h"
+
+/* Runtime to exchange EP addresses during LrtsInit() */
+#if CMK_OFI_USE_PMI
+#include "runtime-pmi.c"
+#elif CMK_OFI_USE_PMI2
+#include "runtime-pmi2.c"
+#endif
+
+#define USE_MEMPOOL 0
+
+#if USE_MEMPOOL
+
+#include "mempool.h"
+#define MEMPOOL_INIT_SIZE_MB_DEFAULT   8
+#define MEMPOOL_EXPAND_SIZE_MB_DEFAULT 4
+#define MEMPOOL_MAX_SIZE_MB_DEFAULT    512
+#define MEMPOOL_LB_DEFAULT             1024
+#define MEMPOOL_RB_DEFAULT             67108864
+#define ONE_MB                         1048576
+
+CpvDeclare(mempool_type*, mempool);
+
+#endif /* USE_MEMPOOL */
+
+#define CmiSetMsgSize(msg, sz)  ((((CmiMsgHeaderBasic *)msg)->size) = (sz))
+
+#define CACHELINE_LEN 64
+
+#define OFI_NUM_RECV_REQS_DEFAULT    8
+#define OFI_NUM_RECV_REQS_MAX        4096
+
+#define OFI_EAGER_MAXSIZE_DEFAULT    65536
+#define OFI_EAGER_MAXSIZE_MAX        1048576
+
+#define OFI_CQ_ENTRIES_COUNT_DEFAULT 8
+#define OFI_CQ_ENTRIES_COUNT_MAX     1024
+
+#define OFI_USE_INJECT_DEFAULT       1
+
+#define OFI_KEY_FORMAT_EPNAME "ofi-epname-%i"
+
+#define OFI_OP_SHORT 0x1ULL
+#define OFI_OP_LONG  0x2ULL
+#define OFI_OP_ACK   0x3ULL
+#define OFI_OP_NAMES 0x4ULL
+
+#define OFI_OP_MASK  0x3ULL
+
+#define MR_ACCESS_PERMISSIONS (FI_REMOTE_READ | FI_READ | FI_RECV | FI_SEND)
+
+static inline int process_completion_queue();
+
+#ifdef HAVE_BUILTIN_EXPECT
+#  define unlikely(x_) __builtin_expect(!!(x_),0)
+#  define likely(x_)   __builtin_expect(!!(x_),1)
+#else
+#  define unlikely(x_) (x_)
+#  define likely(x_)   (x_)
+#endif
+
+#define ALIGNED_ALLOC(ptr, size)                                        \
+  do {                                                                  \
+      int pm_ret = posix_memalign((void**)(&ptr), CACHELINE_LEN, size); \
+      if (unlikely((pm_ret != 0) || !ptr))                              \
+      {                                                                 \
+          CmiPrintf("posix_memalign: ret %d", pm_ret);                  \
+          if (pm_ret == ENOMEM)                                         \
+              CmiAbort("posix_memalign: out of memory");                \
+          else                                                          \
+              CmiAbort("posix_memalign: error");                        \
+      }                                                                 \
+  } while (0)
+
+#define OFI_RETRY(func)                                 \
+    do {                                                \
+        ssize_t _ret;                                   \
+        do {                                            \
+            _ret = func;                                \
+            if (likely(_ret == 0)) break;               \
+            if (_ret != -FI_EAGAIN) {                   \
+                CmiPrintf("OFI_RETRY: ret %d\n", _ret); \
+                CmiAbort("OFI_RETRY error");            \
+            }                                           \
+            process_completion_queue();                 \
+        } while (_ret == -FI_EAGAIN);                   \
+    } while (0)
+
+/* OFI_INFO is used to print information messages during LrtsInit() */
+#define OFI_INFO(...) \
+    if (*myNodeID == 0) CmiPrintf("Charm++>ofi> " __VA_ARGS__)
+
+#define PRINT_THREAD_INFO(message)              \
+    MACHSTATE5(2,"thread info: process_idx=%i " \
+              "local_thread_idx=%i "            \
+              "global_thread_idx=%i "           \
+              "local_worker_thread_count=%i "   \
+              "is_comm_thread=%i\n",            \
+              CmiMyNode(),                      \
+              CmiMyRank(),                      \
+              CmiMyPe(),                        \
+              CmiMyNodeSize(),                  \
+              CmiInCommThread());
+
+/**
+ * OFI RMA Header
+ * Message sent by sender to receiver during RMA Read of long messages.
+ *  - nodeNo: Target node number
+ *  - src_msg: Address of source msg; Sent back as part of OFIRmaAck
+ *  - len: Length of message
+ *  - key: Remote key
+ *  - mr: Address of memory region; Sent back as part of OFIRmaAck
+ */
+typedef struct OFIRmaHeader {
+    uint64_t src_msg;
+    uint64_t len;
+    uint64_t key;
+    uint64_t mr;
+    int      nodeNo;
+} OFIRmaHeader;
+
+/**
+ * OFI RMA Ack
+ * Message sent by receiver to sender during RMA Read of long messages.
+ *  - src_msg: Address of source msg; Received as part of OFIRmaHeader
+ *  - mr: Address of memory region; Received as part of OFIRmaHeader
+ */
+typedef struct OFIRmaAck {
+    uint64_t src_msg;
+    uint64_t mr;
+} OFIRmaAck;
+
+/**
+ * OFI Long Message
+ * Structure stored by the receiver about ongoing RMA Read of long message.
+ *  - asm_msg: Assembly buffer where the data is RMA Read into
+ *  - nodeNo: Target node number
+ *  - rma_ack: OFI Rma Ack sent to sender once all the data has been RMA Read
+ *  - completion_count: Number of expected RMA Read completions
+ *  - mr: Memory Region where the data is RMA Read into
+ */
+typedef struct OFILongMsg {
+    char                *asm_msg;
+    int                 nodeNo;
+    struct OFIRmaAck    rma_ack;
+    size_t              completion_count;
+    struct fid_mr       *mr;
+} OFILongMsg;
+
+/**
+ * TO_OFI_REQ is used retrieve the request associated with a given fi_context.
+ */
+#define TO_OFI_REQ(_ptr_context) \
+    container_of((_ptr_context), OFIRequest, context)
+
+typedef struct OFIContext {
+    /** Endpoint to communicate on */
+    struct fid_ep *ep;
+
+    /** Completion queue handle */
+    struct fid_cq *cq;
+
+    /**
+     * Maximum size for eager messages.
+     * RMA Read for larger messages.
+     */
+    size_t eager_maxsize;
+
+    /**
+     * Maximum inject size.
+     */
+    size_t inject_maxsize;
+
+    /**
+     * Maximum number of completion queue entries that
+     * can be retrieved by each fi_cq_read() call.
+     */
+    size_t cq_entries_count;
+
+    /**
+     * Whether to use buffered send (aka inject)
+     */
+    int use_inject;
+
+#if USE_OFIREQUEST_CACHE
+    /** OFIRequest allocator */
+    request_cache_t *request_cache;
+#endif
+
+#if CMK_SMP
+    /**
+     * Producer/Consumer Queue used in CMK_SMP mode:
+     *  - worker thread pushes messages to the queue
+     *  - comm thread pops and sends
+     * Locking is already done by PCQueue.
+     */
+    PCQueue send_queue;
+#endif
+
+    /** Fabric Domain handle */
+    struct fid_fabric *fabric;
+
+    /** Access Domain handle */
+    struct fid_domain *domain;
+
+    /** Address vector handle */
+    struct fid_av *av;
+
+    /**
+     * Maximum size for RMA operations.
+     * Multiple RMA operations for larger messages.
+     */
+    size_t rma_maxsize;
+
+    /**
+     * MR mode:
+     *  - FI_MR_SCALABLE allows us to register all the memory with our own key,
+     *  - FI_MR_BASIC requires us to register the RMA buffers
+     *    and to exchange the keys.
+     */
+    enum fi_mr_mode mr_mode;
+
+    /** Used as unique key value in FI_MR_SCALABLE mode */
+    uint64_t mr_counter;
+
+    /** Number of pre-posted receive requests */
+    int num_recv_reqs;
+
+    /** Pre-posted receive requests */
+    OFIRequest **recv_reqs;
+
+#if USE_MEMPOOL
+    size_t mempool_init_size;
+    size_t mempool_expand_size;
+    size_t mempool_max_size;
+    size_t mempool_lb_size;
+    size_t mempool_rb_size;
+#endif
+} OFIContext __attribute__ ((aligned (CACHELINE_LEN)));
+
+static void recv_callback(struct fi_cq_tagged_entry *e, OFIRequest *req);
+static int fill_av(int myid, int nnodes, struct fid_ep *ep,
+                   struct fid_av *av, struct fid_cq *cq);
+static int fill_av_ofi(int myid, int nnodes, struct fid_ep *ep,
+                       struct fid_av *av, struct fid_cq *cq);
+
+static OFIContext context;
+
+/* ### Beginning of Machine-startup Related Functions ### */
+void LrtsInit(int *argc, char ***argv, int *numNodes, int *myNodeID)
+{
+    struct fi_info        *providers;
+    struct fi_info        *prov;
+    struct fi_info        *hints;
+    struct fi_domain_attr domain_attr = {0};
+    struct fi_tx_attr     tx_attr = { 0 };
+    struct fi_cq_attr     cq_attr = { 0 };
+    struct fi_av_attr     av_attr = { 0 };
+    int                   fi_version;
+    size_t                max_header_size;
+
+    int i;
+    int ret;
+
+    /**
+     * Initialize our runtime environment -- e.g. PMI.
+     */
+    ret = runtime_init(myNodeID, numNodes);
+    if (ret) {
+        CmiAbort("OFI::LrtsInit::runtime_init failed");
+    }
+
+    /**
+     * Hints to filter providers
+     * See man fi_getinfo for a list of all filters
+     * mode: This OFI machine will pass in context into communication calls
+     * ep_type: Reliable datagram operation
+     * resource_mgmt: Let the provider manage the resources
+     * caps: Capabilities required from the provider. We want to use the
+     *       tagged message queue and rma read APIs.
+     */
+    hints = fi_allocinfo();
+    CmiAssert(NULL != hints);
+    hints->mode                          = FI_CONTEXT;
+    hints->ep_attr->type                 = FI_EP_RDM;
+    hints->domain_attr->resource_mgmt    = FI_RM_ENABLED;
+    hints->caps                          = FI_TAGGED;
+    hints->caps                         |= FI_RMA;
+    hints->caps                         |= FI_REMOTE_READ;
+
+    /**
+     * FI_VERSION provides binary backward and forward compatibility support
+     * Specify the version of OFI this machine is coded to, the provider will
+     * select struct layouts that are compatible with this version.
+     */
+    fi_version = FI_VERSION(1, 0);
+
+    ret = fi_getinfo(fi_version, NULL, NULL, 0ULL, hints, &providers);
+    if (ret < 0) {
+        CmiAbort("OFI::LrtsInit::fi_getinfo error");
+    }
+
+    if (providers == NULL) {
+        CmiAbort("OFI::LrtsInit::No provider found");
+    }
+
+    /**
+     * Here we elect to use the first provider from the list.
+     * Further filtering could be done at this point (e.g. name).
+     */
+    prov = providers;
+
+    OFI_INFO("provider: %s\n", prov->fabric_attr->prov_name);
+    OFI_INFO("control progress: %d\n", prov->domain_attr->control_progress);
+    OFI_INFO("data progress: %d\n", prov->domain_attr->data_progress);
+
+    context.inject_maxsize = prov->tx_attr->inject_size;
+    OFI_INFO("maximum inject message size: %ld\n", context.inject_maxsize);
+
+    context.eager_maxsize = OFI_EAGER_MAXSIZE_DEFAULT;
+    CmiGetArgInt(*argv, "+ofi_eager_maxsize", (int*)&context.eager_maxsize);
+    if (context.eager_maxsize > prov->ep_attr->max_msg_size)
+        CmiAbort("OFI::LrtsInit::Eager max size > max msg size.");
+    if (context.eager_maxsize > OFI_EAGER_MAXSIZE_MAX || context.eager_maxsize < 0)
+        CmiAbort("OFI::LrtsInit::Eager max size range error.");
+    max_header_size = (sizeof(OFIRmaHeader) >= sizeof(OFIRmaAck)) ? sizeof(OFIRmaHeader) : sizeof(OFIRmaAck);
+    if (context.eager_maxsize < max_header_size)
+        CmiAbort("OFI::LrtsInit::Eager max size too small to fit headers.");
+    OFI_INFO("eager maximum message size: %ld (maximum header size: %ld)\n",
+             context.eager_maxsize, max_header_size);
+
+    context.cq_entries_count = OFI_CQ_ENTRIES_COUNT_DEFAULT;
+    CmiGetArgInt(*argv, "+ofi_cq_entries_count", (int*)&context.cq_entries_count);
+    if (context.cq_entries_count > OFI_CQ_ENTRIES_COUNT_MAX || context.cq_entries_count <= 0)
+        CmiAbort("OFI::LrtsInit::Cq entries count range error");
+    OFI_INFO("cq entries count: %ld\n", context.cq_entries_count);
+
+    context.use_inject = OFI_USE_INJECT_DEFAULT;
+    CmiGetArgInt(*argv, "+ofi_use_inject", &context.use_inject);
+    if (context.use_inject < 0)
+        CmiAbort("OFI::LrtsInit::Use inject value error");
+    OFI_INFO("use inject: %d\n", context.use_inject);
+
+    context.rma_maxsize = prov->ep_attr->max_msg_size;
+    context.mr_mode = prov->domain_attr->mr_mode;
+    context.mr_counter = 0;
+
+    OFI_INFO("maximum rma size: %ld\n", context.rma_maxsize);
+    OFI_INFO("mr mode: 0x%x\n", context.mr_mode);
+
+    if ((context.mr_mode != FI_MR_BASIC) &&
+        (context.mr_mode != FI_MR_SCALABLE)) {
+        CmiAbort("OFI::LrtsInit::Unsupported MR mode");
+    }
+
+    OFI_INFO("use memory pool: %d\n", USE_MEMPOOL);
+
+#if USE_MEMPOOL
+    size_t mempool_init_size_mb = MEMPOOL_INIT_SIZE_MB_DEFAULT;
+    CmiGetArgInt(*argv, "+ofi_mempool_init_size_mb", (int*)&mempool_init_size_mb);
+    context.mempool_init_size = mempool_init_size_mb * ONE_MB;
+
+    size_t mempool_expand_size_mb = MEMPOOL_EXPAND_SIZE_MB_DEFAULT;
+    CmiGetArgInt(*argv, "+ofi_mempool_expand_size_mb", (int*)&mempool_expand_size_mb);
+    context.mempool_expand_size = mempool_expand_size_mb * ONE_MB;
+
+    size_t mempool_max_size_mb = MEMPOOL_MAX_SIZE_MB_DEFAULT;
+    CmiGetArgInt(*argv, "+ofi_mempool_max_size_mb", (int*)&mempool_max_size_mb);
+    context.mempool_max_size = mempool_max_size_mb * ONE_MB;
+
+    context.mempool_lb_size = MEMPOOL_LB_DEFAULT;
+    CmiGetArgInt(*argv, "+ofi_mempool_lb_size", (int*)&context.mempool_lb_size);
+
+    context.mempool_rb_size = MEMPOOL_RB_DEFAULT;
+    CmiGetArgInt(*argv, "+ofi_mempool_rb_size", (int*)&context.mempool_rb_size);
+
+    if (context.mempool_lb_size > context.mempool_rb_size)
+        CmiAbort("OFI::LrtsInit::Mempool left border should be less or equal to right border");
+
+    OFI_INFO("mempool init size: %ld\n", context.mempool_init_size);
+    OFI_INFO("mempool expand size: %ld\n", context.mempool_expand_size);
+    OFI_INFO("mempool max size: %ld\n", context.mempool_max_size);
+    OFI_INFO("mempool left border size: %ld\n", context.mempool_lb_size);
+    OFI_INFO("mempool right border size: %ld\n", context.mempool_rb_size);
+#endif
+
+    /**
+     * Open fabric
+     * The getinfo struct returns a fabric attribute struct that can be used to
+     * instantiate the virtual or physical network. This opens a "fabric
+     * provider". See man fi_fabric for details.
+     */
+    ret = fi_fabric(prov->fabric_attr, &context.fabric, NULL);
+    if (ret < 0) {
+        fi_freeinfo(providers);
+        CmiAbort("OFI::LrtsInit::fi_fabric error");
+    }
+
+    /**
+     * Create the access domain, which is the physical or virtual network or
+     * hardware port/collection of ports.  Returns a domain object that can be
+     * used to create endpoints.  See man fi_domain for details.
+     */
+    ret = fi_domain(context.fabric, prov, &context.domain, NULL);
+    if (ret < 0) {
+        fi_freeinfo(providers);
+        CmiAbort("OFI::LrtsInit::fi_domain error");
+    }
+
+    /**
+     * Create a transport level communication endpoint.  To use the endpoint,
+     * it must be bound to completion counters or event queues and enabled,
+     * and the resources consumed by it, such as address vectors, counters,
+     * completion queues, etc. See man fi_endpoint for more details.
+     */
+    ret = fi_endpoint(context.domain, /* In:  Domain object   */
+                      prov,           /* In:  Provider        */
+                      &context.ep,    /* Out: Endpoint object */
+                      NULL);          /* Optional context     */
+    if (ret < 0) {
+        fi_freeinfo(providers);
+        CmiAbort("OFI::LrtsInit::fi_endpoint error");
+    }
+
+    /**
+     * Create the objects that will be bound to the endpoint.
+     * The objects include:
+     *     - completion queue for events
+     *     - address vector of other endpoint addresses
+     */
+    cq_attr.format = FI_CQ_FORMAT_TAGGED;
+    ret = fi_cq_open(context.domain, &cq_attr, &context.cq, NULL);
+    if (ret < 0) {
+        CmiAbort("OFI::LrtsInit::fi_cq_open error");
+    }
+
+    /**
+     * Since the communications happen between Nodes and that each Node
+     * has a number (NodeNo), we can use the Address Vector in FI_AV_TABLE
+     * mode. The addresses of the Nodes simply need to be inserted in order
+     * so that the NodeNo becomes the index in the AV. The advantage being
+     * that the fi_addrs are stored by the OFI provider.
+     */
+    av_attr.type = FI_AV_TABLE;
+    ret = fi_av_open(context.domain, &av_attr, &context.av, NULL);
+    if (ret < 0) {
+        CmiAbort("OFI::LrtsInit::fi_av_open error");
+    }
+
+    /**
+     * Bind the CQ and AV to the endpoint object.
+     */
+    ret = fi_ep_bind(context.ep,
+                     (fid_t)context.cq,
+                     FI_RECV | FI_TRANSMIT);
+    if (ret < 0) {
+        CmiAbort("OFI::LrtsInit::fi_bind EP-CQ error");
+    }
+    ret = fi_ep_bind(context.ep,
+                     (fid_t)context.av,
+                     0);
+    if (ret < 0) {
+        CmiAbort("OFI::LrtsInit::fi_bind EP-AV error");
+    }
+
+    /**
+     * Enable the endpoint for communication
+     * This commits the bind operations.
+     */
+    ret = fi_enable(context.ep);
+    if (ret < 0) {
+        CmiAbort("OFI::LrtsInit::fi_enable error");
+    }
+
+    OFI_INFO("use request cache: %d\n", USE_OFIREQUEST_CACHE);
+
+#if USE_OFIREQUEST_CACHE
+    /**
+     * Create request cache.
+     */
+    context.request_cache = create_request_cache();
+#endif
+
+    /**
+     * Create local receive buffers and pre-post them.
+     */
+    context.num_recv_reqs = OFI_NUM_RECV_REQS_DEFAULT;
+    CmiGetArgInt(*argv, "+ofi_num_recvs", &context.num_recv_reqs);
+    if (context.num_recv_reqs > OFI_NUM_RECV_REQS_MAX || context.num_recv_reqs <= 0)
+        CmiAbort("OFI::LrtsInit::Num recv reqs range error");
+    OFI_INFO("number of pre-allocated recvs: %i\n", context.num_recv_reqs);
+
+    /**
+     * Exchange EP names and insert them into the AV.
+     */
+    if (CmiGetArgFlag(*argv, "+ofi_runtime_tcp")) {
+        OFI_INFO("exchanging addresses over TCP\n");
+        ret = fill_av(*myNodeID, *numNodes, context.ep,
+                       context.av, context.cq);
+        if (ret < 0) {
+            CmiAbort("OFI::LrtsInit::fill_av");
+        }
+    } else {
+        OFI_INFO("exchanging addresses over OFI\n");
+        ret = fill_av_ofi(*myNodeID, *numNodes, context.ep,
+                           context.av, context.cq);
+        if (ret < 0) {
+            CmiAbort("OFI::LrtsInit::fill_av_ofi");
+        }
+    }
+
+#if CMK_SMP
+    /**
+     * Initialize send queue.
+     */
+    context.send_queue = PCQueueCreate();
+#endif
+
+    /**
+     * Free providers info since it's not needed anymore.
+     */
+    fi_freeinfo(hints);
+    hints = NULL;
+    fi_freeinfo(providers);
+    providers = NULL;
+}
+
+static inline
+void prepost_buffers()
+{
+    OFIRequest **reqs;
+    ALIGNED_ALLOC(reqs, sizeof(void*) * context.num_recv_reqs);
+
+    int i;
+    for (i = 0; i < context.num_recv_reqs; i++) {
+#if USE_OFIREQUEST_CACHE
+        reqs[i] = alloc_request(context.request_cache);
+#else
+        reqs[i] = CmiAlloc(sizeof(OFIRequest));
+#endif
+        reqs[i]->callback = recv_callback;
+        reqs[i]->data.recv_buffer = CmiAlloc(context.eager_maxsize);
+        CmiAssert(reqs[i]->data.recv_buffer);
+
+        MACHSTATE2(3, "---> posting recv req %p buf=%p",
+                   reqs[i], reqs[i]->data.recv_buffer);
+
+        /* Receive from any node with any tag */
+        OFI_RETRY(fi_trecv(context.ep,
+                           reqs[i]->data.recv_buffer,
+                           context.eager_maxsize,
+                           NULL,
+                           FI_ADDR_UNSPEC,
+                           0,
+                           OFI_OP_MASK,
+                           &(reqs[i]->context)));
+    }
+    context.recv_reqs = reqs;
+}
+
+static inline
+void send_short_callback(struct fi_cq_tagged_entry *e, OFIRequest *req)
+{
+    /**
+     * A short message was sent.
+     * Free up resources.
+     */
+    char *msg;
+
+    MACHSTATE(3, "OFI::send_short_callback {");
+
+    msg = req->data.short_msg;
+    CmiAssert(msg);
+    MACHSTATE1(3, "--> msg=%p", msg);
+    CmiFree(msg);
+
+#if USE_OFIREQUEST_CACHE
+    free_request(req);
+#else
+    CmiFree(req);
+#endif
+
+    MACHSTATE(3, "} OFI::send_short_callback done");
+}
+
+static inline
+void send_rma_callback(struct fi_cq_tagged_entry *e, OFIRequest *req)
+{
+    /**
+     * An OFIRmaHeader was sent.
+     * Free up resources.
+     */
+    OFIRmaHeader *header;
+
+    MACHSTATE(3, "OFI::send_rma_callback {");
+
+    header = req->data.rma_header;
+    free(header);
+
+#if USE_OFIREQUEST_CACHE
+    free_request(req);
+#else
+    CmiFree(req);
+#endif
+
+    MACHSTATE(3, "} OFI::send_rma_callback done");
+}
+
+static inline
+void ofi_send(void *buf, size_t buf_size, int addr, uint64_t tag, OFIRequest *req)
+{
+    if (context.use_inject && buf_size <= context.inject_maxsize)
+    {
+        /**
+         * The message is small enough to be injected.
+         * This won't generate any completion, so we can free the msg now.
+         */
+        MACHSTATE(3, "----> inject");
+
+        OFI_RETRY(fi_tinject(context.ep,
+                             buf,
+                             buf_size,
+                             addr,
+                             tag));
+        req->callback(NULL, req);
+    }
+    else
+    {
+        /* Else, use regular send. */
+        OFI_RETRY(fi_tsend(context.ep,
+                           buf,
+                           buf_size,
+                           NULL,
+                           addr,
+                           tag,
+                           &req->context));
+    }
+}
+
+/**
+ * sendMsg is used to send a message.
+ * In CMK_SMP mode, this is called by the comm thread.
+ */
+static inline int sendMsg(OFIRequest *req)
+{
+    int       ret;
+    uint64_t  op;
+    char     *buf;
+    size_t    len;
+
+    MACHSTATE5(2,
+               "OFI::sendMsg destNode=%i destPE=%i size=%i msg=%p mode=%i {",
+               req->destNode, req->destPE, req->size, req->data, req->mode);
+
+    if (req->size <= context.eager_maxsize) {
+        /**
+         * The message is small enough to be sent entirely.
+         */
+        MACHSTATE(3, "--> eager");
+
+        op = OFI_OP_SHORT;
+        buf = req->data.short_msg;
+        len = req->size;
+    } else {
+        /**
+         * The message is too long to be sent directly.
+         * Let other side use RMA Read instead by sending an OFIRmaHeader.
+         */
+        MACHSTATE(3, "--> long");
+
+        op = OFI_OP_LONG;
+        buf = (char *)req->data.rma_header;
+        len = sizeof(OFIRmaHeader);
+    }
+
+    ofi_send(buf, len, req->destNode, op, req);
+
+    MACHSTATE(2, "} OFI::sendMsg");
+    return 0;
+}
+
+/**
+ * In non-SMP mode, this is used to send a message.
+ * In CMK_SMP mode, this is called by a worker thread to send a message.
+ */
+CmiCommHandle LrtsSendFunc(int destNode, int destPE, int size, char *msg, int mode)
+{
+
+    int           ret;
+    OFIRequest    *req;
+
+    MACHSTATE5(2,
+            "OFI::LrtsSendFunc destNode=%i destPE=%i size=%i msg=%p mode=%i {",
+            destNode, destPE, size, msg, mode);
+
+    CmiSetMsgSize(msg, size);
+
+#if USE_OFIREQUEST_CACHE
+    req = alloc_request(context.request_cache);
+#else
+    req = CmiAlloc(sizeof(OFIRequest));
+#endif
+    CmiAssert(req);
+
+    req->destNode = destNode;
+    req->destPE   = destPE;
+    req->size     = size;
+    req->mode     = mode;
+
+    if (size <= context.eager_maxsize) {
+        /**
+         * The message is small enough to be sent entirely.
+         */
+        MACHSTATE(3, "--> eager");
+
+        req->callback = send_short_callback;
+        req->data.short_msg = msg;
+    } else {
+        /**
+         * The message is too long to be sent directly.
+         * Let other side use RMA Read instead by sending an OFIRmaHeader.
+         */
+        OFIRmaHeader  *rma_header;
+        struct fid_mr *mr;
+        uint64_t      requested_key = 0;
+
+        MACHSTATE(3, "--> long");
+
+        ALIGNED_ALLOC(rma_header, sizeof(*rma_header));
+
+        if (FI_MR_SCALABLE == context.mr_mode) {
+            /**
+             *  In FI_MR_SCALABLE mode, we need to specify a unique key when
+             *  registering memory. Here we simply increment a counter
+             *  atomically.
+             */
+            requested_key = __sync_fetch_and_add(&(context.mr_counter), 1);
+        }
+
+        /* Register new MR to RMA Read from */
+        ret = fi_mr_reg(context.domain,        /* In:  domain object */
+                        msg,                   /* In:  lower memory address */
+                        size,                  /* In:  length */
+                        MR_ACCESS_PERMISSIONS, /* In:  access permissions */
+                        0ULL,                  /* In:  offset (not used) */
+                        requested_key,         /* In:  requested key */
+                        0ULL,                  /* In:  flags */
+                        &mr,                   /* Out: memregion object */
+                        NULL);                 /* In:  context (not used) */
+
+        if (ret) {
+            MACHSTATE1(3, "fi_mr_reg error: %d\n", ret);
+            CmiAbort("fi_mr_reg error");
+        }
+
+        rma_header->nodeNo  = CmiMyNode();
+        rma_header->src_msg = (uint64_t)msg;
+        rma_header->len     = size;
+        rma_header->key     = fi_mr_key(mr);
+        rma_header->mr      = (uint64_t)mr;
+
+        req->callback        = send_rma_callback;
+        req->data.rma_header = rma_header;
+    }
+
+#if CMK_SMP
+    /* Enqueue message */
+    MACHSTATE2(2, " --> (PE=%i) enqueuing message (queue depth=%i)",
+               CmiMyPe(), PCQueueLength(context.send_queue));
+    PCQueuePush(context.send_queue, (char *)req);
+#else
+    /* Send directly */
+    sendMsg(req);
+#endif
+
+    MACHSTATE(2, "} OFI::LrtsSendFunc");
+
+    return (CmiCommHandle)req;
+}
+
+static inline
+void send_ack_callback(struct fi_cq_tagged_entry *e, OFIRequest *req)
+{
+    /**
+     * An OFIRmaAck was sent (see rma_read_callback()).
+     * We are done with the RMA Read operation. Free up the resources.
+     */
+    OFILongMsg *long_msg;
+
+    MACHSTATE(3, "OFI::send_ack_callback {");
+
+    long_msg = req->data.long_msg;
+    CmiAssert(long_msg);
+
+    if (long_msg->mr)
+        fi_close((struct fid*)long_msg->mr);
+
+    free(long_msg);
+
+#if USE_OFIREQUEST_CACHE
+    free_request(req);
+#else
+    CmiFree(req);
+#endif
+
+    MACHSTATE(3, "} OFI::send_ack_callback done");
+}
+
+static inline
+void rma_read_callback(struct fi_cq_tagged_entry *e, OFIRequest *req)
+{
+    /**
+     * An RMA Read operation completed.
+     */
+    OFILongMsg *long_msg;
+
+    MACHSTATE(3, "OFI::rma_read_callback {");
+
+    long_msg = req->data.long_msg;
+    CmiAssert(long_msg);
+    CmiAssert(long_msg->completion_count > 0);
+
+    long_msg->completion_count--;
+    MACHSTATE1(3, "--> completion_count=%ld", long_msg->completion_count);
+
+    if (0 == long_msg->completion_count) {
+        /**
+         * long_msg can be destroyed in case of fi_tinject,
+         * so save pointer to assembly buffer to use it below.
+         */
+        char* asm_msg = long_msg->asm_msg;
+
+        /**
+         *  The message has been RMA Read completely.
+         *  Send ACK to notify the other side that we are done.
+         *  The resources are freed by send_ack_callback().
+         */
+        req->callback = send_ack_callback;
+        req->data.long_msg = long_msg;
+
+        ofi_send(&long_msg->rma_ack,
+                 sizeof long_msg->rma_ack,
+                 long_msg->nodeNo,
+                 OFI_OP_ACK,
+                 req);
+
+        /**
+         * Pass received message to upper layer.
+         */
+        MACHSTATE1(3, "--> Finished receiving msg size=%i", CMI_MSG_SIZE(asm_msg));
+
+        handleOneRecvedMsg(CMI_MSG_SIZE(asm_msg), asm_msg);
+    } else {
+#if USE_OFIREQUEST_CACHE
+      free_request(req);
+#else
+      CmiFree(req);
+#endif
+    }
+
+    MACHSTATE(3, "} OFI::rma_read_callback done");
+}
+
+static inline
+void process_short_recv(struct fi_cq_tagged_entry *e, OFIRequest *req)
+{
+    /**
+     * A short message was received:
+     *   - Pass the message to the upper layer,
+     *   - Allocate new recv buffer.
+     */
+
+    char    *data;
+    size_t  msg_size;
+
+    data = req->data.recv_buffer;
+    CmiAssert(data);
+
+    msg_size = CMI_MSG_SIZE(data);
+    MACHSTATE2(3, "--> eager msg (e->len=%ld msg_size=%ld)", e->len, msg_size);
+
+    req->data.recv_buffer = CmiAlloc(context.eager_maxsize);
+    CmiAssert(req->data.recv_buffer);
+
+    handleOneRecvedMsg(e->len, data);
+}
+
+static inline
+void process_long_recv(struct fi_cq_tagged_entry *e, OFIRequest *req)
+{
+    /**
+     * An OFIRmaHeader was received:
+     *   - Allocate enough space to store the long message,
+     *   - Create OFILongMsg to keep track of the data retrieval,
+     *   - Issue the RMA Read operation(s) to retrieve the data.
+     */
+
+    int ret;
+    OFILongMsg *long_msg;
+    OFIRequest *rma_req;
+    OFIRmaHeader *rma_header;
+    struct fid_mr *mr = NULL;
+    char *asm_buf;
+    int nodeNo;
+    uint64_t rbuf;
+    size_t len;
+    uint64_t rkey;
+    uint64_t rmsg;
+    uint64_t rmr;
+    char *lbuf;
+    size_t remaining;
+    size_t chunk_size;
+
+    CmiAssert(e->len == sizeof(OFIRmaHeader));
+
+    /**
+     * Parse header
+     */
+    rma_header = req->data.rma_header;
+
+    nodeNo = rma_header->nodeNo;
+    rmsg   = rma_header->src_msg;
+    len    = rma_header->len;
+    rkey   = rma_header->key;
+    rmr    = rma_header->mr;
+
+    MACHSTATE2(3, "--> Receiving long msg len=%ld rkey=0x%lx", len, rkey);
+
+    /**
+     * Prepare buffer
+     */
+    asm_buf = CmiAlloc(len);
+    CmiAssert(asm_buf);
+
+    if (FI_MR_BASIC == context.mr_mode) {
+        /* Register local MR to read into */
+        ret = fi_mr_reg(context.domain,        /* In:  domain object */
+                        asm_buf,               /* In:  lower memory address */
+                        len,                   /* In:  length */
+                        MR_ACCESS_PERMISSIONS, /* In:  access permissions */
+                        0ULL,                  /* In:  offset (not used) */
+                        0ULL,                  /* In:  requested key (none)*/
+                        0ULL,                  /* In:  flags */
+                        &mr,                   /* Out: memregion object */
+                        NULL);                 /* In:  context (not used) */
+        if (ret) {
+            MACHSTATE1(3, "fi_mr_reg error: %d\n", ret);
+            CmiAbort("fi_mr_reg error");
+        }
+    }
+
+    /**
+     * Save some information about the RMA Read operation(s)
+     */
+    ALIGNED_ALLOC(long_msg, sizeof(*long_msg));
+    long_msg->asm_msg          = asm_buf;
+    long_msg->nodeNo           = nodeNo;
+    long_msg->rma_ack.src_msg  = rmsg;
+    long_msg->rma_ack.mr       = rmr;
+    long_msg->completion_count = 0;
+    long_msg->mr               = mr;
+
+    /**
+     * Issue RMA Read request(s)
+     */
+    remaining = len;
+    lbuf      = asm_buf;
+    rbuf      = (FI_MR_SCALABLE == context.mr_mode) ? 0 : rmsg;
+
+    while (remaining > 0) {
+        /* Determine size of operation */
+        chunk_size = (remaining <= context.rma_maxsize) ? remaining : context.rma_maxsize;
+
+#if USE_OFIREQUEST_CACHE
+        rma_req = alloc_request(context.request_cache);
+#else
+        rma_req = CmiAlloc(sizeof(OFIRequest));
+#endif
+        CmiAssert(rma_req);
+        rma_req->callback = rma_read_callback;
+        rma_req->data.long_msg = long_msg;
+
+        /* Increment number of expected completions */
+        long_msg->completion_count++;
+
+        MACHSTATE5(3, "---> RMA Read lbuf %p rbuf %p rmsg %p len %ld chunk #%d",
+                   lbuf, rbuf, rmsg, chunk_size, long_msg->completion_count);
+
+        OFI_RETRY(fi_read(context.ep,
+                          lbuf,
+                          chunk_size,
+                          (mr) ? fi_mr_desc(mr) : NULL,
+                          nodeNo,
+                          rbuf,
+                          rkey,
+                          &rma_req->context));
+
+        remaining  -= chunk_size;
+        lbuf       += chunk_size;
+        rbuf       += chunk_size;
+    }
+}
+
+static inline
+void process_long_send_ack(struct fi_cq_tagged_entry *e, OFIRequest *req)
+{
+    /**
+     * An OFIRmaAck was received; Close memory region and free original msg.
+     */
+
+    struct fid *mr;
+    char *msg;
+
+    mr = (struct fid*)req->data.rma_ack->mr;
+    CmiAssert(mr);
+    fi_close(mr);
+
+    msg = (char *)req->data.rma_ack->src_msg;
+    CmiAssert(msg);
+
+    MACHSTATE1(3, "--> Finished sending msg size=%i", CMI_MSG_SIZE(msg));
+
+    CmiFree(msg);
+}
+
+static inline
+void recv_callback(struct fi_cq_tagged_entry *e, OFIRequest *req)
+{
+    /**
+     * Some data was received:
+     *  - the tag tells us what type of message it is; process it
+     *  - repost recv request
+     */
+    MACHSTATE(3, "OFI::recv_callback {");
+
+    switch (e->tag) {
+    case OFI_OP_SHORT:
+        process_short_recv(e, req);
+        break;
+    case OFI_OP_LONG:
+        process_long_recv(e, req);
+        break;
+    case OFI_OP_ACK:
+        process_long_send_ack(e, req);
+        break;
+    default:
+        MACHSTATE2(3, "--> unknown operation %x len=%ld", e->tag, e->len);
+        CmiAbort("!! Wrong operation !!");
+    }
+
+    MACHSTATE2(3, "Reposting recv req %p buf=%p", req, req->data.recv_buffer);
+    OFI_RETRY(fi_trecv(context.ep,
+                       req->data.recv_buffer,
+                       context.eager_maxsize,
+                       NULL,
+                       FI_ADDR_UNSPEC,
+                       0,
+                       OFI_OP_MASK,
+                       &req->context));
+
+    MACHSTATE(3, "} OFI::recv_callback done");
+}
+
+static inline
+int process_completion_queue()
+{
+    int ret;
+    struct fi_cq_tagged_entry entries[context.cq_entries_count];
+    struct fi_cq_err_entry error;
+    OFIRequest *req;
+
+    ret = fi_cq_read(context.cq, entries, context.cq_entries_count);
+    if (ret > 0)
+    {
+        /* One or more completions were found */
+        int idx;
+        for (idx = 0; idx < ret; idx++)
+        {
+            struct fi_cq_tagged_entry* e = &(entries[idx]);
+            CmiAssert(e->op_context != NULL);
+
+            /* Retrieve request from context */
+            req = container_of((e->op_context), OFIRequest, context);
+
+            /* Execute request callback */
+            if ((e->flags & FI_SEND) ||
+                (e->flags & FI_RECV) ||
+                (e->flags & FI_RMA))
+            {
+                req->callback(e, req);
+            }
+            else
+            {
+                MACHSTATE1(3, "Missed event with flags=%x", e->flags);
+                CmiAbort("!! Missed an event !!");
+            }
+        }
+    }
+    else if (ret == -FI_EAGAIN)
+    {
+        /* Completion Queue is empty */
+        ret = 0;
+    }
+    else if (ret < 0)
+    {
+        MACHSTATE1(3, "POLL: Error %d\n", ret);
+        CmiPrintf("POLL: Error %d\n", ret);
+        if (ret == -FI_EAVAIL)
+        {
+            MACHSTATE(3, "POLL: error available\n");
+            CmiPrintf("POLL: error available\n");
+            ret = fi_cq_readerr(context.cq, (void *)&error, sizeof(error));
+            if (ret < 0)
+            {
+                CmiAbort("can't retrieve error");
+            }
+            MACHSTATE2(3, "POLL: error is %d (ret=%d)\n", error.err, ret);
+            CmiPrintf("POLL: error is %d (ret=%d)\n", error.err, ret);
+        }
+        CmiAbort("Polling error");
+    }
+    return ret;
+}
+
+#if CMK_SMP
+static inline
+int process_send_queue()
+{
+    OFIRequest *req;
+    int ret = 0;
+    /**
+     * Comm thread sends the next message that is waiting in the send_queue.
+     */
+    req = (OFIRequest*)PCQueuePop(context.send_queue);
+    if (req)
+    {
+        MACHSTATE2(2, " --> (PE=%i) dequeuing message (queue depth: %i)",
+                CmiMyPe(), PCQueueLength(context.send_queue));
+        MACHSTATE5(2,
+                " --> dequeuing destNode=%i destPE=%i size=%d msg=%p mode=%d",
+                req->destNode, req->destPE, req->size, req->data, req->mode);
+        sendMsg(req);
+        ret = 1;
+    }
+    return ret;
+}
+#endif
+
+#if USE_MEMPOOL
+
+void *alloc_mempool_block(size_t *size, mem_handle_t *mem_hndl, int expand_flag)
+{
+    size_t alloc_size =  expand_flag ? context.mempool_expand_size : context.mempool_init_size;
+    if (*size < alloc_size) *size = alloc_size;
+    if (*size > context.mempool_max_size)
+    {
+        CmiPrintf("Error: there is attempt to allocate memory block with size %lld which is greater than the maximum mempool allowed %lld.\n"
+                  "Please increase the maximum mempool size by using +ofi-mempool-max-size\n",
+                  *size, context.mempool_max_size);
+        CmiAbort("alloc_mempool_block");
+    }
+
+    void *pool;
+    ALIGNED_ALLOC(pool, *size);
+    return pool;
+}
+
+void free_mempool_block(void *ptr, mem_handle_t mem_hndl)
+{
+    free(ptr);
+}
+
+#endif
+
+void LrtsPreCommonInit(int everReturn)
+{
+    MACHSTATE(2, "OFI::LrtsPreCommonInit {");
+
+    PRINT_THREAD_INFO();
+
+#if USE_MEMPOOL
+    CpvInitialize(mempool_type*, mempool);
+    CpvAccess(mempool) = mempool_init(context.mempool_init_size,
+                                      alloc_mempool_block,
+                                      free_mempool_block,
+                                      context.mempool_max_size);
+#endif
+
+    if (!CmiMyRank()) prepost_buffers();
+
+    MACHSTATE(2, "} OFI::LrtsPreCommonInit");
+}
+
+void LrtsPostCommonInit(int everReturn)
+{
+    MACHSTATE(2, "OFI::LrtsPostCommonInit {");
+    MACHSTATE(2, "} OFI::LrtsPostCommonInit");
+}
+
+void LrtsAdvanceCommunication(int whileidle)
+{
+    int processed_count;
+    MACHSTATE(2, "OFI::LrtsAdvanceCommunication {");
+
+    do
+    {
+        processed_count = 0;
+        processed_count += process_completion_queue();
+#if CMK_SMP
+        processed_count += process_send_queue();
+#endif
+    } while (processed_count > 0);
+
+    MACHSTATE(2, "} OFI::LrtsAdvanceCommunication done");
+}
+
+void LrtsDrainResources() /* used when exiting */
+{
+    int ret;
+    MACHSTATE1(2, "OFI::LrtsDrainResources (PE=%i {", CmiMyPe());
+    LrtsAdvanceCommunication(0);
+    ret = runtime_barrier();
+    if (ret) {
+        MACHSTATE1(2, "runtime_barrier() returned %i", ret);
+        CmiAbort("OFI::LrtsDrainResources failed");
+    }
+    MACHSTATE(2, "} OFI::LrtsDrainResources");
+}
+
+void* LrtsAlloc(int n_bytes, int header)
+{
+    void *ptr = NULL;
+    size_t size = n_bytes + header;
+
+#if USE_MEMPOOL
+    if (size <= context.mempool_lb_size || size >= context.mempool_rb_size)
+        ALIGNED_ALLOC(ptr, size);
+    else
+        ptr = mempool_malloc(CpvAccess(mempool), size, 1);
+#else
+    ALIGNED_ALLOC(ptr, size);
+#endif
+
+    if (!ptr) CmiAbort("LrtsAlloc");
+    return ptr;
+}
+
+void LrtsFree(void *msg)
+{
+#if USE_MEMPOOL
+    CmiUInt4 size = SIZEFIELD((char*)msg + sizeof(CmiChunkHeader)) + sizeof(CmiChunkHeader);
+    if (size <= context.mempool_lb_size || size >= context.mempool_rb_size)
+        free(msg);
+    else
+#if CMK_SMP
+        mempool_free_thread(msg);
+#else
+        mempool_free(CpvAccess(mempool), msg);
+#endif /* CMK_SMP */
+#else
+    free(msg);
+#endif /* USE_MEMPOOL */
+}
+
+void LrtsExit()
+{
+    int        ret;
+    int        i;
+    OFIRequest *req;
+
+    MACHSTATE(2, "OFI::LrtsExit {");
+
+    LrtsAdvanceCommunication(0);
+
+    for (i = 0; i < context.num_recv_reqs; i++)
+    {
+        req = context.recv_reqs[i];
+        ret = fi_cancel((fid_t)context.ep, (void *)&(req->context));
+        if (ret < 0) CmiAbort("fi_cancel error");
+        CmiFree(req->data.recv_buffer);
+#if USE_OFIREQUEST_CACHE
+        free_request(req);
+#else
+        CmiFree(req);
+#endif
+    }
+
+#if CMK_SMP
+    PCQueueDestroy(context.send_queue);
+#endif
+
+    if (context.recv_reqs)
+        free(context.recv_reqs);
+    if (context.av)
+        fi_close((struct fid *)(context.av));
+    if (context.cq)
+        fi_close((struct fid *)(context.cq));
+    if (context.ep)
+        fi_close((struct fid *)(context.ep));
+    if (context.domain)
+        fi_close((struct fid *)(context.domain));
+    if (context.fabric)
+        fi_close((struct fid *)(context.fabric));
+
+#if USE_OFIREQUEST_CACHE
+    destroy_request_cache(context.request_cache);
+#endif
+
+#if USE_MEMPOOL
+    mempool_destroy(CpvAccess(mempool));
+#endif
+
+    if(!CharmLibInterOperate || userDrivenMode) {
+        ret = runtime_barrier();
+        if (ret) {
+            MACHSTATE1(2, "runtime_barrier() returned %i", ret);
+            CmiAbort("OFI::LrtsExit failed");
+        }
+        ret = runtime_fini();
+        if (ret) {
+            MACHSTATE1(2, "runtime_fini() returned %i", ret);
+            CmiAbort("OFI::LrtsExit failed");
+        }
+        exit(0);
+    }
+
+    MACHSTATE(2, "} OFI::LrtsExit");
+}
+
+#if CMK_MACHINE_PROGRESS_DEFINED
+void CmiMachineProgressImpl()
+{
+    MACHSTATE(2, "OFI::CmiMachineProgressImpl {");
+    if (CmiMyRank() == CmiMyNodeSize()) {
+        CommunicationServerThread(0);
+    } else {
+        MACHSTATE(2, "skipping");
+    }
+    MACHSTATE(2, "} OFI::CmiMachineProgressImpl");
+}
+#endif
+
+/* In CMK_SMP, this is called by worker thread */
+void LrtsPostNonLocal()
+{
+    MACHSTATE(2, "OFI::LrtsPostNonLocal {");
+    MACHSTATE(2, "} OFI::LrtsPostNonLocal");
+}
+
+void LrtsAbort(const char *message)
+{
+    MACHSTATE1(2, "OFI::LrtsAbort '%s' {", message);
+    exit(1);
+    MACHSTATE(2, "} OFI::LrtsAbort");
+}
+
+void  LrtsNotifyIdle()
+{
+    MACHSTATE(2, "OFI::LrtsNotifyIdle {");
+    MACHSTATE(2, "} OFI::LrtsNotifyIdle");
+}
+
+void  LrtsBeginIdle()
+{
+    MACHSTATE(2, "OFI::LrtsBeginIdle {");
+    MACHSTATE(2, "} OFI::LrtsBeginIdle");
+}
+
+void  LrtsStillIdle()
+{
+    MACHSTATE(2, "OFI::LrtsStillIdle {");
+    MACHSTATE(2, "} OFI::LrtsStillIdle");
+}
+
+void  LrtsBarrier()
+{
+    int ret;
+    MACHSTATE(2, "OFI::LrtsBarrier {");
+    ret = runtime_barrier();
+    if (ret) {
+        MACHSTATE1(2, "runtime_barrier() returned %i", ret);
+        CmiAbort("OFI::LrtsBarrier failed");
+    }
+    MACHSTATE(2, "} OFI::LrtsBarrier");
+}
+
+/* Other assist function */
+
+/**
+ * fill_av_ofi() is used during LrtsInit to exchange all the EP names and to
+ * insert them into the AV. The exchange is performed using both PMI and OFI.
+ * This is used by default. See +ofi_runtime_tcp flag for other option.
+ */
+static
+int fill_av_ofi(int myid,
+                int nnodes,
+                struct fid_ep *ep,
+                struct fid_av *av,
+                struct fid_cq *cq)
+{
+    char                       my_epname[FI_NAME_MAX];
+    size_t                     epnamelen;
+    int                        max_keylen;
+    char                      *key;
+    char                      *epnames;
+    size_t                     epnameslen;
+    struct fi_context         *epnames_contexts;
+    struct fi_cq_tagged_entry  e;
+    size_t                     nexpectedcomps;
+    int                        ret;
+    int                        i;
+
+    /**
+     * Get our EP name. This will be exchanged with the other nodes.
+     */
+    epnamelen = sizeof(my_epname);
+    ret = fi_getname((fid_t)ep, &my_epname, &epnamelen);
+    CmiAssert(FI_NAME_MAX >= epnamelen);
+    if (ret < 0) {
+        CmiAbort("OFI::LrtsInit::fi_getname error");
+    }
+
+    /**
+     * Publish our EP name.
+     */
+    ret = runtime_get_max_keylen(&max_keylen);
+    if (ret) {
+        CmiAbort("OFI::LrtsInit::runtime_get_max_keylen error");
+    }
+
+    key = malloc(max_keylen);
+    CmiAssert(key);
+
+    ret = snprintf(key, max_keylen, OFI_KEY_FORMAT_EPNAME, myid);
+    if (ret < 0) {
+        CmiAbort("OFI::LrtsInit::snprintf error");
+    }
+
+    ret = runtime_kvs_put(key, &my_epname, epnamelen);
+    if (ret) {
+        CmiAbort("OFI::LrtsInit::runtime_kvs_put error");
+    }
+
+    /**
+     * Allocate buffer which will contain all the EP names ordered by Node Id.
+     * Once all the names are exchanged, they will be inserted into the AV.
+     */
+    epnameslen = FI_NAME_MAX * nnodes;
+    epnames = malloc(epnameslen);
+    CmiAssert(epnames);
+    memset(epnames, 0, epnameslen);
+
+    if (myid != 0) {
+        /**
+         * Non-root nodes expect a message which contains the EP names.
+         */
+        epnames_contexts = malloc(sizeof(struct fi_context));
+        CmiAssert(epnames_contexts);
+
+        ret = fi_trecv(ep,
+                       epnames,
+                       epnameslen,
+                       NULL,
+                       FI_ADDR_UNSPEC,
+                       OFI_OP_NAMES,
+                       0ULL,
+                       &epnames_contexts[0]);
+
+        /* Reap 1 recv completion */
+        nexpectedcomps = 1;
+    }
+
+    /**
+     * Wait for all the other nodes to publish their EP names.
+     */
+    ret = runtime_barrier();
+    if (ret) {
+        CmiAbort("OFI::LrtsInit::runtime_barrier error");
+    }
+
+    if (myid == 0) {
+        /**
+         * Root gathers all the epnames and sends them to the other nodes.
+         */
+
+        /* Retrieve all epnames */
+        for (i=0; i<nnodes; ++i) {
+            memset(key, 0, max_keylen);
+            ret = snprintf(key, max_keylen, OFI_KEY_FORMAT_EPNAME, i);
+            if (ret < 0) {
+                CmiAbort("OFI::LrtsInit::snprintf error");
+            }
+
+            ret = runtime_kvs_get(key, epnames+(i*epnamelen), epnamelen);
+            if (ret) {
+                CmiAbort("OFI::LrtsInit::runtime_kvs_get error");
+            }
+        }
+
+        /* AV insert */
+        ret = fi_av_insert(av, epnames, nnodes, NULL, 0, NULL);
+        if (ret < 0) {
+            CmiAbort("OFI::LrtsInit::fi_av_insert error");
+        }
+
+        /* Send epnames to everyone */
+        epnames_contexts = malloc(nnodes * sizeof(struct fi_context));
+        CmiAssert(epnames_contexts);
+        for (i=1; i<nnodes; ++i) {
+            ret = fi_tsend(ep,
+                           epnames,
+                           epnameslen,
+                           NULL,
+                           i,
+                           OFI_OP_NAMES,
+                           &epnames_contexts[i]);
+            if (ret) {
+                CmiAbort("OFI::LrtsInit::fi_tsend error");
+            }
+        }
+
+        /* Reap 1 send completion per non-root node */
+        nexpectedcomps = nnodes - 1;
+    }
+
+    while (nexpectedcomps > 0) {
+        memset(&e, 0, sizeof e);
+        ret = fi_cq_read(cq, &e, 1);
+        if (ret > 0) {
+            /* A completion was found */
+            if (((e.flags & FI_SEND) && (myid != 0)) ||
+                ((e.flags & FI_RECV) && (myid == 0))) {
+                /* This message was received in error */
+                CmiAbort("OFI::LrtsInit::fi_cq_read unexpected completion.");
+            }
+            nexpectedcomps--;
+        } else if(ret == -FI_EAGAIN) {
+            /* Completion Queue is empty */
+            continue;
+        } else if (ret < 0) {
+           CmiAbort("OFI::LrtsInit::fi_cq_read error.");
+        }
+    }
+
+    if (myid != 0) {
+        /**
+         * Non-root nodes
+         */
+
+        /* AV insert */
+        ret = fi_av_insert(av, epnames, nnodes, NULL, 0, NULL);
+        if (ret < 0) {
+            CmiAbort("OFI::LrtsInit::fi_av_insert error");
+        }
+    }
+
+    free(key);
+    free(epnames);
+    free(epnames_contexts);
+
+    return 0;
+}
+
+/**
+ * fill_av() is used during LrtsInit to exchange all the EP names and to insert
+ * them into the AV. The exchange is performed using PMI only. Currently
+ * enabled only if +ofi_runtime_tcp flag is set.
+ */
+static
+int fill_av(int myid,
+            int nnodes,
+            struct fid_ep *ep,
+            struct fid_av *av,
+            struct fid_cq *cq)
+{
+    char    my_epname[FI_NAME_MAX];
+    size_t  epnamelen;
+    int     max_keylen;
+    char   *key;
+    char   *epnames;
+    size_t  epnameslen;
+    int     ret;
+    int     i;
+
+    /**
+     * Get our EP name. This will be exchanged with the other nodes.
+     */
+    epnamelen = sizeof(my_epname);
+    ret = fi_getname((fid_t)ep, &my_epname, &epnamelen);
+    CmiAssert(FI_NAME_MAX >= epnamelen);
+    if (ret < 0) {
+        CmiAbort("OFI::LrtsInit::fi_getname error");
+    }
+
+    /**
+     * Publish our EP name.
+     */
+    ret = runtime_get_max_keylen(&max_keylen);
+    if (ret) {
+        CmiAbort("OFI::LrtsInit::runtime_get_max_keylen error");
+    }
+
+    key = malloc(max_keylen);
+    CmiAssert(key);
+
+    ret = snprintf(key, max_keylen, OFI_KEY_FORMAT_EPNAME, myid);
+    if (ret < 0) {
+        CmiAbort("OFI::LrtsInit::snprintf error");
+    }
+
+    ret = runtime_kvs_put(key, &my_epname, epnamelen);
+    if (ret) {
+        CmiAbort("OFI::LrtsInit::runtime_kvs_put error");
+    }
+
+    /**
+     * Allocate buffer which will contain all the EP names ordered by Node Id.
+     * Once all the names are exchanged, they will be inserted into the AV.
+     */
+    epnameslen = FI_NAME_MAX * nnodes;
+    epnames = malloc(epnameslen);
+    CmiAssert(epnames);
+    memset(epnames, 0, epnameslen);
+
+    /**
+     * Wait for all the other nodes to publish their EP names.
+     */
+    ret = runtime_barrier();
+    if (ret) {
+        CmiAbort("OFI::LrtsInit::runtime_barrier error");
+    }
+
+    /**
+     * Retrieve all the EP names in order.
+     */
+    for (i=0; i<nnodes; ++i) {
+        memset(key, 0, max_keylen);
+        ret = snprintf(key, max_keylen, OFI_KEY_FORMAT_EPNAME, i);
+        if (ret < 0) {
+            CmiAbort("OFI::LrtsInit::snprintf error");
+        }
+
+        ret = runtime_kvs_get(key, epnames+(i*epnamelen), epnamelen);
+        if (ret) {
+            CmiAbort("OFI::LrtsInit::runtime_kvs_get error");
+        }
+
+    }
+
+    /**
+     * Insert all the EP names into the AV.
+     */
+    ret = fi_av_insert(av, epnames, nnodes, NULL, 0, NULL);
+    if (ret < 0) {
+        CmiAbort("OFI::LrtsInit::fi_av_insert error");
+    }
+
+    free(key);
+    free(epnames);
+
+    return 0;
+}
diff --git a/src/arch/ofi/request.h b/src/arch/ofi/request.h
new file mode 100644 (file)
index 0000000..2330032
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2017, Intel Corporation. All rights reserved.
+ * See LICENSE in this directory.
+ */
+
+#ifndef REQUEST_H_
+#define REQUEST_H_
+
+#include <rdma/fabric.h>
+#include <rdma/fi_eq.h>
+
+#include "converse.h"
+
+#define sizeofa(x)         (sizeof(x) / sizeof(*(x)))
+#define REQUEST_CACHE_SIZE 32768
+
+#define ZERO_REQUEST(r)            \
+    do                             \
+    {                              \
+        (r)->mr               = 0; \
+        (r)->callback         = 0; \
+        (r)->destNode        = -1; \
+        (r)->destPE          = -1; \
+        (r)->size             = 0; \
+        (r)->mode             = 0; \
+        (r)->data.recv_buffer = 0; \
+    } while (0)
+
+typedef enum request_state
+{
+    rs_none     = 0, /* message object/slot is empty */
+    rs_progress = 1  /* slot is in progress */
+} request_state;
+
+struct OFIRmaHeader;
+struct OFIRmaAck;
+struct OFILongMsg;
+
+/**
+ * OFI Request
+ * Structure representing data movement operations.
+ *  - context: fi_context
+ *  - state: writing rs_progress value to this value must be done atomically
+ *  - index: request index
+ *  - callback: Request callback called upon completion
+ *  - destNode: destination NodeNo
+ *  - destPE: destination PE
+ *  - size: message size
+ *  - mode: sending mode (unused at this time)
+ *  - mr: memory region associated with RMA buffer
+ *  - data: Pointer to data associated with the request
+ *      - recv_buffer: used when posting a receive buffer
+ *      - rma_header: used when an OFIRmaHeader was received or sent
+ *      - rma_ack: used when an OFIRmaAck was received
+ *      - long_msg: used when an RMA Read operation completed
+ *      - short_msg: used when a short message was sent
+ */
+typedef struct OFIRequest
+{
+    struct fi_context  context;
+    request_state      state;
+    int                index;
+    void               (*callback)(struct fi_cq_tagged_entry*, struct OFIRequest*);
+    int                destNode;
+    int                destPE;
+    int                size;
+    int                mode;
+    struct fid_mr      *mr;
+    union
+    {
+        void                *recv_buffer;
+        struct OFIRmaHeader *rma_header;
+        struct OFIRmaAck    *rma_ack;
+        struct OFILongMsg   *long_msg;
+        void                *short_msg;
+    } data;
+} OFIRequest;
+
+#if USE_OFIREQUEST_CACHE
+typedef struct request_cache_t
+{
+    /** Array of OFIRequest */
+    OFIRequest request[REQUEST_CACHE_SIZE];
+
+    /**
+     * Index of first request in current cache. This is used to find the
+     * request by index.
+     */
+    int index;
+
+    /** Pointer to next cache */
+    struct request_cache_t* next;
+} request_cache_t;
+
+static inline request_cache_t* create_request_cache()
+{
+    request_cache_t* cache = malloc(sizeof(*cache));
+    if (cache)
+    {
+        cache->next = 0;
+        cache->index = 0;
+        int i;
+        for (i = 0; i < sizeofa(cache->request); i++)
+            cache->request[i].state = rs_none;
+        return cache;
+    }
+    else
+        return 0;
+}
+
+static inline void destroy_request_cache(request_cache_t* cache)
+{
+    while (cache)
+    {
+        struct request_cache_t* c = cache->next;
+        free(cache);
+        cache = c;
+    }
+}
+
+static inline void free_request(OFIRequest* req)
+{
+    CmiAssert(req);
+    CmiAssert(req->state == rs_progress);
+    if (req)
+    {
+        ZERO_REQUEST(req);
+        __atomic_store_n(&req->state, rs_none, __ATOMIC_RELEASE);
+    }
+}
+
+/**
+ * Find/create empty request slot. This function is thread safe thanks to the
+ * use of atomic primitives to locate/update entries.
+ */
+static inline OFIRequest* alloc_request(request_cache_t* req_cache)
+{
+    CmiAssert(req_cache);
+    int i;
+    OFIRequest* request = 0;
+    /* try to find free request (state == rs_none) */
+    struct request_cache_t* cache = req_cache;
+    while (cache)
+    {
+        for (i = 0; i < sizeofa(cache->request); i++)
+        {
+            if (__sync_bool_compare_and_swap(&cache->request[i].state, rs_none, rs_progress))
+            {
+                /* Found one entry */
+                cache->request[i].index = cache->index + i;
+                ZERO_REQUEST(&cache->request[i]);
+                request = cache->request + i;
+                goto fn_exit;
+            }
+        }
+        /* no entries in current cache element, try next one... */
+        cache = cache->next;
+    }
+
+    /* still no free entries; create new cache entry */
+    cache = create_request_cache();
+    CmiAssert(cache);
+    if (cache) {
+        /* use first request entry in the new cache */
+        cache->request[0].state = rs_progress;
+        /* append new cache entry to list */
+        struct request_cache_t* c = req_cache;
+        /* here is the trick: __sync_val_compare_and_swap updates c->next only if
+         * it was 0. otherwise, keep iterating. */
+        for (i = 0; c; i++)
+        {
+            /* set index value to 'current' count */
+            cache->index = (i + 1) * sizeofa(c->request);
+            c = __sync_val_compare_and_swap(&c->next, 0, cache);
+        }
+        cache->request->index = cache->index;
+        ZERO_REQUEST(cache->request);
+        request = cache->request;
+        goto fn_exit;
+    }
+
+fn_exit:
+    return request;
+}
+
+/**
+ * Lookup request by index: list all cache arrays till index is inside the
+ * current one.
+ */
+static inline OFIRequest* lookup_request(request_cache_t* cache, int index)
+{
+    while (cache)
+    {
+        if (index >= cache->index && index < cache->index + sizeofa(cache->request))
+            return cache->request + (index - cache->index);
+        cache = cache->next;
+    }
+    return 0;
+}
+#endif /* USE_OFIREQUEST_CACHE */
+
+#endif /* REQUEST_H_ */
diff --git a/src/arch/ofi/runtime-codec.h b/src/arch/ofi/runtime-codec.h
new file mode 100644 (file)
index 0000000..ee900fa
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Base16 encode/decode copied from Sandia OpenSHMEM
+ * https://github.com/Sandia-OpenSHMEM
+ *
+ * See LICENSE-SandiaOpenSHMEM in this directory.
+ */
+static int
+encode(const void *inval, int invallen, char *outval, int outvallen)
+{
+    static unsigned char encodings[] = {
+        '0','1','2','3','4','5','6','7', \
+        '8','9','a','b','c','d','e','f' };
+    int i;
+
+    if (invallen * 2 + 1 > outvallen) {
+        return 1;
+    }
+
+    for (i = 0; i < invallen; i++) {
+        outval[2 * i] = encodings[((unsigned char *)inval)[i] & 0xf];
+        outval[2 * i + 1] = encodings[((unsigned char *)inval)[i] >> 4];
+    }
+
+    outval[invallen * 2] = '\0';
+
+    return 0;
+}
+
+static int
+decode(const char *inval, void *outval, int outvallen)
+{
+    int i;
+    char *ret = (char*) outval;
+
+    if (outvallen != strlen(inval) / 2) {
+        return 1;
+    }
+
+    for (i = 0 ; i < outvallen ; ++i) {
+        if (*inval >= '0' && *inval <= '9') {
+            ret[i] = *inval - '0';
+        } else {
+            ret[i] = *inval - 'a' + 10;
+        }
+        inval++;
+        if (*inval >= '0' && *inval <= '9') {
+            ret[i] |= ((*inval - '0') << 4);
+        } else {
+            ret[i] |= ((*inval - 'a' + 10) << 4);
+        }
+        inval++;
+    }
+
+    return 0;
+}
diff --git a/src/arch/ofi/runtime-pmi.c b/src/arch/ofi/runtime-pmi.c
new file mode 100644 (file)
index 0000000..45a7225
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2017, Intel Corporation. All rights reserved.
+ * See LICENSE in this directory.
+ *
+ * Runtime functions used by OFI LRTS machine layer to exchange
+ * addresses during the initialization.
+ *
+ * This example uses the PMI API as described in pmi.h.
+ */
+#include <stdlib.h>
+#include <string.h>
+
+#include <pmi.h>
+
+#include "runtime.h"
+
+#if CMK_OFI_USE_SIMPLEPMI
+#include "simple_pmi.c"
+#include "simple_pmiutil.c"
+#endif
+
+/* For encode/decode functions */
+#include "runtime-codec.h"
+
+static int initialized;
+static int max_keylen;
+static int max_valuelen;
+static char *kvsname;
+static char *key;
+static char *value;
+
+int runtime_init(int *rank, int *jobsize)
+{
+    int ret;
+    int first_spawned;
+    int max_kvsnamelen;
+
+    ret = PMI_Init(&first_spawned);
+    if (PMI_SUCCESS != ret) {
+        return 1;
+    }
+
+    ret = PMI_Get_size(jobsize);
+    if (PMI_SUCCESS != ret) {
+        return 2;
+    }
+
+    ret = PMI_Get_rank(rank);
+    if (PMI_SUCCESS != ret) {
+        return 3;
+    }
+
+    ret = PMI_KVS_Get_name_length_max(&max_kvsnamelen);
+    if (PMI_SUCCESS != ret) {
+        return 4;
+    }
+
+    kvsname = calloc(max_kvsnamelen, sizeof(char));
+    if (!kvsname) {
+        return 5;
+    }
+
+    ret = PMI_KVS_Get_my_name(kvsname, max_kvsnamelen);
+    if (PMI_SUCCESS != ret) {
+        free(kvsname);
+        return 6;
+    }
+
+    ret = PMI_KVS_Get_key_length_max(&max_keylen);
+    if (PMI_SUCCESS != ret) {
+        free(kvsname);
+        return 7;
+    }
+
+    key = calloc(max_keylen, sizeof(char));
+    if (!key) {
+        free(kvsname);
+        return 8;
+    }
+
+    ret = PMI_KVS_Get_value_length_max(&max_valuelen);
+    if (PMI_SUCCESS != ret) {
+        free(key);
+        free(kvsname);
+        return 9;
+    }
+
+    value = calloc(max_valuelen, sizeof(char));
+    if (!value) {
+        free(key);
+        free(kvsname);
+        return 10;
+    }
+
+    initialized = 1;
+    return 0;
+}
+
+int runtime_fini()
+{
+    int ret;
+
+    if (initialized) {
+        ret = PMI_Finalize();
+        if (PMI_SUCCESS != ret) {
+            return 1;
+        }
+    }
+
+    if (value) {
+        free(value);
+        value = NULL;
+    }
+    if (key) {
+        free(key);
+        key = NULL;
+    }
+    if (kvsname) {
+        free(kvsname);
+        kvsname = NULL;
+    }
+
+    initialized = 0;
+    return 0;
+}
+
+int runtime_get_max_keylen(int *len)
+{
+    if (!initialized) {
+        return 1;
+    }
+    *len = max_keylen;
+    return 0;
+}
+
+int runtime_kvs_put(const char *k, const void *v, int vlen)
+{
+    int ret;
+    int keylen;
+
+    if (!initialized) {
+        return 1;
+    }
+
+    keylen = strlen(k);
+    if (keylen > max_keylen) {
+        return 2;
+    }
+
+    if (vlen > max_valuelen) {
+        return 3;
+    }
+
+    ret = encode(v, vlen, value, max_valuelen);
+    if (ret) {
+        return 4;
+    }
+
+    ret = PMI_KVS_Put(kvsname, k, value);
+    if (ret) {
+        return 5;
+    }
+
+    ret = PMI_KVS_Commit(kvsname);
+    if (ret) {
+        return 6;
+    }
+
+    return 0;
+}
+
+int runtime_kvs_get(const char *k, void *v, int vlen)
+{
+    int ret;
+
+    if (!initialized) {
+        return 1;
+    }
+
+    ret = PMI_KVS_Get(kvsname, k, value, max_valuelen);
+    if (ret) {
+        return 2;
+    }
+
+    ret = decode(value, v, vlen);
+    if (ret) {
+        return 3;
+    }
+
+    return 0;
+}
+
+int runtime_barrier()
+{
+    int ret;
+
+    if (!initialized) {
+        return 1;
+    }
+
+    ret = PMI_Barrier();
+    if (ret) {
+        return 2;
+    }
+    return 0;
+}
diff --git a/src/arch/ofi/runtime-pmi2.c b/src/arch/ofi/runtime-pmi2.c
new file mode 100644 (file)
index 0000000..85fbd07
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2017, Intel Corporation. All rights reserved.
+ * See LICENSE in this directory.
+ *
+ * Runtime functions used by OFI LRTS machine layer to exchange
+ * addresses during the initialization.
+ *
+ * This example uses the PMI2 API as described in pmi2.h.
+ */
+#include <stdlib.h>
+#include <string.h>
+
+#include <pmi2.h>
+
+#include "runtime.h"
+
+/* For encode/decode functions */
+#include "runtime-codec.h"
+
+static int initialized;
+static int max_keylen = PMI2_MAX_KEYLEN;
+static int max_valuelen = PMI2_MAX_VALLEN;
+static char *kvsname;
+static char *key;
+static char *value;
+
+int runtime_init(int *rank, int *jobsize)
+{
+    int ret;
+    int spawned;
+    int appnum;
+    int max_kvsnamelen = PMI2_MAX_VALLEN;
+
+    ret = PMI2_Init(&spawned, jobsize, rank, &appnum);
+    if (PMI2_SUCCESS != ret) {
+        return 1;
+    }
+
+    kvsname = calloc(max_kvsnamelen, sizeof(char));
+    if (!kvsname) {
+        return 2;
+    }
+
+    ret = PMI2_Job_GetId(kvsname, max_kvsnamelen);
+    if (PMI2_SUCCESS != ret) {
+        return 3;
+    }
+
+    key = calloc(max_keylen, sizeof(char));
+    if (!key) {
+        free(kvsname);
+        return 4;
+    }
+
+    value = calloc(max_valuelen, sizeof(char));
+    if (!value) {
+        free(key);
+        free(kvsname);
+        return 5;
+    }
+
+    initialized = 1;
+    return 0;
+}
+
+int runtime_fini()
+{
+    int ret;
+
+    if (initialized) {
+        ret = PMI2_Finalize();
+        if (PMI2_SUCCESS != ret) {
+            return 1;
+        }
+    }
+
+    if (value) {
+        free(value);
+        value = NULL;
+    }
+    if (key) {
+        free(key);
+        key = NULL;
+    }
+    if (kvsname) {
+        free(kvsname);
+        kvsname = NULL;
+    }
+
+    initialized = 0;
+    return 0;
+}
+
+int runtime_get_max_keylen(int *len)
+{
+    if (!initialized) {
+        return 1;
+    }
+    *len = max_keylen;
+    return 0;
+}
+
+int runtime_kvs_put(const char *k, const void *v, int vlen)
+{
+    int ret;
+    int keylen;
+
+    if (!initialized) {
+        return 1;
+    }
+
+    keylen = strlen(k);
+    if (keylen > max_keylen) {
+        return 2;
+    }
+
+    if (vlen > max_valuelen) {
+        return 3;
+    }
+
+    ret = encode(v, vlen, value, max_valuelen);
+    if (ret) {
+        return 4;
+    }
+
+    ret = PMI2_KVS_Put(k, value);
+    if (ret) {
+        return 5;
+    }
+
+    return 0;
+}
+
+int runtime_kvs_get(const char *k, void *v, int vlen)
+{
+    int ret;
+    int len;
+
+    if (!initialized) {
+        return 1;
+    }
+
+    ret = PMI2_KVS_Get(kvsname, PMI2_ID_NULL, k, value, max_valuelen, &len);
+    if (ret) {
+        return 2;
+    }
+
+    ret = decode(value, v, vlen);
+    if (ret) {
+        return 3;
+    }
+
+    return 0;
+}
+
+int runtime_barrier()
+{
+    int ret;
+
+    if (!initialized) {
+        return 1;
+    }
+
+    ret = PMI2_KVS_Fence();
+    if (ret) {
+        return 2;
+    }
+    return 0;
+}
diff --git a/src/arch/ofi/runtime.h b/src/arch/ofi/runtime.h
new file mode 100644 (file)
index 0000000..5311ca9
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2017, Intel Corporation. All rights reserved.
+ * See LICENSE in this directory.
+ *
+ * Runtime functions used by OFI LRTS machine layer to exchange
+ * addresses during the initialization.
+ *
+ * The idea is that there could be multiple ways of implementing
+ * these functions. The example provided in runtime-pmi.c uses PMI.
+ */
+int runtime_init(int *rank, int *jobsize);
+int runtime_fini();
+
+int runtime_get_max_keylen(int *len);
+int runtime_kvs_put(const char *k, const void *v, int vlen);
+int runtime_kvs_get(const char *k, void *v, int vlen);
+int runtime_barrier();
diff --git a/src/arch/ofi/simple_pmi/LICENSE-MPICH b/src/arch/ofi/simple_pmi/LICENSE-MPICH
new file mode 100644 (file)
index 0000000..97bcea7
--- /dev/null
@@ -0,0 +1,39 @@
+
+                  COPYRIGHT
+
+The following is a notice of limited availability of the code, and disclaimer
+which must be included in the prologue of the code and in all source listings
+of the code.
+
+Copyright Notice
+ + 2002 University of Chicago
+
+Permission is hereby granted to use, reproduce, prepare derivative works, and
+to redistribute to others.  This software was authored by:
+
+Mathematics and Computer Science Division
+Argonne National Laboratory, Argonne IL 60439
+
+(and)
+
+Department of Computer Science
+University of Illinois at Urbana-Champaign
+
+
+                  GOVERNMENT LICENSE
+
+Portions of this material resulted from work developed under a U.S.
+Government Contract and are subject to the following license: the Government
+is granted for itself and others acting on its behalf a paid-up, nonexclusive,
+irrevocable worldwide license in this computer software to reproduce, prepare
+derivative works, and perform publicly and display publicly.
+
+                  DISCLAIMER
+
+This computer code material was prepared, in part, as an account of work
+sponsored by an agency of the United States Government.  Neither the United
+States, nor the University of Chicago, nor any of their employees, makes any
+warranty express or implied, or assumes any legal liability or responsibility
+for the accuracy, completeness, or usefulness of any information, apparatus,
+product, or process disclosed, or represents that its use would not infringe
+privately owned rights.
diff --git a/src/arch/ofi/simple_pmi/README b/src/arch/ofi/simple_pmi/README
new file mode 100644 (file)
index 0000000..327de2c
--- /dev/null
@@ -0,0 +1,28 @@
+Simple implementation of the client-side of PMI.
+
+This is typically provided by resource managers such as SLURM and thus not
+needed.  In environments without SLURM, this allows us to use mpiexec.hydra to
+launch jobs.
+
+MPICH provides a simple implementation which can be found under:
+
+    http://git.mpich.org/mpich.git/tree/HEAD:/src/pmi/simple
+
+This code was directly inspired by Sandia OpenSHMEM's use of it:
+
+    https://github.com/Sandia-OpenSHMEM/SOS/tree/master/pmi-simple
+
+Files copied from Sandia OpenSHMEM:
+
+    - mpl.h
+
+See LICENSE-SandiaOpenSHMEM in parent directory.
+
+Files copied from MPICH:
+
+    - pmi.h
+    - simple_pmi.c
+    - simple_pmiutil.c
+    - simple_pmiutil.h
+
+See LICENSE-MPICH in this directory.
diff --git a/src/arch/ofi/simple_pmi/mpl.h b/src/arch/ofi/simple_pmi/mpl.h
new file mode 100644 (file)
index 0000000..e6d2206
--- /dev/null
@@ -0,0 +1,176 @@
+/* -*- C -*-
+ *
+ * This code is derived from MPICH, which is copyright
+ *  (C) 2001 by Argonne National Laboratory.
+ *
+ * Copyright 2016 Sandia Corporation. Under the terms of Contract
+ * DE-AC04-94AL85000 with Sandia Corporation, the U.S.  Government
+ * retains certain rights in this software.
+ *
+ * Copyright (c) 2016 Intel Corporation. All rights reserved.
+ * This software is available to you under the BSD license.
+ *
+ * This file is part of the Sandia OpenSHMEM software package. For license
+ * information, see the LICENSE file in the top level directory of the
+ * distribution.
+ *
+ */
+
+#ifndef MPL_H
+#define MPL_H
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define ATTRIBUTE __attribute__
+#define MPI_MAX_PORT_NAME 256
+
+#define MPL_snprintf(...) snprintf(__VA_ARGS__)
+
+/*
+ * MPL_strncpy - Copy at most n characters.  Stop once a null is reached.
+ *
+ * This is different from strncpy, which null pads so that exactly
+ * n characters are copied.  The strncpy behavior is correct for many
+ * applications because it guarantees that the string has no uninitialized
+ * data.
+ *
+ * If n characters are copied without reaching a null, return an error.
+ * Otherwise, return 0.
+ *
+ * Question: should we provide a way to request the length of the string,
+ * since we know it?
+ */
+/*@ MPL_strncpy - Copy a string with a maximum length
+
+Input Parameters:
++   instr - String to copy
+-   maxlen - Maximum total length of 'outstr'
+
+Output Parameters:
+.   outstr - String to copy into
+
+    Notes:
+    This routine is the routine that you wish 'strncpy' was.  In copying
+    'instr' to 'outstr', it stops when either the end of 'outstr' (the
+    null character) is seen or the maximum length 'maxlen' is reached.
+    Unlike 'strncpy', it does not add enough nulls to 'outstr' after
+    copying 'instr' in order to move precisely 'maxlen' characters.
+    Thus, this routine may be used anywhere 'strcpy' is used, without any
+    performance cost related to large values of 'maxlen'.
+
+    If there is insufficient space in the destination, the destination is
+    still null-terminated, to avoid potential failures in routines that neglect
+    to check the error code return from this routine.
+
+  Module:
+  Utility
+  @*/
+static inline int MPL_strncpy(char *dest, const char *src, size_t n)
+{
+    char *d_ptr = dest;
+    const char *s_ptr = src;
+    register int i;
+
+    if (n == 0)
+        return 0;
+
+    i = (int) n;
+    while (*s_ptr && i-- > 0) {
+        *d_ptr++ = *s_ptr++;
+    }
+
+    if (i > 0) {
+        *d_ptr = 0;
+        return 0;
+    }
+    else {
+        /* Force a null at the end of the string (gives better safety
+         * in case the user fails to check the error code) */
+        dest[n - 1] = 0;
+        /* We may want to force an error message here, at least in the
+         * debugging version */
+        /*printf("failure in copying %s with length %d\n", src, n); */
+        return 1;
+    }
+}
+
+
+static inline void MPL_exit(int exit_code)
+{
+    exit(exit_code);
+}
+
+
+/*@ MPL_strnapp - Append to a string with a maximum length
+
+Input Parameters:
++   instr - String to copy
+-   maxlen - Maximum total length of 'outstr'
+
+Output Parameters:
+.   outstr - String to copy into
+
+    Notes:
+    This routine is similar to 'strncat' except that the 'maxlen' argument
+    is the maximum total length of 'outstr', rather than the maximum
+    number of characters to move from 'instr'.  Thus, this routine is
+    easier to use when the declared size of 'instr' is known.
+
+  Module:
+  Utility
+  @*/
+static inline int MPL_strnapp(char *dest, const char *src, size_t n)
+{
+    char *d_ptr = dest;
+    const char *s_ptr = src;
+    register int i;
+
+    /* Get to the end of dest */
+    i = (int) n;
+    while (i-- > 0 && *d_ptr)
+        d_ptr++;
+    if (i <= 0)
+        return 1;
+
+    /* Append.  d_ptr points at first null and i is remaining space. */
+    while (*s_ptr && i-- > 0) {
+        *d_ptr++ = *s_ptr++;
+    }
+
+    /* We allow i >= (not just >) here because the first while decrements
+     * i by one more than there are characters, leaving room for the null */
+    if (i >= 0) {
+        *d_ptr = 0;
+        return 0;
+    }
+    else {
+        /* Force the null at the end */
+        *--d_ptr = 0;
+
+        /* We may want to force an error message here, at least in the
+         * debugging version */
+        return 1;
+    }
+}
+
+
+static inline int MPL_internal_error_printf(const char *str, ...)
+{
+    int n;
+    va_list list;
+    const char *format_str;
+
+    va_start(list, str);
+    format_str = str;
+    n = vfprintf(stderr, format_str, list);
+    va_end(list);
+
+    fflush(stderr);
+
+    return n;
+}
+
+#endif /* MPL_H */
diff --git a/src/arch/ofi/simple_pmi/pmi.h b/src/arch/ofi/simple_pmi/pmi.h
new file mode 100644 (file)
index 0000000..eeb1f8e
--- /dev/null
@@ -0,0 +1,473 @@
+/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
+/*
+ *  (C) 2001 by Argonne National Laboratory.
+ *      See COPYRIGHT in top-level directory.
+ */
+
+#ifndef PMI_H_INCLUDED
+#define PMI_H_INCLUDED
+
+#ifdef USE_PMI2_API
+#error This header file defines the PMI v1 API, but PMI2 was selected
+#endif
+
+/* prototypes for the PMI interface in MPICH */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*D
+PMI_CONSTANTS - PMI definitions
+
+Error Codes:
++ PMI_SUCCESS - operation completed successfully
+. PMI_FAIL - operation failed
+. PMI_ERR_NOMEM - input buffer not large enough
+. PMI_ERR_INIT - PMI not initialized
+. PMI_ERR_INVALID_ARG - invalid argument
+. PMI_ERR_INVALID_KEY - invalid key argument
+. PMI_ERR_INVALID_KEY_LENGTH - invalid key length argument
+. PMI_ERR_INVALID_VAL - invalid val argument
+. PMI_ERR_INVALID_VAL_LENGTH - invalid val length argument
+. PMI_ERR_INVALID_LENGTH - invalid length argument
+. PMI_ERR_INVALID_NUM_ARGS - invalid number of arguments
+. PMI_ERR_INVALID_ARGS - invalid args argument
+. PMI_ERR_INVALID_NUM_PARSED - invalid num_parsed length argument
+. PMI_ERR_INVALID_KEYVALP - invalid keyvalp argument
+- PMI_ERR_INVALID_SIZE - invalid size argument
+
+Booleans:
++ PMI_TRUE - true
+- PMI_FALSE - false
+
+D*/
+#define PMI_SUCCESS                  0
+#define PMI_FAIL                    -1
+#define PMI_ERR_INIT                 1
+#define PMI_ERR_NOMEM                2
+#define PMI_ERR_INVALID_ARG          3
+#define PMI_ERR_INVALID_KEY          4
+#define PMI_ERR_INVALID_KEY_LENGTH   5
+#define PMI_ERR_INVALID_VAL          6
+#define PMI_ERR_INVALID_VAL_LENGTH   7
+#define PMI_ERR_INVALID_LENGTH       8
+#define PMI_ERR_INVALID_NUM_ARGS     9
+#define PMI_ERR_INVALID_ARGS        10
+#define PMI_ERR_INVALID_NUM_PARSED  11
+#define PMI_ERR_INVALID_KEYVALP     12
+#define PMI_ERR_INVALID_SIZE        13
+
+/* PMI Group functions */
+
+/*@
+PMI_Init - initialize the Process Manager Interface
+
+Output Parameter:
+. spawned - spawned flag
+
+Return values:
++ PMI_SUCCESS - initialization completed successfully
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - initialization failed
+
+Notes:
+Initialize PMI for this process group. The value of spawned indicates whether
+this process was created by 'PMI_Spawn_multiple'.  'spawned' will be 'PMI_TRUE' if
+this process group has a parent and 'PMI_FALSE' if it does not.
+
+@*/
+int PMI_Init( int *spawned );
+
+/*@
+PMI_Initialized - check if PMI has been initialized
+
+Output Parameter:
+. initialized - boolean value
+
+Return values:
++ PMI_SUCCESS - initialized successfully set
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - unable to set the variable
+
+Notes:
+On successful output, initialized will either be 'PMI_TRUE' or 'PMI_FALSE'.
+
++ PMI_TRUE - initialize has been called.
+- PMI_FALSE - initialize has not been called or previously failed.
+
+@*/
+int PMI_Initialized( int *initialized );
+
+/*@
+PMI_Finalize - finalize the Process Manager Interface
+
+Return values:
++ PMI_SUCCESS - finalization completed successfully
+- PMI_FAIL - finalization failed
+
+Notes:
+ Finalize PMI for this process group.
+
+@*/
+int PMI_Finalize( void );
+
+/*@
+PMI_Get_size - obtain the size of the process group
+
+Output Parameters:
+. size - pointer to an integer that receives the size of the process group
+
+Return values:
++ PMI_SUCCESS - size successfully obtained
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - unable to return the size
+
+Notes:
+This function returns the size of the process group to which the local process
+belongs.
+
+@*/
+int PMI_Get_size( int *size );
+
+/*@
+PMI_Get_rank - obtain the rank of the local process in the process group
+
+Output Parameters:
+. rank - pointer to an integer that receives the rank in the process group
+
+Return values:
++ PMI_SUCCESS - rank successfully obtained
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - unable to return the rank
+
+Notes:
+This function returns the rank of the local process in its process group.
+
+@*/
+int PMI_Get_rank( int *rank );
+
+/*@
+PMI_Get_universe_size - obtain the universe size
+
+Output Parameters:
+. size - pointer to an integer that receives the size
+
+Return values:
++ PMI_SUCCESS - size successfully obtained
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - unable to return the size
+
+
+@*/
+int PMI_Get_universe_size( int *size );
+
+/*@
+PMI_Get_appnum - obtain the application number
+
+Output parameters:
+. appnum - pointer to an integer that receives the appnum
+
+Return values:
++ PMI_SUCCESS - appnum successfully obtained
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - unable to return the size
+
+
+@*/
+int PMI_Get_appnum( int *appnum );
+
+/*@
+PMI_Publish_name - publish a name 
+
+Input parameters:
+. service_name - string representing the service being published
+. port - string representing the port on which to contact the service
+
+Return values:
++ PMI_SUCCESS - port for service successfully published
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - unable to publish service
+
+
+@*/
+int PMI_Publish_name( const char service_name[], const char port[] );
+
+/*@
+PMI_Unpublish_name - unpublish a name
+
+Input parameters:
+. service_name - string representing the service being unpublished
+
+Return values:
++ PMI_SUCCESS - port for service successfully published
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - unable to unpublish service
+
+
+@*/
+int PMI_Unpublish_name( const char service_name[] );
+
+/*@
+PMI_Lookup_name - lookup a service by name
+
+Input parameters:
+. service_name - string representing the service being published
+
+Output parameters:
+. port - string representing the port on which to contact the service
+
+Return values:
++ PMI_SUCCESS - port for service successfully obtained
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - unable to lookup service
+
+
+@*/
+int PMI_Lookup_name( const char service_name[], char port[] );
+
+/*@
+PMI_Barrier - barrier across the process group
+
+Return values:
++ PMI_SUCCESS - barrier successfully finished
+- PMI_FAIL - barrier failed
+
+Notes:
+This function is a collective call across all processes in the process group
+the local process belongs to.  It will not return until all the processes
+have called 'PMI_Barrier()'.
+
+@*/
+int PMI_Barrier( void );
+
+/*@
+PMI_Abort - abort the process group associated with this process
+
+Input Parameters:
++ exit_code - exit code to be returned by this process
+- error_msg - error message to be printed
+
+Return values:
+. none - this function should not return
+@*/
+int PMI_Abort(int exit_code, const char error_msg[]);
+
+/* PMI Keymap functions */
+/*@
+PMI_KVS_Get_my_name - obtain the name of the keyval space the local process group has access to
+
+Input Parameters:
+. length - length of the kvsname character array
+
+Output Parameters:
+. kvsname - a string that receives the keyval space name
+
+Return values:
++ PMI_SUCCESS - kvsname successfully obtained
+. PMI_ERR_INVALID_ARG - invalid argument
+. PMI_ERR_INVALID_LENGTH - invalid length argument
+- PMI_FAIL - unable to return the kvsname
+
+Notes:
+This function returns the name of the keyval space that this process and all
+other processes in the process group have access to.  The output parameter,
+kvsname, must be at least as long as the value returned by
+'PMI_KVS_Get_name_length_max()'.
+
+@*/
+int PMI_KVS_Get_my_name( char kvsname[], int length );
+
+/*@
+PMI_KVS_Get_name_length_max - obtain the length necessary to store a kvsname
+
+Output Parameter:
+. length - maximum length required to hold a keyval space name
+
+Return values:
++ PMI_SUCCESS - length successfully set
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - unable to set the length
+
+Notes:
+This function returns the string length required to store a keyval space name.
+
+A routine is used rather than setting a maximum value in 'pmi.h' to allow
+different implementations of PMI to be used with the same executable.  These
+different implementations may allow different maximum lengths; by using a 
+routine here, we can interface with a variety of implementations of PMI.
+
+@*/
+int PMI_KVS_Get_name_length_max( int *length );
+
+/*@
+PMI_KVS_Get_key_length_max - obtain the length necessary to store a key
+
+Output Parameter:
+. length - maximum length required to hold a key string.
+
+Return values:
++ PMI_SUCCESS - length successfully set
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - unable to set the length
+
+Notes:
+This function returns the string length required to store a key.
+
+@*/
+int PMI_KVS_Get_key_length_max( int *length );
+
+/*@
+PMI_KVS_Get_value_length_max - obtain the length necessary to store a value
+
+Output Parameter:
+. length - maximum length required to hold a keyval space value
+
+Return values:
++ PMI_SUCCESS - length successfully set
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - unable to set the length
+
+Notes:
+This function returns the string length required to store a value from a
+keyval space.
+
+@*/
+int PMI_KVS_Get_value_length_max( int *length );
+
+/*@
+PMI_KVS_Put - put a key/value pair in a keyval space
+
+Input Parameters:
++ kvsname - keyval space name
+. key - key
+- value - value
+
+Return values:
++ PMI_SUCCESS - keyval pair successfully put in keyval space
+. PMI_ERR_INVALID_KVS - invalid kvsname argument
+. PMI_ERR_INVALID_KEY - invalid key argument
+. PMI_ERR_INVALID_VAL - invalid val argument
+- PMI_FAIL - put failed
+
+Notes:
+This function puts the key/value pair in the specified keyval space.  The
+value is not visible to other processes until 'PMI_KVS_Commit()' is called.  
+The function may complete locally.  After 'PMI_KVS_Commit()' is called, the
+value may be retrieved by calling 'PMI_KVS_Get()'.  All keys put to a keyval
+space must be unique to the keyval space.  You may not put more than once
+with the same key.
+
+@*/
+int PMI_KVS_Put( const char kvsname[], const char key[], const char value[]);
+
+/*@
+PMI_KVS_Commit - commit all previous puts to the keyval space
+
+Input Parameters:
+. kvsname - keyval space name
+
+Return values:
++ PMI_SUCCESS - commit succeeded
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - commit failed
+
+Notes:
+This function commits all previous puts since the last 'PMI_KVS_Commit()' into
+the specified keyval space. It is a process local operation.
+
+@*/
+int PMI_KVS_Commit( const char kvsname[] );
+
+/*@
+PMI_KVS_Get - get a key/value pair from a keyval space
+
+Input Parameters:
++ kvsname - keyval space name
+. key - key
+- length - length of value character array
+
+Output Parameters:
+. value - value
+
+Return values:
++ PMI_SUCCESS - get succeeded
+. PMI_ERR_INVALID_KVS - invalid kvsname argument
+. PMI_ERR_INVALID_KEY - invalid key argument
+. PMI_ERR_INVALID_VAL - invalid val argument
+. PMI_ERR_INVALID_LENGTH - invalid length argument
+- PMI_FAIL - get failed
+
+Notes:
+This function gets the value of the specified key in the keyval space.
+
+@*/
+int PMI_KVS_Get( const char kvsname[], const char key[], char value[], int length);
+
+/* PMI Process Creation functions */
+
+/*S
+PMI_keyval_t - keyval structure used by PMI_Spawn_mulitiple
+
+Fields:
++ key - name of the key
+- val - value of the key
+
+S*/
+typedef struct PMI_keyval_t
+{
+    const char * key;
+    char * val;
+} PMI_keyval_t;
+
+/*@
+PMI_Spawn_multiple - spawn a new set of processes
+
+Input Parameters:
++ count - count of commands
+. cmds - array of command strings
+. argvs - array of argv arrays for each command string
+. maxprocs - array of maximum processes to spawn for each command string
+. info_keyval_sizes - array giving the number of elements in each of the 
+  'info_keyval_vectors'
+. info_keyval_vectors - array of keyval vector arrays
+. preput_keyval_size - Number of elements in 'preput_keyval_vector'
+- preput_keyval_vector - array of keyvals to be pre-put in the spawned keyval space
+
+Output Parameter:
+. errors - array of errors for each command
+
+Return values:
++ PMI_SUCCESS - spawn successful
+. PMI_ERR_INVALID_ARG - invalid argument
+- PMI_FAIL - spawn failed
+
+Notes:
+This function spawns a set of processes into a new process group.  The 'count'
+field refers to the size of the array parameters - 'cmd', 'argvs', 'maxprocs',
+'info_keyval_sizes' and 'info_keyval_vectors'.  The 'preput_keyval_size' refers
+to the size of the 'preput_keyval_vector' array.  The 'preput_keyval_vector'
+contains keyval pairs that will be put in the keyval space of the newly
+created process group before the processes are started.  The 'maxprocs' array
+specifies the desired number of processes to create for each 'cmd' string.  
+The actual number of processes may be less than the numbers specified in
+maxprocs.  The acceptable number of processes spawned may be controlled by
+``soft'' keyvals in the info arrays.  The ``soft'' option is specified by
+mpiexec in the MPI-2 standard.  Environment variables may be passed to the
+spawned processes through PMI implementation specific 'info_keyval' parameters.
+@*/
+int PMI_Spawn_multiple(int count,
+                       const char * cmds[],
+                       const char ** argvs[],
+                       const int maxprocs[],
+                       const int info_keyval_sizesp[],
+                       const PMI_keyval_t * info_keyval_vectors[],
+                       int preput_keyval_size,
+                       const PMI_keyval_t preput_keyval_vector[],
+                       int errors[]);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/src/arch/ofi/simple_pmi/simple_pmi.c b/src/arch/ofi/simple_pmi/simple_pmi.c
new file mode 100644 (file)
index 0000000..adcd996
--- /dev/null
@@ -0,0 +1,1377 @@
+/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
+/*  
+ *  (C) 2001 by Argonne National Laboratory.
+ *      See COPYRIGHT in top-level directory.
+ */
+
+/*********************** PMI implementation ********************************/
+/*
+ * This file implements the client-side of the PMI interface.
+ *
+ * Note that the PMI client code must not print error messages (except 
+ * when an abort is required) because MPI error handling is based on
+ * reporting error codes to which messages are attached.  
+ *
+ * In v2, we should require a PMI client interface to use MPI error codes
+ * to provide better integration with MPICH.  
+ */
+/***************************************************************************/
+
+//YOHANN #include "mpichconf.h"
+
+/* YOHANN */
+#define HAVE_SYS_SOCKET_H 1
+#define USE_PMI_PORT 1
+/* YOHANN (end) */
+
+#define PMI_VERSION    1
+#define PMI_SUBVERSION 1
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef USE_PMI_PORT
+#ifndef MAXHOSTNAME
+#define MAXHOSTNAME 256
+#endif
+#endif
+/* This should be moved to pmiu for shutdown */
+#if defined(HAVE_SYS_SOCKET_H)
+#include <sys/socket.h>
+#endif
+
+#include "mpl.h"            /* Get ATTRIBUTE, some base functions */
+/* mpimem includes the definitions for MPL_malloc and MPL_free */
+//YOHANN #include "mpir_mem.h"
+
+/* Temporary debug definitions */
+/* #define DBG_PRINTF(args) printf args ; fflush(stdout) */
+#define DBG_PRINTF(args)
+
+#include "pmi.h"
+#include "simple_pmiutil.h"
+
+//YOHANN #include "mpi.h"              /* to get MPI_MAX_PORT_NAME */
+
+/* 
+   These are global variable used *ONLY* in this file, and are hence
+   declared static.
+ */
+
+
+static int PMI_fd = -1;
+static int PMI_size = 1;
+static int PMI_rank = 0;
+
+/* Set PMI_initialized to 1 for singleton init but no process manager
+   to help.  Initialized to 2 for normal initialization.  Initialized
+   to values higher than 2 when singleton_init by a process manager.
+   All values higher than 1 invlove a PM in some way.
+*/
+typedef enum { PMI_UNINITIALIZED = 0, 
+               SINGLETON_INIT_BUT_NO_PM = 1,
+              NORMAL_INIT_WITH_PM,
+              SINGLETON_INIT_WITH_PM } PMIState;
+static PMIState PMI_initialized = PMI_UNINITIALIZED;
+
+/* ALL GLOBAL VARIABLES MUST BE INITIALIZED TO AVOID POLLUTING THE 
+   LIBRARY WITH COMMON SYMBOLS */
+static int PMI_kvsname_max = 0;
+static int PMI_keylen_max = 0;
+static int PMI_vallen_max = 0;
+
+static int PMI_debug = 0;
+static int PMI_debug_init = 0;    /* Set this to true to debug the init
+                                    handshakes */
+static int PMI_spawned = 0;
+
+/* Function prototypes for internal routines */
+static int PMII_getmaxes( int *kvsname_max, int *keylen_max, int *vallen_max );
+static int PMII_Set_from_port( int, int );
+static int PMII_Connect_to_pm( char *, int );
+
+static int GetResponse( const char [], const char [], int );
+static int getPMIFD( int * );
+
+#ifdef USE_PMI_PORT
+static int PMII_singinit(void);
+static int PMI_totalview = 0;
+#endif
+static int PMIi_InitIfSingleton(void);
+static int accept_one_connection(int);
+static int cached_singinit_inuse = 0;
+static char cached_singinit_key[PMIU_MAXLINE];
+static char cached_singinit_val[PMIU_MAXLINE];
+static char singinit_kvsname[256];
+
+/******************************** Group functions *************************/
+
+int PMI_Init( int *spawned )
+{
+    char *p;
+    int notset = 1;
+    int rc;
+    
+    PMI_initialized = PMI_UNINITIALIZED;
+    
+    /* FIXME: Why is setvbuf commented out? */
+    /* FIXME: What if the output should be fully buffered (directed to file)?
+       unbuffered (user explicitly set?) */
+    /* setvbuf(stdout,0,_IONBF,0); */
+    setbuf(stdout,NULL);
+    /* PMIU_printf( 1, "PMI_INIT\n" ); */
+
+    /* Get the value of PMI_DEBUG from the environment if possible, since
+       we may have set it to help debug the setup process */
+    p = getenv( "PMI_DEBUG" );
+    if (p) PMI_debug = atoi( p );
+
+    /* Get the fd for PMI commands; if none, we're a singleton */
+    rc = getPMIFD(&notset);
+    if (rc) {
+       return rc;
+    }
+
+    if ( PMI_fd == -1 ) {
+       /* Singleton init: Process not started with mpiexec, 
+          so set size to 1, rank to 0 */
+       PMI_size = 1;
+       PMI_rank = 0;
+       *spawned = 0;
+       
+       PMI_initialized = SINGLETON_INIT_BUT_NO_PM;
+       /* 256 is picked as the minimum allowed length by the PMI servers */
+       PMI_kvsname_max = 256;
+       PMI_keylen_max  = 256;
+       PMI_vallen_max  = 256;
+       
+       return PMI_SUCCESS;
+    }
+
+    /* If size, rank, and debug are not set from a communication port,
+       use the environment */
+    if (notset) {
+       if ( ( p = getenv( "PMI_SIZE" ) ) )
+           PMI_size = atoi( p );
+       else 
+           PMI_size = 1;
+       
+       if ( ( p = getenv( "PMI_RANK" ) ) ) {
+           PMI_rank = atoi( p );
+           /* Let the util routine know the rank of this process for 
+              any messages (usually debugging or error) */
+           PMIU_Set_rank( PMI_rank );
+       }
+       else 
+           PMI_rank = 0;
+       
+       if ( ( p = getenv( "PMI_DEBUG" ) ) )
+           PMI_debug = atoi( p );
+       else 
+           PMI_debug = 0;
+
+       /* Leave unchanged otherwise, which indicates that no value
+          was set */
+    }
+
+/* FIXME: Why does this depend on their being a port??? */
+/* FIXME: What is this for? */
+#ifdef USE_PMI_PORT
+    if ( ( p = getenv( "PMI_TOTALVIEW" ) ) )
+       PMI_totalview = atoi( p );
+    if ( PMI_totalview ) {
+       char buf[PMIU_MAXLINE], cmd[PMIU_MAXLINE];
+       /* FIXME: This should use a cmd/response rather than a expecting the
+          server to set a value in this and only this case */
+       /* FIXME: And it most ceratainly should not happen *before* the
+          initialization handshake */
+       PMIU_readline( PMI_fd, buf, PMIU_MAXLINE );
+       PMIU_parse_keyvals( buf );
+       PMIU_getval( "cmd", cmd, PMIU_MAXLINE );
+       if ( strncmp( cmd, "tv_ready", PMIU_MAXLINE ) != 0 ) {
+           PMIU_printf( 1, "expecting cmd=tv_ready, got %s\n", buf );
+           return PMI_FAIL;
+       }
+    }
+#endif
+
+    PMII_getmaxes( &PMI_kvsname_max, &PMI_keylen_max, &PMI_vallen_max );
+
+    /* FIXME: This is something that the PM should tell the process,
+       rather than deliver it through the environment */
+    if ( ( p = getenv( "PMI_SPAWNED" ) ) )
+       PMI_spawned = atoi( p );
+    else
+       PMI_spawned = 0;
+    if (PMI_spawned)
+       *spawned = 1;
+    else
+       *spawned = 0;
+
+    if ( ! PMI_initialized )
+       PMI_initialized = NORMAL_INIT_WITH_PM;
+
+    return PMI_SUCCESS;
+}
+
+int PMI_Initialized( int *initialized )
+{
+    /* Turn this into a logical value (1 or 0) .  This allows us
+       to use PMI_initialized to distinguish between initialized with
+       an PMI service (e.g., via mpiexec) and the singleton init, 
+       which has no PMI service */
+    *initialized = (PMI_initialized != 0);
+    return PMI_SUCCESS;
+}
+
+int PMI_Get_size( int *size )
+{
+    if ( PMI_initialized )
+       *size = PMI_size;
+    else
+       *size = 1;
+    return PMI_SUCCESS;
+}
+
+int PMI_Get_rank( int *rank )
+{
+    if ( PMI_initialized )
+       *rank = PMI_rank;
+    else
+       *rank = 0;
+    return PMI_SUCCESS;
+}
+
+/* 
+ * Get_universe_size is one of the routines that needs to communicate
+ * with the process manager.  If we started as a singleton init, then
+ * we first need to connect to the process manager and acquire the 
+ * needed information.
+ */
+int PMI_Get_universe_size( int *size)
+{
+    int  err;
+    char size_c[PMIU_MAXLINE];
+
+    /* Connect to the PM if we haven't already */
+    if (PMIi_InitIfSingleton() != 0) return PMI_FAIL;
+
+    if ( PMI_initialized > SINGLETON_INIT_BUT_NO_PM)  {
+       err = GetResponse( "cmd=get_universe_size\n", "universe_size", 0 );
+       if (err == PMI_SUCCESS) {
+           PMIU_getval( "size", size_c, PMIU_MAXLINE );
+           *size = atoi(size_c);
+           return PMI_SUCCESS;
+       }
+       else return err;
+    }
+    else
+       *size = 1;
+    return PMI_SUCCESS;
+}
+
+int PMI_Get_appnum( int *appnum )
+{
+    int  err;
+    char appnum_c[PMIU_MAXLINE];
+
+    if ( PMI_initialized > SINGLETON_INIT_BUT_NO_PM) {
+       err = GetResponse( "cmd=get_appnum\n", "appnum", 0 );
+       if (err == PMI_SUCCESS) {
+           PMIU_getval( "appnum", appnum_c, PMIU_MAXLINE );
+           *appnum = atoi(appnum_c);
+           return PMI_SUCCESS;
+       }
+       else return err;
+       
+    }
+    else
+       *appnum = -1;
+
+    return PMI_SUCCESS;
+}
+
+int PMI_Barrier( void )
+{
+    int err = PMI_SUCCESS;
+
+    if ( PMI_initialized > SINGLETON_INIT_BUT_NO_PM) {
+       err = GetResponse( "cmd=barrier_in\n", "barrier_out", 0 );
+    }
+
+    return err;
+}
+
+/* Inform the process manager that we're in finalize */
+int PMI_Finalize( void )
+{
+    int err = PMI_SUCCESS;
+
+    if ( PMI_initialized > SINGLETON_INIT_BUT_NO_PM) {
+       err = GetResponse( "cmd=finalize\n", "finalize_ack", 0 );
+       shutdown( PMI_fd, SHUT_RDWR );
+       close( PMI_fd );
+    }
+
+    return err;
+}
+
+int PMI_Abort(int exit_code, const char error_msg[])
+{
+    char buf[PMIU_MAXLINE];
+
+    /* include exit_code in the abort command */
+    MPL_snprintf( buf, PMIU_MAXLINE, "cmd=abort exitcode=%d\n", exit_code);
+
+    PMIU_printf(PMI_debug, "aborting job:\n%s\n", error_msg);
+    GetResponse( buf, "", 0 );
+
+    /* the above command should not return */
+    return PMI_FAIL;
+}
+
+/************************************* Keymap functions **********************/
+
+/*FIXME: need to return an error if the value of the kvs name returned is 
+  truncated because it is larger than length */
+/* FIXME: My name should be cached rather than re-acquired, as it is
+   unchanging (after singleton init) */
+int PMI_KVS_Get_my_name( char kvsname[], int length )
+{
+    int err;
+
+    if (PMI_initialized == SINGLETON_INIT_BUT_NO_PM) {
+       /* Return a dummy name */
+       /* FIXME: We need to support a distinct kvsname for each 
+          process group */
+       MPL_snprintf( kvsname, length, "singinit_kvs_%d_0", (int)getpid() );
+       return PMI_SUCCESS;
+    }
+    err = GetResponse( "cmd=get_my_kvsname\n", "my_kvsname", 0 );
+    if (err == PMI_SUCCESS) {
+       PMIU_getval( "kvsname", kvsname, length );
+    }
+    return err;
+}
+
+int PMI_KVS_Get_name_length_max( int *maxlen )
+{
+    if (maxlen == NULL)
+       return PMI_ERR_INVALID_ARG;
+    *maxlen = PMI_kvsname_max;
+    return PMI_SUCCESS;
+}
+
+int PMI_KVS_Get_key_length_max( int *maxlen )
+{
+    if (maxlen == NULL)
+       return PMI_ERR_INVALID_ARG;
+    *maxlen = PMI_keylen_max;
+    return PMI_SUCCESS;
+}
+
+int PMI_KVS_Get_value_length_max( int *maxlen )
+{
+    if (maxlen == NULL)
+       return PMI_ERR_INVALID_ARG;
+    *maxlen = PMI_vallen_max;
+    return PMI_SUCCESS;
+}
+
+int PMI_KVS_Put( const char kvsname[], const char key[], const char value[] )
+{
+    char buf[PMIU_MAXLINE];
+    int  err = PMI_SUCCESS;
+    int  rc;
+
+    /* This is a special hack to support singleton initialization */
+    if (PMI_initialized == SINGLETON_INIT_BUT_NO_PM) {
+        if (cached_singinit_inuse)
+            return PMI_FAIL;
+       rc = MPL_strncpy(cached_singinit_key,key,PMI_keylen_max);
+       if (rc != 0) return PMI_FAIL;
+       rc = MPL_strncpy(cached_singinit_val,value,PMI_vallen_max);
+       if (rc != 0) return PMI_FAIL;
+        cached_singinit_inuse = 1;
+       return PMI_SUCCESS;
+    }
+    
+    rc = MPL_snprintf( buf, PMIU_MAXLINE, 
+                       "cmd=put kvsname=%s key=%s value=%s\n",
+                       kvsname, key, value);
+    if (rc < 0) return PMI_FAIL;
+    err = GetResponse( buf, "put_result", 1 );
+    return err;
+}
+
+int PMI_KVS_Commit( const char kvsname[] ATTRIBUTE((unused)))
+{
+    /* no-op in this implementation */
+    return PMI_SUCCESS;
+}
+
+/*FIXME: need to return an error if the value returned is truncated
+  because it is larger than length */
+int PMI_KVS_Get( const char kvsname[], const char key[], char value[], 
+                int length)
+{
+    char buf[PMIU_MAXLINE];
+    int err = PMI_SUCCESS;
+    int  rc;
+
+    /* Connect to the PM if we haven't already.  This is needed in case
+       we're doing an MPI_Comm_join or MPI_Comm_connect/accept from
+       the singleton init case.  This test is here because, in the way in 
+       which MPICH uses PMI, this is where the test needs to be. */
+    if (PMIi_InitIfSingleton() != 0) return PMI_FAIL;
+
+    rc = MPL_snprintf( buf, PMIU_MAXLINE, "cmd=get kvsname=%s key=%s\n", 
+                       kvsname, key );
+    if (rc < 0) return PMI_FAIL;
+
+    err = GetResponse( buf, "get_result", 0 );
+    if (err == PMI_SUCCESS) {
+       PMIU_getval( "rc", buf, PMIU_MAXLINE );
+       rc = atoi( buf );
+       if ( rc == 0 ) {
+           PMIU_getval( "value", value, length );
+           return PMI_SUCCESS;
+       }
+       else {
+           return PMI_FAIL;
+       }
+    }
+
+    return err;
+}
+
+/*************************** Name Publishing functions **********************/
+
+int PMI_Publish_name( const char service_name[], const char port[] )
+{
+    char buf[PMIU_MAXLINE], cmd[PMIU_MAXLINE];
+    int err;
+
+    if ( PMI_initialized > SINGLETON_INIT_BUT_NO_PM) {
+        MPL_snprintf( cmd, PMIU_MAXLINE, 
+                      "cmd=publish_name service=%s port=%s\n",
+                      service_name, port );
+       err = GetResponse( cmd, "publish_result", 0 );
+       if (err == PMI_SUCCESS) {
+           PMIU_getval( "rc", buf, PMIU_MAXLINE );
+           if ( strcmp(buf,"0") != 0 ) {
+                PMIU_getval( "msg", buf, PMIU_MAXLINE );
+                PMIU_printf( PMI_debug, "publish failed; reason = %s\n", buf );
+
+               return PMI_FAIL;
+           }
+       }
+    }
+    else
+    {
+       PMIU_printf( 1, "PMI_Publish_name called before init\n" );
+       return PMI_FAIL;
+    }
+
+    return PMI_SUCCESS;
+}
+
+int PMI_Unpublish_name( const char service_name[] )
+{
+    char buf[PMIU_MAXLINE], cmd[PMIU_MAXLINE];
+    int err = PMI_SUCCESS;
+
+    if ( PMI_initialized > SINGLETON_INIT_BUT_NO_PM) {
+        MPL_snprintf( cmd, PMIU_MAXLINE, "cmd=unpublish_name service=%s\n", 
+                      service_name );
+       err = GetResponse( cmd, "unpublish_result", 0 );
+       if (err == PMI_SUCCESS) {
+           PMIU_getval( "rc", buf, PMIU_MAXLINE );
+           if ( strcmp(buf,"0") != 0 ) {
+                PMIU_getval( "msg", buf, PMIU_MAXLINE );
+                PMIU_printf( PMI_debug, "unpublish failed; reason = %s\n", buf );
+
+                return PMI_FAIL;
+           }
+       }
+    }
+    else
+    {
+       PMIU_printf( 1, "PMI_Unpublish_name called before init\n" );
+       return PMI_FAIL;
+    }
+
+    return PMI_SUCCESS;
+}
+
+int PMI_Lookup_name( const char service_name[], char port[] )
+{
+    char buf[PMIU_MAXLINE], cmd[PMIU_MAXLINE];
+    int err;
+
+    if ( PMI_initialized > SINGLETON_INIT_BUT_NO_PM) {
+        MPL_snprintf( cmd, PMIU_MAXLINE, "cmd=lookup_name service=%s\n", 
+                      service_name );
+       err = GetResponse( cmd, "lookup_result", 0 );
+       if (err == PMI_SUCCESS) {
+           PMIU_getval( "rc", buf, PMIU_MAXLINE );
+           if ( strcmp(buf,"0") != 0 ) {
+                PMIU_getval( "msg", buf, PMIU_MAXLINE );
+                PMIU_printf( PMI_debug, "lookup failed; reason = %s\n", buf );
+
+               return PMI_FAIL;
+           }
+           PMIU_getval( "port", port, MPI_MAX_PORT_NAME );
+       }
+    }
+    else
+    {
+       PMIU_printf( 1, "PMI_Lookup_name called before init\n" );
+       return PMI_FAIL;
+    }
+
+    return PMI_SUCCESS;
+}
+
+
+/************************** Process Creation functions **********************/
+
+int PMI_Spawn_multiple(int count,
+                       const char * cmds[],
+                       const char ** argvs[],
+                       const int maxprocs[],
+                       const int info_keyval_sizes[],
+                       const PMI_keyval_t * info_keyval_vectors[],
+                       int preput_keyval_size,
+                       const PMI_keyval_t preput_keyval_vector[],
+                       int errors[])
+{
+    int  i,rc,argcnt,spawncnt,total_num_processes,num_errcodes_found;
+    char buf[PMIU_MAXLINE], tempbuf[PMIU_MAXLINE], cmd[PMIU_MAXLINE];
+    char *lead, *lag;
+
+    /* Connect to the PM if we haven't already */
+    if (PMIi_InitIfSingleton() != 0) return PMI_FAIL;
+
+    total_num_processes = 0;
+
+    for (spawncnt=0; spawncnt < count; spawncnt++)
+    {
+        total_num_processes += maxprocs[spawncnt];
+
+        rc = MPL_snprintf(buf, PMIU_MAXLINE, 
+                          "mcmd=spawn\nnprocs=%d\nexecname=%s\n",
+                          maxprocs[spawncnt], cmds[spawncnt] );
+       if (rc < 0) {
+           return PMI_FAIL;
+       }
+
+       rc = MPL_snprintf(tempbuf, PMIU_MAXLINE,
+                          "totspawns=%d\nspawnssofar=%d\n",
+                          count, spawncnt+1);
+
+       if (rc < 0) { 
+           return PMI_FAIL;
+       }
+       rc = MPL_strnapp(buf,tempbuf,PMIU_MAXLINE);
+       if (rc != 0) {
+           return PMI_FAIL;
+       }
+
+        argcnt = 0;
+        if ((argvs != NULL) && (argvs[spawncnt] != NULL)) {
+            for (i=0; argvs[spawncnt][i] != NULL; i++)
+            {
+               /* FIXME (protocol design flaw): command line arguments
+                  may contain both = and <space> (and even tab!).
+               */
+               /* Note that part of this fixme was really a design error -
+                  because this uses the mcmd form, the data can be
+                  sent in multiple writelines.  This code now takes 
+                  advantage of that.  Note also that a correct parser 
+                  of the commands will permit any character other than a 
+                  new line in the argument, since the form is 
+                  argn=<any nonnewline><newline> */
+                rc = MPL_snprintf(tempbuf,PMIU_MAXLINE,"arg%d=%s\n",
+                                  i+1,argvs[spawncnt][i]);
+               if (rc < 0) {
+                   return PMI_FAIL;
+               }
+                rc = MPL_strnapp(buf,tempbuf,PMIU_MAXLINE);
+               if (rc != 0) {
+                   return PMI_FAIL;
+               }
+                argcnt++;
+               rc = PMIU_writeline( PMI_fd, buf );
+                if (rc)
+                    return PMI_FAIL;
+               buf[0] = 0;
+
+            }
+        }
+        rc = MPL_snprintf(tempbuf,PMIU_MAXLINE,"argcnt=%d\n",argcnt);
+       if (rc < 0) {
+           return PMI_FAIL;
+       }
+        rc = MPL_strnapp(buf,tempbuf,PMIU_MAXLINE);
+       if (rc != 0) {
+           return PMI_FAIL;
+       }
+    
+        rc = MPL_snprintf(tempbuf,PMIU_MAXLINE,"preput_num=%d\n", 
+                          preput_keyval_size);
+       if (rc < 0) {
+           return PMI_FAIL;
+       }
+
+        rc = MPL_strnapp(buf,tempbuf,PMIU_MAXLINE);
+       if (rc != 0) {
+           return PMI_FAIL;
+       }
+        for (i=0; i < preput_keyval_size; i++) {
+           rc = MPL_snprintf(tempbuf,PMIU_MAXLINE,"preput_key_%d=%s\n",
+                              i,preput_keyval_vector[i].key);
+           if (rc < 0) {
+               return PMI_FAIL;
+           }
+           rc = MPL_strnapp(buf,tempbuf,PMIU_MAXLINE); 
+           if (rc != 0) {
+               return PMI_FAIL;
+           }
+           rc = MPL_snprintf(tempbuf,PMIU_MAXLINE,"preput_val_%d=%s\n",
+                              i,preput_keyval_vector[i].val);
+           if (rc < 0) {
+               return PMI_FAIL;
+           }
+           rc = MPL_strnapp(buf,tempbuf,PMIU_MAXLINE); 
+           if (rc != 0) {
+               return PMI_FAIL;
+           }
+        } 
+        rc = MPL_snprintf(tempbuf,PMIU_MAXLINE,"info_num=%d\n", 
+                          info_keyval_sizes[spawncnt]);
+       if (rc < 0) {
+           return PMI_FAIL;
+       }
+        rc = MPL_strnapp(buf,tempbuf,PMIU_MAXLINE);
+       if (rc != 0) {
+           return PMI_FAIL;
+       }
+       for (i=0; i < info_keyval_sizes[spawncnt]; i++)
+       {
+           rc = MPL_snprintf(tempbuf,PMIU_MAXLINE,"info_key_%d=%s\n",
+                              i,info_keyval_vectors[spawncnt][i].key);
+           if (rc < 0) {
+               return PMI_FAIL;
+           }
+           rc = MPL_strnapp(buf,tempbuf,PMIU_MAXLINE); 
+           if (rc != 0) {
+               return PMI_FAIL;
+           }
+           rc = MPL_snprintf(tempbuf,PMIU_MAXLINE,"info_val_%d=%s\n",
+                              i,info_keyval_vectors[spawncnt][i].val);
+           if (rc < 0) {
+               return PMI_FAIL;
+           }
+           rc = MPL_strnapp(buf,tempbuf,PMIU_MAXLINE); 
+           if (rc != 0) {
+               return PMI_FAIL;
+           }
+       }
+
+        rc = MPL_strnapp(buf, "endcmd\n", PMIU_MAXLINE);
+       if (rc != 0) {
+           return PMI_FAIL;
+       }
+        rc = PMIU_writeline( PMI_fd, buf );
+       if (rc) {
+           return PMI_FAIL;
+       }
+    }
+
+    PMIU_readline( PMI_fd, buf, PMIU_MAXLINE );
+    PMIU_parse_keyvals( buf ); 
+    PMIU_getval( "cmd", cmd, PMIU_MAXLINE );
+    if ( strncmp( cmd, "spawn_result", PMIU_MAXLINE ) != 0 ) {
+       PMIU_printf( 1, "got unexpected response to spawn :%s:\n", buf );
+       return PMI_FAIL;
+    }
+    else {
+       PMIU_getval( "rc", buf, PMIU_MAXLINE );
+       rc = atoi( buf );
+       if ( rc != 0 ) {
+           /****
+           PMIU_getval( "status", tempbuf, PMIU_MAXLINE );
+           PMIU_printf( 1, "pmi_spawn_mult failed; status: %s\n",tempbuf);
+           ****/
+           return PMI_FAIL;
+       }
+    }
+    
+    PMIU_Assert(errors != NULL);
+    if (PMIU_getval( "errcodes", tempbuf, PMIU_MAXLINE )) {
+        num_errcodes_found = 0;
+        lag = &tempbuf[0];
+        do {
+            lead = strchr(lag, ',');
+            if (lead) *lead = '\0';
+            errors[num_errcodes_found++] = atoi(lag);
+            lag = lead + 1; /* move past the null char */
+            PMIU_Assert(num_errcodes_found <= total_num_processes);
+        } while (lead != NULL);
+        PMIU_Assert(num_errcodes_found == total_num_processes);
+    }
+    else {
+        /* gforker doesn't return errcodes, so we'll just pretend that means
+           that it was going to send all `0's. */
+        for (i = 0; i < total_num_processes; ++i) {
+            errors[i] = 0;
+        }
+    }
+
+    return PMI_SUCCESS;
+}
+
+/***************** Internal routines not part of PMI interface ***************/
+
+/* to get all maxes in one message */
+/* FIXME: This mixes init with get maxes */
+static int PMII_getmaxes( int *kvsname_max, int *keylen_max, int *vallen_max )
+{
+    char buf[PMIU_MAXLINE], cmd[PMIU_MAXLINE], errmsg[PMIU_MAXLINE];
+    int err, rc;
+
+    rc = MPL_snprintf( buf, PMIU_MAXLINE, 
+                       "cmd=init pmi_version=%d pmi_subversion=%d\n",
+                       PMI_VERSION, PMI_SUBVERSION );
+    if (rc < 0) {
+       return PMI_FAIL;
+    }
+
+    rc = PMIU_writeline( PMI_fd, buf );
+    if (rc != 0) {
+       PMIU_printf( 1, "Unable to write to PMI_fd\n" );
+       return PMI_FAIL;
+    }
+    buf[0] = 0;   /* Ensure buffer is empty if read fails */
+    err = PMIU_readline( PMI_fd, buf, PMIU_MAXLINE );
+    if (err < 0) {
+       PMIU_printf( 1, "Error reading initack on %d\n", PMI_fd );
+       perror( "Error on readline:" );
+       PMI_Abort(-1, "Above error when reading after init" );
+    }
+    PMIU_parse_keyvals( buf );
+    cmd[0] = 0;
+    PMIU_getval( "cmd", cmd, PMIU_MAXLINE );
+    if ( strncmp( cmd, "response_to_init", PMIU_MAXLINE ) != 0 ) {
+       MPL_snprintf(errmsg, PMIU_MAXLINE, 
+                     "got unexpected response to init :%s: (full line = %s)",
+                     cmd, buf  );
+       PMI_Abort( -1, errmsg );
+    }
+    else {
+       char buf1[PMIU_MAXLINE];
+        PMIU_getval( "rc", buf, PMIU_MAXLINE );
+        if ( strncmp( buf, "0", PMIU_MAXLINE ) != 0 ) {
+            PMIU_getval( "pmi_version", buf, PMIU_MAXLINE );
+            PMIU_getval( "pmi_subversion", buf1, PMIU_MAXLINE );
+           MPL_snprintf(errmsg, PMIU_MAXLINE, 
+                         "pmi_version mismatch; client=%d.%d mgr=%s.%s",
+                         PMI_VERSION, PMI_SUBVERSION, buf, buf1 );
+           PMI_Abort( -1, errmsg );
+        }
+    }
+    err = GetResponse( "cmd=get_maxes\n", "maxes", 0 );
+    if (err == PMI_SUCCESS) {
+       PMIU_getval( "kvsname_max", buf, PMIU_MAXLINE );
+       *kvsname_max = atoi( buf );
+       PMIU_getval( "keylen_max", buf, PMIU_MAXLINE );
+       *keylen_max = atoi( buf );
+       PMIU_getval( "vallen_max", buf, PMIU_MAXLINE );
+       *vallen_max = atoi( buf );
+    }
+    return err;
+}
+
+/* ----------------------------------------------------------------------- */
+/* 
+ * This function is used to request information from the server and check
+ * that the response uses the expected command name.  On a successful
+ * return from this routine, additional PMIU_getval calls may be used
+ * to access information about the returned value.
+ *
+ * If checkRc is true, this routine also checks that the rc value returned
+ * was 0.  If not, it uses the "msg" value to report on the reason for
+ * the failure.
+ */
+static int GetResponse( const char request[], const char expectedCmd[],
+                       int checkRc )
+{
+    int err, n;
+    char *p;
+    char recvbuf[PMIU_MAXLINE];
+    char cmdName[PMIU_MAXLINE];
+
+    /* FIXME: This is an example of an incorrect fix - writeline can change
+       the second argument in some cases, and that will break the const'ness
+       of request.  Instead, writeline should take a const item and return
+       an error in the case in which it currently truncates the data. */
+    err = PMIU_writeline( PMI_fd, (char *)request );
+    if (err) {
+       return err;
+    }
+    n = PMIU_readline( PMI_fd, recvbuf, sizeof(recvbuf) );
+    if (n <= 0) {
+       PMIU_printf( 1, "readline failed\n" );
+       return PMI_FAIL;
+    }
+    err = PMIU_parse_keyvals( recvbuf );
+    if (err) {
+       PMIU_printf( 1, "parse_kevals failed %d\n", err );
+       return err;
+    }
+    p = PMIU_getval( "cmd", cmdName, sizeof(cmdName) );
+    if (!p) {
+       PMIU_printf( 1, "getval cmd failed\n" );
+       return PMI_FAIL;
+    }
+    if (strcmp( expectedCmd, cmdName ) != 0) {
+       PMIU_printf( 1, "expecting cmd=%s, got %s\n", expectedCmd, cmdName );
+       return PMI_FAIL;
+    }
+    if (checkRc) {
+       p = PMIU_getval( "rc", cmdName, PMIU_MAXLINE );
+       if ( p && strcmp(cmdName,"0") != 0 ) {
+           PMIU_getval( "msg", cmdName, PMIU_MAXLINE );
+           PMIU_printf( 1, "Command %s failed, reason='%s'\n", 
+                        request, cmdName );
+           return PMI_FAIL;
+       }
+    }
+
+    return err;
+}
+/* ----------------------------------------------------------------------- */
+
+
+#ifdef USE_PMI_PORT
+/*
+ * This code allows a program to contact a host/port for the PMI socket.
+ */
+#include <errno.h>
+#if defined(HAVE_SYS_TYPES_H)
+#include <sys/types.h>
+#endif
+#include <sys/param.h>
+#include <sys/socket.h>
+
+/* sockaddr_in (Internet) */
+#include <netinet/in.h>
+/* TCP_NODELAY */
+#include <netinet/tcp.h>
+
+/* sockaddr_un (Unix) */
+#include <sys/un.h>
+
+/* defs of gethostbyname */
+#include <netdb.h>
+
+/* fcntl, F_GET/SETFL */
+#include <fcntl.h>
+
+/* This is really IP!? */
+#ifndef TCP
+#define TCP 0
+#endif
+
+/* stub for connecting to a specified host/port instead of using a 
+   specified fd inherited from a parent process */
+static int PMII_Connect_to_pm( char *hostname, int portnum )
+{
+    struct hostent     *hp;
+    struct sockaddr_in sa;
+    int                fd;
+    int                optval = 1;
+    int                q_wait = 1;
+    
+    hp = gethostbyname( hostname );
+    if (!hp) {
+       PMIU_printf( 1, "Unable to get host entry for %s\n", hostname );
+       return PMI_FAIL;
+    }
+    
+    memset( (void *)&sa, 0, sizeof(sa) );
+    /* POSIX might define h_addr_list only and node define h_addr */
+#ifdef HAVE_H_ADDR_LIST
+    memcpy( (void *)&sa.sin_addr, (void *)hp->h_addr_list[0], hp->h_length);
+#else
+    memcpy( (void *)&sa.sin_addr, (void *)hp->h_addr, hp->h_length);
+#endif
+    sa.sin_family = hp->h_addrtype;
+    sa.sin_port   = htons( (unsigned short) portnum );
+    
+    fd = socket( AF_INET, SOCK_STREAM, TCP );
+    if (fd < 0) {
+       PMIU_printf( 1, "Unable to get AF_INET socket\n" );
+       return PMI_FAIL;
+    }
+    
+    if (setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, 
+                   (char *)&optval, sizeof(optval) )) {
+       perror( "Error calling setsockopt:" );
+    }
+
+    /* We wait here for the connection to succeed */
+    if (connect( fd, (struct sockaddr *)&sa, sizeof(sa) ) < 0) {
+       switch (errno) {
+       case ECONNREFUSED:
+           PMIU_printf( 1, "connect failed with connection refused\n" );
+           /* (close socket, get new socket, try again) */
+           if (q_wait)
+               close(fd);
+           return PMI_FAIL;
+           
+       case EINPROGRESS: /*  (nonblocking) - select for writing. */
+           break;
+           
+       case EISCONN: /*  (already connected) */
+           break;
+           
+       case ETIMEDOUT: /* timed out */
+           PMIU_printf( 1, "connect failed with timeout\n" );
+           return PMI_FAIL;
+
+       default:
+           PMIU_printf( 1, "connect failed with errno %d\n", errno );
+           return PMI_FAIL;
+       }
+    }
+
+    return fd;
+}
+
+static int PMII_Set_from_port( int fd, int id )
+{
+    char buf[PMIU_MAXLINE], cmd[PMIU_MAXLINE];
+    int err, rc;
+
+    /* We start by sending a startup message to the server */
+
+    if (PMI_debug) {
+       PMIU_printf( 1, "Writing initack to destination fd %d\n", fd );
+    }
+    /* Handshake and initialize from a port */
+
+    rc = MPL_snprintf( buf, PMIU_MAXLINE, "cmd=initack pmiid=%d\n", id );
+    if (rc < 0) {
+       return PMI_FAIL;
+    }
+    PMIU_printf( PMI_debug, "writing on fd %d line :%s:\n", fd, buf );
+    err = PMIU_writeline( fd, buf );
+    if (err) {
+       PMIU_printf( 1, "Error in writeline initack\n" );
+       return PMI_FAIL;
+    }
+
+    /* cmd=initack */
+    buf[0] = 0;
+    PMIU_printf( PMI_debug, "reading initack\n" );
+    err = PMIU_readline( fd, buf, PMIU_MAXLINE );
+    if (err < 0) {
+       PMIU_printf( 1, "Error reading initack on %d\n", fd );
+       perror( "Error on readline:" );
+       return PMI_FAIL;
+    }
+    PMIU_parse_keyvals( buf );
+    PMIU_getval( "cmd", cmd, PMIU_MAXLINE );
+    if ( strcmp( cmd, "initack" ) ) {
+       PMIU_printf( 1, "got unexpected input %s\n", buf );
+       return PMI_FAIL;
+    }
+    
+    /* Read, in order, size, rank, and debug.  Eventually, we'll want 
+       the handshake to include a version number */
+
+    /* size */
+    PMIU_printf( PMI_debug, "reading size\n" );
+    err = PMIU_readline( fd, buf, PMIU_MAXLINE );
+    if (err < 0) {
+       PMIU_printf( 1, "Error reading size on %d\n", fd );
+       perror( "Error on readline:" );
+       return PMI_FAIL;
+    }
+    PMIU_parse_keyvals( buf );
+    PMIU_getval( "cmd", cmd, PMIU_MAXLINE );
+    if ( strcmp(cmd,"set")) {
+       PMIU_printf( 1, "got unexpected command %s in %s\n", cmd, buf );
+       return PMI_FAIL;
+    }
+    /* cmd=set size=n */
+    PMIU_getval( "size", cmd, PMIU_MAXLINE );
+    PMI_size = atoi(cmd);
+
+    /* rank */
+    PMIU_printf( PMI_debug, "reading rank\n" );
+    err = PMIU_readline( fd, buf, PMIU_MAXLINE );
+    if (err < 0) {
+       PMIU_printf( 1, "Error reading rank on %d\n", fd );
+       perror( "Error on readline:" );
+       return PMI_FAIL;
+    }
+    PMIU_parse_keyvals( buf );
+    PMIU_getval( "cmd", cmd, PMIU_MAXLINE );
+    if ( strcmp(cmd,"set")) {
+       PMIU_printf( 1, "got unexpected command %s in %s\n", cmd, buf );
+       return PMI_FAIL;
+    }
+    /* cmd=set rank=n */
+    PMIU_getval( "rank", cmd, PMIU_MAXLINE );
+    PMI_rank = atoi(cmd);
+    PMIU_Set_rank( PMI_rank );
+
+    /* debug flag */
+    err = PMIU_readline( fd, buf, PMIU_MAXLINE );
+    if (err < 0) {
+       PMIU_printf( 1, "Error reading debug on %d\n", fd );
+       return PMI_FAIL;
+    }
+    PMIU_parse_keyvals( buf );
+    PMIU_getval( "cmd", cmd, PMIU_MAXLINE );
+    if ( strcmp(cmd,"set")) {
+       PMIU_printf( 1, "got unexpected command %s in %s\n", cmd, buf );
+       return PMI_FAIL;
+    }
+    /* cmd=set debug=n */
+    PMIU_getval( "debug", cmd, PMIU_MAXLINE );
+    PMI_debug = atoi(cmd);
+
+    if (PMI_debug) {
+       DBG_PRINTF( ("end of handshake, rank = %d, size = %d\n", 
+                   PMI_rank, PMI_size )); 
+       DBG_PRINTF( ("Completed init\n" ) );
+    }
+
+    return PMI_SUCCESS;
+}
+
+/* ------------------------------------------------------------------------- */
+/* 
+ * Singleton Init.
+ * 
+ * MPI-2 allows processes to become MPI processes and then make MPI calls,
+ * such as MPI_Comm_spawn, that require a process manager (this is different 
+ * than the much simpler case of allowing MPI programs to run with an 
+ * MPI_COMM_WORLD of size 1 without an mpiexec or process manager).
+ *
+ * The process starts when either the client or the process manager contacts
+ * the other.  If the client starts, it sends a singinit command and
+ * waits for the server to respond with its own singinit command.
+ * If the server start, it send a singinit command and waits for the 
+ * client to respond with its own singinit command
+ *
+ * client sends singinit with these required values
+ *   pmi_version=<value of PMI_VERSION>
+ *   pmi_subversion=<value of PMI_SUBVERSION>
+ *
+ * and these optional values
+ *   stdio=[yes|no]
+ *   authtype=[none|shared|<other-to-be-defined>]
+ *   authstring=<string>
+ *
+ * server sends singinit with the same required and optional values as
+ * above.
+ *
+ * At this point, the protocol is now the same in both cases, and has the
+ * following components:
+ *
+ * server sends singinit_info with these required fields
+ *   versionok=[yes|no]
+ *   stdio=[yes|no]
+ *   kvsname=<string>
+ *
+ * The client then issues the init command (see PMII_getmaxes)
+ *
+ * cmd=init pmi_version=<val> pmi_subversion=<val>
+ *
+ * and expects to receive a 
+ *
+ * cmd=response_to_init rc=0 pmi_version=<val> pmi_subversion=<val> 
+ *
+ * (This is the usual init sequence).
+ *
+ */
+/* ------------------------------------------------------------------------- */
+/* This is a special routine used to re-initialize PMI when it is in 
+   the singleton init case.  That is, the executable was started without 
+   mpiexec, and PMI_Init returned as if there was only one process.
+
+   Note that PMI routines should not call PMII_singinit; they should
+   call PMIi_InitIfSingleton(), which both connects to the process mangager
+   and sets up the initial KVS connection entry.
+*/
+
+static int PMII_singinit(void)
+{
+    int pid, rc;
+    int singinit_listen_sock, stdin_sock, stdout_sock, stderr_sock;
+    const char *newargv[8];
+    char charpid[8], port_c[8];
+    struct sockaddr_in sin;
+    socklen_t len;
+
+    /* Create a socket on which to allow an mpiexec to connect back to
+       us */
+    sin.sin_family     = AF_INET;
+    sin.sin_addr.s_addr        = INADDR_ANY;
+    sin.sin_port       = htons(0);    /* anonymous port */
+    singinit_listen_sock = socket(AF_INET, SOCK_STREAM, 0);
+    rc = bind(singinit_listen_sock, (struct sockaddr *)&sin ,sizeof(sin));
+    len = sizeof(struct sockaddr_in);
+    rc = getsockname( singinit_listen_sock, (struct sockaddr *) &sin, &len ); 
+    MPL_snprintf(port_c, sizeof(port_c), "%d",ntohs(sin.sin_port));
+    rc = listen(singinit_listen_sock, 5);
+
+    PMIU_printf( PMI_debug_init, "Starting mpiexec with %s\n", port_c );
+
+    /* Launch the mpiexec process with the name of this port */
+    pid = fork();
+    if (pid < 0) {
+       perror("PMII_singinit: fork failed");
+       exit(-1);
+    }
+    else if (pid == 0) {
+       newargv[0] = "mpiexec";
+       newargv[1] = "-pmi_args";
+       newargv[2] = port_c;
+       /* FIXME: Use a valid hostname */
+       newargv[3] = "default_interface";  /* default interface name, for now */
+       newargv[4] = "default_key";   /* default authentication key, for now */
+       MPL_snprintf(charpid, sizeof(charpid), "%d",getpid());
+       newargv[5] = charpid;
+       newargv[6] = NULL;
+       rc = execvp(newargv[0], (char **)newargv);
+       perror("PMII_singinit: execv failed");
+       PMIU_printf(1, "  This singleton init program attempted to access some feature\n");
+       PMIU_printf(1, "  for which process manager support was required, e.g. spawn or universe_size.\n");
+       PMIU_printf(1, "  But the necessary mpiexec is not in your path.\n");
+       return PMI_FAIL;
+    }
+    else
+    {
+       char buf[PMIU_MAXLINE], cmd[PMIU_MAXLINE];
+       char *p;
+       int connectStdio = 0;
+
+       /* Allow one connection back from the created mpiexec program */
+       PMI_fd =  accept_one_connection(singinit_listen_sock);
+       if (PMI_fd < 0) {
+           PMIU_printf( 1, "Failed to establish singleton init connection\n" );
+           return PMI_FAIL;
+       }
+       /* Execute the singleton init protocol */
+       rc = PMIU_readline( PMI_fd, buf, PMIU_MAXLINE );
+       PMIU_printf( PMI_debug_init, "Singinit: read %s\n", buf );
+
+       PMIU_parse_keyvals( buf );
+       PMIU_getval( "cmd", cmd, PMIU_MAXLINE );
+       if (strcmp( cmd, "singinit" ) != 0) {
+           PMIU_printf( 1, "unexpected command from PM: %s\n", cmd );
+           return PMI_FAIL;
+       }
+       p = PMIU_getval( "authtype", cmd, PMIU_MAXLINE );
+       if (p && strcmp( cmd, "none" ) != 0) {
+           PMIU_printf( 1, "unsupported authentication method %s\n", cmd );
+           return PMI_FAIL;
+       }
+       /* p = PMIU_getval( "authstring", cmd, PMIU_MAXLINE ); */
+       
+       /* If we're successful, send back our own singinit */
+       rc = MPL_snprintf( buf, PMIU_MAXLINE, 
+     "cmd=singinit pmi_version=%d pmi_subversion=%d stdio=yes authtype=none\n",
+                       PMI_VERSION, PMI_SUBVERSION );
+       if (rc < 0) {
+           return PMI_FAIL;
+       }
+       PMIU_printf( PMI_debug_init, "GetResponse with %s\n", buf );
+
+       rc = GetResponse( buf, "singinit_info", 0 );
+       if (rc != 0) {
+           PMIU_printf( 1, "GetResponse failed\n" );
+           return PMI_FAIL;
+       }
+       p = PMIU_getval( "versionok", cmd, PMIU_MAXLINE );
+       if (p && strcmp( cmd, "yes" ) != 0) {
+           PMIU_printf( 1, "Process manager needs a different PMI version\n" );
+           return PMI_FAIL;
+       }
+       p = PMIU_getval( "stdio", cmd, PMIU_MAXLINE );
+       if (p && strcmp( cmd, "yes" ) == 0) {
+           PMIU_printf( PMI_debug_init, "PM agreed to connect stdio\n" );
+           connectStdio = 1;
+       }
+       p = PMIU_getval( "kvsname", singinit_kvsname, sizeof(singinit_kvsname) );
+       PMIU_printf( PMI_debug_init, "kvsname to use is %s\n", 
+                    singinit_kvsname );
+       
+       if (connectStdio) {
+           PMIU_printf( PMI_debug_init, 
+                        "Accepting three connections for stdin, out, err\n" );
+           stdin_sock  = accept_one_connection(singinit_listen_sock);
+           dup2(stdin_sock, 0);
+           stdout_sock = accept_one_connection(singinit_listen_sock);
+           dup2(stdout_sock,1);
+           stderr_sock = accept_one_connection(singinit_listen_sock);
+           dup2(stderr_sock,2);
+       }
+       PMIU_printf( PMI_debug_init, "Done with singinit handshake\n" );
+    }
+    return PMI_SUCCESS;
+}
+
+/* Promote PMI to a fully initialized version if it was started as
+   a singleton init */
+static int PMIi_InitIfSingleton(void)
+{
+    int rc;
+    static int firstcall = 1;
+
+    if (PMI_initialized != SINGLETON_INIT_BUT_NO_PM || !firstcall) return PMI_SUCCESS;
+
+    /* We only try to init as a singleton the first time */
+    firstcall = 0;
+
+    /* First, start (if necessary) an mpiexec, connect to it, 
+       and start the singleton init handshake */
+    rc = PMII_singinit();
+
+    if (rc < 0)
+       return PMI_FAIL;
+    PMI_initialized = SINGLETON_INIT_WITH_PM;    /* do this right away */
+    PMI_size       = 1;
+    PMI_rank       = 0;
+    PMI_debug      = 0;
+    PMI_spawned            = 0;
+
+    PMII_getmaxes( &PMI_kvsname_max, &PMI_keylen_max, &PMI_vallen_max );
+
+    /* FIXME: We need to support a distinct kvsname for each 
+       process group */
+    PMI_KVS_Put( singinit_kvsname, cached_singinit_key, cached_singinit_val );
+
+    return PMI_SUCCESS;
+}
+
+static int accept_one_connection(int list_sock)
+{
+    int gotit, new_sock;
+    struct sockaddr_in from;
+    socklen_t len;
+
+    len = sizeof(from);
+    gotit = 0;
+    while ( ! gotit )
+    {
+       new_sock = accept(list_sock, (struct sockaddr *)&from, &len);
+       if (new_sock == -1)
+       {
+           if (errno == EINTR)    /* interrupted? If so, try again */
+               continue;
+           else
+           {
+               PMIU_printf(1, "accept failed in accept_one_connection\n");
+               exit(-1);
+           }
+       }
+       else
+           gotit = 1;
+    }
+    return(new_sock);
+}
+
+#endif
+/* end USE_PMI_PORT */
+
+/* Get the FD to use for PMI operations.  If a port is used, rather than 
+   a pre-established FD (i.e., via pipe), this routine will handle the 
+   initial handshake.  
+*/
+static int getPMIFD( int *notset )
+{
+    char *p;
+
+    /* Set the default */
+    PMI_fd = -1;
+    
+    p = getenv( "PMI_FD" );
+
+    if (p) {
+       PMI_fd = atoi( p );
+       return PMI_SUCCESS;
+    }
+
+#ifdef USE_PMI_PORT
+    p = getenv( "PMI_PORT" );
+    if (p) {
+       int portnum;
+       char hostname[MAXHOSTNAME+1];
+       char *pn, *ph;
+       int id = 0;
+
+       /* Connect to the indicated port (in format hostname:portnumber) 
+          and get the fd for the socket */
+       
+       /* Split p into host and port */
+       pn = p;
+       ph = hostname;
+       while (*pn && *pn != ':' && (ph - hostname) < MAXHOSTNAME) {
+           *ph++ = *pn++;
+       }
+       *ph = 0;
+
+       if (PMI_debug) {
+           DBG_PRINTF( ("Connecting to %s\n", p) );
+       }
+       if (*pn == ':') {
+           portnum = atoi( pn+1 );
+           /* FIXME: Check for valid integer after : */
+           /* This routine only gets the fd to use to talk to 
+              the process manager. The handshake below is used
+              to setup the initial values */
+           PMI_fd = PMII_Connect_to_pm( hostname, portnum );
+           if (PMI_fd < 0) {
+               PMIU_printf( 1, "Unable to connect to %s on %d\n", 
+                            hostname, portnum );
+               return PMI_FAIL;
+           }
+       }
+       else {
+           PMIU_printf( 1, "unable to decode hostport from %s\n", p );
+           return PMI_FAIL;
+       }
+
+       /* We should first handshake to get size, rank, debug. */
+       p = getenv( "PMI_ID" );
+       if (p) {
+           id = atoi( p );
+           /* PMII_Set_from_port sets up the values that are delivered
+              by enviroment variables when a separate port is not used */
+           PMII_Set_from_port( PMI_fd, id );
+           *notset = 0;
+       }
+       return PMI_SUCCESS;
+    }
+#endif
+
+    /* Singleton init case - its ok to return success with no fd set */
+    return PMI_SUCCESS;
+}
diff --git a/src/arch/ofi/simple_pmi/simple_pmiutil.c b/src/arch/ofi/simple_pmi/simple_pmiutil.c
new file mode 100644 (file)
index 0000000..7c9b23e
--- /dev/null
@@ -0,0 +1,299 @@
+/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
+/*
+ *  (C) 2001 by Argonne National Laboratory.
+ *      See COPYRIGHT in top-level directory.
+ */
+
+/* Allow fprintf to logfile */
+/* style: allow:fprintf:1 sig:0 */
+
+/* Utility functions associated with PMI implementation, but not part of
+   the PMI interface itself.  Reading and writing on pipes, signals, and parsing
+   key=value messages
+*/
+
+//YOHANN #include "mpichconf.h"
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <stdarg.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+
+#include "mpl.h"
+
+#include "pmi.h"
+#include "simple_pmiutil.h"
+
+/* Use the memory definitions from mpich/src/include */
+//YOHANN #include "mpir_mem.h"
+
+#define MAXVALLEN 1024
+#define MAXKEYLEN   32
+
+/* These are not the keyvals in the keyval space that is part of the 
+   PMI specification.
+   They are just part of this implementation's internal utilities.
+*/
+struct PMIU_keyval_pairs {
+    char key[MAXKEYLEN];
+    char value[MAXVALLEN];     
+};
+static struct PMIU_keyval_pairs PMIU_keyval_tab[64] = { { {0}, {0} } };
+static int  PMIU_keyval_tab_idx = 0;
+
+/* This is used to prepend printed output.  Set the initial value to 
+   "unset" */
+static char PMIU_print_id[PMIU_IDSIZE] = "unset";
+
+void PMIU_Set_rank( int PMI_rank )
+{
+    MPL_snprintf( PMIU_print_id, PMIU_IDSIZE, "cli_%d", PMI_rank );
+}
+void PMIU_SetServer( void )
+{
+    MPL_strncpy( PMIU_print_id, "server", PMIU_IDSIZE );
+}
+
+/* Note that vfprintf is part of C89 */
+
+/* style: allow:fprintf:1 sig:0 */
+/* style: allow:vfprintf:1 sig:0 */
+/* This should be combined with the message routines */
+void PMIU_printf( int print_flag, const char *fmt, ... )
+{
+    va_list ap;
+    static FILE *logfile= 0;
+    
+    /* In some cases when we are debugging, the handling of stdout or
+       stderr may be unreliable.  In that case, we make it possible to
+       select an output file. */
+    if (!logfile) {
+       char *p;
+       p = getenv("PMI_USE_LOGFILE");
+       if (p) {
+           char filename[1024];
+           p = getenv("PMI_ID");
+           if (p) {
+               MPL_snprintf( filename, sizeof(filename), 
+                              "testclient-%s.out", p );
+               logfile = fopen( filename, "w" );
+           }
+           else {
+               logfile = fopen( "testserver.out", "w" );
+           }
+       }
+       else 
+           logfile = stderr;
+    }
+
+    if ( print_flag ) {
+       /* MPL_error_printf( "[%s]: ", PMIU_print_id ); */
+       /* FIXME: Decide what role PMIU_printf should have (if any) and
+          select the appropriate MPIU routine */
+       fprintf( logfile, "[%s]: ", PMIU_print_id );
+       va_start( ap, fmt );
+       vfprintf( logfile, fmt, ap );
+       va_end( ap );
+       fflush( logfile );
+    }
+}
+
+#define MAX_READLINE 1024
+/* 
+ * Return the next newline-terminated string of maximum length maxlen.
+ * This is a buffered version, and reads from fd as necessary.  A
+ */
+int PMIU_readline( int fd, char *buf, int maxlen )
+{
+    static char readbuf[MAX_READLINE];
+    static char *nextChar = 0, *lastChar = 0;  /* lastChar is really one past 
+                                                 last char */
+    static int lastfd = -1;
+    ssize_t n;
+    int     curlen;
+    char    *p, ch;
+
+    /* Note: On the client side, only one thread at a time should 
+       be calling this, and there should only be a single fd.  
+       Server side code should not use this routine (see the 
+       replacement version in src/pm/util/pmiserv.c) */
+    if (nextChar != lastChar && fd != lastfd) {
+       MPL_internal_error_printf( "Panic - buffer inconsistent\n" );
+       return PMI_FAIL;
+    }
+
+    p      = buf;
+    curlen = 1;    /* Make room for the null */
+    while (curlen < maxlen) {
+       if (nextChar == lastChar) {
+           lastfd = fd;
+           do {
+               n = read( fd, readbuf, sizeof(readbuf)-1 );
+           } while (n == -1 && errno == EINTR);
+           if (n == 0) {
+               /* EOF */
+               break;
+           }
+           else if (n < 0) {
+               /* Error.  Return a negative value if there is no
+                  data.  Save the errno in case we need to return it
+                  later. */
+               if (curlen == 1) {
+                   curlen = 0;
+               }
+               break;
+           }
+           nextChar = readbuf;
+           lastChar = readbuf + n;
+           /* Add a null at the end just to make it easier to print
+              the read buffer */
+           readbuf[n] = 0;
+           /* FIXME: Make this an optional output */
+           /* printf( "Readline %s\n", readbuf ); */
+       }
+       
+       ch   = *nextChar++;
+       *p++ = ch;
+       curlen++;
+       if (ch == '\n') break;
+    }
+
+    /* We null terminate the string for convenience in printing */
+    *p = 0;
+
+    /* Return the number of characters, not counting the null */
+    return curlen-1;
+}
+
+int PMIU_writeline( int fd, char *buf )        
+{
+    ssize_t size, n;
+
+    size = strlen( buf );
+    if ( size > PMIU_MAXLINE ) {
+       buf[PMIU_MAXLINE-1] = '\0';
+       PMIU_printf( 1, "write_line: message string too big: :%s:\n", buf );
+    }
+    else if ( buf[strlen( buf ) - 1] != '\n' )  /* error:  no newline at end */
+           PMIU_printf( 1, "write_line: message string doesn't end in newline: :%s:\n",
+                      buf );
+    else {
+       do {
+           n = write( fd, buf, size );
+       } while (n == -1 && errno == EINTR);
+
+       if ( n < 0 ) {
+           PMIU_printf( 1, "write_line error; fd=%d buf=:%s:\n", fd, buf );
+           perror("system msg for write_line failure ");
+           return PMI_FAIL;
+       }
+       if ( n < size)
+           PMIU_printf( 1, "write_line failed to write entire message\n" );
+    }
+    return PMI_SUCCESS;
+}
+
+/*
+ * Given an input string st, parse it into internal storage that can be
+ * queried by routines such as PMIU_getval.
+ */
+int PMIU_parse_keyvals( char *st )
+{
+    char *p, *keystart, *valstart;
+    int  offset;
+
+    if ( !st )
+       return PMI_FAIL;
+
+    PMIU_keyval_tab_idx = 0;
+    p = st;
+    while ( 1 ) {
+       while ( *p == ' ' )
+           p++;
+       /* got non-blank */
+       if ( *p == '=' ) {
+           PMIU_printf( 1, "PMIU_parse_keyvals:  unexpected = at character %d in %s\n",
+                      p - st, st );
+           return PMI_FAIL;
+       }
+       if ( *p == '\n' || *p == '\0' )
+           return PMI_SUCCESS; /* normal exit */
+       /* got normal character */
+       keystart = p;           /* remember where key started */
+       while ( *p != ' ' && *p != '=' && *p != '\n' && *p != '\0' )
+           p++;
+       if ( *p == ' ' || *p == '\n' || *p == '\0' ) {
+           PMIU_printf( 1,
+       "PMIU_parse_keyvals: unexpected key delimiter at character %d in %s\n",
+                      p - st, st );
+           return PMI_FAIL;
+       }
+       /* Null terminate the key */
+       *p = 0;
+       /* store key */
+        MPL_strncpy( PMIU_keyval_tab[PMIU_keyval_tab_idx].key, keystart, 
+                     MAXKEYLEN );
+
+       valstart = ++p;                 /* start of value */
+       while ( *p != ' ' && *p != '\n' && *p != '\0' )
+           p++;
+       /* store value */
+        MPL_strncpy( PMIU_keyval_tab[PMIU_keyval_tab_idx].value, valstart, 
+                     MAXVALLEN );
+       offset = (int)(p - valstart);
+       /* When compiled with -fPIC, the pgcc compiler generates incorrect
+          code if "p - valstart" is used instead of using the 
+          intermediate offset */
+       PMIU_keyval_tab[PMIU_keyval_tab_idx].value[offset] = '\0';  
+       PMIU_keyval_tab_idx++;
+       if ( *p == ' ' )
+           continue;
+       if ( *p == '\n' || *p == '\0' )
+           return PMI_SUCCESS; /* value has been set to empty */
+    }
+}
+
+void PMIU_dump_keyvals( void )
+{
+    int i;
+    for (i=0; i < PMIU_keyval_tab_idx; i++) 
+       PMIU_printf(1, "  %s=%s\n",PMIU_keyval_tab[i].key, PMIU_keyval_tab[i].value);
+}
+
+char *PMIU_getval( const char *keystr, char *valstr, int vallen )
+{
+    int i, rc;
+    
+    for (i = 0; i < PMIU_keyval_tab_idx; i++) {
+       if ( strcmp( keystr, PMIU_keyval_tab[i].key ) == 0 ) { 
+           rc = MPL_strncpy( valstr, PMIU_keyval_tab[i].value, vallen );
+           if (rc != 0) {
+               PMIU_printf( 1, "MPL_strncpy failed in PMIU_getval\n" );
+               return NULL;
+           }
+           return valstr;
+       } 
+    }
+    valstr[0] = '\0';
+    return NULL;
+}
+
+void PMIU_chgval( const char *keystr, char *valstr )
+{
+    int i;
+    
+    for ( i = 0; i < PMIU_keyval_tab_idx; i++ ) {
+       if ( strcmp( keystr, PMIU_keyval_tab[i].key ) == 0 ) {
+           MPL_strncpy( PMIU_keyval_tab[i].value, valstr, MAXVALLEN - 1 );
+           PMIU_keyval_tab[i].value[MAXVALLEN - 1] = '\0';
+       }
+    }
+}
diff --git a/src/arch/ofi/simple_pmi/simple_pmiutil.h b/src/arch/ofi/simple_pmi/simple_pmiutil.h
new file mode 100644 (file)
index 0000000..7404ad0
--- /dev/null
@@ -0,0 +1,33 @@
+/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
+/*
+ *  (C) 2001 by Argonne National Laboratory.
+ *      See COPYRIGHT in top-level directory.
+ */
+
+/* maximum sizes for arrays */
+#define PMIU_MAXLINE 1024
+#define PMIU_IDSIZE    32
+
+/* we don't have access to MPIR_Assert and friends here in the PMI code */
+#if defined(HAVE_ASSERT_H)
+#  include <assert.h>
+#  define PMIU_Assert(expr) assert(expr)
+#else
+#  define PMIU_Assert(expr)
+#endif
+
+#if defined HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+
+
+/* prototypes for PMIU routines */
+void PMIU_Set_rank( int PMI_rank );
+void PMIU_SetServer( void );
+void PMIU_printf( int print_flag, const char *fmt, ... );
+int  PMIU_readline( int fd, char *buf, int max );
+int  PMIU_writeline( int fd, char *buf );
+int  PMIU_parse_keyvals( char *st );
+void PMIU_dump_keyvals( void );
+char *PMIU_getval( const char *keystr, char *valstr, int vallen );
+void PMIU_chgval( const char *keystr, char *valstr );
index c56e0b1e539574dc40cd44a24b29c6020604ce17..526c403c13e51252d4fcd3e54788e07a09c1b2ee 100644 (file)
@@ -238,7 +238,7 @@ CpvDeclare(void *, CkGridObject);
 CpvDeclare(void *, CsdGridQueue);
 #endif
 
-#if CMK_CRAYXE || CMK_CRAYXC
+#if CMK_CRAYXE || CMK_CRAYXC || CMK_OFI
 void* LrtsAlloc(int, int);
 void  LrtsFree(void*);
 #endif
@@ -2923,7 +2923,7 @@ void *CmiAlloc(int size)
   res = (char*) arena_malloc(size+sizeof(CmiChunkHeader));
 #elif CMK_USE_IBVERBS | CMK_USE_IBUD
   res = (char *) infi_CmiAlloc(size+sizeof(CmiChunkHeader));
-#elif CMK_CONVERSE_UGNI
+#elif CMK_CONVERSE_UGNI || CMK_OFI
   res =(char *) LrtsAlloc(size, sizeof(CmiChunkHeader));
 #elif CONVERSE_POOL
   res =(char *) CmiPoolAlloc(size+sizeof(CmiChunkHeader));
@@ -3028,7 +3028,7 @@ void CmiFree(void *blk)
       }
 #endif
     infi_CmiFree(BLKSTART(parentBlk));
-#elif CMK_CONVERSE_UGNI
+#elif CMK_CONVERSE_UGNI || CMK_OFI
     LrtsFree(BLKSTART(parentBlk));
 #elif CONVERSE_POOL
     CmiPoolFree(BLKSTART(parentBlk));
index f771a3bd6c4473dfac1eb6e82850a26aab89c3eb..b2bf4c53d9ef4544478a1548bb0a6641948cc85c 100755 (executable)
@@ -3367,6 +3367,56 @@ _ACEOF
 
 fi
 
+#### test if we can build OFI ####
+if test "$CMK_BUILD_OFI" = 1
+then
+cat > $tc <<EOT
+#include <rdma/fabric.h>
+int main(int argc, char **argv)
+{
+  struct fi_info *providers;
+  int ret = fi_getinfo(FI_VERSION(1,0), NULL, NULL, 0ULL, NULL, &providers);
+  return 0;
+}
+EOT
+test_cc "whether build on OFI" "yes" "no" "-lfabric"
+
+cat >>confdefs.h <<_ACEOF
+#define CMK_BUILD_ON_OFI $strictpass
+_ACEOF
+
+BUILD_OFI=$strictpass
+
+if test $BUILD_OFI -eq 0
+then
+  echo "Error: Unable to compile OFI"
+  test_finish 1
+else
+  test_linkc "whether -lfabric" "ok" "no" "-lfabric"
+  if test $pass -eq 0
+  then
+    #test for psm incompatibility
+    PSM_COMPAT_DIR=/usr/lib64/psm2-compat
+    if test -d $PSM_COMPAT_DIR
+    then
+      add_flag CMK_LIBDIR='"$CMK_LIBDIR -Wl,-rpath=/usr/lib64/psm2-compat"' "psm2-compat lib"
+      CMK_LIBDIR="$CMK_LIBDIR -Wl,-rpath=/usr/lib64/psm2-compat"
+      test_linkc "whether -lfabric after adding psm2-compatible library" "ok" "no" "-lfabric"
+      if test $pass -eq 0
+      then
+        echo "Error: -lfabric not found or not working. Pass '--basedir=/path/to/dir/' if -lfabric is located in a different directory"
+        test_finish 1
+      fi
+    else
+      echo "Error: -lfabric not working, $PSM_COMPAT_DIR not found"
+      echo "Pass '--basedir=/path/to/dir/' if -lfabric is located in a different directory"
+      test_finish 1
+    fi
+  fi
+fi
+
+fi
+
 #### test if we can build MPI ####
 if test "$CMK_BUILD_MPI" = 1
 then
index a1182a1aa3732ed49d03c91b95ae63903f49a6b1..668e0d7f27fdf448b339ef72033fb3572ebc4405 100644 (file)
@@ -1276,6 +1276,52 @@ test_cxx "whether switching TLS register (32-bit) is supported" "yes" "no" ""
 AC_DEFINE_UNQUOTED(CMK_TLS_SWITCHING32, $strictpass, [Allows switching TLS in 32-bit.])
 fi
 
+#### test if we can build OFI ####
+if test "$CMK_BUILD_OFI" = 1
+then
+cat > $tc <<EOT
+#include <rdma/fabric.h>
+int main(int argc, char **argv)
+{
+  struct fi_info *providers;
+  int ret = fi_getinfo(FI_VERSION(1,0), NULL, NULL, 0ULL, NULL, &providers);
+  return 0;
+}
+EOT
+test_cc "whether build on OFI" "yes" "no" "-lfabric"
+AC_DEFINE_UNQUOTED(CMK_BUILD_ON_OFI, $strictpass, [build OFI.])
+BUILD_OFI=$strictpass
+
+if test $BUILD_OFI -eq 0
+then
+  echo "Error: Unable to compile OFI"
+  test_finish 1
+else
+  test_linkc "whether -lfabric" "ok" "no" "-lfabric"
+  if test $pass -eq 0
+  then
+    #test for psm incompatibility
+    PSM_COMPAT_DIR=/usr/lib64/psm2-compat
+    if test -d $PSM_COMPAT_DIR
+    then
+      add_flag CMK_LIBDIR='"$CMK_LIBDIR -Wl,-rpath=/usr/lib64/psm2-compat"' "psm2-compat lib"
+      CMK_LIBDIR="$CMK_LIBDIR -Wl,-rpath=/usr/lib64/psm2-compat"
+      test_linkc "whether -lfabric after adding psm2-compatible library" "ok" "no" "-lfabric"
+      if test $pass -eq 0
+      then
+        echo "Error: -lfabric not found or not working. Pass '--basedir=/path/to/dir/' if -lfabric is located in a different directory"
+        test_finish 1
+      fi
+    else
+      echo "Error: -lfabric not working, $PSM_COMPAT_DIR not found"
+      echo "Pass '--basedir=/path/to/dir/' if -lfabric is located in a different directory"
+      test_finish 1
+    fi
+  fi
+fi
+
+fi
+
 #### test if we can build MPI ####
 if test "$CMK_BUILD_MPI" = 1
 then