Jui-min Lee has uploaded this change for review. ( 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.

We also add a new option in System.py so people can specify the path of
the unix socket providing such service.

Basically, the service works in this way:
1. client connect to the unix socket created by a gem5 system
2. client send a request {start, end} to gem5
3. the system 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.

Bug: 222145732
Test: None
Change-Id: I9d42fd8a41fc28dcfebb45dec10bc9ebb8e21d11
---
M src/base/socket.hh
M src/mem/SConscript
M src/mem/physical.cc
M src/mem/physical.hh
A src/mem/shared_memory_server.cc
A src/mem/shared_memory_server.hh
M src/sim/System.py
M src/sim/system.cc
M src/sim/system.hh
9 files changed, 356 insertions(+), 11 deletions(-)



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..c0cc4d7 100644
--- a/src/mem/SConscript
+++ b/src/mem/SConscript
@@ -80,6 +80,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/physical.cc b/src/mem/physical.cc
index ebc9ca8..c012ef7 100644
--- a/src/mem/physical.cc
+++ b/src/mem/physical.cc
@@ -248,7 +248,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..37bcd46 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, off_t shm_offset)
         : 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,19 @@
       * 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
+      * unspecified and should not be rely upon.
+      */
+     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..3b78527
--- /dev/null
+++ b/src/mem/shared_memory_server.cc
@@ -0,0 +1,206 @@
+/*
+ * 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/pollevent.hh"
+#include "base/socket.hh"
+
+namespace gem5
+{
+namespace memory
+{
+
+namespace
+{
+bool
+tryReadAll(int fd, void* buffer, size_t size)
+{
+    char* char_buffer = reinterpret_cast<char*>(buffer);
+    for (size_t offset = 0; offset < size;) {
+        ssize_t retv = recv(fd, char_buffer + offset, size - offset, 0);
+        if (retv >= 0) {
+            offset += retv;
+        } else if (errno != EINTR) {
+            // If the client goes wrong, just shut it down.
+            warn("recv failed: %s", strerror(errno));
+            return false;
+        }
+    }
+    return true;
+}
+}  // namespace
+
+SharedMemoryServer::SharedMemoryServer(const std::string& unix_socket_path,
+                                       PhysicalMemory* phys_mem)
+    : unixSocketPath(unix_socket_path), physMem(phys_mem), serverFd(-1)
+{
+    // Ensure the unix socket path to use is not occupied.
+    unlink(unixSocketPath.c_str());
+    // Create a new unix socket.
+    serverFd = ListenSocket::socketCloexec(AF_UNIX, SOCK_STREAM, 0);
+ fatal_if(serverFd < 0, "cannot create unix socket: %s", 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(),
+            "unix socket path truncated, expect '%s' but get '%s'",
+            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, "cannot bind unix socket: %s", strerror(errno));
+    // Start listening.
+    int listen_retv = listen(serverFd, 1);
+    fatal_if(listen_retv != 0, "listen failed: %s", strerror(errno));
+    listenSocketEvent.reset(new ListenSocketEvent(serverFd, this));
+    pollQueue.schedule(listenSocketEvent.get());
+}
+
+SharedMemoryServer::~SharedMemoryServer()
+{
+    int unlink_retv = unlink(unixSocketPath.c_str());
+    warn_if(unlink_retv != 0, "cannot unlink unix socket: %s",
+            strerror(errno));
+    int close_retv = close(serverFd);
+ warn_if(close_retv != 0, "cannot close unix socket: %s", strerror(errno));
+}
+
+SharedMemoryServer::BaseShmPollEvent::BaseShmPollEvent(
+    int fd, SharedMemoryServer* shm_server)
+    : PollEvent(fd, POLLIN), shmServer(shm_server)
+{
+}
+
+void
+SharedMemoryServer::ListenSocketEvent::process(int revents)
+{
+    panic_if(revents & (POLLERR | POLLNVAL), "listen socket is broken");
+    int cli_fd = ListenSocket::acceptCloexec(pfd.fd, nullptr, nullptr);
+    panic_if(cli_fd < 0, "accept failed: %s", strerror(errno));
+    panic_if(shmServer->clientSocketEvent.get(),
+             "cannot serve two clients at once");
+    shmServer->clientSocketEvent.reset(
+        new ClientSocketEvent(cli_fd, shmServer));
+    pollQueue.schedule(shmServer->clientSocketEvent.get());
+}
+
+void
+SharedMemoryServer::ClientSocketEvent::process(int revents)
+{
+ // If anything is wrong with the socket (e.g. client drops), just close it.
+    if (revents & (POLLHUP | POLLERR | POLLNVAL)) {
+        close(pfd.fd);
+        shmServer->clientSocketEvent.reset();
+        return;
+    }
+
+ // Receive a request packet. We ignore the endianness as unix socket only
+    // allows communication on the same system anyway.
+    RequestType req_type;
+    // Assuming the type to be kGetPhysRange.
+    struct
+    {
+        uint64_t start;
+        uint64_t end;
+    } request;
+    if (!tryReadAll(pfd.fd, &req_type, sizeof(req_type)) ||
+        req_type != RequestType::kGetPhysRange ||
+        !tryReadAll(pfd.fd, &request, sizeof(request))) {
+        // If the client goes wrong, just shut it down.
+        shutdown(pfd.fd, SHUT_RDWR);
+        return;
+    }
+    AddrRange range(request.start, request.end);
+
+    // Identify the backing store.
+    const auto& stores = shmServer->physMem->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("Cannot find shared memory at %s", range.to_string().c_str());
+        // Refuse to serve the client anymore if it send something invalid.
+        shutdown(pfd.fd, SHUT_RDWR);
+        return;
+    }
+
+    // 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.
+    union
+    {
+        off_t offset;
+        char raw_data[0];
+    } 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.raw_data, .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);
+    fatal_if(retv < 0, "sendmsg failed: %s", strerror(errno));
+ fatal_if(retv != sizeof(response), "failed to send all response at once");
+}
+
+} // 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..eeb0f03
--- /dev/null
+++ b/src/mem/shared_memory_server.hh
@@ -0,0 +1,86 @@
+/*
+ * 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 "mem/physical.hh"
+
+namespace gem5 {
+namespace memory {
+
+class SharedMemoryServer
+{
+  public:
+    enum class RequestType : int
+    {
+        kGetPhysRange = 0,
+    };
+
+    explicit SharedMemoryServer(const std::string& unix_socket_path,
+                                PhysicalMemory* phys_mem);
+    ~SharedMemoryServer();
+
+  private:
+    class BaseShmPollEvent : public PollEvent
+    {
+      public:
+        BaseShmPollEvent(int fd, SharedMemoryServer* shm_server);
+
+        SharedMemoryServer* shmServer;
+    };
+
+    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;
+    PhysicalMemory* physMem;
+
+    int serverFd;
+    std::unique_ptr<ListenSocketEvent> listenSocketEvent;
+    std::unique_ptr<ClientSocketEvent> clientSocketEvent;
+};
+
+}  // namespace memory
+}  // namespace gem5
+
+#endif  // __MEM_SHARED_MEMORY_SERVER_HH__
diff --git a/src/sim/System.py b/src/sim/System.py
index b5bd5df..f4dba71 100644
--- a/src/sim/System.py
+++ b/src/sim/System.py
@@ -90,6 +90,9 @@
auto_unlink_shared_backstore = Param.Bool(False, "Automatically remove the "
         "shmem segment file upon destruction. This is used only if "
         "shared_backstore is non-empty.")
+ shared_memory_server = Param.String("", "The unix socket path where the "
+        "shared memory server should be running upon. Leave this empty to "
+        "disable the server.")

     cache_line_size = Param.Unsigned(64, "Cache line size in bytes")

diff --git a/src/sim/system.cc b/src/sim/system.cc
index b7fba8a..7772988 100644
--- a/src/sim/system.cc
+++ b/src/sim/system.cc
@@ -47,6 +47,7 @@
 #include "base/cprintf.hh"
 #include "base/loader/object_file.hh"
 #include "base/loader/symtab.hh"
+#include "base/output.hh"
 #include "base/str.hh"
 #include "base/trace.hh"
 #include "config/the_isa.hh"
@@ -222,6 +223,12 @@
     // Set back pointers to the system in all memories
     for (int x = 0; x < params().memories.size(); x++)
         params().memories[x]->system(this);
+
+    // Create shared memory server if requested.
+    if (!p.shared_memory_server.empty()) {
+        shmServer.reset(new memory::SharedMemoryServer(
+            simout.resolve(p.shared_memory_server), &physmem));
+    }
 }

 System::~System()
diff --git a/src/sim/system.hh b/src/sim/system.hh
index bb855e0..8fad70b 100644
--- a/src/sim/system.hh
+++ b/src/sim/system.hh
@@ -58,6 +58,7 @@
 #include "mem/physical.hh"
 #include "mem/port.hh"
 #include "mem/port_proxy.hh"
+#include "mem/shared_memory_server.hh"
 #include "params/System.hh"
 #include "sim/futex_map.hh"
 #include "sim/redirect_path.hh"
@@ -404,6 +405,7 @@
     KvmVM *kvmVM = nullptr;

     memory::PhysicalMemory physmem;
+    std::unique_ptr<memory::SharedMemoryServer> shmServer;

     AddrRangeList ShadowRomRanges;


--
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: 1
Gerrit-Owner: Jui-min Lee <f...@google.com>
Gerrit-MessageType: newchange
_______________________________________________
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