VirtualBox

source: vbox/trunk/src/VBox/VMM/FTM.cpp@ 32268

Last change on this file since 32268 was 32268, checked in by vboxsync, 14 years ago

FT updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.3 KB
Line 
1/* $Id: FTM.cpp 32268 2010-09-07 09:40:47Z vboxsync $ */
2/** @file
3 * FTM - Fault Tolerance Manager
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_FTM
23#include "FTMInternal.h"
24#include <VBox/vm.h>
25#include <VBox/vmm.h>
26#include <VBox/err.h>
27#include <VBox/param.h>
28#include <VBox/ssm.h>
29#include <VBox/log.h>
30#include <VBox/pgm.h>
31#include <VBox/pdm.h>
32
33#include <iprt/assert.h>
34#include <iprt/thread.h>
35#include <iprt/string.h>
36#include <iprt/mem.h>
37#include <iprt/tcp.h>
38#include <iprt/socket.h>
39#include <iprt/semaphore.h>
40#include <iprt/asm.h>
41
42#include <include/internal/vm.h>
43#include <include/internal/em.h>
44#include <include/internal/pgm.h>
45
46/*******************************************************************************
47 * Structures and Typedefs *
48 *******************************************************************************/
49
50/**
51 * TCP stream header.
52 *
53 * This is an extra layer for fixing the problem with figuring out when the SSM
54 * stream ends.
55 */
56typedef struct FTMTCPHDR
57{
58 /** Magic value. */
59 uint32_t u32Magic;
60 /** The size of the data block following this header.
61 * 0 indicates the end of the stream, while UINT32_MAX indicates
62 * cancelation. */
63 uint32_t cb;
64} FTMTCPHDR;
65/** Magic value for FTMTCPHDR::u32Magic. (Egberto Gismonti Amin) */
66#define FTMTCPHDR_MAGIC UINT32_C(0x19471205)
67/** The max block size. */
68#define FTMTCPHDR_MAX_SIZE UINT32_C(0x00fffff8)
69
70/**
71 * TCP stream header.
72 *
73 * This is an extra layer for fixing the problem with figuring out when the SSM
74 * stream ends.
75 */
76typedef struct FTMTCPHDRMEM
77{
78 /** Magic value. */
79 uint32_t u32Magic;
80 /** Size (Uncompressed) of the pages following the header. */
81 uint32_t cbPageRange;
82 /** GC Physical address of the page(s) to sync. */
83 RTGCPHYS GCPhys;
84 /** The size of the data block following this header.
85 * 0 indicates the end of the stream, while UINT32_MAX indicates
86 * cancelation. */
87 uint32_t cb;
88} FTMTCPHDRMEM;
89
90/*******************************************************************************
91* Global Variables *
92*******************************************************************************/
93static const char g_szWelcome[] = "VirtualBox-Fault-Tolerance-Sync-1.0\n";
94
95static DECLCALLBACK(int) ftmR3PageTreeDestroyCallback(PAVLOGCPHYSNODECORE pBaseNode, void *pvUser);
96
97/**
98 * Initializes the FTM.
99 *
100 * @returns VBox status code.
101 * @param pVM The VM to operate on.
102 */
103VMMR3DECL(int) FTMR3Init(PVM pVM)
104{
105 /*
106 * Assert alignment and sizes.
107 */
108 AssertCompile(sizeof(pVM->ftm.s) <= sizeof(pVM->ftm.padding));
109 AssertCompileMemberAlignment(FTM, CritSect, sizeof(uintptr_t));
110
111 /** @todo saved state for master nodes! */
112 pVM->ftm.s.pszAddress = NULL;
113 pVM->ftm.s.pszPassword = NULL;
114 pVM->fFaultTolerantMaster = false;
115 pVM->ftm.s.fIsStandbyNode = false;
116 pVM->ftm.s.standby.hServer = NIL_RTTCPSERVER;
117 pVM->ftm.s.master.hShutdownEvent = NIL_RTSEMEVENT;
118 pVM->ftm.s.hSocket = NIL_RTSOCKET;
119
120 /*
121 * Initialize the PGM critical section.
122 */
123 int rc = PDMR3CritSectInit(pVM, &pVM->ftm.s.CritSect, RT_SRC_POS, "FTM");
124 AssertRCReturn(rc, rc);
125
126 /*
127 * Register statistics.
128 */
129 STAM_REL_REG(pVM, &pVM->ftm.s.StatReceivedMem, STAMTYPE_COUNTER, "/FT/Received/Mem", STAMUNIT_BYTES, "The amount of memory pages that was received.");
130 STAM_REL_REG(pVM, &pVM->ftm.s.StatReceivedState, STAMTYPE_COUNTER, "/FT/Received/State", STAMUNIT_BYTES, "The amount of state information that was received.");
131 STAM_REL_REG(pVM, &pVM->ftm.s.StatSentMem, STAMTYPE_COUNTER, "/FT/Sent/Mem", STAMUNIT_BYTES, "The amount of memory pages that was sent.");
132 STAM_REL_REG(pVM, &pVM->ftm.s.StatSentState, STAMTYPE_COUNTER, "/FT/Sent/State", STAMUNIT_BYTES, "The amount of state information that was sent.");
133 STAM_REL_REG(pVM, &pVM->ftm.s.StatDeltaVM, STAMTYPE_COUNTER, "/FT/Sync/DeltaVM", STAMUNIT_OCCURENCES, "Number of delta vm syncs.");
134 STAM_REL_REG(pVM, &pVM->ftm.s.StatFullSync, STAMTYPE_COUNTER, "/FT/Sync/Full", STAMUNIT_OCCURENCES, "Number of full vm syncs.");
135 STAM_REL_REG(pVM, &pVM->ftm.s.StatDeltaMem, STAMTYPE_COUNTER, "/FT/Sync/DeltaMem", STAMUNIT_OCCURENCES, "Number of delta mem syncs.");
136 STAM_REL_REG(pVM, &pVM->ftm.s.StatCheckpointStorage, STAMTYPE_COUNTER, "/FT/Checkpoint/Storage", STAMUNIT_OCCURENCES, "Number of storage checkpoints.");
137 STAM_REL_REG(pVM, &pVM->ftm.s.StatCheckpointNetwork, STAMTYPE_COUNTER, "/FT/Checkpoint/Network", STAMUNIT_OCCURENCES, "Number of network checkpoints.");
138#ifdef VBOX_WITH_STATISTICS
139 STAM_REG(pVM, &pVM->ftm.s.StatCheckpoint, STAMTYPE_PROFILE, "/FT/Checkpoint", STAMUNIT_TICKS_PER_CALL, "Profiling of FTMR3SetCheckpoint.");
140#endif
141 return VINF_SUCCESS;
142}
143
144/**
145 * Terminates the FTM.
146 *
147 * Termination means cleaning up and freeing all resources,
148 * the VM itself is at this point powered off or suspended.
149 *
150 * @returns VBox status code.
151 * @param pVM The VM to operate on.
152 */
153VMMR3DECL(int) FTMR3Term(PVM pVM)
154{
155 if (pVM->ftm.s.master.hShutdownEvent != NIL_RTSEMEVENT)
156 {
157 RTSemEventDestroy(pVM->ftm.s.master.hShutdownEvent);
158 pVM->ftm.s.master.hShutdownEvent = NIL_RTSEMEVENT;
159 }
160 if (pVM->ftm.s.hSocket != NIL_RTSOCKET)
161 {
162 RTTcpClientClose(pVM->ftm.s.hSocket);
163 pVM->ftm.s.hSocket = NIL_RTSOCKET;
164 }
165 if (pVM->ftm.s.standby.hServer)
166 {
167 RTTcpServerDestroy(pVM->ftm.s.standby.hServer);
168 pVM->ftm.s.standby.hServer = NULL;
169 }
170 if (pVM->ftm.s.pszAddress)
171 RTMemFree(pVM->ftm.s.pszAddress);
172 if (pVM->ftm.s.pszPassword)
173 RTMemFree(pVM->ftm.s.pszPassword);
174
175 /* Remove all pending memory updates. */
176 if (pVM->ftm.s.standby.ppPhysPageTree)
177 {
178 RTAvloGCPhysDestroy(pVM->ftm.s.standby.ppPhysPageTree, ftmR3PageTreeDestroyCallback, NULL);
179 pVM->ftm.s.standby.ppPhysPageTree = NULL;
180 }
181
182 pVM->ftm.s.pszAddress = NULL;
183 pVM->ftm.s.pszPassword = NULL;
184
185 PDMR3CritSectDelete(&pVM->ftm.s.CritSect);
186 return VINF_SUCCESS;
187}
188
189
190static int ftmR3TcpWriteACK(PVM pVM)
191{
192 int rc = RTTcpWrite(pVM->ftm.s.hSocket, "ACK\n", sizeof("ACK\n") - 1);
193 if (RT_FAILURE(rc))
194 {
195 LogRel(("FTSync: RTTcpWrite(,ACK,) -> %Rrc\n", rc));
196 }
197 return rc;
198}
199
200
201static int ftmR3TcpWriteNACK(PVM pVM, int32_t rc2, const char *pszMsgText = NULL)
202{
203 char szMsg[256];
204 size_t cch;
205 if (pszMsgText && *pszMsgText)
206 {
207 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d;%s\n", rc2, pszMsgText);
208 for (size_t off = 6; off + 1 < cch; off++)
209 if (szMsg[off] == '\n')
210 szMsg[off] = '\r';
211 }
212 else
213 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d\n", rc2);
214 int rc = RTTcpWrite(pVM->ftm.s.hSocket, szMsg, cch);
215 if (RT_FAILURE(rc))
216 LogRel(("FTSync: RTTcpWrite(,%s,%zu) -> %Rrc\n", szMsg, cch, rc));
217 return rc;
218}
219
220/**
221 * Reads a string from the socket.
222 *
223 * @returns VBox status code.
224 *
225 * @param pState The teleporter state structure.
226 * @param pszBuf The output buffer.
227 * @param cchBuf The size of the output buffer.
228 *
229 */
230static int ftmR3TcpReadLine(PVM pVM, char *pszBuf, size_t cchBuf)
231{
232 char *pszStart = pszBuf;
233 RTSOCKET Sock = pVM->ftm.s.hSocket;
234
235 AssertReturn(cchBuf > 1, VERR_INTERNAL_ERROR);
236 *pszBuf = '\0';
237
238 /* dead simple approach. */
239 for (;;)
240 {
241 char ch;
242 int rc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
243 if (RT_FAILURE(rc))
244 {
245 LogRel(("FTSync: RTTcpRead -> %Rrc while reading string ('%s')\n", rc, pszStart));
246 return rc;
247 }
248 if ( ch == '\n'
249 || ch == '\0')
250 return VINF_SUCCESS;
251 if (cchBuf <= 1)
252 {
253 LogRel(("FTSync: String buffer overflow: '%s'\n", pszStart));
254 return VERR_BUFFER_OVERFLOW;
255 }
256 *pszBuf++ = ch;
257 *pszBuf = '\0';
258 cchBuf--;
259 }
260}
261
262/**
263 * Reads an ACK or NACK.
264 *
265 * @returns VBox status code.
266 * @param pVM The VM to operate on.
267 * @param pszWhich Which ACK is this this?
268 * @param pszNAckMsg Optional NACK message.
269 */
270static int ftmR3TcpReadACK(PVM pVM, const char *pszWhich, const char *pszNAckMsg = NULL)
271{
272 char szMsg[256];
273 int rc = ftmR3TcpReadLine(pVM, szMsg, sizeof(szMsg));
274 if (RT_FAILURE(rc))
275 return rc;
276
277 if (!strcmp(szMsg, "ACK"))
278 return VINF_SUCCESS;
279
280 if (!strncmp(szMsg, "NACK=", sizeof("NACK=") - 1))
281 {
282 char *pszMsgText = strchr(szMsg, ';');
283 if (pszMsgText)
284 *pszMsgText++ = '\0';
285
286 int32_t vrc2;
287 rc = RTStrToInt32Full(&szMsg[sizeof("NACK=") - 1], 10, &vrc2);
288 if (rc == VINF_SUCCESS)
289 {
290 /*
291 * Well formed NACK, transform it into an error.
292 */
293 if (pszNAckMsg)
294 {
295 LogRel(("FTSync: %s: NACK=%Rrc (%d)\n", pszWhich, vrc2, vrc2));
296 return VERR_INTERNAL_ERROR;
297 }
298
299 if (pszMsgText)
300 {
301 pszMsgText = RTStrStrip(pszMsgText);
302 for (size_t off = 0; pszMsgText[off]; off++)
303 if (pszMsgText[off] == '\r')
304 pszMsgText[off] = '\n';
305
306 LogRel(("FTSync: %s: NACK=%Rrc (%d) - '%s'\n", pszWhich, vrc2, vrc2, pszMsgText));
307 }
308 return VERR_INTERNAL_ERROR_2;
309 }
310
311 if (pszMsgText)
312 pszMsgText[-1] = ';';
313 }
314 return VERR_INTERNAL_ERROR_3;
315}
316
317/**
318 * Submitts a command to the destination and waits for the ACK.
319 *
320 * @returns VBox status code.
321 *
322 * @param pVM The VM to operate on.
323 * @param pszCommand The command.
324 * @param fWaitForAck Whether to wait for the ACK.
325 */
326static int ftmR3TcpSubmitCommand(PVM pVM, const char *pszCommand, bool fWaitForAck = true)
327{
328 int rc = RTTcpSgWriteL(pVM->ftm.s.hSocket, 2, pszCommand, strlen(pszCommand), "\n", sizeof("\n") - 1);
329 if (RT_FAILURE(rc))
330 return rc;
331 if (!fWaitForAck)
332 return VINF_SUCCESS;
333 return ftmR3TcpReadACK(pVM, pszCommand);
334}
335
336/**
337 * @copydoc SSMSTRMOPS::pfnWrite
338 */
339static DECLCALLBACK(int) ftmR3TcpOpWrite(void *pvUser, uint64_t offStream, const void *pvBuf, size_t cbToWrite)
340{
341 PVM pVM = (PVM)pvUser;
342
343 AssertReturn(cbToWrite > 0, VINF_SUCCESS);
344 AssertReturn(cbToWrite < UINT32_MAX, VERR_OUT_OF_RANGE);
345 AssertReturn(pVM->fFaultTolerantMaster, VERR_INVALID_HANDLE);
346
347 for (;;)
348 {
349 FTMTCPHDR Hdr;
350 Hdr.u32Magic = FTMTCPHDR_MAGIC;
351 Hdr.cb = RT_MIN((uint32_t)cbToWrite, FTMTCPHDR_MAX_SIZE);
352 int rc = RTTcpSgWriteL(pVM->ftm.s.hSocket, 2, &Hdr, sizeof(Hdr), pvBuf, (size_t)Hdr.cb);
353 if (RT_FAILURE(rc))
354 {
355 LogRel(("FTSync/TCP: Write error: %Rrc (cb=%#x)\n", rc, Hdr.cb));
356 return rc;
357 }
358 pVM->ftm.s.StatSentState.c += Hdr.cb + sizeof(Hdr);
359 pVM->ftm.s.syncstate.uOffStream += Hdr.cb;
360 if (Hdr.cb == cbToWrite)
361 return VINF_SUCCESS;
362
363 /* advance */
364 cbToWrite -= Hdr.cb;
365 pvBuf = (uint8_t const *)pvBuf + Hdr.cb;
366 }
367}
368
369
370/**
371 * Selects and poll for close condition.
372 *
373 * We can use a relatively high poll timeout here since it's only used to get
374 * us out of error paths. In the normal cause of events, we'll get a
375 * end-of-stream header.
376 *
377 * @returns VBox status code.
378 *
379 * @param pState The teleporter state data.
380 */
381static int ftmR3TcpReadSelect(PVM pVM)
382{
383 int rc;
384 do
385 {
386 rc = RTTcpSelectOne(pVM->ftm.s.hSocket, 1000);
387 if (RT_FAILURE(rc) && rc != VERR_TIMEOUT)
388 {
389 pVM->ftm.s.syncstate.fIOError = true;
390 LogRel(("FTSync/TCP: Header select error: %Rrc\n", rc));
391 break;
392 }
393 if (pVM->ftm.s.syncstate.fStopReading)
394 {
395 rc = VERR_EOF;
396 break;
397 }
398 } while (rc == VERR_TIMEOUT);
399 return rc;
400}
401
402
403/**
404 * @copydoc SSMSTRMOPS::pfnRead
405 */
406static DECLCALLBACK(int) ftmR3TcpOpRead(void *pvUser, uint64_t offStream, void *pvBuf, size_t cbToRead, size_t *pcbRead)
407{
408 PVM pVM = (PVM)pvUser;
409 AssertReturn(!pVM->fFaultTolerantMaster, VERR_INVALID_HANDLE);
410
411 for (;;)
412 {
413 int rc;
414
415 /*
416 * Check for various conditions and may have been signalled.
417 */
418 if (pVM->ftm.s.syncstate.fEndOfStream)
419 return VERR_EOF;
420 if (pVM->ftm.s.syncstate.fStopReading)
421 return VERR_EOF;
422 if (pVM->ftm.s.syncstate.fIOError)
423 return VERR_IO_GEN_FAILURE;
424
425 /*
426 * If there is no more data in the current block, read the next
427 * block header.
428 */
429 if (!pVM->ftm.s.syncstate.cbReadBlock)
430 {
431 rc = ftmR3TcpReadSelect(pVM);
432 if (RT_FAILURE(rc))
433 return rc;
434 FTMTCPHDR Hdr;
435 rc = RTTcpRead(pVM->ftm.s.hSocket, &Hdr, sizeof(Hdr), NULL);
436 if (RT_FAILURE(rc))
437 {
438 pVM->ftm.s.syncstate.fIOError = true;
439 LogRel(("FTSync/TCP: Header read error: %Rrc\n", rc));
440 return rc;
441 }
442 pVM->ftm.s.StatReceivedState.c += sizeof(Hdr);
443
444 if (RT_UNLIKELY( Hdr.u32Magic != FTMTCPHDR_MAGIC
445 || Hdr.cb > FTMTCPHDR_MAX_SIZE
446 || Hdr.cb == 0))
447 {
448 if ( Hdr.u32Magic == FTMTCPHDR_MAGIC
449 && ( Hdr.cb == 0
450 || Hdr.cb == UINT32_MAX)
451 )
452 {
453 pVM->ftm.s.syncstate.fEndOfStream = true;
454 pVM->ftm.s.syncstate.cbReadBlock = 0;
455 return Hdr.cb ? VERR_SSM_CANCELLED : VERR_EOF;
456 }
457 pVM->ftm.s.syncstate.fIOError = true;
458 LogRel(("FTSync/TCP: Invalid block: u32Magic=%#x cb=%#x\n", Hdr.u32Magic, Hdr.cb));
459 return VERR_IO_GEN_FAILURE;
460 }
461
462 pVM->ftm.s.syncstate.cbReadBlock = Hdr.cb;
463 if (pVM->ftm.s.syncstate.fStopReading)
464 return VERR_EOF;
465 }
466
467 /*
468 * Read more data.
469 */
470 rc = ftmR3TcpReadSelect(pVM);
471 if (RT_FAILURE(rc))
472 return rc;
473
474 uint32_t cb = (uint32_t)RT_MIN(pVM->ftm.s.syncstate.cbReadBlock, cbToRead);
475 rc = RTTcpRead(pVM->ftm.s.hSocket, pvBuf, cb, pcbRead);
476 if (RT_FAILURE(rc))
477 {
478 pVM->ftm.s.syncstate.fIOError = true;
479 LogRel(("FTSync/TCP: Data read error: %Rrc (cb=%#x)\n", rc, cb));
480 return rc;
481 }
482 if (pcbRead)
483 {
484 cb = (uint32_t)*pcbRead;
485 pVM->ftm.s.StatReceivedState.c += cb;
486 pVM->ftm.s.syncstate.uOffStream += cb;
487 pVM->ftm.s.syncstate.cbReadBlock -= cb;
488 return VINF_SUCCESS;
489 }
490 pVM->ftm.s.StatReceivedState.c += cb;
491 pVM->ftm.s.syncstate.uOffStream += cb;
492 pVM->ftm.s.syncstate.cbReadBlock -= cb;
493 if (cbToRead == cb)
494 return VINF_SUCCESS;
495
496 /* Advance to the next block. */
497 cbToRead -= cb;
498 pvBuf = (uint8_t *)pvBuf + cb;
499 }
500}
501
502
503/**
504 * @copydoc SSMSTRMOPS::pfnSeek
505 */
506static DECLCALLBACK(int) ftmR3TcpOpSeek(void *pvUser, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
507{
508 return VERR_NOT_SUPPORTED;
509}
510
511
512/**
513 * @copydoc SSMSTRMOPS::pfnTell
514 */
515static DECLCALLBACK(uint64_t) ftmR3TcpOpTell(void *pvUser)
516{
517 PVM pVM = (PVM)pvUser;
518 return pVM->ftm.s.syncstate.uOffStream;
519}
520
521
522/**
523 * @copydoc SSMSTRMOPS::pfnSize
524 */
525static DECLCALLBACK(int) ftmR3TcpOpSize(void *pvUser, uint64_t *pcb)
526{
527 return VERR_NOT_SUPPORTED;
528}
529
530
531/**
532 * @copydoc SSMSTRMOPS::pfnIsOk
533 */
534static DECLCALLBACK(int) ftmR3TcpOpIsOk(void *pvUser)
535{
536 PVM pVM = (PVM)pvUser;
537
538 if (pVM->fFaultTolerantMaster)
539 {
540 /* Poll for incoming NACKs and errors from the other side */
541 int rc = RTTcpSelectOne(pVM->ftm.s.hSocket, 0);
542 if (rc != VERR_TIMEOUT)
543 {
544 if (RT_SUCCESS(rc))
545 {
546 LogRel(("FTSync/TCP: Incoming data detect by IsOk, assuming it is a cancellation NACK.\n"));
547 rc = VERR_SSM_CANCELLED;
548 }
549 else
550 LogRel(("FTSync/TCP: RTTcpSelectOne -> %Rrc (IsOk).\n", rc));
551 return rc;
552 }
553 }
554
555 return VINF_SUCCESS;
556}
557
558
559/**
560 * @copydoc SSMSTRMOPS::pfnClose
561 */
562static DECLCALLBACK(int) ftmR3TcpOpClose(void *pvUser, bool fCanceled)
563{
564 PVM pVM = (PVM)pvUser;
565
566 if (pVM->fFaultTolerantMaster)
567 {
568 FTMTCPHDR EofHdr;
569 EofHdr.u32Magic = FTMTCPHDR_MAGIC;
570 EofHdr.cb = fCanceled ? UINT32_MAX : 0;
571 int rc = RTTcpWrite(pVM->ftm.s.hSocket, &EofHdr, sizeof(EofHdr));
572 if (RT_FAILURE(rc))
573 {
574 LogRel(("FTSync/TCP: EOF Header write error: %Rrc\n", rc));
575 return rc;
576 }
577 }
578 else
579 {
580 ASMAtomicWriteBool(&pVM->ftm.s.syncstate.fStopReading, true);
581 }
582
583 return VINF_SUCCESS;
584}
585
586
587/**
588 * Method table for a TCP based stream.
589 */
590static SSMSTRMOPS const g_ftmR3TcpOps =
591{
592 SSMSTRMOPS_VERSION,
593 ftmR3TcpOpWrite,
594 ftmR3TcpOpRead,
595 ftmR3TcpOpSeek,
596 ftmR3TcpOpTell,
597 ftmR3TcpOpSize,
598 ftmR3TcpOpIsOk,
599 ftmR3TcpOpClose,
600 SSMSTRMOPS_VERSION
601};
602
603
604/**
605 * VMR3ReqCallWait callback
606 *
607 * @param pVM The VM handle.
608 *
609 */
610static DECLCALLBACK(void) ftmR3WriteProtectMemory(PVM pVM)
611{
612 int rc = PGMR3PhysWriteProtectRAM(pVM);
613 AssertRC(rc);
614}
615
616
617/**
618 * Sync the VM state
619 *
620 * @returns VBox status code.
621 * @param pVM The VM handle.
622 */
623static int ftmR3PerformFullSync(PVM pVM)
624{
625 bool fSuspended = false;
626
627 int rc = VMR3Suspend(pVM);
628 AssertRCReturn(rc, rc);
629
630 STAM_REL_COUNTER_INC(&pVM->ftm.s.StatFullSync);
631
632 RTSocketRetain(pVM->ftm.s.hSocket); /* For concurrent access by I/O thread and EMT. */
633
634 /* Reset the sync state. */
635 pVM->ftm.s.syncstate.uOffStream = 0;
636 pVM->ftm.s.syncstate.cbReadBlock = 0;
637 pVM->ftm.s.syncstate.fStopReading = false;
638 pVM->ftm.s.syncstate.fIOError = false;
639 pVM->ftm.s.syncstate.fEndOfStream = false;
640
641 rc = ftmR3TcpSubmitCommand(pVM, "full-sync");
642 AssertRC(rc);
643
644 pVM->ftm.s.fDeltaLoadSaveActive = false;
645 rc = VMR3SaveFT(pVM, &g_ftmR3TcpOps, pVM, &fSuspended, false /* fSkipStateChanges */);
646 AssertRC(rc);
647
648 rc = ftmR3TcpReadACK(pVM, "full-sync-complete");
649 AssertRC(rc);
650
651 RTSocketRelease(pVM->ftm.s.hSocket);
652
653 /* Write protect all memory. */
654 rc = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)ftmR3WriteProtectMemory, 1, pVM);
655 AssertRCReturn(rc, rc);
656
657 rc = VMR3Resume(pVM);
658 AssertRC(rc);
659
660 return rc;
661}
662
663
664/**
665 * PGMR3PhysEnumDirtyFTPages callback for syncing dirty physical pages
666 *
667 * @param pVM VM Handle.
668 * @param GCPhys GC physical address
669 * @param pRange HC virtual address of the page(s)
670 * @param cbRange Size of the dirty range in bytes.
671 * @param pvUser User argument
672 */
673static DECLCALLBACK(int) ftmR3SyncDirtyPage(PVM pVM, RTGCPHYS GCPhys, uint8_t *pRange, unsigned cbRange, void *pvUser)
674{
675 FTMTCPHDRMEM Hdr;
676 Hdr.u32Magic = FTMTCPHDR_MAGIC;
677 Hdr.GCPhys = GCPhys;
678 Hdr.cbPageRange = cbRange;
679 Hdr.cb = cbRange;
680 /** @todo compress page(s). */
681 int rc = RTTcpSgWriteL(pVM->ftm.s.hSocket, 2, &Hdr, sizeof(Hdr), pRange, (size_t)Hdr.cb);
682 if (RT_FAILURE(rc))
683 {
684 LogRel(("FTSync/TCP: Write error (ftmR3SyncDirtyPage): %Rrc (cb=%#x)\n", rc, Hdr.cb));
685 return rc;
686 }
687 pVM->ftm.s.StatSentMem.c += Hdr.cb + sizeof(Hdr);
688 return (pVM->ftm.s.fCheckpointingActive) ? VERR_INTERRUPTED : VINF_SUCCESS;
689}
690
691/**
692 * Thread function which starts syncing process for this master VM
693 *
694 * @param Thread The thread id.
695 * @param pvUser Not used
696 * @return VINF_SUCCESS (ignored).
697 *
698 */
699static DECLCALLBACK(int) ftmR3MasterThread(RTTHREAD Thread, void *pvUser)
700{
701 int rc = VINF_SUCCESS;
702 PVM pVM = (PVM)pvUser;
703
704 for (;;)
705 {
706 /*
707 * Try connect to the standby machine.
708 */
709 Log(("ftmR3MasterThread: client connect to %s %d\n", pVM->ftm.s.pszAddress, pVM->ftm.s.uPort));
710 rc = RTTcpClientConnect(pVM->ftm.s.pszAddress, pVM->ftm.s.uPort, &pVM->ftm.s.hSocket);
711 if (RT_SUCCESS(rc))
712 {
713 Log(("ftmR3MasterThread: CONNECTED\n"));
714
715 /* Disable Nagle. */
716 rc = RTTcpSetSendCoalescing(pVM->ftm.s.hSocket, false /*fEnable*/);
717 AssertRC(rc);
718
719 /* Read and check the welcome message. */
720 char szLine[RT_MAX(128, sizeof(g_szWelcome))];
721 RT_ZERO(szLine);
722 rc = RTTcpRead(pVM->ftm.s.hSocket, szLine, sizeof(g_szWelcome) - 1, NULL);
723 if ( RT_SUCCESS(rc)
724 && !strcmp(szLine, g_szWelcome))
725 {
726 /* password */
727 if (pVM->ftm.s.pszPassword)
728 rc = RTTcpWrite(pVM->ftm.s.hSocket, pVM->ftm.s.pszPassword, strlen(pVM->ftm.s.pszPassword));
729
730 if (RT_SUCCESS(rc))
731 {
732 /* ACK */
733 rc = ftmR3TcpReadACK(pVM, "password", "Invalid password");
734 if (RT_SUCCESS(rc))
735 {
736 /** todo: verify VM config. */
737 break;
738 }
739 }
740 }
741 /* Failed, so don't bother anymore. */
742 return VINF_SUCCESS;
743 }
744 rc = RTSemEventWait(pVM->ftm.s.master.hShutdownEvent, 1000 /* 1 second */);
745 if (rc != VERR_TIMEOUT)
746 return VINF_SUCCESS; /* told to quit */
747 }
748
749 /* Successfully initialized the connection to the standby node.
750 * Start the sync process.
751 */
752
753 /* First sync all memory and write protect everything so
754 * we can send changed pages later on.
755 */
756
757 rc = ftmR3PerformFullSync(pVM);
758
759 for (;;)
760 {
761 rc = RTSemEventWait(pVM->ftm.s.master.hShutdownEvent, pVM->ftm.s.uInterval);
762 if (rc != VERR_TIMEOUT)
763 break; /* told to quit */
764
765 if (!pVM->ftm.s.fCheckpointingActive)
766 {
767 rc = PDMCritSectEnter(&pVM->ftm.s.CritSect, VERR_SEM_BUSY);
768 AssertMsg(rc == VINF_SUCCESS, ("%Rrc\n", rc));
769
770 rc = ftmR3TcpSubmitCommand(pVM, "mem-sync");
771 AssertRC(rc);
772
773 /* sync the changed memory with the standby node. */
774 /* Write protect all memory. */
775 if (!pVM->ftm.s.fCheckpointingActive)
776 {
777 rc = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)ftmR3WriteProtectMemory, 1, pVM);
778 AssertRC(rc);
779 }
780
781 /* Enumerate all dirty pages and send them to the standby VM. */
782 if (!pVM->ftm.s.fCheckpointingActive)
783 {
784 rc = PGMR3PhysEnumDirtyFTPages(pVM, ftmR3SyncDirtyPage, NULL /* pvUser */);
785 Assert(rc == VINF_SUCCESS || rc == VERR_INTERRUPTED);
786 }
787
788 /* Send last memory header to signal the end. */
789 FTMTCPHDRMEM Hdr;
790 Hdr.u32Magic = FTMTCPHDR_MAGIC;
791 Hdr.GCPhys = 0;
792 Hdr.cbPageRange = 0;
793 Hdr.cb = 0;
794 rc = RTTcpSgWriteL(pVM->ftm.s.hSocket, 1, &Hdr, sizeof(Hdr));
795 if (RT_FAILURE(rc))
796 LogRel(("FTSync/TCP: Write error (ftmR3MasterThread): %Rrc (cb=%#x)\n", rc, Hdr.cb));
797
798 rc = ftmR3TcpReadACK(pVM, "mem-sync-complete");
799 AssertRC(rc);
800
801 PDMCritSectLeave(&pVM->ftm.s.CritSect);
802 }
803 }
804 return rc;
805}
806
807/**
808 * Syncs memory from the master VM
809 *
810 * @returns VBox status code.
811 * @param pVM VM Handle.
812 */
813static int ftmR3SyncMem(PVM pVM)
814{
815 while (true)
816 {
817 FTMTCPHDRMEM Hdr;
818 void *pPage;
819 RTGCPHYS GCPhys;
820
821 /* Read memory header. */
822 int rc = RTTcpRead(pVM->ftm.s.hSocket, &Hdr, sizeof(Hdr), NULL);
823 if (RT_FAILURE(rc))
824 {
825 Log(("RTTcpRead failed with %Rrc\n", rc));
826 break;
827 }
828 pVM->ftm.s.StatReceivedMem.c += sizeof(Hdr);
829
830 if (Hdr.cb == 0)
831 break; /* end of sync. */
832
833 Assert(Hdr.cb == Hdr.cbPageRange); /** @todo uncompress */
834 GCPhys = Hdr.GCPhys;
835
836 /* Must be a multiple of PAGE_SIZE. */
837 Assert((Hdr.cbPageRange & 0xfff) == 0);
838
839 while (Hdr.cbPageRange)
840 {
841 PFTMPHYSPAGETREENODE pNode = (PFTMPHYSPAGETREENODE)RTAvloGCPhysGet(pVM->ftm.s.standby.ppPhysPageTree, GCPhys);
842 if (!pNode)
843 {
844 /* Allocate memory for the node and page. */
845 pNode = (PFTMPHYSPAGETREENODE)RTMemAllocZ(sizeof(*pNode) + PAGE_SIZE);
846 AssertBreak(pNode);
847
848 /* Insert the node into the tree. */
849 pNode->Core.Key = GCPhys;
850 pNode->pPage = (void *)(pNode + 1);
851 bool fRet = RTAvloGCPhysInsert(pVM->ftm.s.standby.ppPhysPageTree, &pNode->Core);
852 Assert(fRet);
853 }
854 pPage = pNode->pPage;
855
856 /* Fetch the page. */
857 rc = RTTcpRead(pVM->ftm.s.hSocket, pPage, PAGE_SIZE, NULL);
858 if (RT_FAILURE(rc))
859 {
860 Log(("RTTcpRead page data (%d bytes) failed with %Rrc\n", Hdr.cb, rc));
861 break;
862 }
863 pVM->ftm.s.StatReceivedMem.c += PAGE_SIZE;
864 Hdr.cbPageRange -= PAGE_SIZE;
865 }
866 }
867 return VINF_SUCCESS;
868}
869
870
871/**
872 * Callback handler for RTAvloGCPhysDestroy
873 *
874 * @returns 0 to continue, otherwise stop
875 * @param pBaseNode Node to destroy
876 * @param pvUser User parameter
877 */
878static DECLCALLBACK(int) ftmR3PageTreeDestroyCallback(PAVLOGCPHYSNODECORE pBaseNode, void *pvUser)
879{
880 PVM pVM = (PVM)pvUser;
881 PFTMPHYSPAGETREENODE pNode = (PFTMPHYSPAGETREENODE)pBaseNode;
882
883 if (pVM) /* NULL when the VM is destroyed. */
884 {
885 /* Update the guest memory of the standby VM. */
886 int rc = PGMR3PhysWriteExternal(pVM, pNode->Core.Key, pNode->pPage, PAGE_SIZE, "FTMemSync");
887 AssertRC(rc);
888 }
889 RTMemFree(pNode);
890 return 0;
891}
892
893
894/**
895 * Listen for incoming traffic destined for the standby VM.
896 *
897 * @copydoc FNRTTCPSERVE
898 *
899 * @returns VINF_SUCCESS or VERR_TCP_SERVER_STOP.
900 */
901static DECLCALLBACK(int) ftmR3StandbyServeConnection(RTSOCKET Sock, void *pvUser)
902{
903 PVM pVM = (PVM)pvUser;
904
905 pVM->ftm.s.hSocket = Sock;
906
907 /*
908 * Disable Nagle.
909 */
910 int rc = RTTcpSetSendCoalescing(Sock, false /*fEnable*/);
911 AssertRC(rc);
912
913 /* Send the welcome message to the master node. */
914 rc = RTTcpWrite(Sock, g_szWelcome, sizeof(g_szWelcome) - 1);
915 if (RT_FAILURE(rc))
916 {
917 LogRel(("Teleporter: Failed to write welcome message: %Rrc\n", rc));
918 return VINF_SUCCESS;
919 }
920
921 /*
922 * Password.
923 */
924 const char *pszPassword = pVM->ftm.s.pszPassword;
925 if (pszPassword)
926 {
927 unsigned off = 0;
928 while (pszPassword[off])
929 {
930 char ch;
931 rc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
932 if ( RT_FAILURE(rc)
933 || pszPassword[off] != ch)
934 {
935 if (RT_FAILURE(rc))
936 LogRel(("FTSync: Password read failure (off=%u): %Rrc\n", off, rc));
937 else
938 LogRel(("FTSync: Invalid password (off=%u)\n", off));
939 ftmR3TcpWriteNACK(pVM, VERR_AUTHENTICATION_FAILURE);
940 return VINF_SUCCESS;
941 }
942 off++;
943 }
944 }
945 rc = ftmR3TcpWriteACK(pVM);
946 if (RT_FAILURE(rc))
947 return VINF_SUCCESS;
948
949 /** todo: verify VM config. */
950
951 /*
952 * Stop the server.
953 *
954 * Note! After this point we must return VERR_TCP_SERVER_STOP, while prior
955 * to it we must not return that value!
956 */
957 RTTcpServerShutdown(pVM->ftm.s.standby.hServer);
958
959 /*
960 * Command processing loop.
961 */
962 bool fDone = false;
963 for (;;)
964 {
965 bool fFullSync = false;
966 char szCmd[128];
967
968 rc = ftmR3TcpReadLine(pVM, szCmd, sizeof(szCmd));
969 if (RT_FAILURE(rc))
970 break;
971
972 if (!strcmp(szCmd, "mem-sync"))
973 {
974 rc = ftmR3TcpWriteACK(pVM);
975 AssertRC(rc);
976 if (RT_FAILURE(rc))
977 continue;
978
979 rc = ftmR3SyncMem(pVM);
980 AssertRC(rc);
981
982 rc = ftmR3TcpWriteACK(pVM);
983 AssertRC(rc);
984 }
985 else
986 if ( !strcmp(szCmd, "checkpoint")
987 || !strcmp(szCmd, "full-sync")
988 || (fFullSync = true)) /* intended assignment */
989 {
990 rc = ftmR3TcpWriteACK(pVM);
991 AssertRC(rc);
992 if (RT_FAILURE(rc))
993 continue;
994
995 /* Flush all pending memory updates. */
996 if (pVM->ftm.s.standby.ppPhysPageTree)
997 {
998 RTAvloGCPhysDestroy(pVM->ftm.s.standby.ppPhysPageTree, ftmR3PageTreeDestroyCallback, pVM);
999 pVM->ftm.s.standby.ppPhysPageTree = NULL;
1000 }
1001
1002 RTSocketRetain(pVM->ftm.s.hSocket); /* For concurrent access by I/O thread and EMT. */
1003
1004 /* Reset the sync state. */
1005 pVM->ftm.s.syncstate.uOffStream = 0;
1006 pVM->ftm.s.syncstate.cbReadBlock = 0;
1007 pVM->ftm.s.syncstate.fStopReading = false;
1008 pVM->ftm.s.syncstate.fIOError = false;
1009 pVM->ftm.s.syncstate.fEndOfStream = false;
1010
1011 pVM->ftm.s.fDeltaLoadSaveActive = (fFullSync == false);
1012 rc = VMR3LoadFromStreamFT(pVM, &g_ftmR3TcpOps, pVM);
1013 pVM->ftm.s.fDeltaLoadSaveActive = false;
1014 RTSocketRelease(pVM->ftm.s.hSocket);
1015 AssertRC(rc);
1016 if (RT_FAILURE(rc))
1017 {
1018 LogRel(("FTSync: VMR3LoadFromStream -> %Rrc\n", rc));
1019 ftmR3TcpWriteNACK(pVM, rc);
1020 continue;
1021 }
1022
1023 /* The EOS might not have been read, make sure it is. */
1024 pVM->ftm.s.syncstate.fStopReading = false;
1025 size_t cbRead;
1026 rc = ftmR3TcpOpRead(pVM, pVM->ftm.s.syncstate.uOffStream, szCmd, 1, &cbRead);
1027 if (rc != VERR_EOF)
1028 {
1029 LogRel(("FTSync: Draining teleporterTcpOpRead -> %Rrc\n", rc));
1030 ftmR3TcpWriteNACK(pVM, rc);
1031 continue;
1032 }
1033
1034 rc = ftmR3TcpWriteACK(pVM);
1035 AssertRC(rc);
1036 }
1037 }
1038 LogFlowFunc(("returns mRc=%Rrc\n", rc));
1039 return VERR_TCP_SERVER_STOP;
1040}
1041
1042/**
1043 * Powers on the fault tolerant virtual machine.
1044 *
1045 * @returns VBox status code.
1046 *
1047 * @param pVM The VM to operate on.
1048 * @param fMaster FT master or standby
1049 * @param uInterval FT sync interval
1050 * @param pszAddress Standby VM address
1051 * @param uPort Standby VM port
1052 * @param pszPassword FT password (NULL for none)
1053 *
1054 * @thread Any thread.
1055 * @vmstate Created
1056 * @vmstateto PoweringOn+Running (master), PoweringOn+Running_FT (standby)
1057 */
1058VMMR3DECL(int) FTMR3PowerOn(PVM pVM, bool fMaster, unsigned uInterval, const char *pszAddress, unsigned uPort, const char *pszPassword)
1059{
1060 int rc = VINF_SUCCESS;
1061
1062 VMSTATE enmVMState = VMR3GetState(pVM);
1063 AssertMsgReturn(enmVMState == VMSTATE_CREATED,
1064 ("%s\n", VMR3GetStateName(enmVMState)),
1065 VERR_INTERNAL_ERROR_4);
1066 AssertReturn(pszAddress, VERR_INVALID_PARAMETER);
1067
1068 if (pVM->ftm.s.uInterval)
1069 pVM->ftm.s.uInterval = uInterval;
1070 else
1071 pVM->ftm.s.uInterval = 50; /* standard sync interval of 50ms */
1072
1073 pVM->ftm.s.uPort = uPort;
1074 pVM->ftm.s.pszAddress = RTStrDup(pszAddress);
1075 if (pszPassword)
1076 pVM->ftm.s.pszPassword = RTStrDup(pszPassword);
1077 if (fMaster)
1078 {
1079 rc = RTSemEventCreate(&pVM->ftm.s.master.hShutdownEvent);
1080 if (RT_FAILURE(rc))
1081 return rc;
1082
1083 rc = RTThreadCreate(NULL, ftmR3MasterThread, pVM,
1084 0, RTTHREADTYPE_IO /* higher than normal priority */, 0, "ftmMaster");
1085 if (RT_FAILURE(rc))
1086 return rc;
1087
1088 pVM->fFaultTolerantMaster = true;
1089 if (PGMIsUsingLargePages(pVM))
1090 {
1091 /* Must disable large page usage as 2 MB pages are too big to write monitor. */
1092 LogRel(("FTSync: disabling large page usage.\n"));
1093 PGMSetLargePageUsage(pVM, false);
1094 }
1095 /** @todo might need to disable page fusion as well */
1096
1097 return VMR3PowerOn(pVM);
1098 }
1099 else
1100 {
1101 /* standby */
1102 rc = RTTcpServerCreateEx(pszAddress, uPort, &pVM->ftm.s.standby.hServer);
1103 if (RT_FAILURE(rc))
1104 return rc;
1105 pVM->ftm.s.fIsStandbyNode = true;
1106
1107 rc = RTTcpServerListen(pVM->ftm.s.standby.hServer, ftmR3StandbyServeConnection, pVM);
1108 /** @todo deal with the exit code to check if we should activate this standby VM. */
1109
1110 if (pVM->ftm.s.standby.hServer)
1111 {
1112 RTTcpServerDestroy(pVM->ftm.s.standby.hServer);
1113 pVM->ftm.s.standby.hServer = NULL;
1114 }
1115 if (rc == VERR_TCP_SERVER_SHUTDOWN)
1116 rc = VINF_SUCCESS; /* ignore this error; the standby process was cancelled. */
1117 }
1118 return rc;
1119}
1120
1121/**
1122 * Powers off the fault tolerant virtual machine (standby).
1123 *
1124 * @returns VBox status code.
1125 *
1126 * @param pVM The VM to operate on.
1127 */
1128VMMR3DECL(int) FTMR3CancelStandby(PVM pVM)
1129{
1130 AssertReturn(!pVM->fFaultTolerantMaster, VERR_NOT_SUPPORTED);
1131 Assert(pVM->ftm.s.standby.hServer);
1132
1133 return RTTcpServerShutdown(pVM->ftm.s.standby.hServer);
1134}
1135
1136/**
1137 * Rendezvous callback used by FTMR3SetCheckpoint
1138 * Sync state + changed memory with the standby node.
1139 *
1140 * This is only called on one of the EMTs while the other ones are waiting for
1141 * it to complete this function.
1142 *
1143 * @returns VINF_SUCCESS (VBox strict status code).
1144 * @param pVM The VM handle.
1145 * @param pVCpu The VMCPU for the EMT we're being called on. Unused.
1146 * @param pvUser User parameter
1147 */
1148static DECLCALLBACK(VBOXSTRICTRC) ftmR3SetCheckpointRendezvous(PVM pVM, PVMCPU pVCpu, void *pvUser)
1149{
1150 int rc = VINF_SUCCESS;
1151 bool fSuspended = false;
1152
1153 /** We don't call VMR3Suspend here to avoid the overhead of state changes and notifications. This
1154 * is only a short suspend.
1155 */
1156 PDMR3Suspend(pVM);
1157
1158 /** Hack alert: as EM is responsible for dealing with the suspend state. We must do this here ourselves, but only for this EMT.*/
1159 EMR3NotifySuspend(pVM);
1160
1161 STAM_REL_COUNTER_INC(&pVM->ftm.s.StatDeltaVM);
1162
1163 RTSocketRetain(pVM->ftm.s.hSocket); /* For concurrent access by I/O thread and EMT. */
1164
1165 /* Reset the sync state. */
1166 pVM->ftm.s.syncstate.uOffStream = 0;
1167 pVM->ftm.s.syncstate.cbReadBlock = 0;
1168 pVM->ftm.s.syncstate.fStopReading = false;
1169 pVM->ftm.s.syncstate.fIOError = false;
1170 pVM->ftm.s.syncstate.fEndOfStream = false;
1171
1172 rc = ftmR3TcpSubmitCommand(pVM, "checkpoint");
1173 AssertRC(rc);
1174
1175 pVM->ftm.s.fDeltaLoadSaveActive = true;
1176 rc = VMR3SaveFT(pVM, &g_ftmR3TcpOps, pVM, &fSuspended, true /* fSkipStateChanges */);
1177 pVM->ftm.s.fDeltaLoadSaveActive = false;
1178 AssertRC(rc);
1179
1180 rc = ftmR3TcpReadACK(pVM, "checkpoint-complete");
1181 AssertRC(rc);
1182
1183 RTSocketRelease(pVM->ftm.s.hSocket);
1184
1185 /* Write protect all memory. */
1186 rc = PGMR3PhysWriteProtectRAM(pVM);
1187 AssertRC(rc);
1188
1189 /** We don't call VMR3Resume here to avoid the overhead of state changes and notifications. This
1190 * is only a short suspend.
1191 */
1192 PGMR3ResetNoMorePhysWritesFlag(pVM);
1193 PDMR3Resume(pVM);
1194
1195 /** Hack alert as EM is responsible for dealing with the suspend state. We must do this here ourselves, but only for this EMT.*/
1196 EMR3NotifyResume(pVM);
1197 return rc;
1198}
1199
1200/**
1201 * Performs a full sync to the standby node
1202 *
1203 * @returns VBox status code.
1204 *
1205 * @param pVM The VM to operate on.
1206 * @param enmCheckpoint Checkpoint type
1207 */
1208VMMR3DECL(int) FTMR3SetCheckpoint(PVM pVM, FTMCHECKPOINTTYPE enmCheckpoint)
1209{
1210 int rc;
1211
1212 if (!pVM->fFaultTolerantMaster)
1213 return VINF_SUCCESS;
1214
1215 switch (enmCheckpoint)
1216 {
1217 case FTMCHECKPOINTTYPE_NETWORK:
1218 STAM_REL_COUNTER_INC(&pVM->ftm.s.StatCheckpointNetwork);
1219 break;
1220
1221 case FTMCHECKPOINTTYPE_STORAGE:
1222 STAM_REL_COUNTER_INC(&pVM->ftm.s.StatCheckpointStorage);
1223 break;
1224
1225 default:
1226 break;
1227 }
1228 pVM->ftm.s.fCheckpointingActive = true;
1229 if (VM_IS_EMT(pVM))
1230 {
1231 PVMCPU pVCpu = VMMGetCpu(pVM);
1232
1233 /* We must take special care here as the memory sync is competing with us and requires a responsive EMT. */
1234 while ((rc = PDMCritSectTryEnter(&pVM->ftm.s.CritSect)) == VERR_SEM_BUSY)
1235 {
1236 if (VM_FF_ISPENDING(pVM, VM_FF_EMT_RENDEZVOUS))
1237 {
1238 rc = VMMR3EmtRendezvousFF(pVM, pVCpu);
1239 AssertRC(rc);
1240 }
1241
1242 if (VM_FF_ISPENDING(pVM, VM_FF_REQUEST))
1243 {
1244 rc = VMR3ReqProcessU(pVM->pUVM, VMCPUID_ANY);
1245 AssertRC(rc);
1246 }
1247 }
1248 }
1249 else
1250 rc = PDMCritSectEnter(&pVM->ftm.s.CritSect, VERR_SEM_BUSY);
1251
1252 AssertMsg(rc == VINF_SUCCESS, ("%Rrc\n", rc));
1253
1254 STAM_PROFILE_START(&pVM->ftm.s.StatCheckpoint, a);
1255
1256 rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ONCE, ftmR3SetCheckpointRendezvous, NULL);
1257
1258 STAM_PROFILE_STOP(&pVM->ftm.s.StatCheckpoint, a);
1259
1260 PDMCritSectLeave(&pVM->ftm.s.CritSect);
1261 pVM->ftm.s.fCheckpointingActive = false;
1262
1263 return rc;
1264}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette