VirtualBox

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

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

*: scm --update-copyright-year

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