VirtualBox

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

Last change on this file since 68643 was 62851, checked in by vboxsync, 8 years ago

GAs/common: warnings

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