mem: Add SharedMemoryClient
Add a client implementation corresponding to the server so people who want to utilize the server have an example. Change-Id: Idf1a8f5a406271bbcbe0b338e709e89b1512c8a8 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/61050 Reviewed-by: Jason Lowe-Power <power.jg@gmail.com> Tested-by: kokoro <noreply+kokoro@google.com> Maintainer: Jason Lowe-Power <power.jg@gmail.com>
This commit is contained in:
43
util/mem/Makefile
Normal file
43
util/mem/Makefile
Normal file
@@ -0,0 +1,43 @@
|
||||
# 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.
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
CXXFLAGS ?= -g -O2
|
||||
CPPFLAGS ?= -MD -MP
|
||||
|
||||
SRCS = shared_memory_client_example.cc
|
||||
EXES = $(SRCS:.cc=)
|
||||
DEPS = $(SRCS:.cc=.d)
|
||||
|
||||
all: $(EXES)
|
||||
|
||||
clean:
|
||||
rm -rf $(EXES) $(DEPS)
|
||||
|
||||
$(EXES): %: %.cc
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS)
|
||||
|
||||
-include $(DEPS)
|
||||
278
util/mem/shared_memory_client.hh
Normal file
278
util/mem/shared_memory_client.hh
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* 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 __UTIL_MEM_SHARED_MEMORY_CLIENT_HH__
|
||||
#define __UTIL_MEM_SHARED_MEMORY_CLIENT_HH__
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace gem5
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace memory
|
||||
{
|
||||
|
||||
class SharedMemoryClient
|
||||
{
|
||||
public:
|
||||
enum RequestType : int
|
||||
{
|
||||
kGetPhysRange = 0
|
||||
};
|
||||
|
||||
explicit SharedMemoryClient(const std::string& server_path);
|
||||
|
||||
// Request to access the range [start, end] of physical memory from the
|
||||
// viewpoint of the gem5 system providing the shared memory service.
|
||||
// There is no guarantee that the physical memory range is not used by
|
||||
// others. It is the user's responsibility to make sure not to break other
|
||||
// services or IPs accessing the same range. For example, you might want to
|
||||
// configure the kernel running in gem5 simulator to reserve such range.
|
||||
void* MapMemory(uint64_t start, uint64_t end);
|
||||
|
||||
// Unmap previous mapped region, no client is needed here.
|
||||
static bool UnmapMemory(void* mem);
|
||||
|
||||
private:
|
||||
using AllocRecordStorage = std::unordered_map<void*, size_t>;
|
||||
|
||||
int GetConnection();
|
||||
bool SendGetPhysRangeRequest(int sock_fd, uint64_t start, uint64_t end);
|
||||
bool RecvGetPhysRangeResponse(int sock_fd, int* ptr_fd, off_t* ptr_offset);
|
||||
void* DoMap(int shm_fd, off_t shm_offset, size_t size);
|
||||
|
||||
bool SendAll(int sock_fd, const void* buffer, size_t size);
|
||||
|
||||
static AllocRecordStorage& GetAllocRecordStorage();
|
||||
|
||||
std::string server_path_;
|
||||
};
|
||||
|
||||
inline SharedMemoryClient::SharedMemoryClient(const std::string& server_path)
|
||||
: server_path_(server_path)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
inline void*
|
||||
SharedMemoryClient::MapMemory(uint64_t start, uint64_t end)
|
||||
{
|
||||
void* mem = nullptr;
|
||||
int sock_fd = -1;
|
||||
int shm_fd = -1;
|
||||
off_t shm_offset;
|
||||
|
||||
do {
|
||||
if (start > end) {
|
||||
warnx("invalid range %" PRIu64 "-%" PRIu64, start, end);
|
||||
break;
|
||||
}
|
||||
sock_fd = GetConnection();
|
||||
if (sock_fd < 0) {
|
||||
warnx("cannot connect to shared memory server");
|
||||
break;
|
||||
}
|
||||
if (!SendGetPhysRangeRequest(sock_fd, start, end)) {
|
||||
warnx("cannot send request to shared memory server");
|
||||
break;
|
||||
}
|
||||
if (!RecvGetPhysRangeResponse(sock_fd, &shm_fd, &shm_offset)) {
|
||||
warnx("failed to read shared memory server response");
|
||||
break;
|
||||
}
|
||||
mem = DoMap(shm_fd, shm_offset, end - start + 1);
|
||||
if (mem == nullptr) {
|
||||
warnx("failed to create memory mapping");
|
||||
break;
|
||||
}
|
||||
} while (false);
|
||||
|
||||
if (sock_fd >= 0) {
|
||||
close(sock_fd);
|
||||
}
|
||||
if (shm_fd >= 0) {
|
||||
close(shm_fd);
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
inline bool
|
||||
SharedMemoryClient::UnmapMemory(void* mem)
|
||||
{
|
||||
auto& storage = GetAllocRecordStorage();
|
||||
auto it = storage.find(mem);
|
||||
if (it == storage.end()) {
|
||||
return false;
|
||||
}
|
||||
if (munmap(mem, it->second) < 0) {
|
||||
warn("munmap failed");
|
||||
return false;
|
||||
}
|
||||
storage.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline int
|
||||
SharedMemoryClient::GetConnection()
|
||||
{
|
||||
int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
warn("create unix socket failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sockaddr_un serv_addr;
|
||||
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr.sun_family = AF_UNIX;
|
||||
strncpy(serv_addr.sun_path, server_path_.c_str(),
|
||||
sizeof(serv_addr.sun_path) - 1);
|
||||
if (strlen(serv_addr.sun_path) != server_path_.size()) {
|
||||
warnx("server address truncated");
|
||||
close(sock_fd);
|
||||
return -1;
|
||||
}
|
||||
if (connect(sock_fd, reinterpret_cast<sockaddr*>(&serv_addr),
|
||||
sizeof(serv_addr)) < 0) {
|
||||
warn("connect failed");
|
||||
close(sock_fd);
|
||||
return -1;
|
||||
}
|
||||
return sock_fd;
|
||||
}
|
||||
|
||||
inline bool
|
||||
SharedMemoryClient::SendGetPhysRangeRequest(int sock_fd, uint64_t start,
|
||||
uint64_t end)
|
||||
{
|
||||
int req_type = RequestType::kGetPhysRange;
|
||||
struct
|
||||
{
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
} request = {start, end};
|
||||
return SendAll(sock_fd, &req_type, sizeof(req_type)) &&
|
||||
SendAll(sock_fd, &request, sizeof(request));
|
||||
}
|
||||
|
||||
inline bool
|
||||
SharedMemoryClient::RecvGetPhysRangeResponse(int sock_fd, int* ptr_fd,
|
||||
off_t* ptr_offset)
|
||||
{
|
||||
if (!ptr_fd || !ptr_offset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
msghdr msg = {};
|
||||
// Setup ptr_offset as buffer.
|
||||
iovec io = {.iov_base = ptr_offset, .iov_len = sizeof(*ptr_offset)};
|
||||
msg.msg_iov = &io;
|
||||
msg.msg_iovlen = 1;
|
||||
// Setup buffer for fd.
|
||||
union
|
||||
{
|
||||
char buffer[CMSG_SPACE(sizeof(*ptr_fd))];
|
||||
struct cmsghdr align;
|
||||
} cmsgs;
|
||||
msg.msg_control = cmsgs.buffer;
|
||||
msg.msg_controllen = sizeof(cmsgs.buffer);
|
||||
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(*ptr_fd));
|
||||
// Try receive the message.
|
||||
ssize_t retv = recvmsg(sock_fd, &msg, 0);
|
||||
if (retv < 0) {
|
||||
warn("recvmsg failed");
|
||||
return false;
|
||||
}
|
||||
if (retv != sizeof(*ptr_offset)) {
|
||||
warnx("cannot receive all response");
|
||||
return false;
|
||||
}
|
||||
memcpy(ptr_fd, CMSG_DATA(cmsg), sizeof(*ptr_fd));
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void*
|
||||
SharedMemoryClient::DoMap(int shm_fd, off_t shm_offset, size_t size)
|
||||
{
|
||||
void* mem = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd,
|
||||
shm_offset);
|
||||
if (mem == MAP_FAILED) {
|
||||
warn("mmap failed");
|
||||
return nullptr;
|
||||
}
|
||||
// If we cannot record a new mapping, our mapping are probably corrupted.
|
||||
if (!GetAllocRecordStorage().emplace(mem, size).second) {
|
||||
errx(EXIT_FAILURE, "cannot register memory mapping!");
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
|
||||
inline bool
|
||||
SharedMemoryClient::SendAll(int sock_fd, const void* buffer, size_t size)
|
||||
{
|
||||
const char* char_buffer = reinterpret_cast<const char*>(buffer);
|
||||
for (size_t offset = 0; offset < size;) {
|
||||
ssize_t retv = send(sock_fd, char_buffer + offset, size - offset, 0);
|
||||
if (retv >= 0) {
|
||||
offset += retv;
|
||||
} else if (errno != EINTR) {
|
||||
warn("send failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline SharedMemoryClient::AllocRecordStorage&
|
||||
SharedMemoryClient::GetAllocRecordStorage()
|
||||
{
|
||||
static auto storage = new SharedMemoryClient::AllocRecordStorage();
|
||||
return *storage;
|
||||
}
|
||||
|
||||
} // namespace memory
|
||||
} // namespace util
|
||||
} // namespace gem5
|
||||
|
||||
#endif // __UTIL_MEM_SHARED_MEMORY_CLIENT_HH__
|
||||
117
util/mem/shared_memory_client_example.cc
Normal file
117
util/mem/shared_memory_client_example.cc
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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 <iomanip>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "shared_memory_client.hh"
|
||||
|
||||
/**
|
||||
* Consider that
|
||||
* 1. you have a SimpleMemory that spans from 0x0 to 0x10000
|
||||
* 2. you have a SharedMemoryServer with server_path=ram.sock
|
||||
* 3. The SHM server and the Mem is under the same System
|
||||
*
|
||||
* You should see a unix socket m5out/ram.sock, and you should be able to run
|
||||
* our example with commands like:
|
||||
* `./shared_memory_client_example m5out/ram.sock 0x1000 0x1010`
|
||||
*
|
||||
* The example will use the client to map the range 0x1000-0x1010 of the
|
||||
* SimpleMemory into the address space of the example. As a result, the example
|
||||
* will be able to access the backing store of the SimpleMemory with just a
|
||||
* normal pointer.
|
||||
*/
|
||||
|
||||
static void PrintAsHexString(char *buffer, uint64_t size);
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 4) {
|
||||
std::cerr << "Usage: " << argv[0] << " <shm_sock_path> <start> <end>"
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Each SharedMemoryServer in gem5 will create a unix socket at the
|
||||
// location specified in its server_path parameter, and the socket can be
|
||||
// used by the client to communicate with the server.
|
||||
gem5::util::memory::SharedMemoryClient shm_client(argv[1]);
|
||||
|
||||
// Before request access to the simulated physical memory in gem5, we need
|
||||
// to first determine what's the range we'd like to access.
|
||||
uint64_t start = std::stoull(argv[2], nullptr, 0);
|
||||
uint64_t end = std::stoull(argv[3], nullptr, 0);
|
||||
|
||||
// One thing important is that, non-align request is not supported now, so
|
||||
// we'll need to ensure the start address is on page boundary.
|
||||
long page_size = sysconf(_SC_PAGESIZE);
|
||||
if (page_size < 0) {
|
||||
std::cerr << "Cannot determine page size" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (start % page_size != 0) {
|
||||
std::cerr << "Start address must be aligned" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// The Map request, if success, will return a void* pointer, which can then
|
||||
// be used as a backdoor to the physical memory range in gem5. If there's
|
||||
// any error, nullptr will be returned.
|
||||
char *mem = reinterpret_cast<char *>(shm_client.MapMemory(start, end));
|
||||
uint64_t size = end - start + 1;
|
||||
if (mem == nullptr) {
|
||||
std::cerr << "Unable to map memory" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// A simple use case that print and randomly fill the memory.
|
||||
std::cout << "Content was: ";
|
||||
PrintAsHexString(mem, size);
|
||||
// Override content with random value.
|
||||
std::random_device rand_dev;
|
||||
std::uniform_int_distribution<int> rand_dist(0, 255);
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
mem[i] = rand_dist(rand_dev);
|
||||
}
|
||||
std::cout << "Content is: ";
|
||||
PrintAsHexString(mem, size);
|
||||
}
|
||||
|
||||
static void
|
||||
PrintAsHexString(char *buffer, uint64_t size)
|
||||
{
|
||||
for (uint64_t i = 0; i < size; ++i) {
|
||||
std::cout << std::setw(2) << std::setfill('0') << std::hex
|
||||
<< (static_cast<int>(buffer[i]) & 0xff);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
Reference in New Issue
Block a user