Skip to content

Commit 6455470

Browse files
committed
Rename cold start to VM snapshot, add user area
1 parent 19d3dcf commit 6455470

File tree

6 files changed

+81
-51
lines changed

6 files changed

+81
-51
lines changed

lib/tinykvm/common.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ namespace tinykvm
9696
bool executable_heap = false;
9797
/* Enable file-backed memory mappings for large files */
9898
bool mmap_backed_files = false;
99-
/* Enable fast cold start by file-mapping all physical memory
99+
/* Enable VM snapshot by file-mapping all physical memory
100100
to the given file. The file is created if it does not exist,
101101
and must be of the correct size if it does exist. */
102-
std::string fast_cold_start_file;
102+
std::string snapshot_file;
103103
/* When using hugepages, cover the given size with
104104
hugepages, unless 0, in which case the entire
105105
main memory will be covered. */

lib/tinykvm/machine.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ Machine::Machine(std::string_view binary, const MachineOptions& options)
3838
m_mt {nullptr} /* Explicitly */
3939
{
4040
assert(kvm_fd != -1 && "Call Machine::init() first");
41-
if (options.mmap_backed_files && !options.fast_cold_start_file.empty()) {
42-
throw MachineException("Cannot have fast cold start with mmap-backed files at the same time");
41+
if (options.mmap_backed_files && !options.snapshot_file.empty()) {
42+
throw MachineException("Cannot have VM snapshot with mmap-backed files at the same time");
4343
}
4444

4545
this->fd = create_kvm_vm();
@@ -48,9 +48,12 @@ Machine::Machine(std::string_view binary, const MachineOptions& options)
4848

4949
this->vcpu.init(0, *this, options);
5050

51-
if (memory.has_loadable_cold_start_state()) {
52-
if (this->load_cold_start_state()) {
53-
printf("Loaded fast cold start state\n");
51+
if (memory.has_loadable_snapshot_state()) {
52+
this->m_loaded_from_snapshot = this->load_snapshot_state();
53+
if (this->m_loaded_from_snapshot) {
54+
if (options.verbose_loader) {
55+
printf("Loaded VM snapshot state\n");
56+
}
5457
return;
5558
}
5659
// If the file does not exist, or anything else failed, we continue

lib/tinykvm/machine.hpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,12 @@ struct Machine
298298
/* Store non-memory VM state to the already existing cold
299299
start state area in memory. Any failure will throw an
300300
exception. The memory must have been pre-allocated. */
301-
void save_cold_start_state_now() const;
301+
void save_snapshot_state_now() const;
302+
/* Check if the VM was loaded from a snapshot state. */
303+
bool has_snapshot_state() const noexcept { return m_loaded_from_snapshot; }
304+
/* Get pointer to user area in snapshot state memory, or nullptr
305+
if no snapshot state is present. */
306+
void* get_snapshot_state_user_area() const;
302307

303308
static void init();
304309
static void setup_linux_system_calls(bool unsafe_syscalls = false);
@@ -326,13 +331,14 @@ struct Machine
326331
void remote_update_gigapage_mappings(Machine& other, bool forced = false);
327332
/* Prepare for resume with a pagetable reload */
328333
void prepare_vmresume(address_t fsbase = 0, bool reload_pagetables = true);
329-
bool load_cold_start_state();
334+
bool load_snapshot_state();
330335

331336
vCPU vcpu;
332337
int fd = 0;
333338
bool m_prepped = false;
334339
bool m_forked = false;
335340
bool m_just_reset = false;
341+
bool m_loaded_from_snapshot = false;
336342
bool m_remote_pfaults = false;
337343
bool m_permanent_remote_connection = false;
338344
bool m_relocate_fixed_mmap = false;

lib/tinykvm/machine_state.cpp

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ struct ColdStartSocketPair {
5050
int type;
5151
};
5252

53-
struct ColdStartState {
53+
struct SnapshotState {
5454
static constexpr uint32_t MAGIC = 0x564D4353; // 'VMCS'
5555
uint32_t magic;
56-
uint32_t version;
56+
uint32_t size;
5757
tinykvm_x86regs regs;
5858
kvm_sregs sregs;
5959
tinykvm_x86fpuregs fpu;
@@ -83,31 +83,29 @@ struct ColdStartState {
8383
current = reinterpret_cast<char*>(current) + sizeof(T);
8484
// Bounds-check against end-of-structure
8585
if (reinterpret_cast<char*>(current) > reinterpret_cast<char*>(this) + Size()) {
86-
throw std::runtime_error("Out of bounds access on ColdStartState");
86+
throw std::runtime_error("Out of bounds access on SnapshotState");
8787
}
8888
return ret;
8989
}
9090
};
91-
bool Machine::load_cold_start_state()
91+
bool Machine::load_snapshot_state()
9292
{
93-
if (!memory.has_loadable_cold_start_state()) {
93+
if (!memory.has_loadable_snapshot_state()) {
9494
return false;
9595
}
96-
if (!this->memory.has_cold_start_area) {
97-
throw std::runtime_error("No cold start state area allocated");
96+
if (!this->memory.has_snapshot_area) {
97+
throw std::runtime_error("No snapshot state area allocated");
9898
}
9999
if (this->is_forked()) {
100-
throw std::runtime_error("Cannot load cold start state into a forked VM");
100+
throw std::runtime_error("Cannot load snapshot state into a forked VM");
101101
}
102-
void* map = this->memory.get_cold_start_state_area();
103-
ColdStartState& state = *reinterpret_cast<ColdStartState*>(map);
104-
if (state.magic != ColdStartState::MAGIC) {
105-
throw std::runtime_error("No valid cold start state found");
102+
void* map = this->memory.get_snapshot_state_area();
103+
SnapshotState& state = *reinterpret_cast<SnapshotState*>(map);
104+
if (state.magic != SnapshotState::MAGIC) {
105+
throw std::runtime_error("No valid snapshot state found");
106106
}
107-
if (state.version != 1) {
108-
fprintf(stderr, "Warning: Cold start state version mismatch: %u != %u\n",
109-
state.version, 1u);
110-
return false;
107+
if (state.size < sizeof(SnapshotState) || state.size > SnapshotState::Size()) {
108+
throw std::runtime_error("Invalid snapshot state size");
111109
}
112110

113111
// Load the state into the VM
@@ -130,7 +128,7 @@ bool Machine::load_cold_start_state()
130128
this->memory.main_memory_writes = state.main_memory_writes;
131129
this->memory.page_tables = state.m_page_tables;
132130

133-
void* current = reinterpret_cast<char*>(&state) + sizeof(ColdStartState);
131+
void* current = reinterpret_cast<char*>(&state) + sizeof(SnapshotState);
134132
// Load the thread states
135133
ColdStartThreads* threads = state.next<ColdStartThreads>(current);
136134
if (threads->count > 0) {
@@ -178,16 +176,16 @@ bool Machine::load_cold_start_state()
178176
}
179177
return true;
180178
}
181-
void Machine::save_cold_start_state_now() const
179+
void Machine::save_snapshot_state_now() const
182180
{
183181
if (this->is_forked()) {
184-
throw std::runtime_error("Cannot save cold start state of a forked VM");
182+
throw std::runtime_error("Cannot save snapshot state of a forked VM");
185183
}
186-
void* map = this->memory.get_cold_start_state_area();
187-
ColdStartState& state = *reinterpret_cast<ColdStartState*>(map);
184+
void* map = this->memory.get_snapshot_state_area();
185+
SnapshotState& state = *reinterpret_cast<SnapshotState*>(map);
188186
try {
189-
state.magic = ColdStartState::MAGIC;
190-
state.version = 1;
187+
state.magic = SnapshotState::MAGIC;
188+
state.size = 0; // Invalid (for now)
191189
state.regs = this->registers();
192190
state.sregs = this->get_special_registers();
193191
state.fpu = this->fpu_registers();
@@ -206,7 +204,7 @@ void Machine::save_cold_start_state_now() const
206204
state.main_memory_writes = this->memory.main_memory_writes;
207205
state.m_page_tables = this->memory.page_tables;
208206

209-
void* current = reinterpret_cast<char*>(&state) + sizeof(ColdStartState);
207+
void* current = reinterpret_cast<char*>(&state) + sizeof(SnapshotState);
210208
// Save the multi-threading state
211209
ColdStartThreads* threads = state.next<ColdStartThreads>(current);
212210
if (this->has_threads()) {
@@ -262,27 +260,50 @@ void Machine::save_cold_start_state_now() const
262260
csp->type = int(sp.type);
263261
}
264262

263+
// Finally, set the size
264+
state.size = static_cast<uint32_t>(
265+
reinterpret_cast<char*>(current) - reinterpret_cast<char*>(&state));
266+
if (state.size < sizeof(SnapshotState) || state.size > SnapshotState::Size()) {
267+
throw std::runtime_error("Snapshot state size was invalid");
268+
}
269+
265270
} catch (const MachineException& me) {
266271
throw std::runtime_error(std::string("Failed to get cold start state: ") + me.what());
267272
}
268273
}
269274

270-
void* vMemory::get_cold_start_state_area() const
275+
void* vMemory::get_snapshot_state_area() const
271276
{
272-
if (!this->has_cold_start_area) {
277+
if (!this->has_snapshot_area) {
273278
throw std::runtime_error("No cold start state area allocated");
274279
}
275280
// The cold start state area is after the end of the memory
276281
return (void*)(this->ptr + this->size);
277282
}
278-
bool vMemory::has_loadable_cold_start_state() const noexcept
283+
bool vMemory::has_loadable_snapshot_state() const noexcept
279284
{
280-
if (this->has_cold_start_area) {
281-
void* area = this->get_cold_start_state_area();
285+
if (this->has_snapshot_area) {
286+
void* area = this->get_snapshot_state_area();
282287
uint32_t* magic = reinterpret_cast<uint32_t*>(area);
283-
return *magic == ColdStartState::MAGIC;
288+
return *magic == SnapshotState::MAGIC;
284289
}
285290
return false;
286291
}
292+
void* Machine::get_snapshot_state_user_area() const
293+
{
294+
if (!this->memory.has_snapshot_area) {
295+
return nullptr;
296+
}
297+
void* map = this->memory.get_snapshot_state_area();
298+
SnapshotState& state = *reinterpret_cast<SnapshotState*>(map);
299+
if (state.magic != SnapshotState::MAGIC) {
300+
return nullptr;
301+
}
302+
if (state.size < sizeof(SnapshotState) || state.size > SnapshotState::Size()) {
303+
return nullptr;
304+
}
305+
// The user area is after the SnapshotState + size
306+
return reinterpret_cast<char*>(map) + sizeof(SnapshotState) + state.size;
307+
}
287308

288309
} // namespace tinykvm

lib/tinykvm/memory.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ vMemory::vMemory(Machine& m, const MachineOptions& options,
2626
: machine(m), physbase(ph), safebase(sf),
2727
// Over-allocate in order to avoid trouble with 2MB-aligned operations
2828
ptr(p), size(overaligned_memsize(s)), owned(own),
29-
has_cold_start_area(own && !options.fast_cold_start_file.empty()),
29+
has_snapshot_area(own && !options.snapshot_file.empty()),
3030
main_memory_writes(options.master_direct_memory_writes),
3131
split_hugepages(options.split_hugepages),
3232
executable_heap(options.executable_heap),
@@ -393,35 +393,35 @@ vMemory::AllocationResult
393393
// Add the cold start state area
394394
size += ColdStartStateSize();
395395
// Open the to-be memory-mapped file
396-
const std::string& filename = options.fast_cold_start_file;
396+
const std::string& filename = options.snapshot_file;
397397
if (filename.empty()) {
398-
throw std::runtime_error("No fast cold start file specified");
398+
throw std::runtime_error("No VM snapshot file specified");
399399
}
400400
const int fd = open(filename.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600);
401401
if (fd < 0) {
402-
throw std::runtime_error("Failed to open fast cold start file: " + filename);
402+
throw std::runtime_error("Failed to open VM snapshot file: " + filename);
403403
}
404404
struct stat st;
405405
if (fstat(fd, &st) != 0) {
406406
close(fd);
407-
throw std::runtime_error("Failed to stat fast cold start file: " + filename);
407+
throw std::runtime_error("Failed to stat VM snapshot file: " + filename);
408408
}
409409
if (st.st_size != off_t(size)) {
410410
if (st.st_size != 0) {
411411
close(fd);
412-
throw std::runtime_error("Fast cold start file has incorrect size: " + filename);
412+
throw std::runtime_error("VM snapshot file has incorrect size: " + filename);
413413
}
414414
// Create the file with the correct size
415415
if (ftruncate(fd, size) != 0) {
416416
close(fd);
417-
throw std::runtime_error("Failed to set size of fast cold start file: " + filename);
417+
throw std::runtime_error("Failed to set size of VM snapshot file: " + filename);
418418
}
419419
}
420420
char* ptr = (char*) mmap(NULL, size, PROT_READ | PROT_WRITE,
421421
MAP_SHARED | MAP_NORESERVE, fd, 0);
422422
close(fd);
423423
if (ptr == MAP_FAILED) {
424-
memory_exception("Failed to mmap fast cold start file", 0, size);
424+
memory_exception("Failed to mmap VM snapshot file", 0, size);
425425
}
426426
return AllocationResult{ptr, size - ColdStartStateSize()};
427427
}
@@ -434,7 +434,7 @@ vMemory vMemory::New(Machine& m, const MachineOptions& options,
434434
// Over-allocate in order to avoid trouble with 2MB-aligned operations
435435
size = vMemory::overaligned_memsize(size);
436436
// Use file-backed memory if requested
437-
if (!options.fast_cold_start_file.empty()) {
437+
if (!options.snapshot_file.empty()) {
438438
const auto [res_ptr, res_size] = allocate_filebacked_memory(options, size);
439439
return vMemory(m, options, phys, safe, res_ptr, res_size);
440440
}

lib/tinykvm/memory.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ struct vMemory {
2828
char* ptr;
2929
size_t size;
3030
bool owned = true;
31-
bool has_cold_start_area = false;
31+
bool has_snapshot_area = false;
3232
/* Remote end pointer for this memory */
3333
uint64_t remote_end = 0;
3434
bool remote_must_update_gigapages = true;
@@ -113,8 +113,8 @@ struct vMemory {
113113
static size_t ColdStartStateSize() {
114114
return 2UL << 20; // 2MB
115115
}
116-
bool has_loadable_cold_start_state() const noexcept;
117-
void* get_cold_start_state_area() const;
116+
bool has_loadable_snapshot_state() const noexcept;
117+
void* get_snapshot_state_area() const;
118118
private:
119119
using AllocationResult = std::tuple<char*, size_t>;
120120
static AllocationResult allocate_mapped_memory(const MachineOptions&, size_t size);

0 commit comments

Comments
 (0)