VirtualBox

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

Last change on this file since 76409 was 70173, checked in by vboxsync, 7 years ago

VBoxServiceNT: Temporarily a duplicate of VBoxService.

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