VirtualBox

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

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

FT: disable large page usage
Removed debug code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.4 KB
Line 
1/* $Id: FTM.cpp 31807 2010-08-20 09:15:56Z 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
32#include <iprt/assert.h>
33#include <iprt/thread.h>
34#include <iprt/string.h>
35#include <iprt/mem.h>
36#include <iprt/tcp.h>
37#include <iprt/semaphore.h>
38
39/*******************************************************************************
40* Global Variables *
41*******************************************************************************/
42static const char g_szWelcome[] = "VirtualBox-Fault-Tolerance-Sync-1.0\n";
43
44/**
45 * Initializes the FTM.
46 *
47 * @returns VBox status code.
48 * @param pVM The VM to operate on.
49 */
50VMMR3DECL(int) FTMR3Init(PVM pVM)
51{
52 /*
53 * Assert alignment and sizes.
54 */
55 AssertCompile(sizeof(pVM->ftm.s) <= sizeof(pVM->ftm.padding));
56 AssertCompileMemberAlignment(FTM, CritSect, sizeof(uintptr_t));
57
58 /** @todo saved state for master nodes! */
59 pVM->ftm.s.pszAddress = NULL;
60 pVM->ftm.s.pszPassword = NULL;
61 pVM->fFaultTolerantMaster = false;
62 pVM->ftm.s.fIsStandbyNode = false;
63 pVM->ftm.s.standby.hServer = NIL_RTTCPSERVER;
64 pVM->ftm.s.master.hShutdownEvent = NIL_RTSEMEVENT;
65 pVM->ftm.s.hSocket = NIL_RTSOCKET;
66
67 /*
68 * Initialize the PGM critical section.
69 */
70 int rc = PDMR3CritSectInit(pVM, &pVM->ftm.s.CritSect, RT_SRC_POS, "FTM");
71 AssertRCReturn(rc, rc);
72
73 STAM_REL_REG(pVM, &pVM->ftm.s.StatReceivedMem, STAMTYPE_COUNTER, "/FT/Received/Mem", STAMUNIT_BYTES, "The amount of memory pages that was received.");
74 STAM_REL_REG(pVM, &pVM->ftm.s.StatReceivedState, STAMTYPE_COUNTER, "/FT/Received/State", STAMUNIT_BYTES, "The amount of state information that was received.");
75 STAM_REL_REG(pVM, &pVM->ftm.s.StatSentMem, STAMTYPE_COUNTER, "/FT/Sent/Mem", STAMUNIT_BYTES, "The amount of memory pages that was sent.");
76 STAM_REL_REG(pVM, &pVM->ftm.s.StatSentState, STAMTYPE_COUNTER, "/FT/Sent/State", STAMUNIT_BYTES, "The amount of state information that was sent.");
77
78 return VINF_SUCCESS;
79}
80
81/**
82 * Terminates the FTM.
83 *
84 * Termination means cleaning up and freeing all resources,
85 * the VM itself is at this point powered off or suspended.
86 *
87 * @returns VBox status code.
88 * @param pVM The VM to operate on.
89 */
90VMMR3DECL(int) FTMR3Term(PVM pVM)
91{
92 if (pVM->ftm.s.pszAddress)
93 RTMemFree(pVM->ftm.s.pszAddress);
94 if (pVM->ftm.s.pszPassword)
95 RTMemFree(pVM->ftm.s.pszPassword);
96 if (pVM->ftm.s.hSocket != NIL_RTSOCKET)
97 RTTcpClientClose(pVM->ftm.s.hSocket);
98 if (pVM->ftm.s.standby.hServer)
99 RTTcpServerDestroy(pVM->ftm.s.standby.hServer);
100 if (pVM->ftm.s.master.hShutdownEvent != NIL_RTSEMEVENT)
101 RTSemEventDestroy(pVM->ftm.s.master.hShutdownEvent);
102
103 PDMR3CritSectDelete(&pVM->ftm.s.CritSect);
104 return VINF_SUCCESS;
105}
106
107
108static int ftmR3TcpWriteACK(PVM pVM)
109{
110 int rc = RTTcpWrite(pVM->ftm.s.hSocket, "ACK\n", sizeof("ACK\n") - 1);
111 if (RT_FAILURE(rc))
112 {
113 LogRel(("FTSync: RTTcpWrite(,ACK,) -> %Rrc\n", rc));
114 }
115 return rc;
116}
117
118
119static int ftmR3TcpWriteNACK(PVM pVM, int32_t rc2, const char *pszMsgText = NULL)
120{
121 char szMsg[256];
122 size_t cch;
123 if (pszMsgText && *pszMsgText)
124 {
125 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d;%s\n", rc2, pszMsgText);
126 for (size_t off = 6; off + 1 < cch; off++)
127 if (szMsg[off] == '\n')
128 szMsg[off] = '\r';
129 }
130 else
131 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d\n", rc2);
132 int rc = RTTcpWrite(pVM->ftm.s.hSocket, szMsg, cch);
133 if (RT_FAILURE(rc))
134 LogRel(("FTSync: RTTcpWrite(,%s,%zu) -> %Rrc\n", szMsg, cch, rc));
135 return rc;
136}
137
138/**
139 * Reads a string from the socket.
140 *
141 * @returns VBox status code.
142 *
143 * @param pState The teleporter state structure.
144 * @param pszBuf The output buffer.
145 * @param cchBuf The size of the output buffer.
146 *
147 */
148static int ftmR3TcpReadLine(PVM pVM, char *pszBuf, size_t cchBuf)
149{
150 char *pszStart = pszBuf;
151 RTSOCKET Sock = pVM->ftm.s.hSocket;
152
153 AssertReturn(cchBuf > 1, VERR_INTERNAL_ERROR);
154 *pszBuf = '\0';
155
156 /* dead simple approach. */
157 for (;;)
158 {
159 char ch;
160 int rc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
161 if (RT_FAILURE(rc))
162 {
163 LogRel(("FTSync: RTTcpRead -> %Rrc while reading string ('%s')\n", rc, pszStart));
164 return rc;
165 }
166 if ( ch == '\n'
167 || ch == '\0')
168 return VINF_SUCCESS;
169 if (cchBuf <= 1)
170 {
171 LogRel(("FTSync: String buffer overflow: '%s'\n", pszStart));
172 return VERR_BUFFER_OVERFLOW;
173 }
174 *pszBuf++ = ch;
175 *pszBuf = '\0';
176 cchBuf--;
177 }
178}
179
180/**
181 * Reads an ACK or NACK.
182 *
183 * @returns VBox status code.
184 * @param pVM The VM to operate on.
185 * @param pszWhich Which ACK is this this?
186 * @param pszNAckMsg Optional NACK message.
187 */
188static int ftmR3TcpReadACK(PVM pVM, const char *pszWhich, const char *pszNAckMsg /*= NULL*/)
189{
190 char szMsg[256];
191 int rc = ftmR3TcpReadLine(pVM, szMsg, sizeof(szMsg));
192 if (RT_FAILURE(rc))
193 return rc;
194
195 if (!strcmp(szMsg, "ACK"))
196 return VINF_SUCCESS;
197
198 if (!strncmp(szMsg, "NACK=", sizeof("NACK=") - 1))
199 {
200 char *pszMsgText = strchr(szMsg, ';');
201 if (pszMsgText)
202 *pszMsgText++ = '\0';
203
204 int32_t vrc2;
205 rc = RTStrToInt32Full(&szMsg[sizeof("NACK=") - 1], 10, &vrc2);
206 if (rc == VINF_SUCCESS)
207 {
208 /*
209 * Well formed NACK, transform it into an error.
210 */
211 if (pszNAckMsg)
212 {
213 LogRel(("FTSync: %s: NACK=%Rrc (%d)\n", pszWhich, vrc2, vrc2));
214 return VERR_INTERNAL_ERROR;
215 }
216
217 if (pszMsgText)
218 {
219 pszMsgText = RTStrStrip(pszMsgText);
220 for (size_t off = 0; pszMsgText[off]; off++)
221 if (pszMsgText[off] == '\r')
222 pszMsgText[off] = '\n';
223
224 LogRel(("FTSync: %s: NACK=%Rrc (%d) - '%s'\n", pszWhich, vrc2, vrc2, pszMsgText));
225 }
226 return VERR_INTERNAL_ERROR_2;
227 }
228
229 if (pszMsgText)
230 pszMsgText[-1] = ';';
231 }
232 return VERR_INTERNAL_ERROR_3;
233}
234
235/**
236 * Thread function which starts syncing process for this master VM
237 *
238 * @param Thread The thread id.
239 * @param pvUser Not used
240 * @return VINF_SUCCESS (ignored).
241 *
242 * @note Locks the Console object for writing.
243 */
244static DECLCALLBACK(int) ftmR3MasterThread(RTTHREAD Thread, void *pvUser)
245{
246 int rc = VINF_SUCCESS;
247 PVM pVM = (PVM)pvUser;
248
249 for (;;)
250 {
251 /*
252 * Try connect to the standby machine.
253 */
254 rc = RTTcpClientConnect(pVM->ftm.s.pszAddress, pVM->ftm.s.uPort, &pVM->ftm.s.hSocket);
255 if (RT_SUCCESS(rc))
256 {
257 /* Disable Nagle. */
258 rc = RTTcpSetSendCoalescing(pVM->ftm.s.hSocket, false /*fEnable*/);
259 AssertRC(rc);
260
261 /* Read and check the welcome message. */
262 char szLine[RT_MAX(128, sizeof(g_szWelcome))];
263 RT_ZERO(szLine);
264 rc = RTTcpRead(pVM->ftm.s.hSocket, szLine, sizeof(g_szWelcome) - 1, NULL);
265 if ( RT_SUCCESS(rc)
266 && !strcmp(szLine, g_szWelcome))
267 {
268 /* password */
269 rc = RTTcpWrite(pVM->ftm.s.hSocket, pVM->ftm.s.pszPassword, strlen(pVM->ftm.s.pszPassword));
270 if (RT_SUCCESS(rc))
271 {
272 /* ACK */
273 rc = ftmR3TcpReadACK(pVM, "password", "Invalid password");
274 if (RT_SUCCESS(rc))
275 {
276 /** todo: verify VM config. */
277 break;
278 }
279 }
280 }
281 rc = RTTcpClientClose(pVM->ftm.s.hSocket);
282 AssertRC(rc);
283 pVM->ftm.s.hSocket = NIL_RTSOCKET;
284 }
285 rc = RTSemEventWait(pVM->ftm.s.master.hShutdownEvent, 1000 /* 1 second */);
286 if (rc != VERR_TIMEOUT)
287 return VINF_SUCCESS; /* told to quit */
288 }
289
290 /* Successfully initialized the connection to the standby node.
291 * Start the sync process.
292 */
293
294 for (;;)
295 {
296 if (!pVM->ftm.s.fCheckpointingActive)
297 {
298 rc = PDMCritSectEnter(&pVM->ftm.s.CritSect, VERR_SEM_BUSY);
299 AssertMsg(rc == VINF_SUCCESS, ("%Rrc\n", rc));
300
301 /* sync the changed memory with the standby node. */
302
303 PDMCritSectLeave(&pVM->ftm.s.CritSect);
304 }
305 rc = RTSemEventWait(pVM->ftm.s.master.hShutdownEvent, pVM->ftm.s.uInterval);
306 if (rc != VERR_TIMEOUT)
307 break; /* told to quit */
308 }
309 return rc;
310}
311
312/**
313 * Listen for incoming traffic destined for the standby VM.
314 *
315 * @copydoc FNRTTCPSERVE
316 *
317 * @returns VINF_SUCCESS or VERR_TCP_SERVER_STOP.
318 */
319static DECLCALLBACK(int) ftmR3StandbyServeConnection(RTSOCKET Sock, void *pvUser)
320{
321 PVM pVM = (PVM)pvUser;
322
323 pVM->ftm.s.hSocket = Sock;
324
325 /*
326 * Disable Nagle.
327 */
328 int rc = RTTcpSetSendCoalescing(Sock, false /*fEnable*/);
329 AssertRC(rc);
330
331 /* Send the welcome message to the master node. */
332 rc = RTTcpWrite(Sock, g_szWelcome, sizeof(g_szWelcome) - 1);
333 if (RT_FAILURE(rc))
334 {
335 LogRel(("Teleporter: Failed to write welcome message: %Rrc\n", rc));
336 return VINF_SUCCESS;
337 }
338
339 /*
340 * Password.
341 */
342 const char *pszPassword = pVM->ftm.s.pszPassword;
343 unsigned off = 0;
344 while (pszPassword[off])
345 {
346 char ch;
347 rc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
348 if ( RT_FAILURE(rc)
349 || pszPassword[off] != ch)
350 {
351 if (RT_FAILURE(rc))
352 LogRel(("FTSync: Password read failure (off=%u): %Rrc\n", off, rc));
353 else
354 LogRel(("FTSync: Invalid password (off=%u)\n", off));
355 ftmR3TcpWriteNACK(pVM, VERR_AUTHENTICATION_FAILURE);
356 return VINF_SUCCESS;
357 }
358 off++;
359 }
360 rc = ftmR3TcpWriteACK(pVM);
361 if (RT_FAILURE(rc))
362 return VINF_SUCCESS;
363
364 /** todo: verify VM config. */
365
366 /*
367 * Stop the server.
368 *
369 * Note! After this point we must return VERR_TCP_SERVER_STOP, while prior
370 * to it we must not return that value!
371 */
372 RTTcpServerShutdown(pVM->ftm.s.standby.hServer);
373
374 /*
375 * Command processing loop.
376 */
377 bool fDone = false;
378 for (;;)
379 {
380 char szCmd[128];
381 rc = ftmR3TcpReadLine(pVM, szCmd, sizeof(szCmd));
382 if (RT_FAILURE(rc))
383 break;
384
385 if (!strcmp(szCmd, "mem-sync"))
386 {
387 }
388 else
389 if (!strcmp(szCmd, "heartbeat"))
390 {
391 }
392 else
393 if (!strcmp(szCmd, "checkpoint"))
394 {
395 }
396 if (RT_FAILURE(rc))
397 break;
398 }
399 LogFlowFunc(("returns mRc=%Rrc\n", rc));
400 return VERR_TCP_SERVER_STOP;
401}
402
403/**
404 * Powers on the fault tolerant virtual machine.
405 *
406 * @returns VBox status code.
407 *
408 * @param pVM The VM to operate on.
409 * @param fMaster FT master or standby
410 * @param uInterval FT sync interval
411 * @param pszAddress Standby VM address
412 * @param uPort Standby VM port
413 * @param pszPassword FT password (NULL for none)
414 *
415 * @thread Any thread.
416 * @vmstate Created
417 * @vmstateto PoweringOn+Running (master), PoweringOn+Running_FT (standby)
418 */
419VMMR3DECL(int) FTMR3PowerOn(PVM pVM, bool fMaster, unsigned uInterval, const char *pszAddress, unsigned uPort, const char *pszPassword)
420{
421 int rc = VINF_SUCCESS;
422
423 VMSTATE enmVMState = VMR3GetState(pVM);
424 AssertMsgReturn(enmVMState == VMSTATE_POWERING_ON,
425 ("%s\n", VMR3GetStateName(enmVMState)),
426 VERR_INTERNAL_ERROR_4);
427 AssertReturn(pszAddress, VERR_INVALID_PARAMETER);
428
429 if (pVM->ftm.s.uInterval)
430 pVM->ftm.s.uInterval = uInterval;
431 else
432 pVM->ftm.s.uInterval = 50; /* standard sync interval of 50ms */
433
434 pVM->ftm.s.uPort = uPort;
435 pVM->ftm.s.pszAddress = RTStrDup(pszAddress);
436 if (pszPassword)
437 pVM->ftm.s.pszPassword = RTStrDup(pszPassword);
438 if (fMaster)
439 {
440 rc = RTSemEventCreate(&pVM->ftm.s.master.hShutdownEvent);
441 if (RT_FAILURE(rc))
442 return rc;
443
444 rc = RTThreadCreate(NULL, ftmR3MasterThread, pVM,
445 0, RTTHREADTYPE_IO /* higher than normal priority */, 0, "ftmR3MasterThread");
446 if (RT_FAILURE(rc))
447 return rc;
448
449 pVM->fFaultTolerantMaster = true;
450 if (PGMIsUsingLargePages(pVM))
451 {
452 /* Must disable large page usage as 2 MB pages are too big to write monitor. */
453 LogRel(("FTSync: disabling large page usage.\n"));
454 PGMSetLargePageUsage(pVM, false);
455 }
456 /** @todo might need to disable page fusion as well */
457
458 return VMR3PowerOn(pVM);
459 }
460 else
461 {
462 /* standby */
463 rc = RTTcpServerCreateEx(pszAddress, uPort, &pVM->ftm.s.standby.hServer);
464 if (RT_FAILURE(rc))
465 return rc;
466 pVM->ftm.s.fIsStandbyNode = true;
467
468 rc = RTTcpServerListen(pVM->ftm.s.standby.hServer, ftmR3StandbyServeConnection, pVM);
469 /** @todo deal with the exit code to check if we should activate this standby VM. */
470
471 RTTcpServerDestroy(pVM->ftm.s.standby.hServer);
472 pVM->ftm.s.standby.hServer = NULL;
473 }
474 return rc;
475}
476
477/**
478 * Powers off the fault tolerant virtual machine (standby).
479 *
480 * @returns VBox status code.
481 *
482 * @param pVM The VM to operate on.
483 */
484VMMR3DECL(int) FTMR3CancelStandby(PVM pVM)
485{
486 AssertReturn(!pVM->fFaultTolerantMaster, VERR_NOT_SUPPORTED);
487 Assert(pVM->ftm.s.standby.hServer);
488
489 return RTTcpServerShutdown(pVM->ftm.s.standby.hServer);
490}
491
492
493/**
494 * Performs a full sync to the standby node
495 *
496 * @returns VBox status code.
497 *
498 * @param pVM The VM to operate on.
499 */
500VMMR3DECL(int) FTMR3SyncState(PVM pVM)
501{
502 if (!pVM->fFaultTolerantMaster)
503 return VINF_SUCCESS;
504
505 pVM->ftm.s.fCheckpointingActive = true;
506 int rc = PDMCritSectEnter(&pVM->ftm.s.CritSect, VERR_SEM_BUSY);
507 AssertMsg(rc == VINF_SUCCESS, ("%Rrc\n", rc));
508
509 PDMCritSectLeave(&pVM->ftm.s.CritSect);
510 pVM->ftm.s.fCheckpointingActive = false;
511
512 return VERR_NOT_IMPLEMENTED;
513}
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