VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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