2023-08-28 12:31:18 +02:00
|
|
|
/* Memory managment.
|
2018-08-21 00:25:44 +02:00
|
|
|
*
|
2023-08-31 11:17:07 +02:00
|
|
|
* Author: Daniel Krebs <github@daniel-krebs.net>
|
|
|
|
* SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2023-08-28 12:31:18 +02:00
|
|
|
*/
|
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 <fstream>
|
2023-09-07 13:19:19 +02:00
|
|
|
#include <sstream>
|
2018-08-21 00:25:44 +02:00
|
|
|
|
|
|
|
#include <villas/memory.hpp>
|
|
|
|
|
|
|
|
namespace villas {
|
|
|
|
|
|
|
|
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
2023-09-07 13:19:19 +02:00
|
|
|
HostRam::HostRamAllocator::allocateBlock(size_t size) {
|
|
|
|
// Align to next bigger page size chunk
|
|
|
|
if (size & size_t(0xFFF)) {
|
|
|
|
size += size_t(0x1000);
|
|
|
|
size &= size_t(~0xFFF);
|
|
|
|
}
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2019-06-27 01:37:56 +02:00
|
|
|
#if defined(__linux__) && !(defined(__arm__) || defined(__aarch64__))
|
2023-09-07 13:19:19 +02:00
|
|
|
const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT;
|
2018-08-22 11:31:04 +02:00
|
|
|
#else
|
2023-09-07 13:19:19 +02:00
|
|
|
const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
2018-08-21 15:55:32 +02:00
|
|
|
#endif
|
2023-09-07 13:19:19 +02:00
|
|
|
const int mmap_protection = PROT_READ | PROT_WRITE;
|
2018-08-21 15:55:32 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
const void *addr = mmap(nullptr, size, mmap_protection, mmap_flags, 0, 0);
|
|
|
|
if (addr == nullptr) {
|
|
|
|
throw std::bad_alloc();
|
|
|
|
}
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
auto &mm = MemoryManager::get();
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
// Assemble name for this block
|
|
|
|
std::stringstream name;
|
|
|
|
name << std::showbase << std::hex << reinterpret_cast<uintptr_t>(addr);
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
auto blockAddrSpaceId = mm.getProcessAddressSpaceMemoryBlock(name.str());
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
const auto localAddr = reinterpret_cast<uintptr_t>(addr);
|
|
|
|
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> mem(
|
|
|
|
new MemoryBlock(localAddr, size, blockAddrSpaceId), this->free);
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
insertMemoryBlock(*mem);
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return mem;
|
2018-08-21 00:25:44 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
LinearAllocator::LinearAllocator(
|
2023-09-08 09:02:39 +00:00
|
|
|
const MemoryManager::AddressSpaceId &memoryAddrSpaceId, size_t memorySize,
|
2023-09-07 13:19:19 +02:00
|
|
|
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
|
|
|
|
if (const size_t paddingBytes = getAlignmentPadding(internalOffset)) {
|
|
|
|
assert(paddingBytes < memorySize);
|
|
|
|
|
|
|
|
internalOffset += paddingBytes;
|
|
|
|
memorySize -= paddingBytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deallocation callback
|
|
|
|
free = [&](MemoryBlock *mem) {
|
|
|
|
logger->debug(
|
|
|
|
"Deallocating memory block at local addr {:#x} (addr space {})",
|
|
|
|
mem->getOffset(), mem->getAddrSpaceId());
|
|
|
|
|
|
|
|
removeMemoryBlock(*mem);
|
|
|
|
|
|
|
|
allocationCount--;
|
|
|
|
if (allocationCount == 0) {
|
|
|
|
logger->debug("All allocations are deallocated now, freeing memory");
|
|
|
|
|
|
|
|
// All allocations have been deallocated, free all memory
|
|
|
|
nextFreeAddress = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
logger->debug("Available memory: {:#x} bytes", getAvailableMemory());
|
|
|
|
};
|
2018-08-21 00:25:44 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
std::string LinearAllocator::getName() const {
|
|
|
|
std::stringstream name;
|
|
|
|
name << "LinearAlloc" << getAddrSpaceId();
|
|
|
|
if (internalOffset != 0) {
|
|
|
|
name << "@0x" << std::hex << internalOffset;
|
|
|
|
}
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return name.str();
|
2018-08-21 00:25:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
2023-09-07 13:19:19 +02:00
|
|
|
LinearAllocator::allocateBlock(size_t size) {
|
|
|
|
if (size > getAvailableMemory()) {
|
|
|
|
throw std::bad_alloc();
|
|
|
|
}
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
// Assign address
|
|
|
|
const uintptr_t localAddr = nextFreeAddress + internalOffset;
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
// Reserve memory
|
|
|
|
nextFreeAddress += size;
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
// Make sure it is aligned
|
|
|
|
if (const size_t paddingBytes = getAlignmentPadding(nextFreeAddress)) {
|
|
|
|
nextFreeAddress += paddingBytes;
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
// If next free address is outside this block due to padding, cap it
|
|
|
|
nextFreeAddress = std::min(nextFreeAddress, memorySize);
|
|
|
|
}
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
auto &mm = MemoryManager::get();
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
// Assemble name for this block
|
|
|
|
std::stringstream blockName;
|
|
|
|
blockName << std::showbase << std::hex << localAddr;
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
// Create address space
|
|
|
|
auto addrSpaceName = mm.getSlaveAddrSpaceName(getName(), blockName.str());
|
|
|
|
auto addrSpaceId = mm.getOrCreateAddressSpace(addrSpaceName);
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
logger->debug("Allocated {:#x} bytes for {}, {:#x} bytes remaining", size,
|
|
|
|
addrSpaceId, getAvailableMemory());
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> mem(
|
|
|
|
new MemoryBlock(localAddr, size, addrSpaceId), this->free);
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
// Mount block into the memory graph
|
|
|
|
insertMemoryBlock(*mem);
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
// Increase the allocation count
|
|
|
|
allocationCount++;
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return mem;
|
2018-08-21 00:25:44 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
HostRam::HostRamAllocator::HostRamAllocator()
|
|
|
|
: BaseAllocator(MemoryManager::get().getProcessAddressSpace()) {
|
|
|
|
free = [&](MemoryBlock *mem) {
|
|
|
|
if (::munmap(reinterpret_cast<void *>(mem->getOffset()), mem->getSize()) !=
|
|
|
|
0) {
|
|
|
|
logger->warn("munmap() failed for {:#x} of size {:#x}", mem->getOffset(),
|
|
|
|
mem->getSize());
|
|
|
|
}
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
removeMemoryBlock(*mem);
|
|
|
|
};
|
2018-08-21 00:25:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::map<int, std::unique_ptr<HostDmaRam::HostDmaRamAllocator>>
|
2023-09-07 13:19:19 +02:00
|
|
|
HostDmaRam::allocators;
|
|
|
|
|
|
|
|
HostDmaRam::HostDmaRamAllocator::HostDmaRamAllocator(int num)
|
|
|
|
: LinearAllocator(
|
|
|
|
MemoryManager::get().getOrCreateAddressSpace(getUdmaBufName(num)),
|
|
|
|
getUdmaBufBufSize(num)),
|
|
|
|
num(num) {
|
|
|
|
auto &mm = MemoryManager::get();
|
2024-07-29 12:35:42 +02:00
|
|
|
logger = Log::get(getName());
|
2023-09-07 13:19:19 +02:00
|
|
|
|
|
|
|
if (getSize() == 0) {
|
|
|
|
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);
|
|
|
|
if (bufFd != -1) {
|
|
|
|
void *buf =
|
|
|
|
mmap(nullptr, getSize(), PROT_READ | PROT_WRITE, MAP_SHARED, bufFd, 0);
|
|
|
|
close(bufFd);
|
|
|
|
|
|
|
|
if (buf != MAP_FAILED)
|
|
|
|
mm.createMapping(reinterpret_cast<uintptr_t>(buf), 0, getSize(),
|
|
|
|
getName() + "-VA", mm.getProcessAddressSpace(),
|
|
|
|
getAddrSpaceId());
|
|
|
|
else
|
|
|
|
logger->warn("Cannot map {}", bufPath);
|
|
|
|
} else
|
|
|
|
logger->warn("Cannot open {}", bufPath);
|
|
|
|
|
|
|
|
logger->info("Mapped {} of size {} bytes", bufPath, getSize());
|
2018-08-21 00:25:44 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
HostDmaRam::HostDmaRamAllocator::~HostDmaRamAllocator() {
|
|
|
|
auto &mm = MemoryManager::get();
|
|
|
|
|
|
|
|
void *baseVirt;
|
|
|
|
try {
|
|
|
|
auto translation = mm.getTranslationFromProcess(getAddrSpaceId());
|
|
|
|
baseVirt = reinterpret_cast<void *>(translation.getLocalAddr(0));
|
|
|
|
} catch (const std::out_of_range &) {
|
|
|
|
// Not mapped, nothing to do
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
logger->debug("Unmapping {}", getName());
|
|
|
|
|
|
|
|
// Try to unmap it
|
|
|
|
if (::munmap(baseVirt, getSize()) != 0) {
|
|
|
|
logger->warn("munmap() failed for {:p} of size {:#x}", baseVirt, getSize());
|
|
|
|
}
|
2018-08-21 00:25:44 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
std::string HostDmaRam::getUdmaBufName(int num) {
|
|
|
|
std::stringstream name;
|
|
|
|
name << "udmabuf" << num;
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return name.str();
|
2018-08-21 00:25:44 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
std::string HostDmaRam::getUdmaBufBasePath(int num) {
|
|
|
|
std::stringstream path;
|
|
|
|
path << "/sys/class/udmabuf/udmabuf" << num << "/";
|
|
|
|
return path.str();
|
2018-08-21 00:25:44 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
size_t HostDmaRam::getUdmaBufBufSize(int num) {
|
|
|
|
std::fstream s(getUdmaBufBasePath(num) + "size", s.in);
|
|
|
|
if (s.is_open()) {
|
|
|
|
std::string line;
|
|
|
|
if (std::getline(s, line)) {
|
|
|
|
return std::strtoul(line.c_str(), nullptr, 10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2018-08-21 00:25:44 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
uintptr_t HostDmaRam::getUdmaBufPhysAddr(int num) {
|
|
|
|
std::fstream s(getUdmaBufBasePath(num) + "phys_addr", s.in);
|
|
|
|
if (s.is_open()) {
|
|
|
|
std::string line;
|
|
|
|
if (std::getline(s, line)) {
|
|
|
|
return std::strtoul(line.c_str(), nullptr, 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return UINTPTR_MAX;
|
2018-08-21 00:25:44 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
HostDmaRam::HostDmaRamAllocator &HostDmaRam::getAllocator(int num) {
|
|
|
|
auto &allocator = allocators[num];
|
|
|
|
if (not allocator) {
|
|
|
|
allocator = std::make_unique<HostDmaRamAllocator>(num);
|
|
|
|
}
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return *allocator;
|
2018-08-21 00:25:44 +02:00
|
|
|
}
|
|
|
|
|
2022-12-02 17:16:44 +01:00
|
|
|
} // namespace villas
|