VirtualBox

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

Last change on this file since 59131 was 58182, checked in by vboxsync, 9 years ago

VBoxService: fixed semaphore destruction race.

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