Jui-min Lee has submitted this change. ( https://gem5-review.googlesource.com/c/public/gem5/+/57729 )

Change subject: mem: Add SharedMemoryServer
......................................................................

mem: Add SharedMemoryServer

Add an utility class that provides a service for another process
query and get the fd of the corresponding region in gem5's physmem.

Basically, the service works in this way:
1. client connect to the unix socket created by a SharedMemoryServer
2. client send a request {start, end} to gem5
3. the server locates the corresponding shared memory
4. gem5 response {offset} and pass {fd} in ancillary data

mmap fd at offset will provide the client the view into the physical
memory of the request range.

Change-Id: I9d42fd8a41fc28dcfebb45dec10bc9ebb8e21d11
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/57729
Reviewed-by: Gabe Black <gabe.bl...@gmail.com>
Maintainer: Gabe Black <gabe.bl...@gmail.com>
Reviewed-by: Boris Shingarov <shinga...@labware.com>
Tested-by: kokoro <noreply+kok...@google.com>
---
M src/base/socket.hh
M src/mem/SConscript
A src/mem/SharedMemoryServer.py
M src/mem/physical.cc
M src/mem/physical.hh
A src/mem/shared_memory_server.cc
A src/mem/shared_memory_server.hh
7 files changed, 410 insertions(+), 11 deletions(-)

Approvals:
  Boris Shingarov: Looks good to me, approved
  Gabe Black: Looks good to me, approved; Looks good to me, approved
  kokoro: Regressions pass




diff --git a/src/base/socket.hh b/src/base/socket.hh
index 4ed6185..3375ccc 100644
--- a/src/base/socket.hh
+++ b/src/base/socket.hh
@@ -62,14 +62,6 @@
      */
     static void cleanup();

-  private:
-    /* Create a socket, adding SOCK_CLOEXEC if available. */
-    static int socketCloexec(int domain, int type, int protocol);
-    /* Accept a connection, adding SOCK_CLOEXEC if available. */
-    static int acceptCloexec(int sockfd, struct sockaddr *addr,
-                              socklen_t *addrlen);
-
-
   public:
     /**
      * @ingroup api_socket
@@ -84,6 +76,12 @@

     int getfd() const { return fd; }
     bool islistening() const { return listening; }
+
+    /* Create a socket, adding SOCK_CLOEXEC if available. */
+    static int socketCloexec(int domain, int type, int protocol);
+    /* Accept a connection, adding SOCK_CLOEXEC if available. */
+    static int acceptCloexec(int sockfd, struct sockaddr *addr,
+                              socklen_t *addrlen);
     /** @} */ // end of api_socket
 };

diff --git a/src/mem/SConscript b/src/mem/SConscript
index 7790e1d..e07942e 100644
--- a/src/mem/SConscript
+++ b/src/mem/SConscript
@@ -56,6 +56,7 @@
 SimObject('ExternalMaster.py', sim_objects=['ExternalMaster'])
 SimObject('ExternalSlave.py', sim_objects=['ExternalSlave'])
 SimObject('CfiMemory.py', sim_objects=['CfiMemory'])
+SimObject('SharedMemoryServer.py', sim_objects=['SharedMemoryServer'])
 SimObject('SimpleMemory.py', sim_objects=['SimpleMemory'])
 SimObject('XBar.py', sim_objects=[
     'BaseXBar', 'NoncoherentXBar', 'CoherentXBar', 'SnoopFilter'])
@@ -80,6 +81,7 @@
 Source('packet_queue.cc')
 Source('port_proxy.cc')
 Source('physical.cc')
+Source('shared_memory_server.cc')
 Source('simple_mem.cc')
 Source('snoop_filter.cc')
 Source('stack_dist_calc.cc')
diff --git a/src/mem/SharedMemoryServer.py b/src/mem/SharedMemoryServer.py
new file mode 100644
index 0000000..3a63f45
--- /dev/null
+++ b/src/mem/SharedMemoryServer.py
@@ -0,0 +1,15 @@
+from m5.SimObject import SimObject
+from m5.params import Param
+from m5.proxy import Parent
+
+
+class SharedMemoryServer(SimObject):
+    type = "SharedMemoryServer"
+    cxx_header = "mem/shared_memory_server.hh"
+    cxx_class = "gem5::memory::SharedMemoryServer"
+
+    system = Param.System(
+        Parent.any,
+        "The system where the target shared memory is actually stored.")
+    server_path = Param.String(
+        "The unix socket path where the server should be running upon.")
diff --git a/src/mem/physical.cc b/src/mem/physical.cc
index 08707eb..cae37a0 100644
--- a/src/mem/physical.cc
+++ b/src/mem/physical.cc
@@ -247,7 +247,8 @@
     // remember this backing store so we can checkpoint it and unmap
     // it appropriately
     backingStore.emplace_back(range, pmem,
-                              conf_table_reported, in_addr_map, kvm_map);
+                              conf_table_reported, in_addr_map, kvm_map,
+                              shm_fd, map_offset);

     // point the memories to their backing store
     for (const auto& m : _memories) {
diff --git a/src/mem/physical.hh b/src/mem/physical.hh
index ff0dc61..3a976ed 100644
--- a/src/mem/physical.hh
+++ b/src/mem/physical.hh
@@ -70,9 +70,11 @@
      * pointers, because PhysicalMemory is responsible for that.
      */
     BackingStoreEntry(AddrRange range, uint8_t* pmem,
- bool conf_table_reported, bool in_addr_map, bool kvm_map) + bool conf_table_reported, bool in_addr_map, bool kvm_map,
+                      int shm_fd=-1, off_t shm_offset=0)
         : range(range), pmem(pmem), confTableReported(conf_table_reported),
-          inAddrMap(in_addr_map), kvmMap(kvm_map)
+          inAddrMap(in_addr_map), kvmMap(kvm_map), shmFd(shm_fd),
+          shmOffset(shm_offset)
         {}

     /**
@@ -101,6 +103,18 @@
       * acceleration.
       */
      bool kvmMap;
+
+     /**
+ * If this backing store is based on a shared memory, this is the fd to
+      * the shared memory. Otherwise, it should be -1.
+      */
+     int shmFd;
+
+     /**
+ * If this backing store is based on a shared memory, this is the offset + * of this backing store in the share memory. Otherwise, the value is 0.
+      */
+     off_t shmOffset;
 };

 /**
diff --git a/src/mem/shared_memory_server.cc b/src/mem/shared_memory_server.cc
new file mode 100644
index 0000000..24dd9f6
--- /dev/null
+++ b/src/mem/shared_memory_server.cc
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * 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 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.
+ *
+ * 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.
+ */
+
+#include "mem/shared_memory_server.hh"
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cerrno>
+#include <cstring>
+
+#include "base/logging.hh"
+#include "base/output.hh"
+#include "base/pollevent.hh"
+#include "base/socket.hh"
+
+namespace gem5
+{
+namespace memory
+{
+
+SharedMemoryServer::SharedMemoryServer(const SharedMemoryServerParams& params) + : SimObject(params), unixSocketPath(simout.resolve(params.server_path)),
+      system(params.system), serverFd(-1)
+{
+    fatal_if(system == nullptr, "Requires a system to share memory from!");
+    // Ensure the unix socket path to use is not occupied. Also, if there's
+ // actually anything to be removed, warn the user something might be off.
+    if (unlink(unixSocketPath.c_str()) == 0) {
+        warn(
+            "The server path %s was occupied and will be replaced. Please "
+            "make sure there is no other server using the same path.",
+            unixSocketPath.c_str());
+    }
+    // Create a new unix socket.
+    serverFd = ListenSocket::socketCloexec(AF_UNIX, SOCK_STREAM, 0);
+ panic_if(serverFd < 0, "%s: cannot create unix socket: %s", name().c_str(),
+             strerror(errno));
+    // Bind to the specified path.
+    sockaddr_un serv_addr = {};
+    serv_addr.sun_family = AF_UNIX;
+    strncpy(serv_addr.sun_path, unixSocketPath.c_str(),
+            sizeof(serv_addr.sun_path) - 1);
+    warn_if(strlen(serv_addr.sun_path) != unixSocketPath.size(),
+            "%s: unix socket path truncated, expect '%s' but get '%s'",
+            name().c_str(), unixSocketPath.c_str(), serv_addr.sun_path);
+    int bind_retv = bind(serverFd, reinterpret_cast<sockaddr*>(&serv_addr),
+                         sizeof(serv_addr));
+ fatal_if(bind_retv != 0, "%s: cannot bind unix socket: %s", name().c_str(),
+             strerror(errno));
+    // Start listening.
+    int listen_retv = listen(serverFd, 1);
+    fatal_if(listen_retv != 0, "%s: listen failed: %s", name().c_str(),
+             strerror(errno));
+    listenSocketEvent.reset(new ListenSocketEvent(serverFd, this));
+    pollQueue.schedule(listenSocketEvent.get());
+    inform("%s: listening at %s", name().c_str(), unixSocketPath.c_str());
+}
+
+SharedMemoryServer::~SharedMemoryServer()
+{
+    int unlink_retv = unlink(unixSocketPath.c_str());
+    warn_if(unlink_retv != 0, "%s: cannot unlink unix socket: %s",
+            name().c_str(), strerror(errno));
+    int close_retv = close(serverFd);
+    warn_if(close_retv != 0, "%s: cannot close unix socket: %s",
+            name().c_str(), strerror(errno));
+}
+
+SharedMemoryServer::BaseShmPollEvent::BaseShmPollEvent(
+    int fd, SharedMemoryServer* shm_server)
+    : PollEvent(fd, POLLIN), shmServer(shm_server),
+      eventName(shmServer->name() + ".fd" + std::to_string(fd))
+{
+}
+
+const std::string&
+SharedMemoryServer::BaseShmPollEvent::name() const
+{
+    return eventName;
+}
+
+bool
+SharedMemoryServer::BaseShmPollEvent::tryReadAll(void* buffer, size_t size)
+{
+    char* char_buffer = reinterpret_cast<char*>(buffer);
+    for (size_t offset = 0; offset < size;) {
+ ssize_t retv = recv(pfd.fd, char_buffer + offset, size - offset, 0);
+        if (retv >= 0) {
+            offset += retv;
+        } else if (errno != EINTR) {
+            warn("%s: recv failed: %s", name().c_str(), strerror(errno));
+            return false;
+        }
+    }
+    return true;
+}
+
+void
+SharedMemoryServer::ListenSocketEvent::process(int revents)
+{
+    panic_if(revents & (POLLERR | POLLNVAL), "%s: listen socket is broken",
+             name().c_str());
+    int cli_fd = ListenSocket::acceptCloexec(pfd.fd, nullptr, nullptr);
+    panic_if(cli_fd < 0, "%s: accept failed: %s", name().c_str(),
+             strerror(errno));
+    panic_if(shmServer->clientSocketEvent.get(),
+             "%s: cannot serve two clients at once", name().c_str());
+    inform("%s: accept new connection %d", name().c_str(), cli_fd);
+    shmServer->clientSocketEvent.reset(
+        new ClientSocketEvent(cli_fd, shmServer));
+    pollQueue.schedule(shmServer->clientSocketEvent.get());
+}
+
+void
+SharedMemoryServer::ClientSocketEvent::process(int revents)
+{
+    do {
+        // Ensure the connection is not closed nor broken.
+        if (revents & (POLLHUP | POLLERR | POLLNVAL)) {
+            break;
+        }
+
+ // Receive a request packet. We ignore the endianness as unix socket
+        // only allows communication on the same system anyway.
+        RequestType req_type;
+        struct
+        {
+            uint64_t start;
+            uint64_t end;
+        } request;
+        if (!tryReadAll(&req_type, sizeof(req_type))) {
+            break;
+        }
+        if (req_type != RequestType::kGetPhysRange) {
+            warn("%s: receive unknown request: %d", name().c_str(),
+                 static_cast<int>(req_type));
+            break;
+        }
+        if (!tryReadAll(&request, sizeof(request))) {
+            break;
+        }
+        AddrRange range(request.start, request.end);
+        inform("%s: receive request: %s", name().c_str(),
+               range.to_string().c_str());
+
+        // Identify the backing store.
+ const auto& stores = shmServer->system->getPhysMem().getBackingStore();
+        auto it = std::find_if(
+ stores.begin(), stores.end(), [&](const BackingStoreEntry& entry) {
+                return entry.shmFd >= 0 && range.isSubset(entry.range);
+            });
+        if (it == stores.end()) {
+            warn("%s: cannot find backing store for %s", name().c_str(),
+                 range.to_string().c_str());
+            break;
+        }
+        inform("%s: find shared backing store for %s at %s, shm=%d:%lld",
+               name().c_str(), range.to_string().c_str(),
+               it->range.to_string().c_str(), it->shmFd,
+               (unsigned long long)it->shmOffset);
+
+        // Populate response message.
+        // mmap fd @ offset <===> [start, end] in simulated phys mem.
+        msghdr msg = {};
+ // Setup iovec for fields other than fd. We ignore the endianness as
+        // unix socket only allows communication on the same system anyway.
+        struct
+        {
+            off_t offset;
+        } response;
+        // (offset of the request range in shared memory) =
+        //     (offset of the full range in shared memory) +
+        //     (offset of the request range in the full range)
+ response.offset = it->shmOffset + (range.start() - it->range.start());
+        iovec ios = {.iov_base = &response, .iov_len = sizeof(response)};
+        msg.msg_iov = &ios;
+        msg.msg_iovlen = 1;
+        // Setup fd as an ancillary data.
+        union
+        {
+            char buf[CMSG_SPACE(sizeof(it->shmFd))];
+            struct cmsghdr align;
+        } cmsgs;
+        msg.msg_control = cmsgs.buf;
+        msg.msg_controllen = sizeof(cmsgs.buf);
+        cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(it->shmFd));
+        memcpy(CMSG_DATA(cmsg), &it->shmFd, sizeof(it->shmFd));
+        // Send the response.
+        int retv = sendmsg(pfd.fd, &msg, 0);
+        if (retv < 0) {
+ warn("%s: sendmsg failed: %s", name().c_str(), strerror(errno));
+            break;
+        }
+        if (retv != sizeof(response)) {
+ warn("%s: failed to send all response at once", name().c_str());
+            break;
+        }
+
+        // Request done.
+        inform("%s: request done", name().c_str());
+        return;
+    } while (false);
+
+    // If we ever reach here, our client either close the connection or is
+    // somehow broken. We'll just close the connection and move on.
+    inform("%s: closing connection", name().c_str());
+    close(pfd.fd);
+    shmServer->clientSocketEvent.reset();
+}
+
+} // namespace memory
+} // namespace gem5
diff --git a/src/mem/shared_memory_server.hh b/src/mem/shared_memory_server.hh
new file mode 100644
index 0000000..9102d74
--- /dev/null
+++ b/src/mem/shared_memory_server.hh
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2022 Google, Inc.
+ *
+ * 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 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.
+ *
+ * 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.
+ */
+
+#ifndef __MEM_SHARED_MEMORY_SERVER_HH__
+#define __MEM_SHARED_MEMORY_SERVER_HH__
+
+#include <memory>
+#include <string>
+
+#include "base/pollevent.hh"
+#include "params/SharedMemoryServer.hh"
+#include "sim/sim_object.hh"
+#include "sim/system.hh"
+
+namespace gem5
+{
+namespace memory
+{
+
+class SharedMemoryServer : public SimObject
+{
+  public:
+    enum class RequestType : int
+    {
+        kGetPhysRange = 0,
+    };
+
+    explicit SharedMemoryServer(const SharedMemoryServerParams& params);
+    ~SharedMemoryServer();
+
+  private:
+    class BaseShmPollEvent : public PollEvent
+    {
+      public:
+        BaseShmPollEvent(int fd, SharedMemoryServer* shm_server);
+
+        const std::string& name() const;
+
+      protected:
+        bool tryReadAll(void* buffer, size_t size);
+
+        SharedMemoryServer* shmServer;
+        std::string eventName;
+    };
+
+    class ListenSocketEvent : public BaseShmPollEvent
+    {
+      public:
+        using BaseShmPollEvent::BaseShmPollEvent;
+        void process(int revent) override;
+    };
+
+    class ClientSocketEvent : public BaseShmPollEvent
+    {
+      public:
+        using BaseShmPollEvent::BaseShmPollEvent;
+        void process(int revent) override;
+    };
+
+    std::string unixSocketPath;
+    System* system;
+
+    int serverFd;
+    std::unique_ptr<ListenSocketEvent> listenSocketEvent;
+    std::unique_ptr<ClientSocketEvent> clientSocketEvent;
+};
+
+} // namespace memory
+} // namespace gem5
+
+#endif  // __MEM_SHARED_MEMORY_SERVER_HH__

--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/57729
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: I9d42fd8a41fc28dcfebb45dec10bc9ebb8e21d11
Gerrit-Change-Number: 57729
Gerrit-PatchSet: 14
Gerrit-Owner: Jui-min Lee <f...@google.com>
Gerrit-Reviewer: Boris Shingarov <shinga...@labware.com>
Gerrit-Reviewer: Earl Ou <shunhsin...@google.com>
Gerrit-Reviewer: Gabe Black <gabe.bl...@gmail.com>
Gerrit-Reviewer: Jui-min Lee <f...@google.com>
Gerrit-Reviewer: Nikos Nikoleris <nikos.nikole...@arm.com>
Gerrit-Reviewer: Yu-hsin Wang <yuhsi...@google.com>
Gerrit-Reviewer: kokoro <noreply+kok...@google.com>
Gerrit-MessageType: merged
_______________________________________________
gem5-dev mailing list -- gem5-dev@gem5.org
To unsubscribe send an email to gem5-dev-le...@gem5.org
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s

Reply via email to