memory_manager.hpp 8.1 KB
Newer Older
Steffen Vogel's avatar
Steffen Vogel committed
1
2
3
4
/** Memory manager.
 *
 * @file
 * @author Daniel Krebs <github@daniel-krebs.net>
Steffen Vogel's avatar
Steffen Vogel committed
5
 * @copyright 2014-2021, Institute for Automation of Complex Power Systems, EONERC
Steffen Vogel's avatar
Steffen Vogel committed
6
7
 * @license GNU General Public License (version 3)
 *
8
 * VILLAScommon
Steffen Vogel's avatar
Steffen Vogel committed
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *********************************************************************************/

#pragma once

#include <cstdint>
#include <string>
#include <map>
#include <stdexcept>
#include <unistd.h>

#include <villas/log.hpp>
#include <villas/graph/directed.hpp>

namespace villas {

/**
 * @brief Translation between a local (master) to a foreign (slave) address space
 *
 * Memory translations can be chained together using the `+=` operator which is
 * used internally by the MemoryManager to compute a translation through
 * multiple hops (memory mappings).
 */
class MemoryTranslation {
public:

	/**
	 * @brief MemoryTranslation
	 * @param src	Base address of local address space
	 * @param dst	Base address of foreign address space
	 * @param size	Size of "memory window"
	 */
	MemoryTranslation(uintptr_t src, uintptr_t dst, size_t size) :
54
55
56
57
		src(src),
		dst(dst),
		size(size)
	{ }
Steffen Vogel's avatar
Steffen Vogel committed
58
59
60
61
62
63
64
65
66
67
68
69

	uintptr_t
	getLocalAddr(uintptr_t addrInForeignAddrSpace) const;

	uintptr_t
	getForeignAddr(uintptr_t addrInLocalAddrSpace) const;

	size_t
	getSize() const
	{ return size; }

	friend std::ostream&
70
	operator<< (std::ostream &stream, const MemoryTranslation &translation)
Steffen Vogel's avatar
Steffen Vogel committed
71
72
73
74
75
76
77
78
79
	{
		return stream << std::hex
		              << "(src=0x"   << translation.src
		              << ", dst=0x"  << translation.dst
		              << ", size=0x" << translation.size
		              << ")";
	}

	/// Merge two MemoryTranslations together
80
	MemoryTranslation &operator+=(const MemoryTranslation &other);
Steffen Vogel's avatar
Steffen Vogel committed
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

private:
	uintptr_t	src;	///< Base address of local address space
	uintptr_t	dst;	///< Base address of foreign address space
	size_t		size;	///< Size of "memory window"
};


/**
 * @brief Global memory manager to resolve addresses across address spaces
 *
 * Every entity in the system has to register its (master) address space and
 * create mappings to other (slave) address spaces that it can access. A
 * directed graph is then constructed which allows to traverse addresses spaces
 * through multiple mappings and resolve addresses through this "tunnel" of
 * memory mappings.
 */
class MemoryManager {
private:
	// This is a singleton, so private constructor ...
	MemoryManager() :
102
103
		memoryGraph("memory:graph"),
		logger(logging.get("memory:manager"))
Steffen Vogel's avatar
Steffen Vogel committed
104
	{
105
		pathCheckFunc = [&](const MemoryGraph::Path &path) {
Steffen Vogel's avatar
Steffen Vogel committed
106
107
108
109
110
111
			return this->pathCheck(path);
		};
	}

	// ... and no copying or assigning
	MemoryManager(const MemoryManager&) = delete;
112
	MemoryManager &operator=(const MemoryManager&) = delete;
Steffen Vogel's avatar
Steffen Vogel committed
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

	/**
	 * @brief Custom edge in memory graph representing a memory mapping
	 *
	 * A memory mapping maps from one address space into another and can only be
	 * traversed in the forward direction which reflects the nature of real
	 * memory mappings.
	 *
	 * Implementation Notes:
	 * The member #src is the address in the "from" address space, where the
	 * destination address space is mapped. The member #dest is the address in
	 * the destination address space, where the mapping points to. Often, #dest
	 * will be zero for mappings to hardware, but consider the example when
	 * mapping FPGA to application memory:
	 * The application allocates a block 1kB at address 0x843001000 in its
	 * address space. The mapping would then have a #dest address of 0x843001000
	 * and a #size of 1024.
	 */
	class Mapping : public graph::Edge {
	public:
		std::string	name;	///< Human-readable name
		uintptr_t	src;	///< Base address in "from" address space
		uintptr_t	dest;	///< Base address in "to" address space
		size_t		size;	///< Size of the mapping

		friend std::ostream&
139
		operator<< (std::ostream &stream, const Mapping &mapping)
Steffen Vogel's avatar
Steffen Vogel committed
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
		{
			return stream << static_cast<const Edge&>(mapping) << " = "
			              << mapping.name
			              << std::hex
			              << " (src=0x"  << mapping.src
			              << ", dest=0x" << mapping.dest
			              << ", size=0x" << mapping.size
			              << ")";
		}
	};


	/**
	 * @brief Custom vertex in memory graph representing an address space
	 *
	 * Since most information in the memory graph is stored in the edges (memory
	 * mappings), this is just a small extension to the default vertex. It only
	 * associates an additional string #name for human-readability.
	 */
	class AddressSpace : public graph::Vertex {
	public:
		std::string name;	///< Human-readable name

		friend std::ostream&
164
		operator<< (std::ostream &stream, const AddressSpace &addrSpace)
Steffen Vogel's avatar
Steffen Vogel committed
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
		{
			return stream << static_cast<const Vertex&>(addrSpace) << " = "
			              << addrSpace.name;
		}
	};

	/// Memory graph with custom edges and vertices for address resolution
	using MemoryGraph = graph::DirectedGraph<AddressSpace, Mapping>;

public:
	using AddressSpaceId = MemoryGraph::VertexIdentifier;
	using MappingId = MemoryGraph::EdgeIdentifier;

	struct InvalidTranslation : public std::exception {};

	/// Get singleton instance
	static MemoryManager&
	get();

Steffen Vogel's avatar
Steffen Vogel committed
184
	MemoryGraph & getGraph()
185
186
	{ return memoryGraph; }

Steffen Vogel's avatar
Steffen Vogel committed
187
188
	AddressSpaceId
	getProcessAddressSpace()
189
	{ return getOrCreateAddressSpace("process"); }
Steffen Vogel's avatar
Steffen Vogel committed
190
191
192

	AddressSpaceId
	getPciAddressSpace()
193
	{ return getOrCreateAddressSpace("pcie"); }
Steffen Vogel's avatar
Steffen Vogel committed
194
195

	AddressSpaceId
196
	getProcessAddressSpaceMemoryBlock(const std::string &memoryBlock)
197
	{ return getOrCreateAddressSpace(getSlaveAddrSpaceName("process", memoryBlock)); }
Steffen Vogel's avatar
Steffen Vogel committed
198
199
200
201
202
203
204
205
206
207
208
209


	AddressSpaceId
	getOrCreateAddressSpace(std::string name);

	void
	removeAddressSpace(AddressSpaceId addrSpaceId)
	{ memoryGraph.removeVertex(addrSpaceId); }

	/// Create a default mapping
	MappingId
	createMapping(uintptr_t src, uintptr_t dest, size_t size,
210
	              const std::string &name,
Steffen Vogel's avatar
Steffen Vogel committed
211
212
213
214
215
216
217
218
219
220
221
222
223
224
	              AddressSpaceId fromAddrSpace,
	              AddressSpaceId toAddrSpace);

	/// Add a mapping
	///
	/// Can be used to derive from Mapping in order to implement custom
	/// constructor/destructor.
	MappingId
	addMapping(std::shared_ptr<Mapping> mapping,
	           AddressSpaceId fromAddrSpace,
	           AddressSpaceId toAddrSpace);


	AddressSpaceId
225
	findAddressSpace(const std::string &name);
Steffen Vogel's avatar
Steffen Vogel committed
226
227
228
229
230
231
232

	std::list<AddressSpaceId>
	findPath(AddressSpaceId fromAddrSpaceId, AddressSpaceId toAddrSpaceId);

	MemoryTranslation
	getTranslation(AddressSpaceId fromAddrSpaceId, AddressSpaceId toAddrSpaceId);

Steffen Vogel's avatar
Steffen Vogel committed
233
234
	// cppcheck-suppress passedByValue
	MemoryTranslation getTranslationFromProcess(AddressSpaceId foreignAddrSpaceId)
Steffen Vogel's avatar
Steffen Vogel committed
235
236
237
	{ return getTranslation(getProcessAddressSpace(), foreignAddrSpaceId); }

	static std::string
238
	getSlaveAddrSpaceName(const std::string &ipInstance, const std::string &memoryBlock)
Steffen Vogel's avatar
Steffen Vogel committed
239
240
241
	{ return ipInstance + "/" + memoryBlock; }

	static std::string
242
	getMasterAddrSpaceName(const std::string &ipInstance, const std::string &busInterface)
Steffen Vogel's avatar
Steffen Vogel committed
243
244
245
246
247
	{ return ipInstance + ":" + busInterface; }

private:
	/// Convert a Mapping to MemoryTranslation for calculations
	static MemoryTranslation
248
	getTranslationFromMapping(const Mapping &mapping)
Steffen Vogel's avatar
Steffen Vogel committed
249
250
251
	{ return MemoryTranslation(mapping.src, mapping.dest, mapping.size); }

	bool
252
	pathCheck(const MemoryGraph::Path &path);
Steffen Vogel's avatar
Steffen Vogel committed
253
254
255
256
257
258
259
260

	/// Directed graph that stores address spaces and memory mappings
	MemoryGraph memoryGraph;

	/// Cache mapping of names to address space ids for fast lookup
	std::map<std::string, AddressSpaceId> addrSpaceLookup;

	/// Logger for universal access in this class
261
	Logger logger;
Steffen Vogel's avatar
Steffen Vogel committed
262
263
264
265
266
267
268

	MemoryGraph::check_path_fn pathCheckFunc;

	/// Static pointer to global instance, because this is a singleton
	static MemoryManager* instance;
};

269
} /* namespace villas */