add page cache with read-ahead and dirty writeback, file-backed and anonymous mmap with CoW, VAD-based virtual memory replacing the old user region model, PFN database with eviction and reclaim, file identity for cross-open section sharing, FAT32 path cache and per-file cluster chain cache, serial.sys for COM1 keyboard input, xHCI MSI with INTx and polled fallback, PciEnableMsiAuto, waitable input queue, KeWaitForAnyEvent, per-process thread info syscall, expanded kernel stats (PFN/PTE/VAD/file-cache/disk), pool allocation tags, async I/O user buffer locking, file flush on close, add docs.md.
This commit is contained in:
1 parent
61ec2442eb
commit
2c4a68a75f
114 files changed
+18069
-2620
No files matched your search
@@ -0,0 +1,227 @@
|
||||
// Multi-threaded file-cache/VAD fault regression test.
|
||||
// Several threads map the same file mapping and fault/read pages concurrently.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define THREAD_COUNT 6u
|
||||
#define ITERATIONS 16u
|
||||
#define TEST_SIZE 32768u
|
||||
|
||||
static uint8_t gPattern[TEST_SIZE];
|
||||
|
||||
typedef struct THREAD_CTX {
|
||||
uint32_t Index;
|
||||
uint32_t Mapping;
|
||||
volatile uint32_t Status;
|
||||
volatile uint32_t IterationsDone;
|
||||
} THREAD_CTX;
|
||||
|
||||
static THREAD_CTX gThreads[THREAD_COUNT];
|
||||
|
||||
static void print(const char* s) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
static void print_u64(uint64_t v) {
|
||||
char buf[32];
|
||||
int i = 0;
|
||||
if (v == 0) { print("0"); return; }
|
||||
while (v && i < (int)sizeof(buf)) {
|
||||
buf[i++] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
while (i--) {
|
||||
char c[2] = { buf[i], 0 };
|
||||
print(c);
|
||||
}
|
||||
}
|
||||
|
||||
static void exit_now(int code) {
|
||||
for (;;) SysExitProcess(code);
|
||||
}
|
||||
|
||||
static void fail(const char* msg) {
|
||||
print("pcache_multithread: ");
|
||||
print(msg);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
static void fail_status(const char* what, uint32_t status) {
|
||||
print("pcache_multithread: ");
|
||||
print(what);
|
||||
print(" failed status=");
|
||||
print_u64(status);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
|
||||
static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
return SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, out, sizeof(*out), 0);
|
||||
}
|
||||
|
||||
static void delete_path(const char* path) {
|
||||
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
||||
(void)SysDeleteFile(&req);
|
||||
}
|
||||
|
||||
static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
|
||||
static const char* paths[] = {
|
||||
"pcache_multithread.tmp",
|
||||
"/pcache_multithread.tmp",
|
||||
"1:/pcache_multithread.tmp",
|
||||
"0:/pcache_multithread.tmp",
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
|
||||
uint32_t h = 0;
|
||||
delete_path(paths[i]);
|
||||
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
|
||||
uint32_t s = SysCreateFile(&req);
|
||||
if (s == SYS_STATUS_SUCCESS) {
|
||||
*fileOut = h;
|
||||
if (pathOut) *pathOut = paths[i];
|
||||
return SYS_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return SYS_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
static void fill_pattern(void) {
|
||||
for (size_t i = 0; i < TEST_SIZE; ++i)
|
||||
gPattern[i] = (uint8_t)(0x5Au ^ (uint8_t)(i * 13u) ^ (uint8_t)(i >> 3));
|
||||
}
|
||||
|
||||
static uint32_t validate_mapping(const uint8_t* base) {
|
||||
for (size_t i = 0; i < TEST_SIZE; ++i) {
|
||||
if (base[i] != gPattern[i])
|
||||
return 0x10000000u | (uint32_t)(i & 0x0FFFFFFFu);
|
||||
}
|
||||
return SYS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void thread_main(uintptr_t arg) {
|
||||
THREAD_CTX* ctx = (THREAD_CTX*)arg;
|
||||
if (!ctx) SysExitThread(1);
|
||||
|
||||
for (uint32_t iter = 0; iter < ITERATIONS; ++iter) {
|
||||
void* base = 0;
|
||||
uint32_t s = SysMapFile(ctx->Mapping, 0, TEST_SIZE,
|
||||
SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &base);
|
||||
if (s != SYS_STATUS_SUCCESS || !base) {
|
||||
ctx->Status = s ? s : SYS_STATUS_INVALID_PARAMETER;
|
||||
SysExitThread(1);
|
||||
}
|
||||
|
||||
volatile uint8_t* touch = (volatile uint8_t*)base;
|
||||
for (size_t off = 0; off < TEST_SIZE; off += 4096)
|
||||
(void)touch[off];
|
||||
(void)touch[TEST_SIZE - 1];
|
||||
|
||||
s = validate_mapping((const uint8_t*)base);
|
||||
uint32_t fs = SysFreeVm(base);
|
||||
if (s != SYS_STATUS_SUCCESS) {
|
||||
ctx->Status = s;
|
||||
SysExitThread(1);
|
||||
}
|
||||
if (fs != SYS_STATUS_SUCCESS) {
|
||||
ctx->Status = fs;
|
||||
SysExitThread(1);
|
||||
}
|
||||
ctx->IterationsDone++;
|
||||
}
|
||||
|
||||
ctx->Status = SYS_STATUS_SUCCESS;
|
||||
SysExitThread(0);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
print("pcache_multithread: start\n");
|
||||
fill_pattern();
|
||||
|
||||
uint32_t file = 0;
|
||||
const char* path = 0;
|
||||
uint32_t s = create_temp(&file, &path);
|
||||
if (s != SYS_STATUS_SUCCESS) fail_status("create temp", s);
|
||||
|
||||
size_t written = 0;
|
||||
SYS_FILE_IO_REQUEST writeReq = SysMakeFileIoRequest(file, gPattern, sizeof(gPattern), &written);
|
||||
s = SysWriteFile(&writeReq);
|
||||
if (s != SYS_STATUS_SUCCESS) fail_status("write pattern", s);
|
||||
if (written != sizeof(gPattern)) fail("short write");
|
||||
|
||||
uint32_t mapping = 0;
|
||||
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, TEST_SIZE, &mapping);
|
||||
if (s != SYS_STATUS_SUCCESS) fail_status("create mapping", s);
|
||||
|
||||
SYS_FILE_CACHE_STATS before;
|
||||
SYS_FILE_CACHE_STATS after;
|
||||
if (query_cache(&before) != SYS_STATUS_SUCCESS) fail("initial stats query failed");
|
||||
|
||||
uint32_t handles[THREAD_COUNT];
|
||||
uint32_t tids[THREAD_COUNT];
|
||||
memset(handles, 0, sizeof(handles));
|
||||
memset(tids, 0, sizeof(tids));
|
||||
memset(gThreads, 0, sizeof(gThreads));
|
||||
|
||||
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
|
||||
gThreads[i].Index = i;
|
||||
gThreads[i].Mapping = mapping;
|
||||
gThreads[i].Status = SYS_STATUS_PENDING;
|
||||
SYS_CREATE_THREAD_REQUEST req = {
|
||||
(uintptr_t)thread_main,
|
||||
(uintptr_t)&gThreads[i],
|
||||
1,
|
||||
0,
|
||||
&handles[i],
|
||||
&tids[i]
|
||||
};
|
||||
s = SysCreateThread(&req);
|
||||
if (s != SYS_STATUS_SUCCESS) fail_status("create thread", s);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
|
||||
s = SysWait(handles[i], SYS_WAIT_FLAG_NONE);
|
||||
if (s != SYS_STATUS_SUCCESS) fail_status("wait thread", s);
|
||||
SysCloseHandle(handles[i]);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
|
||||
if (gThreads[i].Status != SYS_STATUS_SUCCESS) {
|
||||
print("pcache_multithread: worker ");
|
||||
print_u64(i);
|
||||
print(" status=");
|
||||
print_u64(gThreads[i].Status);
|
||||
print(" iterations=");
|
||||
print_u64(gThreads[i].IterationsDone);
|
||||
print("\n");
|
||||
exit_now(1);
|
||||
}
|
||||
if (gThreads[i].IterationsDone != ITERATIONS) fail("worker iteration count mismatch");
|
||||
}
|
||||
|
||||
if (query_cache(&after) != SYS_STATUS_SUCCESS) fail("final stats query failed");
|
||||
if (after.Loads <= before.Loads) fail("concurrent mmap did not load cache pages");
|
||||
if (after.Hits <= before.Hits) fail("concurrent mmap did not reuse cache pages");
|
||||
|
||||
SysCloseHandle(mapping);
|
||||
SysCloseHandle(file);
|
||||
delete_path(path);
|
||||
|
||||
print("pcache_multithread: loads+=");
|
||||
print_u64(after.Loads - before.Loads);
|
||||
print(" hits+=");
|
||||
print_u64(after.Hits - before.Hits);
|
||||
print(" races+=");
|
||||
print_u64(after.RaceDiscards - before.RaceDiscards);
|
||||
print("\n");
|
||||
print("pcache_multithread: PASS\n");
|
||||
exit_now(0);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user