Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge pull request #154 from simgrid/partial_shared_malloc
[simgrid.git] / src / smpi / smpi_global.cpp
index f08bea5..2774bbd 100644 (file)
@@ -3,15 +3,18 @@
 /* This program is free software; you can redistribute it and/or modify it
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
+#include <dlfcn.h>
+#include <fcntl.h>
 #include <spawn.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <dlfcn.h>
 
 #include "mc/mc.h"
 #include "private.h"
 #include "private.hpp"
 #include "simgrid/s4u/Mailbox.hpp"
+#include "smpi/smpi_shared_malloc.hpp"
 #include "simgrid/sg_config.h"
 #include "src/kernel/activity/SynchroComm.hpp"
 #include "src/mc/mc_record.h"
@@ -19,6 +22,7 @@
 #include "src/msg/msg_private.h"
 #include "src/simix/smx_private.h"
 #include "src/surf/surf_interface.hpp"
+#include "src/smpi/SmpiHost.hpp"
 #include "surf/surf.h"
 #include "xbt/replay.hpp"
 #include <xbt/config.hpp>
 #include <vector>
 #include <memory>
 
+#if HAVE_SENDFILE
+#include <sys/sendfile.h>
+#endif
+
 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(smpi_kernel, smpi, "Logging specific to SMPI (kernel)");
 #include <boost/tokenizer.hpp>
 #include <boost/algorithm/string.hpp> /* trim_right / trim_left */
@@ -122,49 +130,83 @@ void smpi_comm_set_copy_data_callback(void (*callback) (smx_activity_t, void*, s
   smpi_comm_copy_data_callback = callback;
 }
 
+static void print(std::vector<std::pair<size_t, size_t>> vec) {
+    fprintf(stderr, "{");
+    for(auto elt: vec) {
+        fprintf(stderr, "(0x%lx, 0x%lx),", elt.first, elt.second);
+    }
+    fprintf(stderr, "}\n");
+}
+static void memcpy_private(void *dest, const void *src, size_t n, std::vector<std::pair<size_t, size_t>> &private_blocks) {
+  for(auto block : private_blocks) {
+    memcpy((uint8_t*)dest+block.first, (uint8_t*)src+block.first, block.second-block.first);
+  }
+}
+
+static void check_blocks(std::vector<std::pair<size_t, size_t>> &private_blocks, size_t buff_size) {
+  for(auto block : private_blocks) {
+    xbt_assert(block.first <= block.second && block.second <= buff_size, "Oops, bug in shared malloc.");
+  }
+}
+
 void smpi_comm_copy_buffer_callback(smx_activity_t synchro, void *buff, size_t buff_size)
 {
-
   simgrid::kernel::activity::Comm *comm = dynamic_cast<simgrid::kernel::activity::Comm*>(synchro);
-
+  int src_shared=0, dst_shared=0;
+  size_t src_offset=0, dst_offset=0;
+  std::vector<std::pair<size_t, size_t>> src_private_blocks;
+  std::vector<std::pair<size_t, size_t>> dst_private_blocks;
   XBT_DEBUG("Copy the data over");
-  if(smpi_is_shared(buff)){
+  if((src_shared=smpi_is_shared(buff, src_private_blocks, &src_offset))) {
     XBT_DEBUG("Sender %p is shared. Let's ignore it.", buff);
-  }else if(smpi_is_shared((char*)comm->dst_buff)){
+    src_private_blocks = shift_and_frame_private_blocks(src_private_blocks, src_offset, buff_size);
+  }
+  else {
+    src_private_blocks.clear();
+    src_private_blocks.push_back(std::make_pair(0, buff_size));
+  }
+  if((dst_shared=smpi_is_shared((char*)comm->dst_buff, dst_private_blocks, &dst_offset))) {
     XBT_DEBUG("Receiver %p is shared. Let's ignore it.", (char*)comm->dst_buff);
-  }else{
-    void* tmpbuff=buff;
-    if((smpi_privatize_global_variables == SMPI_PRIVATIZE_MMAP) && (static_cast<char*>(buff) >= smpi_start_data_exe)
-        && (static_cast<char*>(buff) < smpi_start_data_exe + smpi_size_data_exe )
-      ){
-         XBT_DEBUG("Privatization : We are copying from a zone inside global memory... Saving data to temp buffer !");
-
-         smpi_switch_data_segment(
-             (static_cast<simgrid::smpi::Process*>((static_cast<simgrid::MsgActorExt*>(comm->src_proc->data)->data))->index()));
-         tmpbuff = static_cast<void*>(xbt_malloc(buff_size));
-         memcpy(tmpbuff, buff, buff_size);
-    }
-
-    if((smpi_privatize_global_variables == SMPI_PRIVATIZE_MMAP) && ((char*)comm->dst_buff >= smpi_start_data_exe)
-        && ((char*)comm->dst_buff < smpi_start_data_exe + smpi_size_data_exe )){
-         XBT_DEBUG("Privatization : We are copying to a zone inside global memory - Switch data segment");
-         smpi_switch_data_segment(
-             (static_cast<simgrid::smpi::Process*>((static_cast<simgrid::MsgActorExt*>(comm->dst_proc->data)->data))->index()));
-    }
-
-    XBT_DEBUG("Copying %zu bytes from %p to %p", buff_size, tmpbuff,comm->dst_buff);
-    memcpy(comm->dst_buff, tmpbuff, buff_size);
+    dst_private_blocks = shift_and_frame_private_blocks(dst_private_blocks, dst_offset, buff_size);
+  }
+  else {
+    dst_private_blocks.clear();
+    dst_private_blocks.push_back(std::make_pair(0, buff_size));
+  }
+  check_blocks(src_private_blocks, buff_size);
+  check_blocks(dst_private_blocks, buff_size);
+  auto private_blocks = merge_private_blocks(src_private_blocks, dst_private_blocks);
+  check_blocks(private_blocks, buff_size);
+  void* tmpbuff=buff;
+  if((smpi_privatize_global_variables == SMPI_PRIVATIZE_MMAP) && (static_cast<char*>(buff) >= smpi_start_data_exe)
+      && (static_cast<char*>(buff) < smpi_start_data_exe + smpi_size_data_exe )
+    ){
+       XBT_DEBUG("Privatization : We are copying from a zone inside global memory... Saving data to temp buffer !");
+
+       smpi_switch_data_segment(
+           (static_cast<simgrid::smpi::Process*>((static_cast<simgrid::MsgActorExt*>(comm->src_proc->data)->data))->index()));
+       tmpbuff = static_cast<void*>(xbt_malloc(buff_size));
+       memcpy_private(tmpbuff, buff, buff_size, private_blocks);
+  }
 
-    if (comm->detached) {
-      // if this is a detached send, the source buffer was duplicated by SMPI
-      // sender to make the original buffer available to the application ASAP
-      xbt_free(buff);
-      //It seems that the request is used after the call there this should be free somewhere else but where???
-      //xbt_free(comm->comm.src_data);// inside SMPI the request is kept inside the user data and should be free
-      comm->src_buff = nullptr;
-    }
-    if(tmpbuff!=buff)xbt_free(tmpbuff);
+  if((smpi_privatize_global_variables == SMPI_PRIVATIZE_MMAP) && ((char*)comm->dst_buff >= smpi_start_data_exe)
+      && ((char*)comm->dst_buff < smpi_start_data_exe + smpi_size_data_exe )){
+       XBT_DEBUG("Privatization : We are copying to a zone inside global memory - Switch data segment");
+       smpi_switch_data_segment(
+           (static_cast<simgrid::smpi::Process*>((static_cast<simgrid::MsgActorExt*>(comm->dst_proc->data)->data))->index()));
+  }
+  XBT_DEBUG("Copying %zu bytes from %p to %p", buff_size, tmpbuff,comm->dst_buff);
+  memcpy_private(comm->dst_buff, tmpbuff, buff_size, private_blocks);
+
+  if (comm->detached) {
+    // if this is a detached send, the source buffer was duplicated by SMPI
+    // sender to make the original buffer available to the application ASAP
+    xbt_free(buff);
+    //It seems that the request is used after the call there this should be free somewhere else but where???
+    //xbt_free(comm->comm.src_data);// inside SMPI the request is kept inside the user data and should be free
+    comm->src_buff = nullptr;
   }
+  if(tmpbuff!=buff)xbt_free(tmpbuff);
 
 }
 
@@ -506,6 +548,10 @@ int smpi_main(const char* executable, int argc, char *argv[])
 
   SMPI_switch_data_segment = &smpi_switch_data_segment;
 
+  simgrid::s4u::Host::onCreation.connect([](simgrid::s4u::Host& host) {
+    host.extension_set(new simgrid::smpi::SmpiHost(&host));
+  });
+
   // parse the platform file: get the host list
   SIMIX_create_environment(argv[1]);
   SIMIX_comm_set_copy_data_callback(smpi_comm_copy_buffer_callback);
@@ -515,24 +561,50 @@ int smpi_main(const char* executable, int argc, char *argv[])
   if (smpi_privatize_global_variables == SMPI_PRIVATIZE_DLOPEN) {
 
     std::string executable_copy = executable;
-    simix_global->default_function = [executable_copy](std::vector<std::string> args) {
-      return std::function<void()>([executable_copy, args] {
+
+    // Prepare the copy of the binary (open the file and get its size)
+    // (fdin will remain open for the whole process execution. That's a sort of leak but we can live with it)
+    int fdin = open(executable_copy.c_str(), O_RDONLY);
+    xbt_assert(fdin >= 0, "Cannot read from %s", executable_copy.c_str());
+    struct stat fdin_stat;
+    fstat(fdin, &fdin_stat);
+    off_t fdin_size = fdin_stat.st_size;
+
+    simix_global->default_function = [executable_copy, fdin, fdin_size](std::vector<std::string> args) {
+      return std::function<void()>([executable_copy, fdin, fdin_size, args] {
 
         // Copy the dynamic library:
         std::string target_executable = executable_copy
           + "_" + std::to_string(getpid())
           + "_" + std::to_string(rank++) + ".so";
-        // TODO, execute directly instead of relying on cp
-        const char* command1 [] = {
-          "cp", "--reflink=auto", "--", executable_copy.c_str(), target_executable.c_str(),
-          nullptr
-        };
-        const char* command2 [] = {
-          "cp", "--", executable_copy.c_str(), target_executable.c_str(),
-          nullptr
-        };
-        if (execute_command(command1) != 0 && execute_command(command2) != 0)
-          xbt_die("copy failed");
+
+        int fdout = open(target_executable.c_str(), O_WRONLY);
+        xbt_assert(fdout >= 0, "Cannot write into %s", target_executable.c_str());
+
+#if HAVE_SENDFILE
+        sendfile(fdout, fdin, NULL, fdin_size);
+#else
+        XBT_WARN("Copy %d bytes into %s", static_cast<int>(fdin_size), target_executable.c_str());
+        const int bufsize = 1024 * 1024 * 4;
+        char buf[bufsize];
+        while (int got = read(fdin, buf, bufsize)) {
+          if (got == -1) {
+            xbt_assert(errno == EINTR, "Cannot read from %s", executable_copy.c_str());
+          } else {
+            char* p  = buf;
+            int todo = got;
+            while (int done = write(fdout, p, todo)) {
+              if (done == -1) {
+                xbt_assert(errno == EINTR, "Cannot write into %s", target_executable.c_str());
+              } else {
+                p += done;
+                todo -= done;
+              }
+            }
+          }
+        }
+#endif
+        close(fdout);
 
         // Load the copy and resolve the entry point:
         void* handle = dlopen(target_executable.c_str(), RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND);
@@ -543,7 +615,7 @@ int smpi_main(const char* executable, int argc, char *argv[])
         if (!entry_point)
           xbt_die("Could not resolve entry point");
 
-          smpi_run_entry_point(entry_point, args);
+        smpi_run_entry_point(entry_point, args);
       });
     };