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