VirtualBox

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

Last change on this file since 31794 was 31790, checked in by vboxsync, 15 years ago

burn fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.0 KB
Line 
1/* $Id: FTM.cpp 31790 2010-08-19 13:56:46Z 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
31#include <iprt/assert.h>
32#include <iprt/thread.h>
33#include <iprt/string.h>
34#include <iprt/mem.h>
35#include <iprt/tcp.h>
36#include <iprt/semaphore.h>
37
38/*******************************************************************************
39* Global Variables *
40*******************************************************************************/
41static const char g_szWelcome[] = "VirtualBox-Fault-Tolerance-Sync-1.0\n";
42
43/**
44 * Initializes the FTM.
45 *
46 * @returns VBox status code.
47 * @param pVM The VM to operate on.
48 */
49VMMR3DECL(int) FTMR3Init(PVM pVM)
50{
51 /*
52 * Assert alignment and sizes.
53 */
54 AssertCompile(sizeof(pVM->ftm.s) <= sizeof(pVM->ftm.padding));
55 AssertCompileMemberAlignment(FTM, CritSect, sizeof(uintptr_t));
56
57 /** @todo saved state for master nodes! */
58 pVM->ftm.s.pszAddress = NULL;
59 pVM->ftm.s.pszPassword = NULL;
60 pVM->fFaultTolerantMaster = false;
61 pVM->ftm.s.fIsStandbyNode = false;
62 pVM->ftm.s.standby.hServer = NIL_RTTCPSERVER;
63 pVM->ftm.s.master.hShutdownEvent = NIL_RTSEMEVENT;
64 pVM->ftm.s.hSocket = NIL_RTSOCKET;
65
66 /*
67 * Initialize the PGM critical section.
68 */
69 int rc = PDMR3CritSectInit(pVM, &pVM->ftm.s.CritSect, RT_SRC_POS, "FTM");
70 AssertRCReturn(rc, rc);
71
72 STAM_REL_REG(pVM, &pVM->ftm.s.StatReceivedMem, STAMTYPE_COUNTER, "/FT/Received/Mem", STAMUNIT_BYTES, "The amount of memory pages that was received.");
73 STAM_REL_REG(pVM, &pVM->ftm.s.StatReceivedState, STAMTYPE_COUNTER, "/FT/Received/State", STAMUNIT_BYTES, "The amount of state information that was received.");
74 STAM_REL_REG(pVM, &pVM->ftm.s.StatSentMem, STAMTYPE_COUNTER, "/FT/Sent/Mem", STAMUNIT_BYTES, "The amount of memory pages that was sent.");
75 STAM_REL_REG(pVM, &pVM->ftm.s.StatSentState, STAMTYPE_COUNTER, "/FT/Sent/State", STAMUNIT_BYTES, "The amount of state information that was sent.");
76
77 return VINF_SUCCESS;
78}
79
80/**
81 * Terminates the FTM.
82 *
83 * Termination means cleaning up and freeing all resources,
84 * the VM itself is at this point powered off or suspended.
85 *
86 * @returns VBox status code.
87 * @param pVM The VM to operate on.
88 */
89VMMR3DECL(int) FTMR3Term(PVM pVM)
90{
91 if (pVM->ftm.s.pszAddress)
92 RTMemFree(pVM->ftm.s.pszAddress);
93 if (pVM->ftm.s.pszPassword)
94 RTMemFree(pVM->ftm.s.pszPassword);
95 if (pVM->ftm.s.hSocket != NIL_RTSOCKET)
96 RTTcpClientClose(pVM->ftm.s.hSocket);
97 if (pVM->ftm.s.standby.hServer)
98 RTTcpServerDestroy(pVM->ftm.s.standby.hServer);
99 if (pVM->ftm.s.master.hShutdownEvent != NIL_RTSEMEVENT)
100 RTSemEventDestroy(pVM->ftm.s.master.hShutdownEvent);
101
102 PDMR3CritSectDelete(&pVM->ftm.s.CritSect);
103 return VINF_SUCCESS;
104}
105
106
107static int ftmR3TcpWriteACK(PVM pVM)
108{
109 int rc = RTTcpWrite(pVM->ftm.s.hSocket, "ACK\n", sizeof("ACK\n") - 1);
110 if (RT_FAILURE(rc))
111 {
112 LogRel(("FTSync: RTTcpWrite(,ACK,) -> %Rrc\n", rc));
113 }
114 return rc;
115}
116
117
118static int ftmR3TcpWriteNACK(PVM pVM, int32_t rc2, const char *pszMsgText = NULL)
119{
120 char szMsg[256];
121 size_t cch;
122 if (pszMsgText && *pszMsgText)
123 {
124 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d;%s\n", rc2, pszMsgText);
125 for (size_t off = 6; off + 1 < cch; off++)
126 if (szMsg[off] == '\n')
127 szMsg[off] = '\r';
128 }
129 else
130 cch = RTStrPrintf(szMsg, sizeof(szMsg), "NACK=%d\n", rc2);
131 int rc = RTTcpWrite(pVM->ftm.s.hSocket, szMsg, cch);
132 if (RT_FAILURE(rc))
133 LogRel(("FTSync: RTTcpWrite(,%s,%zu) -> %Rrc\n", szMsg, cch, rc));
134 return rc;
135}
136
137/**
138 * Reads a string from the socket.
139 *
140 * @returns VBox status code.
141 *
142 * @param pState The teleporter state structure.
143 * @param pszBuf The output buffer.
144 * @param cchBuf The size of the output buffer.
145 *
146 */
147static int ftmR3TcpReadLine(PVM pVM, char *pszBuf, size_t cchBuf)
148{
149 char *pszStart = pszBuf;
150 RTSOCKET Sock = pVM->ftm.s.hSocket;
151
152 AssertReturn(cchBuf > 1, VERR_INTERNAL_ERROR);
153 *pszBuf = '\0';
154
155 /* dead simple approach. */
156 for (;;)
157 {
158 char ch;
159 int rc = RTTcpRead(Sock, &ch, sizeof(ch), NULL);
160 if (RT_FAILURE(rc))
161 {
162 LogRel(("FTSync: RTTcpRead -> %Rrc while reading string ('%s')\n", rc, pszStart));
163 return rc;
164 }
165 if ( ch == '\n'
166 || ch == '\0')
167 return VINF_SUCCESS;
168 if (cchBuf <= 1)
169 {
170 LogRel(("FTSync: String buffer overflow: '%s'\n", pszStart));
171 return VERR_BUFFER_OVERFLOW;
172 }
173 *pszBuf++ = ch;
174 *pszBuf = '\0';
175 cchBuf--;
176 }
177}
178
179/**
180 * Reads an ACK or NACK.
181 *
182 * @returns VBox status code.
183 * @param pVM The VM to operate on.
184 * @param pszWhich Which ACK is this this?
185 * @param pszNAckMsg Optional NACK message.
186 */
187static int ftmR3TcpReadACK(PVM pVM, const char *pszWhich, const char *pszNAckMsg /*= NULL*/)
188{
189 char szMsg[256];
190 int rc = ftmR3TcpReadLine(pVM, szMsg, sizeof(szMsg));
191 if (RT_FAILURE(rc))
192 return rc;
193
194 if (!strcmp(szMsg, "ACK"))
195 return VINF_SUCCESS;
196
197 if (!strncmp(szMsg, "NACK=", sizeof("NACK=") - 1))
198 {
199 char *pszMsgText = strchr(szMsg, ';');
200 if (pszMsgText)
201 *pszMsgText++ = '\0';
202
203 int32_t vrc2;
204 rc = RTStrToInt32Full(&szMsg[sizeof("NACK=") - 1], 10, &vrc2);
205 if (rc == VINF_SUCCESS)
206 {
207 /*
208 * Well formed NACK, transform it into an error.
209 */
210 if (pszNAckMsg)
211 {
212 LogRel(("FTSync: %s: NACK=%Rrc (%d)\n", pszWhich, vrc2, vrc2));
213 return VERR_INTERNAL_ERROR;
214 }
215
216 if (pszMsgText)
217 {
218 pszMsgText = RTStrStrip(pszMsgText);
219 for (size_t off = 0; pszMsgText[off]; off++)
220 if (pszMsgText[off] == '\r')
221 pszMsgText[off] = '\n';
222
223 LogRel(("FTSync: %s: NACK=%Rrc (%d) - '%s'\n", pszWhich, vrc2, vrc2, pszMsgText));
224 }
225 return VERR_INTERNAL_ERROR_2;
226 }
227
228 if (pszMsgText)
229 pszMsgText[-1] = ';';
230 }
231 return VERR_INTERNAL_ERROR_3;
232}
233
234/**
235 * Thread function which starts syncing process for this master VM
236 *
237 * @param Thread The thread id.
238 * @param pvUser Not used
239 * @return VINF_SUCCESS (ignored).
240 *
241 * @note Locks the Console object for writing.
242 */
243static DECLCALLBACK(int) ftmR3MasterThread(RTTHREAD Thread, void *pvUser)
244{
245 int rc = VINF_SUCCESS;
246 PVM pVM = (PVM)pvUser;
247
248 for (;;)
249 {
250 /*
251 * Try connect to the standby machine.
252 */
253 rc = RTTcpClientConnect(pVM->ftm.s.pszAddress, pVM->ftm.s.uPort, &pVM->ftm.s.hSocket);
254 if (RT_SUCCESS(rc))
255 {
256 /* Disable Nagle. */
257 rc = RTTcpSetSendCoalescing(pVM->ftm.s.hSocket, false /*fEnable*/);
258 AssertRC(rc);
259
260 /* Read and check the welcome message. */
261 char szLine[RT_MAX(128, sizeof(g_szWelcome))];
262 RT_ZERO(szLine);
263 rc = RTTcpRead(pVM->ftm.s.hSocket, szLine, sizeof(g_szWelcome) - 1, NULL);
264 if ( RT_SUCCESS(rc)
265 && !strcmp(szLine, g_szWelcome))
266 {
267 /* password */
268 rc = RTTcpWrite(pVM->ftm.s.hSocket, pVM->ftm.s.pszPassword, strlen(pVM->ftm.s.pszPassword));
269 if (RT_SUCCESS(rc))
270 {
271 /* ACK */
272 rc = ftmR3TcpReadACK(pVM, "password", "Invalid password");
273 if (RT_SUCCESS(rc))
274 {
275 /** todo: verify VM config. */
276 break;
277 }
278 }
279 }
280 rc = RTTcpClientClose(pVM->ftm.s.hSocket);
281 AssertRC(rc);
282 pVM->ftm.s.hSocket = NIL_RTSOCKET;
283 }
284 rc = RTSemEventWait(pVM->ftm.s.master.hShutdownEvent, 1000 /* 1 second */);
285 if (rc != VERR_TIMEOUT)
286 return VINF_SUCCESS; /* told to quit */
287 }
288
289 /* Successfully initialized the connection to the standby node.
290 * Start the sync process.
291 */
292
293 for (;;)
294 {
295 if (!pVM->ftm.s.fCheckpointingActive)
296 {
297 int rc2 = PDMCritSectEnter(&pVM->ftm.s.CritSect, VERR_SEM_BUSY);
298 AssertMsg(rc2 == VINF_SUCCESS, ("%Rrc\n", rc));
299
300 /* sync the changed memory with the standby node. */
301
302 PDMCritSectLeave(&pVM->ftm.s.CritSect);
303
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 return VMR3PowerOn(pVM);
451 }
452 else
453 {
454 /* standby */
455 rc = RTTcpServerCreateEx(pszAddress, uPort, &pVM->ftm.s.standby.hServer);
456 if (RT_FAILURE(rc))
457 return rc;
458 pVM->ftm.s.fIsStandbyNode = true;
459
460 rc = RTTcpServerListen(pVM->ftm.s.standby.hServer, ftmR3StandbyServeConnection, pVM);
461 /** @todo deal with the exit code to check if we should activate this standby VM. */
462
463 RTTcpServerDestroy(pVM->ftm.s.standby.hServer);
464 pVM->ftm.s.standby.hServer = NULL;
465 }
466 return rc;
467}
468
469/**
470 * Powers off the fault tolerant virtual machine (standby).
471 *
472 * @returns VBox status code.
473 *
474 * @param pVM The VM to operate on.
475 */
476VMMR3DECL(int) FTMR3CancelStandby(PVM pVM)
477{
478 AssertReturn(!pVM->fFaultTolerantMaster, VERR_NOT_SUPPORTED);
479 Assert(pVM->ftm.s.standby.hServer);
480
481 return RTTcpServerShutdown(pVM->ftm.s.standby.hServer);
482}
483
484
485/**
486 * Performs a full sync to the standby node
487 *
488 * @returns VBox status code.
489 *
490 * @param pVM The VM to operate on.
491 */
492VMMR3DECL(int) FTMR3SyncState(PVM pVM)
493{
494 if (!pVM->fFaultTolerantMaster)
495 return VINF_SUCCESS;
496
497 pVM->ftm.s.fCheckpointingActive = true;
498 int rc = PDMCritSectEnter(&pVM->ftm.s.CritSect, VERR_SEM_BUSY);
499 AssertMsg(rc == VINF_SUCCESS, ("%Rrc\n", rc));
500
501 PDMCritSectLeave(&pVM->ftm.s.CritSect);
502 pVM->ftm.s.fCheckpointingActive = false;
503
504 return VERR_NOT_IMPLEMENTED;
505}
Note: See TracBrowser for help on using the repository browser.

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