1 | /* $Id: bs3-memalloc-1.c64 106061 2024-09-16 14:03:52Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * BS3Kit - bs3-timers-1, 64-bit C code.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2021-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 | * The contents of this file may alternatively be used under the terms
|
---|
26 | * of the Common Development and Distribution License Version 1.0
|
---|
27 | * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
|
---|
28 | * in the VirtualBox distribution, in which case the provisions of the
|
---|
29 | * CDDL are applicable instead of those of the GPL.
|
---|
30 | *
|
---|
31 | * You may elect to license modified versions of this file under the
|
---|
32 | * terms and conditions of either the GPL or the CDDL or both.
|
---|
33 | *
|
---|
34 | * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
|
---|
35 | */
|
---|
36 |
|
---|
37 |
|
---|
38 | /*********************************************************************************************************************************
|
---|
39 | * Header Files *
|
---|
40 | *********************************************************************************************************************************/
|
---|
41 | #include <bs3kit.h>
|
---|
42 | #include <iprt/asm-amd64-x86.h>
|
---|
43 | #include <VBox/VMMDevTesting.h>
|
---|
44 |
|
---|
45 |
|
---|
46 | /*********************************************************************************************************************************
|
---|
47 | * Global Variables *
|
---|
48 | *********************************************************************************************************************************/
|
---|
49 | /** Copy of interesting E820 entries. */
|
---|
50 | static INT15E820ENTRY g_aEntries[16];
|
---|
51 | /** Number of interesting entires. */
|
---|
52 | static unsigned g_cEntries = 0;
|
---|
53 | /** Number of intersting bytes found. */
|
---|
54 | static uint64_t g_cbInteresting = 0;
|
---|
55 | /** Lowest interesting address. */
|
---|
56 | static uint64_t g_uInterestingStart = UINT64_MAX;
|
---|
57 | /** End of interesting addresses. */
|
---|
58 | static uint64_t g_uInterestingEnd = 0;
|
---|
59 |
|
---|
60 |
|
---|
61 | /**
|
---|
62 | * For subsequence touch iterations that doesn't allocate any RAM.
|
---|
63 | *
|
---|
64 | * This may cause page pool activitiy if we've got more memory than we have room
|
---|
65 | * for in the pool. This depends on amount of guest RAM and how much could be
|
---|
66 | * backed by large pages.
|
---|
67 | */
|
---|
68 | static uint64_t CheckTouchedMemory(void)
|
---|
69 | {
|
---|
70 | unsigned iEntry;
|
---|
71 | uint64_t iPage = 0;
|
---|
72 | uint64_t cErrors = 0;
|
---|
73 | for (iEntry = 0; iEntry < g_cEntries; iEntry++)
|
---|
74 | {
|
---|
75 | uint64_t volatile *pu64Cur = (uint64_t *)g_aEntries[iEntry].uBaseAddr;
|
---|
76 | uint64_t cbLeft = g_aEntries[iEntry].cbRange;
|
---|
77 | while (cbLeft >= X86_PAGE_SIZE)
|
---|
78 | {
|
---|
79 | /* Check first. */
|
---|
80 | if (RT_LIKELY( pu64Cur[0] == iPage
|
---|
81 | && pu64Cur[1] == iPage))
|
---|
82 | { /* likely */ }
|
---|
83 | else
|
---|
84 | {
|
---|
85 | Bs3TestFailedF("%p: %#llx + %#llx, expected twice %#llx\n", pu64Cur, pu64Cur[0], pu64Cur[1], iPage);
|
---|
86 | cErrors++;
|
---|
87 | }
|
---|
88 |
|
---|
89 | /* Then write again. */
|
---|
90 | pu64Cur[0] = iPage;
|
---|
91 | pu64Cur[1] = iPage;
|
---|
92 |
|
---|
93 | /* Advance. */
|
---|
94 | iPage++;
|
---|
95 | pu64Cur += X86_PAGE_SIZE / sizeof(*pu64Cur);
|
---|
96 | cbLeft -= X86_PAGE_SIZE;
|
---|
97 | }
|
---|
98 | }
|
---|
99 | return cErrors;
|
---|
100 | }
|
---|
101 |
|
---|
102 |
|
---|
103 | /**
|
---|
104 | * First touching of memory, assuming content is ZERO.
|
---|
105 | */
|
---|
106 | static uint64_t FirstTouchMemory(void)
|
---|
107 | {
|
---|
108 | unsigned iEntry;
|
---|
109 | uint64_t iPage = 0;
|
---|
110 | for (iEntry = 0; iEntry < g_cEntries; iEntry++)
|
---|
111 | {
|
---|
112 | uint64_t volatile *pu64Cur = (uint64_t volatile *)g_aEntries[iEntry].uBaseAddr;
|
---|
113 | uint64_t cbLeft = g_aEntries[iEntry].cbRange;
|
---|
114 | while (cbLeft >= X86_PAGE_SIZE)
|
---|
115 | {
|
---|
116 | /*
|
---|
117 | * Write to the page first so we won't waste time mapping the zero
|
---|
118 | * page and get straight to the actual page allocation.
|
---|
119 | */
|
---|
120 | pu64Cur[0] = iPage;
|
---|
121 |
|
---|
122 | /* Then check that the 2nd qword is zero before writing it. */
|
---|
123 | if (RT_LIKELY(pu64Cur[1] == 0))
|
---|
124 | { /* likely */ }
|
---|
125 | else
|
---|
126 | Bs3TestFailedF("%p: %#llx, expected zero\n", pu64Cur, pu64Cur[1]);
|
---|
127 | pu64Cur[1] = iPage;
|
---|
128 |
|
---|
129 | /* Advance. */
|
---|
130 | iPage++;
|
---|
131 | pu64Cur += X86_PAGE_SIZE / sizeof(*pu64Cur);
|
---|
132 | cbLeft -= X86_PAGE_SIZE;
|
---|
133 | }
|
---|
134 | }
|
---|
135 | return iPage;
|
---|
136 | }
|
---|
137 |
|
---|
138 |
|
---|
139 | /**
|
---|
140 | * Translates a E820 entry type to a string.
|
---|
141 | */
|
---|
142 | static const char *getEntryTypeName(uint32_t uType)
|
---|
143 | {
|
---|
144 | switch (uType)
|
---|
145 | {
|
---|
146 | case INT15E820_TYPE_USABLE: return "USABLE";
|
---|
147 | case INT15E820_TYPE_RESERVED: return "RESERVED";
|
---|
148 | case INT15E820_TYPE_ACPI_RECLAIMABLE: return "ACPI_RECLAIMABLE";
|
---|
149 | case INT15E820_TYPE_ACPI_NVS: return "ACPI_NVS";
|
---|
150 | case INT15E820_TYPE_BAD: return "BAD";
|
---|
151 | default: return "unknown";
|
---|
152 | }
|
---|
153 | }
|
---|
154 |
|
---|
155 | BS3_DECL(void) Main_lm64()
|
---|
156 | {
|
---|
157 | uint32_t uCont;
|
---|
158 | unsigned i;
|
---|
159 |
|
---|
160 | Bs3TestInit("bs3-memalloc-1");
|
---|
161 |
|
---|
162 | /*
|
---|
163 | * Get the E820 memory descriptors and pick out those describing memory not
|
---|
164 | * already used by the Bs3Kit.
|
---|
165 | */
|
---|
166 | Bs3TestSub("INT15h/E820");
|
---|
167 | for (uCont = i = 0; i < 2048; i++)
|
---|
168 | {
|
---|
169 | uint32_t const uEbxCur = uCont;
|
---|
170 | INT15E820ENTRY Entry = { 0, 0, 0, 0 };
|
---|
171 | uint32_t cbEntry = sizeof(Entry);
|
---|
172 | if (!Bs3BiosInt15hE820_lm64(&Entry, &cbEntry, &uCont))
|
---|
173 | {
|
---|
174 | Bs3TestFailedF("int15h/E820 failed i=%u", i);
|
---|
175 | break;
|
---|
176 | }
|
---|
177 | Bs3TestPrintf("#%u/%#x: %#018llx LB %#018llx %s (%d)\n",
|
---|
178 | i, uEbxCur, Entry.uBaseAddr, Entry.cbRange, getEntryTypeName(Entry.uType), Entry.uType);
|
---|
179 | if (Entry.uType == INT15E820_TYPE_USABLE)
|
---|
180 | {
|
---|
181 | if (Entry.uBaseAddr >= _4G)
|
---|
182 | {
|
---|
183 | if (g_cEntries < RT_ELEMENTS(g_aEntries))
|
---|
184 | {
|
---|
185 | g_cbInteresting += Entry.cbRange;
|
---|
186 | if (g_uInterestingStart > Entry.uBaseAddr)
|
---|
187 | g_uInterestingStart = Entry.uBaseAddr;
|
---|
188 | if (g_uInterestingEnd < Entry.uBaseAddr + Entry.cbRange)
|
---|
189 | g_uInterestingEnd = Entry.uBaseAddr + Entry.cbRange;
|
---|
190 | Bs3MemCpy(&g_aEntries[g_cEntries++], &Entry, sizeof(Entry));
|
---|
191 | }
|
---|
192 | else
|
---|
193 | Bs3TestFailedF("Too many interesting E820 entries! Extend g_aEntries!\n");
|
---|
194 | }
|
---|
195 | }
|
---|
196 |
|
---|
197 | /* Done? */
|
---|
198 | if (uCont == 0)
|
---|
199 | break;
|
---|
200 | }
|
---|
201 | if (g_cEntries == 0)
|
---|
202 | Bs3TestFailedF("No interesting E820 entries! Make sure you've assigned more than 4GB to the VM!\n");
|
---|
203 | else
|
---|
204 | {
|
---|
205 | uint64_t uFailurePoint = 0;
|
---|
206 | int rc;
|
---|
207 | Bs3TestPrintf("Found %u interesting entries covering %#llx bytes (%u GB).\n"
|
---|
208 | "From %#llx to %#llx\n",
|
---|
209 | g_cEntries, g_cbInteresting, (unsigned)(g_cbInteresting / _1G), g_uInterestingStart, g_uInterestingEnd);
|
---|
210 |
|
---|
211 | if (g_uBs3EndOfRamAbove4G < g_uInterestingEnd)
|
---|
212 | Bs3TestFailedF("g_uBs3EndOfRamAbove4G (%#llx) is lower than g_uInterestingEnd (%#llx)!\n",
|
---|
213 | g_uBs3EndOfRamAbove4G, g_uInterestingEnd);
|
---|
214 |
|
---|
215 |
|
---|
216 | /*
|
---|
217 | * Map all the memory (Bs3Kit only maps memory below 4G).
|
---|
218 | */
|
---|
219 | Bs3TestSub("Mapping memory above 4GB");
|
---|
220 | if (!(g_uBs3CpuDetected & BS3CPU_F_PSE))
|
---|
221 | Bs3TestFailedF("PSE was not detected!\n");
|
---|
222 | else if (!(ASMGetCR4() & X86_CR4_PSE))
|
---|
223 | Bs3TestFailedF("PSE was not enabled!\n");
|
---|
224 | else if (RT_SUCCESS(rc = Bs3PagingMapRamAbove4GForLM(&uFailurePoint)))
|
---|
225 | {
|
---|
226 | #define PAGES_2_MB(a_cPages) ((a_cPages) / (_1M / X86_PAGE_SIZE))
|
---|
227 | uint64_t cTotalPages;
|
---|
228 | unsigned iLoop;
|
---|
229 |
|
---|
230 | /*
|
---|
231 | * Time touching all the memory.
|
---|
232 | */
|
---|
233 | Bs3TestSub("Allocation speed");
|
---|
234 | {
|
---|
235 | uint64_t const nsStart = Bs3TestNow();
|
---|
236 | uint64_t const uTscStart = ASMReadTSC();
|
---|
237 | uint64_t const cPages = FirstTouchMemory();
|
---|
238 | uint64_t const cTicksElapsed = ASMReadTSC() - uTscStart;
|
---|
239 | uint64_t const cNsElapsed = Bs3TestNow() - nsStart;
|
---|
240 | uint64_t uThruput;
|
---|
241 | Bs3TestValue("Pages", cPages, VMMDEV_TESTING_UNIT_PAGES);
|
---|
242 | Bs3TestValue("MiBs", PAGES_2_MB(cPages), VMMDEV_TESTING_UNIT_MEGABYTES);
|
---|
243 | Bs3TestValue("Alloc elapsed", cNsElapsed, VMMDEV_TESTING_UNIT_NS);
|
---|
244 | Bs3TestValue("Alloc elapsed in ticks", cTicksElapsed, VMMDEV_TESTING_UNIT_TICKS);
|
---|
245 | Bs3TestValue("Page alloc time", cNsElapsed / cPages, VMMDEV_TESTING_UNIT_NS_PER_PAGE);
|
---|
246 | Bs3TestValue("Page alloc time in ticks", cTicksElapsed / cPages, VMMDEV_TESTING_UNIT_TICKS_PER_PAGE);
|
---|
247 | uThruput = cPages * RT_NS_1SEC / cNsElapsed;
|
---|
248 | Bs3TestValue("Alloc thruput", uThruput, VMMDEV_TESTING_UNIT_PAGES_PER_SEC);
|
---|
249 | Bs3TestValue("Alloc thruput in MiBs", PAGES_2_MB(uThruput), VMMDEV_TESTING_UNIT_MEGABYTES_PER_SEC);
|
---|
250 | cTotalPages = cPages;
|
---|
251 | }
|
---|
252 |
|
---|
253 | /*
|
---|
254 | * Time accessing all the memory again. This might give a clue as to page pool performance.
|
---|
255 | */
|
---|
256 | for (iLoop = 0; iLoop < 2; iLoop++)
|
---|
257 | {
|
---|
258 | Bs3TestSub(iLoop == 0 ? "2nd access" : "3rd access");
|
---|
259 | {
|
---|
260 | uint64_t const nsStart = Bs3TestNow();
|
---|
261 | uint64_t const uTscStart = ASMReadTSC();
|
---|
262 | uint64_t const cErrors = CheckTouchedMemory();
|
---|
263 | uint64_t const cTicksElapsed = ASMReadTSC() - uTscStart;
|
---|
264 | uint64_t const cNsElapsed = Bs3TestNow() - nsStart;
|
---|
265 | uint64_t uThruput;
|
---|
266 | Bs3TestValue("Access elapsed", cNsElapsed, VMMDEV_TESTING_UNIT_NS);
|
---|
267 | Bs3TestValue("Access elapsed in ticks", cTicksElapsed, VMMDEV_TESTING_UNIT_TICKS);
|
---|
268 | Bs3TestValue("Page access time", cNsElapsed / cTotalPages, VMMDEV_TESTING_UNIT_NS_PER_PAGE);
|
---|
269 | Bs3TestValue("Page access time in ticks", cTicksElapsed / cTotalPages, VMMDEV_TESTING_UNIT_TICKS_PER_PAGE);
|
---|
270 | uThruput = cTotalPages * RT_NS_1SEC / cNsElapsed;
|
---|
271 | Bs3TestValue("Access thruput", uThruput, VMMDEV_TESTING_UNIT_PAGES_PER_SEC);
|
---|
272 | Bs3TestValue("Access thruput in MiBs", PAGES_2_MB(uThruput), VMMDEV_TESTING_UNIT_MEGABYTES_PER_SEC);
|
---|
273 | }
|
---|
274 | }
|
---|
275 | }
|
---|
276 | else
|
---|
277 | Bs3TestFailedF("Bs3PagingMapRamAbove4GForLM failed at %#llx: %d", uFailurePoint, rc);
|
---|
278 | }
|
---|
279 |
|
---|
280 | Bs3TestTerm();
|
---|
281 | }
|
---|
282 |
|
---|