VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServicePageSharing.cpp@ 76553

Last change on this file since 76553 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.6 KB
Line 
1/* $Id: VBoxServicePageSharing.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * VBoxService - Guest page sharing.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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/** @page pg_vgsvc_pagesharing VBoxService - Page Sharing
20 *
21 * The Page Sharing subservice is responsible for finding memory mappings
22 * suitable page fusions.
23 *
24 * It is the driving force behind the Page Fusion feature in VirtualBox.
25 * Working with PGM and GMM (ring-0) thru the VMMDev interface. Every so often
26 * it reenumerates the memory mappings (executables and shared libraries) of the
27 * guest OS and reports additions and removals to GMM. For each mapping there
28 * is a filename and version as well as and address range and subsections. GMM
29 * will match the mapping with mapping with the same name and version from other
30 * VMs and see if there are any identical pages between the two.
31 *
32 * To increase the hit rate and reduce the volatility, the service launches a
33 * child process which loads all the Windows system DLLs it can. The child
34 * process is necessary as the DLLs are loaded without running the init code,
35 * and therefore not actually callable for other VBoxService code (may crash).
36 *
37 * This is currently only implemented on Windows. There is no technical reason
38 * for it not to be doable for all the other guests too, it's just a matter of
39 * customer demand and engineering time.
40 *
41 */
42
43
44/*********************************************************************************************************************************
45* Header Files *
46*********************************************************************************************************************************/
47#include <iprt/assert.h>
48#include <iprt/avl.h>
49#include <iprt/asm.h>
50#include <iprt/mem.h>
51#include <iprt/ldr.h>
52#include <iprt/process.h>
53#include <iprt/env.h>
54#include <iprt/stream.h>
55#include <iprt/file.h>
56#include <iprt/string.h>
57#include <iprt/semaphore.h>
58#include <iprt/string.h>
59#include <iprt/system.h>
60#include <iprt/thread.h>
61#include <iprt/time.h>
62#include <VBox/err.h>
63#include <VBox/VMMDev.h>
64#include <VBox/VBoxGuestLib.h>
65
66#ifdef RT_OS_WINDOWS
67#include <iprt/nt/nt-and-windows.h>
68# include <tlhelp32.h>
69# include <psapi.h>
70# include <winternl.h>
71#endif
72
73#include "VBoxServiceInternal.h"
74#include "VBoxServiceUtils.h"
75
76
77/*********************************************************************************************************************************
78* Header Files *
79*********************************************************************************************************************************/
80typedef struct
81{
82 AVLPVNODECORE Core;
83#ifdef RT_OS_WINDOWS
84 HMODULE hModule;
85 char szFileVersion[16];
86 MODULEENTRY32 Info;
87#endif
88} VGSVCPGSHKNOWNMOD, *PVGSVCPGSHKNOWNMOD;
89
90
91/*********************************************************************************************************************************
92* Global Variables *
93*********************************************************************************************************************************/
94/** The semaphore we're blocking on. */
95static RTSEMEVENTMULTI g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
96
97static PAVLPVNODECORE g_pKnownModuleTree = NULL;
98static uint64_t g_idSession = 0;
99
100
101/*********************************************************************************************************************************
102* Internal Functions *
103*********************************************************************************************************************************/
104static DECLCALLBACK(int) vgsvcPageSharingEmptyTreeCallback(PAVLPVNODECORE pNode, void *pvUser);
105
106
107#ifdef RT_OS_WINDOWS
108
109/**
110 * Registers a new module with the VMM
111 * @param pModule Module ptr
112 * @param fValidateMemory Validate/touch memory pages or not
113 */
114static void vgsvcPageSharingRegisterModule(PVGSVCPGSHKNOWNMOD pModule, bool fValidateMemory)
115{
116 VMMDEVSHAREDREGIONDESC aRegions[VMMDEVSHAREDREGIONDESC_MAX];
117 DWORD dwModuleSize = pModule->Info.modBaseSize;
118 BYTE *pBaseAddress = pModule->Info.modBaseAddr;
119
120 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule\n");
121
122 DWORD dwDummy;
123 DWORD cbVersion = GetFileVersionInfoSize(pModule->Info.szExePath, &dwDummy);
124 if (!cbVersion)
125 {
126 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: GetFileVersionInfoSize failed with %d\n", GetLastError());
127 return;
128 }
129 BYTE *pVersionInfo = (BYTE *)RTMemAllocZ(cbVersion);
130 if (!pVersionInfo)
131 return;
132
133 if (!GetFileVersionInfo(pModule->Info.szExePath, 0, cbVersion, pVersionInfo))
134 {
135 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: GetFileVersionInfo failed with %d\n", GetLastError());
136 goto end;
137 }
138
139 /* Fetch default code page. */
140 struct LANGANDCODEPAGE
141 {
142 WORD wLanguage;
143 WORD wCodePage;
144 } *lpTranslate;
145
146 UINT cbTranslate;
147 BOOL fRet = VerQueryValue(pVersionInfo, TEXT("\\VarFileInfo\\Translation"), (LPVOID *)&lpTranslate, &cbTranslate);
148 if ( !fRet
149 || cbTranslate < 4)
150 {
151 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: VerQueryValue failed with %d (cb=%d)\n", GetLastError(), cbTranslate);
152 goto end;
153 }
154
155 unsigned i;
156 UINT cbFileVersion;
157 char *pszFileVersion = NULL; /* Shut up MSC */
158 unsigned cTranslationBlocks = cbTranslate/sizeof(struct LANGANDCODEPAGE);
159
160 pModule->szFileVersion[0] = '\0';
161 for (i = 0; i < cTranslationBlocks; i++)
162 {
163 /* Fetch file version string. */
164 char szFileVersionLocation[256];
165
166/** @todo r=bird: Mixing ANSI and TCHAR crap again. This code is a mess. We
167 * always use the wide version of the API and convert to UTF-8/whatever. */
168
169 sprintf(szFileVersionLocation, TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"), lpTranslate[i].wLanguage, lpTranslate[i].wCodePage);
170 fRet = VerQueryValue(pVersionInfo, szFileVersionLocation, (LPVOID *)&pszFileVersion, &cbFileVersion);
171 if (fRet)
172 {
173 RTStrCopy(pModule->szFileVersion, sizeof(pModule->szFileVersion), pszFileVersion);
174 break;
175 }
176 }
177 if (i == cTranslationBlocks)
178 {
179 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: no file version found!\n");
180 goto end;
181 }
182
183 unsigned idxRegion = 0;
184
185 if (fValidateMemory)
186 {
187 do
188 {
189 MEMORY_BASIC_INFORMATION MemInfo;
190 SIZE_T cbRet = VirtualQuery(pBaseAddress, &MemInfo, sizeof(MemInfo));
191 Assert(cbRet);
192 if (!cbRet)
193 {
194 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: VirtualQueryEx failed with %d\n", GetLastError());
195 break;
196 }
197
198 if ( MemInfo.State == MEM_COMMIT
199 && MemInfo.Type == MEM_IMAGE)
200 {
201 switch (MemInfo.Protect)
202 {
203 case PAGE_EXECUTE:
204 case PAGE_EXECUTE_READ:
205 case PAGE_READONLY:
206 {
207 char *pRegion = (char *)MemInfo.BaseAddress;
208
209 /* Skip the first region as it only contains the image file header. */
210 if (pRegion != (char *)pModule->Info.modBaseAddr)
211 {
212 /* Touch all pages. */
213 while ((uintptr_t)pRegion < (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize)
214 {
215 /* Try to trick the optimizer to leave the page touching code in place. */
216 ASMProbeReadByte(pRegion);
217 pRegion += PAGE_SIZE;
218 }
219 }
220#ifdef RT_ARCH_X86
221 aRegions[idxRegion].GCRegionAddr = (RTGCPTR32)MemInfo.BaseAddress;
222#else
223 aRegions[idxRegion].GCRegionAddr = (RTGCPTR64)MemInfo.BaseAddress;
224#endif
225 aRegions[idxRegion].cbRegion = MemInfo.RegionSize;
226 idxRegion++;
227
228 break;
229 }
230
231 default:
232 break; /* ignore */
233 }
234 }
235
236 pBaseAddress = (BYTE *)MemInfo.BaseAddress + MemInfo.RegionSize;
237 if (dwModuleSize > MemInfo.RegionSize)
238 dwModuleSize -= MemInfo.RegionSize;
239 else
240 {
241 dwModuleSize = 0;
242 break;
243 }
244
245 if (idxRegion >= RT_ELEMENTS(aRegions))
246 break; /* out of room */
247 }
248 while (dwModuleSize);
249 }
250 else
251 {
252 /* We can't probe kernel memory ranges, so pretend it's one big region. */
253#ifdef RT_ARCH_X86
254 aRegions[idxRegion].GCRegionAddr = (RTGCPTR32)pBaseAddress;
255#else
256 aRegions[idxRegion].GCRegionAddr = (RTGCPTR64)pBaseAddress;
257#endif
258 aRegions[idxRegion].cbRegion = dwModuleSize;
259 idxRegion++;
260 }
261 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: VbglR3RegisterSharedModule %s %s base=%p size=%x cregions=%d\n", pModule->Info.szModule, pModule->szFileVersion, pModule->Info.modBaseAddr, pModule->Info.modBaseSize, idxRegion);
262 int rc = VbglR3RegisterSharedModule(pModule->Info.szModule, pModule->szFileVersion, (uintptr_t)pModule->Info.modBaseAddr,
263 pModule->Info.modBaseSize, idxRegion, aRegions);
264 if (RT_FAILURE(rc))
265 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: VbglR3RegisterSharedModule failed with %Rrc\n", rc);
266
267end:
268 RTMemFree(pVersionInfo);
269 return;
270}
271
272
273/**
274 * Inspect all loaded modules for the specified process
275 *
276 * @param dwProcessId Process id
277 * @param ppNewTree The module tree we're assembling from modules found
278 * in this process. Modules found are moved from
279 * g_pKnownModuleTree or created new.
280 */
281static void vgsvcPageSharingInspectModules(DWORD dwProcessId, PAVLPVNODECORE *ppNewTree)
282{
283 /* Get a list of all the modules in this process. */
284 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE /* no child process handle inheritance */, dwProcessId);
285 if (hProcess == NULL)
286 {
287 VGSvcVerbose(3, "vgsvcPageSharingInspectModules: OpenProcess %x failed with %d\n", dwProcessId, GetLastError());
288 return;
289 }
290
291 HANDLE hSnapshot = g_pfnCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
292 if (hSnapshot == INVALID_HANDLE_VALUE)
293 {
294 VGSvcVerbose(3, "vgsvcPageSharingInspectModules: CreateToolhelp32Snapshot failed with %d\n", GetLastError());
295 CloseHandle(hProcess);
296 return;
297 }
298
299 VGSvcVerbose(3, "vgsvcPageSharingInspectModules\n");
300
301 MODULEENTRY32 ModuleInfo;
302 BOOL bRet;
303
304 ModuleInfo.dwSize = sizeof(ModuleInfo);
305 bRet = g_pfnModule32First(hSnapshot, &ModuleInfo);
306 do
307 {
308 /** @todo when changing this make sure VBoxService.exe is excluded! */
309 char *pszDot = strrchr(ModuleInfo.szModule, '.');
310 if ( pszDot
311 && (pszDot[1] == 'e' || pszDot[1] == 'E'))
312 continue; /* ignore executables for now. */
313
314 /* Found it before? */
315 PAVLPVNODECORE pRec = RTAvlPVGet(ppNewTree, ModuleInfo.modBaseAddr);
316 if (!pRec)
317 {
318 pRec = RTAvlPVRemove(&g_pKnownModuleTree, ModuleInfo.modBaseAddr);
319 if (!pRec)
320 {
321 /* New module; register it. */
322 PVGSVCPGSHKNOWNMOD pModule = (PVGSVCPGSHKNOWNMOD)RTMemAllocZ(sizeof(*pModule));
323 Assert(pModule);
324 if (!pModule)
325 break;
326
327 pModule->Info = ModuleInfo;
328 pModule->Core.Key = ModuleInfo.modBaseAddr;
329 pModule->hModule = LoadLibraryEx(ModuleInfo.szExePath, 0, DONT_RESOLVE_DLL_REFERENCES);
330 if (pModule->hModule)
331 vgsvcPageSharingRegisterModule(pModule, true /* validate pages */);
332
333 VGSvcVerbose(3, "\n\n MODULE NAME: %s", ModuleInfo.szModule );
334 VGSvcVerbose(3, "\n executable = %s", ModuleInfo.szExePath );
335 VGSvcVerbose(3, "\n process ID = 0x%08X", ModuleInfo.th32ProcessID );
336 VGSvcVerbose(3, "\n base address = %#010p", (uintptr_t) ModuleInfo.modBaseAddr );
337 VGSvcVerbose(3, "\n base size = %d", ModuleInfo.modBaseSize );
338
339 pRec = &pModule->Core;
340 }
341 bool ret = RTAvlPVInsert(ppNewTree, pRec);
342 Assert(ret); NOREF(ret);
343 }
344 } while (g_pfnModule32Next(hSnapshot, &ModuleInfo));
345
346 CloseHandle(hSnapshot);
347 CloseHandle(hProcess);
348}
349
350
351/**
352 * Inspect all running processes for executables and dlls that might be worth sharing
353 * with other VMs.
354 *
355 */
356static void vgsvcPageSharingInspectGuest(void)
357{
358 VGSvcVerbose(3, "vgsvcPageSharingInspectGuest\n");
359 PAVLPVNODECORE pNewTree = NULL;
360
361 /*
362 * Check loaded modules for all running processes.
363 */
364 if ( g_pfnProcess32First
365 && g_pfnProcess32Next
366 && g_pfnModule32First
367 && g_pfnModule32Next
368 && g_pfnCreateToolhelp32Snapshot)
369 {
370 HANDLE hSnapshot = g_pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
371 if (hSnapshot == INVALID_HANDLE_VALUE)
372 {
373 VGSvcVerbose(3, "vgsvcPageSharingInspectGuest: CreateToolhelp32Snapshot failed with %d\n", GetLastError());
374 return;
375 }
376
377 DWORD const dwProcessId = GetCurrentProcessId();
378
379 PROCESSENTRY32 ProcessInfo;
380 ProcessInfo.dwSize = sizeof(ProcessInfo);
381 g_pfnProcess32First(hSnapshot, &ProcessInfo);
382
383 do
384 {
385 /* Skip our own process. */
386 if (ProcessInfo.th32ProcessID != dwProcessId)
387 vgsvcPageSharingInspectModules(ProcessInfo.th32ProcessID, &pNewTree);
388 }
389 while (g_pfnProcess32Next(hSnapshot, &ProcessInfo));
390
391 CloseHandle(hSnapshot);
392 }
393
394 /*
395 * Check all loaded kernel modules.
396 */
397 if (g_pfnZwQuerySystemInformation)
398 {
399 ULONG cbBuffer = 0;
400 PVOID pBuffer = NULL;
401 PRTL_PROCESS_MODULES pSystemModules;
402
403 NTSTATUS ret = g_pfnZwQuerySystemInformation(SystemModuleInformation, (PVOID)&cbBuffer, 0, &cbBuffer);
404 if (!cbBuffer)
405 {
406 VGSvcVerbose(1, "ZwQuerySystemInformation returned length 0\n");
407 goto skipkernelmodules;
408 }
409
410 pBuffer = RTMemAllocZ(cbBuffer);
411 if (!pBuffer)
412 goto skipkernelmodules;
413
414 ret = g_pfnZwQuerySystemInformation(SystemModuleInformation, pBuffer, cbBuffer, &cbBuffer);
415 if (ret != STATUS_SUCCESS)
416 {
417 VGSvcVerbose(1, "ZwQuerySystemInformation returned %x (1)\n", ret);
418 goto skipkernelmodules;
419 }
420
421 pSystemModules = (PRTL_PROCESS_MODULES)pBuffer;
422 for (unsigned i = 0; i < pSystemModules->NumberOfModules; i++)
423 {
424 VGSvcVerbose(4, "\n\n KERNEL MODULE NAME: %s", pSystemModules->Modules[i].FullPathName[pSystemModules->Modules[i].OffsetToFileName] );
425 VGSvcVerbose(4, "\n executable = %s", pSystemModules->Modules[i].FullPathName );
426 VGSvcVerbose(4, "\n flags = 0x%08X\n", pSystemModules->Modules[i].Flags);
427
428 /* User-mode modules seem to have no flags set; skip them as we detected them above. */
429 if (pSystemModules->Modules[i].Flags == 0)
430 continue;
431
432 /* Found it before? */
433 PAVLPVNODECORE pRec = RTAvlPVGet(&pNewTree, pSystemModules->Modules[i].ImageBase);
434 if (!pRec)
435 {
436 pRec = RTAvlPVRemove(&g_pKnownModuleTree, pSystemModules->Modules[i].ImageBase);
437 if (!pRec)
438 {
439 /* New module; register it. */
440 char szFullFilePath[512];
441 PVGSVCPGSHKNOWNMOD pModule = (PVGSVCPGSHKNOWNMOD)RTMemAllocZ(sizeof(*pModule));
442 Assert(pModule);
443 if (!pModule)
444 break;
445
446/** @todo FullPathName not an UTF-8 string is! An ANSI string it is. */
447 strcpy(pModule->Info.szModule,
448 (const char *)&pSystemModules->Modules[i].FullPathName[pSystemModules->Modules[i].OffsetToFileName]);
449 GetSystemDirectoryA(szFullFilePath, sizeof(szFullFilePath));
450
451 /* skip \Systemroot\system32 */
452 char *lpPath = strchr((char *)&pSystemModules->Modules[i].FullPathName[1], '\\');
453 if (!lpPath)
454 {
455 /* Seen just file names in XP; try to locate the file in the system32 and system32\drivers directories. */
456 strcat(szFullFilePath, "\\");
457 strcat(szFullFilePath, (const char *)pSystemModules->Modules[i].FullPathName);
458 VGSvcVerbose(3, "Unexpected kernel module name try %s\n", szFullFilePath);
459 if (RTFileExists(szFullFilePath) == false)
460 {
461 GetSystemDirectoryA(szFullFilePath, sizeof(szFullFilePath));
462 strcat(szFullFilePath, "\\drivers\\");
463 strcat(szFullFilePath, (const char *)pSystemModules->Modules[i].FullPathName);
464 VGSvcVerbose(3, "Unexpected kernel module name try %s\n", szFullFilePath);
465 if (RTFileExists(szFullFilePath) == false)
466 {
467 VGSvcVerbose(1, "Unexpected kernel module name %s\n", pSystemModules->Modules[i].FullPathName);
468 RTMemFree(pModule);
469 continue;
470 }
471 }
472 }
473 else
474 {
475 lpPath = strchr(lpPath + 1, '\\');
476 if (!lpPath)
477 {
478 VGSvcVerbose(1, "Unexpected kernel module name %s (2)\n", pSystemModules->Modules[i].FullPathName);
479 RTMemFree(pModule);
480 continue;
481 }
482
483 strcat(szFullFilePath, lpPath);
484 }
485
486 strcpy(pModule->Info.szExePath, szFullFilePath);
487 pModule->Info.modBaseAddr = (BYTE *)pSystemModules->Modules[i].ImageBase;
488 pModule->Info.modBaseSize = pSystemModules->Modules[i].ImageSize;
489
490 pModule->Core.Key = pSystemModules->Modules[i].ImageBase;
491 vgsvcPageSharingRegisterModule(pModule, false /* don't check memory pages */);
492
493 VGSvcVerbose(3, "\n\n KERNEL MODULE NAME: %s", pModule->Info.szModule );
494 VGSvcVerbose(3, "\n executable = %s", pModule->Info.szExePath );
495 VGSvcVerbose(3, "\n base address = %#010p", (uintptr_t)pModule->Info.modBaseAddr );
496 VGSvcVerbose(3, "\n flags = 0x%08X", pSystemModules->Modules[i].Flags);
497 VGSvcVerbose(3, "\n base size = %d", pModule->Info.modBaseSize );
498
499 pRec = &pModule->Core;
500 }
501 bool ret = RTAvlPVInsert(&pNewTree, pRec);
502 Assert(ret); NOREF(ret);
503 }
504 }
505skipkernelmodules:
506 if (pBuffer)
507 RTMemFree(pBuffer);
508 }
509
510 /* Delete leftover modules in the old tree. */
511 RTAvlPVDestroy(&g_pKnownModuleTree, vgsvcPageSharingEmptyTreeCallback, NULL);
512
513 /* Check all registered modules. */
514 VbglR3CheckSharedModules();
515
516 /* Activate new module tree. */
517 g_pKnownModuleTree = pNewTree;
518}
519
520
521/**
522 * RTAvlPVDestroy callback.
523 */
524static DECLCALLBACK(int) vgsvcPageSharingEmptyTreeCallback(PAVLPVNODECORE pNode, void *pvUser)
525{
526 PVGSVCPGSHKNOWNMOD pModule = (PVGSVCPGSHKNOWNMOD)pNode;
527 bool *pfUnregister = (bool *)pvUser;
528
529 VGSvcVerbose(3, "vgsvcPageSharingEmptyTreeCallback %s %s\n", pModule->Info.szModule, pModule->szFileVersion);
530
531 /* Dereference module in the hypervisor. */
532 if ( !pfUnregister
533 || *pfUnregister)
534 {
535 int rc = VbglR3UnregisterSharedModule(pModule->Info.szModule, pModule->szFileVersion,
536 (uintptr_t)pModule->Info.modBaseAddr, pModule->Info.modBaseSize);
537 AssertRC(rc);
538 }
539
540 if (pModule->hModule)
541 FreeLibrary(pModule->hModule);
542 RTMemFree(pNode);
543 return 0;
544}
545
546
547#else /* !RT_OS_WINDOWS */
548
549static void vgsvcPageSharingInspectGuest(void)
550{
551 /** @todo other platforms */
552}
553
554#endif /* !RT_OS_WINDOWS */
555
556/** @interface_method_impl{VBOXSERVICE,pfnInit} */
557static DECLCALLBACK(int) vgsvcPageSharingInit(void)
558{
559 VGSvcVerbose(3, "vgsvcPageSharingInit\n");
560
561 int rc = RTSemEventMultiCreate(&g_PageSharingEvent);
562 AssertRCReturn(rc, rc);
563
564#ifdef RT_OS_WINDOWS
565 rc = VbglR3GetSessionId(&g_idSession);
566 if (RT_FAILURE(rc))
567 {
568 if (rc == VERR_IO_GEN_FAILURE)
569 VGSvcVerbose(0, "PageSharing: Page sharing support is not available by the host\n");
570 else
571 VGSvcError("vgsvcPageSharingInit: Failed with rc=%Rrc\n", rc);
572
573 rc = VERR_SERVICE_DISABLED;
574
575 RTSemEventMultiDestroy(g_PageSharingEvent);
576 g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
577
578 }
579#endif
580
581 return rc;
582}
583
584
585/**
586 * @interface_method_impl{VBOXSERVICE,pfnWorker}
587 */
588static DECLCALLBACK(int) vgsvcPageSharingWorker(bool volatile *pfShutdown)
589{
590 /*
591 * Tell the control thread that it can continue
592 * spawning services.
593 */
594 RTThreadUserSignal(RTThreadSelf());
595
596 /*
597 * Now enter the loop retrieving runtime data continuously.
598 */
599 for (;;)
600 {
601 bool fEnabled = VbglR3PageSharingIsEnabled();
602 VGSvcVerbose(3, "vgsvcPageSharingWorker: enabled=%d\n", fEnabled);
603
604 if (fEnabled)
605 vgsvcPageSharingInspectGuest();
606
607 /*
608 * Block for a minute.
609 *
610 * The event semaphore takes care of ignoring interruptions and it
611 * allows us to implement service wakeup later.
612 */
613 if (*pfShutdown)
614 break;
615 int rc = RTSemEventMultiWait(g_PageSharingEvent, 60000);
616 if (*pfShutdown)
617 break;
618 if (rc != VERR_TIMEOUT && RT_FAILURE(rc))
619 {
620 VGSvcError("vgsvcPageSharingWorker: RTSemEventMultiWait failed; rc=%Rrc\n", rc);
621 break;
622 }
623#ifdef RT_OS_WINDOWS
624 uint64_t idNewSession = g_idSession;
625 rc = VbglR3GetSessionId(&idNewSession);
626 AssertRC(rc);
627
628 if (idNewSession != g_idSession)
629 {
630 bool fUnregister = false;
631
632 VGSvcVerbose(3, "vgsvcPageSharingWorker: VM was restored!!\n");
633 /* The VM was restored, so reregister all modules the next time. */
634 RTAvlPVDestroy(&g_pKnownModuleTree, vgsvcPageSharingEmptyTreeCallback, &fUnregister);
635 g_pKnownModuleTree = NULL;
636
637 g_idSession = idNewSession;
638 }
639#endif
640 }
641
642 RTSemEventMultiDestroy(g_PageSharingEvent);
643 g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
644
645 VGSvcVerbose(3, "vgsvcPageSharingWorker: finished thread\n");
646 return 0;
647}
648
649#ifdef RT_OS_WINDOWS
650
651/**
652 * This gets control when VBoxService is launched with "pagefusion" by
653 * vgsvcPageSharingWorkerProcess().
654 *
655 * @returns RTEXITCODE_SUCCESS.
656 *
657 * @remarks It won't normally return since the parent drops the shutdown hint
658 * via RTProcTerminate().
659 */
660RTEXITCODE VGSvcPageSharingWorkerChild(void)
661{
662 VGSvcVerbose(3, "vgsvcPageSharingInitFork\n");
663
664 bool fShutdown = false;
665 vgsvcPageSharingInit();
666 vgsvcPageSharingWorker(&fShutdown);
667
668 return RTEXITCODE_SUCCESS;
669}
670
671
672/**
673 * @interface_method_impl{VBOXSERVICE,pfnWorker}
674 */
675static DECLCALLBACK(int) vgsvcPageSharingWorkerProcess(bool volatile *pfShutdown)
676{
677 RTPROCESS hProcess = NIL_RTPROCESS;
678 int rc;
679
680 /*
681 * Tell the control thread that it can continue
682 * spawning services.
683 */
684 RTThreadUserSignal(RTThreadSelf());
685
686 /*
687 * Now enter the loop retrieving runtime data continuously.
688 */
689 for (;;)
690 {
691 bool fEnabled = VbglR3PageSharingIsEnabled();
692 VGSvcVerbose(3, "vgsvcPageSharingWorkerProcess: enabled=%d\n", fEnabled);
693
694 /*
695 * Start a 2nd VBoxService process to deal with page fusion as we do
696 * not wish to dummy load dlls into this process. (First load with
697 * DONT_RESOLVE_DLL_REFERENCES, 2nd normal -> dll init routines not called!)
698 */
699 if ( fEnabled
700 && hProcess == NIL_RTPROCESS)
701 {
702 char szExeName[256];
703 char *pszExeName = RTProcGetExecutablePath(szExeName, sizeof(szExeName));
704 if (pszExeName)
705 {
706 char const *papszArgs[3];
707 papszArgs[0] = pszExeName;
708 papszArgs[1] = "pagefusion";
709 papszArgs[2] = NULL;
710 rc = RTProcCreate(pszExeName, papszArgs, RTENV_DEFAULT, 0 /* normal child */, &hProcess);
711 if (RT_FAILURE(rc))
712 VGSvcError("vgsvcPageSharingWorkerProcess: RTProcCreate %s failed; rc=%Rrc\n", pszExeName, rc);
713 }
714 }
715
716 /*
717 * Block for a minute.
718 *
719 * The event semaphore takes care of ignoring interruptions and it
720 * allows us to implement service wakeup later.
721 */
722 if (*pfShutdown)
723 break;
724 rc = RTSemEventMultiWait(g_PageSharingEvent, 60000);
725 if (*pfShutdown)
726 break;
727 if (rc != VERR_TIMEOUT && RT_FAILURE(rc))
728 {
729 VGSvcError("vgsvcPageSharingWorkerProcess: RTSemEventMultiWait failed; rc=%Rrc\n", rc);
730 break;
731 }
732 }
733
734 if (hProcess != NIL_RTPROCESS)
735 RTProcTerminate(hProcess);
736
737 VGSvcVerbose(3, "vgsvcPageSharingWorkerProcess: finished thread\n");
738 return 0;
739}
740
741#endif /* RT_OS_WINDOWS */
742
743/**
744 * @interface_method_impl{VBOXSERVICE,pfnStop}
745 */
746static DECLCALLBACK(void) vgsvcPageSharingStop(void)
747{
748 RTSemEventMultiSignal(g_PageSharingEvent);
749}
750
751
752/**
753 * @interface_method_impl{VBOXSERVICE,pfnTerm}
754 */
755static DECLCALLBACK(void) vgsvcPageSharingTerm(void)
756{
757 if (g_PageSharingEvent != NIL_RTSEMEVENTMULTI)
758 {
759 RTSemEventMultiDestroy(g_PageSharingEvent);
760 g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
761 }
762}
763
764
765/**
766 * The 'pagesharing' service description.
767 */
768VBOXSERVICE g_PageSharing =
769{
770 /* pszName. */
771 "pagesharing",
772 /* pszDescription. */
773 "Page Sharing",
774 /* pszUsage. */
775 NULL,
776 /* pszOptions. */
777 NULL,
778 /* methods */
779 VGSvcDefaultPreInit,
780 VGSvcDefaultOption,
781 vgsvcPageSharingInit,
782#ifdef RT_OS_WINDOWS
783 vgsvcPageSharingWorkerProcess,
784#else
785 vgsvcPageSharingWorker,
786#endif
787 vgsvcPageSharingStop,
788 vgsvcPageSharingTerm
789};
790
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use