1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-16 00:00:02 +01:00
VILLASnode/common/lib/memory.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

291 lines
7.3 KiB
C++
Raw Normal View History

2018-08-21 00:25:44 +02:00
/** Memory managment.
*
* @author Daniel Krebs <github@daniel-krebs.net>
2022-03-15 09:05:42 -04:00
* @copyright 2014-2022, Institute for Automation of Complex Power Systems, EONERC
2022-05-19 17:40:10 +02:00
* @license Apache License 2.0
2018-08-21 00:25:44 +02:00
*********************************************************************************/
#include <sys/mman.h>
2020-07-01 15:35:15 +02:00
#include <fcntl.h>
2018-08-21 00:25:44 +02:00
#include <unistd.h>
#include <sstream>
#include <fstream>
#include <villas/memory.hpp>
namespace villas {
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
HostRam::HostRamAllocator::allocateBlock(size_t size)
{
// Align to next bigger page size chunk
2018-08-21 00:25:44 +02:00
if (size & size_t(0xFFF)) {
size += size_t(0x1000);
size &= size_t(~0xFFF);
}
#if defined(__linux__) && !(defined(__arm__) || defined(__aarch64__))
2018-08-22 11:31:04 +02:00
const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT;
#else
const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
2018-08-21 15:55:32 +02:00
#endif
2018-08-22 11:31:04 +02:00
const int mmap_protection = PROT_READ | PROT_WRITE;
2018-08-21 15:55:32 +02:00
2018-08-21 00:25:44 +02:00
const void* addr = mmap(nullptr, size, mmap_protection, mmap_flags, 0, 0);
2020-06-11 18:33:34 +02:00
if (addr == nullptr) {
2018-08-21 00:25:44 +02:00
throw std::bad_alloc();
}
2020-06-14 21:53:18 +02:00
auto &mm = MemoryManager::get();
2018-08-21 00:25:44 +02:00
// Assemble name for this block
2018-08-21 00:25:44 +02:00
std::stringstream name;
name << std::showbase << std::hex << reinterpret_cast<uintptr_t>(addr);
auto blockAddrSpaceId = mm.getProcessAddressSpaceMemoryBlock(name.str());
const auto localAddr = reinterpret_cast<uintptr_t>(addr);
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
mem(new MemoryBlock(localAddr, size, blockAddrSpaceId), this->free);
insertMemoryBlock(*mem);
return mem;
}
2021-09-19 19:16:32 +02:00
// cppcheck-suppress passedByValue
2018-08-21 00:25:44 +02:00
LinearAllocator::LinearAllocator(MemoryManager::AddressSpaceId memoryAddrSpaceId,
size_t memorySize,
size_t internalOffset) :
BaseAllocator(memoryAddrSpaceId),
nextFreeAddress(0),
memorySize(memorySize),
internalOffset(internalOffset),
allocationCount(0)
{
// Make sure to start at aligned offset, reduce size in case we need padding
2020-06-11 18:33:34 +02:00
if (const size_t paddingBytes = getAlignmentPadding(internalOffset)) {
2018-08-21 00:25:44 +02:00
assert(paddingBytes < memorySize);
internalOffset += paddingBytes;
memorySize -= paddingBytes;
}
// Deallocation callback
2018-08-21 00:25:44 +02:00
free = [&](MemoryBlock* mem) {
logger->debug("Deallocating memory block at local addr {:#x} (addr space {})",
mem->getOffset(), mem->getAddrSpaceId());
removeMemoryBlock(*mem);
allocationCount--;
2020-06-11 18:33:34 +02:00
if (allocationCount == 0) {
2018-08-21 00:25:44 +02:00
logger->debug("All allocations are deallocated now, freeing memory");
// All allocations have been deallocated, free all memory
2018-08-21 00:25:44 +02:00
nextFreeAddress = 0;
}
logger->debug("Available memory: {:#x} bytes", getAvailableMemory());
};
}
std::string
LinearAllocator::getName() const
{
std::stringstream name;
name << "LinearAlloc" << getAddrSpaceId();
2020-06-11 18:33:34 +02:00
if (internalOffset != 0) {
2018-08-21 00:25:44 +02:00
name << "@0x" << std::hex << internalOffset;
}
return name.str();
}
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
LinearAllocator::allocateBlock(size_t size)
{
2020-06-11 18:33:34 +02:00
if (size > getAvailableMemory()) {
2018-08-21 00:25:44 +02:00
throw std::bad_alloc();
}
// Assign address
2018-08-21 00:25:44 +02:00
const uintptr_t localAddr = nextFreeAddress + internalOffset;
// Reserve memory
2018-08-21 00:25:44 +02:00
nextFreeAddress += size;
// Make sure it is aligned
2020-06-11 18:33:34 +02:00
if (const size_t paddingBytes = getAlignmentPadding(nextFreeAddress)) {
2018-08-21 00:25:44 +02:00
nextFreeAddress += paddingBytes;
// If next free address is outside this block due to padding, cap it
2018-08-21 00:25:44 +02:00
nextFreeAddress = std::min(nextFreeAddress, memorySize);
}
2020-06-14 21:53:18 +02:00
auto &mm = MemoryManager::get();
2018-08-21 00:25:44 +02:00
// Assemble name for this block
2018-08-21 00:25:44 +02:00
std::stringstream blockName;
blockName << std::showbase << std::hex << localAddr;
// Create address space
2018-08-21 00:25:44 +02:00
auto addrSpaceName = mm.getSlaveAddrSpaceName(getName(), blockName.str());
auto addrSpaceId = mm.getOrCreateAddressSpace(addrSpaceName);
logger->debug("Allocated {:#x} bytes for {}, {:#x} bytes remaining",
size, addrSpaceId, getAvailableMemory());
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
mem(new MemoryBlock(localAddr, size, addrSpaceId), this->free);
// Mount block into the memory graph
2018-08-21 00:25:44 +02:00
insertMemoryBlock(*mem);
// Increase the allocation count
2018-08-21 00:25:44 +02:00
allocationCount++;
return mem;
}
HostRam::HostRamAllocator
HostRam::allocator;
HostRam::HostRamAllocator::HostRamAllocator() :
BaseAllocator(MemoryManager::get().getProcessAddressSpace())
{
free = [&](MemoryBlock* mem) {
2020-06-11 18:33:34 +02:00
if (::munmap(reinterpret_cast<void*>(mem->getOffset()), mem->getSize()) != 0) {
2018-08-21 00:25:44 +02:00
logger->warn("munmap() failed for {:#x} of size {:#x}",
mem->getOffset(), mem->getSize());
}
removeMemoryBlock(*mem);
};
}
std::map<int, std::unique_ptr<HostDmaRam::HostDmaRamAllocator>>
HostDmaRam::allocators;
HostDmaRam::HostDmaRamAllocator::HostDmaRamAllocator(int num) :
LinearAllocator(MemoryManager::get().getOrCreateAddressSpace(getUdmaBufName(num)), getUdmaBufBufSize(num)),
num(num)
{
2020-06-14 21:53:18 +02:00
auto &mm = MemoryManager::get();
2018-10-19 14:33:10 +02:00
logger = logging.get(getName());
2018-08-21 00:25:44 +02:00
2020-06-11 18:33:34 +02:00
if (getSize() == 0) {
2018-08-21 00:25:44 +02:00
logger->error("Zero-sized DMA buffer not supported, is the kernel module loaded?");
throw std::bad_alloc();
}
const uintptr_t base = getUdmaBufPhysAddr(num);
mm.createMapping(base, 0, getSize(), getName() + "-PCI",
mm.getPciAddressSpace(), getAddrSpaceId());
const auto bufPath = std::string("/dev/") + getUdmaBufName(num);
const int bufFd = open(bufPath.c_str(), O_RDWR | O_SYNC);
2020-06-11 18:33:34 +02:00
if (bufFd != -1) {
2018-08-21 00:25:44 +02:00
void* buf = mmap(nullptr, getSize(), PROT_READ|PROT_WRITE, MAP_SHARED, bufFd, 0);
close(bufFd);
2020-06-15 21:05:51 +02:00
if (buf != MAP_FAILED)
2018-08-21 00:25:44 +02:00
mm.createMapping(reinterpret_cast<uintptr_t>(buf), 0, getSize(),
getName() + "-VA",
mm.getProcessAddressSpace(), getAddrSpaceId());
2020-06-15 21:05:51 +02:00
else
2018-08-21 00:25:44 +02:00
logger->warn("Cannot map {}", bufPath);
}
2020-06-15 21:05:51 +02:00
else
logger->warn("Cannot open {}", bufPath);
2018-08-21 00:25:44 +02:00
logger->info("Mapped {} of size {} bytes", bufPath, getSize());
}
HostDmaRam::HostDmaRamAllocator::~HostDmaRamAllocator()
{
2020-06-14 21:53:18 +02:00
auto &mm = MemoryManager::get();
2018-08-21 00:25:44 +02:00
void* baseVirt;
try {
auto translation = mm.getTranslationFromProcess(getAddrSpaceId());
baseVirt = reinterpret_cast<void*>(translation.getLocalAddr(0));
2020-07-27 16:40:48 +02:00
} catch (const std::out_of_range&) {
// Not mapped, nothing to do
2018-08-21 00:25:44 +02:00
return;
}
logger->debug("Unmapping {}", getName());
// Try to unmap it
2020-06-11 18:33:34 +02:00
if (::munmap(baseVirt, getSize()) != 0) {
2018-08-21 00:25:44 +02:00
logger->warn("munmap() failed for {:p} of size {:#x}",
baseVirt, getSize());
}
}
std::string
HostDmaRam::getUdmaBufName(int num)
{
std::stringstream name;
name << "udmabuf" << num;
return name.str();
}
std::string
HostDmaRam::getUdmaBufBasePath(int num)
{
std::stringstream path;
path << "/sys/class/udmabuf/udmabuf" << num << "/";
return path.str();
}
size_t
HostDmaRam::getUdmaBufBufSize(int num)
{
std::fstream s(getUdmaBufBasePath(num) + "size", s.in);
2020-06-11 18:33:34 +02:00
if (s.is_open()) {
2018-08-21 00:25:44 +02:00
std::string line;
2020-06-11 18:33:34 +02:00
if (std::getline(s, line)) {
2018-08-21 00:25:44 +02:00
return std::strtoul(line.c_str(), nullptr, 10);
}
}
return 0;
}
uintptr_t
HostDmaRam::getUdmaBufPhysAddr(int num)
{
std::fstream s(getUdmaBufBasePath(num) + "phys_addr", s.in);
2020-06-11 18:33:34 +02:00
if (s.is_open()) {
2018-08-21 00:25:44 +02:00
std::string line;
2020-06-11 18:33:34 +02:00
if (std::getline(s, line)) {
2018-08-21 00:25:44 +02:00
return std::strtoul(line.c_str(), nullptr, 16);
}
}
return UINTPTR_MAX;
}
HostDmaRam::HostDmaRamAllocator&HostDmaRam::getAllocator(int num)
{
2020-06-14 21:53:18 +02:00
auto &allocator = allocators[num];
2020-06-11 18:33:34 +02:00
if (not allocator) {
2018-08-21 00:25:44 +02:00
allocator = std::make_unique<HostDmaRamAllocator>(num);
}
return *allocator;
}
} // namespace villas