WSL/test/linux/unit_tests/madvise.c
WSL Team 697572d664 Initial open source commit for WSL.
Many Microsoft employees have contributed to the Windows Subsystem for Linux, this commit is the result of their work since 2016.

The entire history of the Windows Subsystem for Linux can't be shared here, but here's an overview of WSL's history after it moved to it own repository in 2021:

Number of commits on the main branch: 2930
Number of contributors: 31

Head over https://github.com/microsoft/WSL/releases for a more detailed history of the features added to WSL since 2021.
2025-05-15 12:09:45 -07:00

538 lines
9.9 KiB
C

/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
madvise.c
Abstract:
This file is a madvise test.
--*/
#include "lxtcommon.h"
#include "unittests.h"
#include <string.h>
#include <sys/mman.h>
#include <sched.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#define LXT_NAME "madvise"
int MadviseVariation2(PLXT_ARGS Args);
int MadviseVariation3(PLXT_ARGS Args);
int MadviseVariation4(PLXT_ARGS Args);
int MadviseVariation5(PLXT_ARGS Args);
//
// Global constants
//
static const LXT_VARIATION g_LxtVariations[] = {
{"Anonymous mapping", MadviseVariation2},
{"Anonymous mapping with fork", MadviseVariation3},
{"File-backed mapping", MadviseVariation4},
{"File-backed with fork", MadviseVariation5}};
int MadviseTestEntry(int Argc, char* Argv[])
/*++
--*/
{
LXT_ARGS Args;
int Result;
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
ErrorExit:
LxtUninitialize();
return !LXT_SUCCESS(Result);
}
#define MADVISE_TEST_BUFFER_SIZE 0x5000
int MadviseRunTest(int FileId, int BufferSize, int DoFork)
{
int Result;
unsigned char* SharedBuffer;
unsigned char* PrivateBuffer;
unsigned char* Ptr;
unsigned char* EndPtr;
unsigned char ExpectedByte;
int MapAnonymousFlag;
int IsChild;
//
// Initialize locals.
//
SharedBuffer = MAP_FAILED;
PrivateBuffer = MAP_FAILED;
IsChild = 0;
MapAnonymousFlag = 0;
if (FileId == -1)
{
MapAnonymousFlag = MAP_ANONYMOUS;
}
//
// Allocate the shared buffer.
//
SharedBuffer = mmap(NULL, BufferSize, PROT_READ | PROT_WRITE, MAP_SHARED | MapAnonymousFlag, FileId, 0);
if (SharedBuffer == MAP_FAILED)
{
Result = errno;
LxtLogError("Could not map shared! %d", Result);
goto cleanup;
}
//
// Allocate the private buffer.
//
PrivateBuffer = mmap(NULL, BufferSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MapAnonymousFlag, FileId, 0);
if (PrivateBuffer == MAP_FAILED)
{
Result = errno;
LxtLogError("Could not map private! %d", Result);
goto cleanup;
}
//
// Fill the shared buffer.
//
Ptr = SharedBuffer;
EndPtr = Ptr + BufferSize;
for (; Ptr < EndPtr; Ptr += 1)
{
*Ptr = 0xDE;
}
//
// Make sure the private buffer sees the shared buffer.
//
if (FileId != -1)
{
Ptr = PrivateBuffer;
EndPtr = Ptr + BufferSize;
for (; Ptr < EndPtr; Ptr += 1)
{
if (*Ptr != 0xDE)
{
*((volatile int*)0) = 0xDEADBEEF;
}
}
}
//
// Fill the private buffer.
//
Ptr = PrivateBuffer;
EndPtr = Ptr + BufferSize;
for (; Ptr < EndPtr; Ptr += 1)
{
*Ptr = 0xCA;
}
//
// Protect one of the two pages that will be reverted.
//
Result = mprotect(PrivateBuffer + PAGE_SIZE, PAGE_SIZE, PROT_READ);
if (Result == -1)
{
Result = errno;
LxtLogError("Could not set protection on buffer! %d", Result);
goto cleanup;
}
//
// Fork the process if desired.
//
if (DoFork)
{
Result = fork();
if (Result == -1)
{
Result = errno;
LxtLogError("Could not fork the child! %d", Result);
goto cleanup;
}
if (Result == 0)
{
IsChild = 1;
}
}
//
// Revert the middle two pages back to the shared buffer.
//
Result = madvise((PrivateBuffer + PAGE_SIZE), (PAGE_SIZE * 2), MADV_DONTNEED);
if (Result == -1)
{
Result = errno;
LxtLogError("Could not madvise on private buffer! %d", Result);
goto cleanup;
}
//
// Make sure the first page contains private data.
//
Ptr = PrivateBuffer;
EndPtr = Ptr + PAGE_SIZE;
for (; Ptr < EndPtr; Ptr += 1)
{
if (*Ptr != 0xCA)
{
LxtLogError("Private buffer not contain expected data (1), %d", *Ptr);
Result = EFAULT;
goto cleanup;
}
}
//
// Make sure the next two pages contain shared data or zeroes in the private
// allocation case.
//
EndPtr = Ptr + (PAGE_SIZE * 2);
for (; Ptr < EndPtr; Ptr += 1)
{
if (FileId == -1)
{
ExpectedByte = 0;
}
else
{
ExpectedByte = 0xDE;
}
if (*Ptr != ExpectedByte)
{
LxtLogError("Private buffer not contain expected data (2), %d", *Ptr);
Result = EFAULT;
goto cleanup;
}
}
//
// Make sure the remaining pages contain private data.
//
EndPtr = PrivateBuffer + BufferSize;
for (; Ptr < EndPtr; Ptr += 1)
{
if (*Ptr != 0xCA)
{
LxtLogError("Private buffer not contain expected data (3), %d", *Ptr);
Result = EFAULT;
goto cleanup;
}
}
//
// Make sure that the shared buffer contains all shared data.
//
Ptr = SharedBuffer;
EndPtr = Ptr + BufferSize;
for (; Ptr < EndPtr; Ptr += 1)
{
if (*Ptr != 0xDE)
{
LxtLogError("Shared buffer not contain expected data (4), %d", *Ptr);
Result = EFAULT;
goto cleanup;
}
}
Result = 0;
cleanup:
if (SharedBuffer != MAP_FAILED)
{
munmap(SharedBuffer, BufferSize);
}
if (PrivateBuffer != MAP_FAILED)
{
munmap(PrivateBuffer, BufferSize);
}
if (IsChild)
{
_exit(Result);
}
return Result;
}
int MadviseVariation2(PLXT_ARGS Args)
/*++
--*/
{
int Result;
int FileId;
int BufferSize;
//
// Initialize locals.
//
FileId = -1;
BufferSize = MADVISE_TEST_BUFFER_SIZE;
//
// Run the test on private memory.
//
Result = MadviseRunTest(FileId, BufferSize, 0);
if (Result != 0)
{
LxtLogError("Variation2 failed! %d", Result);
goto ErrorExit;
}
Result = LXT_RESULT_SUCCESS;
ErrorExit:
if (FileId != -1)
{
close(FileId);
}
return Result;
}
int MadviseVariation3(PLXT_ARGS Args)
/*++
--*/
{
int Result;
int FileId;
int BufferSize;
//
// Initialize locals.
//
FileId = -1;
BufferSize = MADVISE_TEST_BUFFER_SIZE;
//
// Run the test on private memory with fork.
//
Result = MadviseRunTest(FileId, BufferSize, 1);
if (Result != 0)
{
LxtLogError("Variation3 failed! %d", Result);
goto ErrorExit;
}
Result = LXT_RESULT_SUCCESS;
ErrorExit:
if (FileId != -1)
{
close(FileId);
}
return Result;
}
int MadviseVariation4(PLXT_ARGS Args)
/*++
--*/
{
int Result;
int FileId;
int BufferSize;
unsigned int WriteData;
int BytesWritten;
int ByteIndex;
//
// Initialize locals.
//
FileId = -1;
BufferSize = MADVISE_TEST_BUFFER_SIZE;
//
// Open the file and fill it to the appropriate size.
//
FileId = open("/data/test/madvise_test.bin", O_RDWR | O_CREAT, S_IRWXU);
if (FileId == -1)
{
Result = errno;
LxtLogError("Could not create test file! %d", Result);
goto ErrorExit;
}
WriteData = (unsigned int)-1;
for (ByteIndex = 0; ByteIndex < BufferSize; ByteIndex += sizeof(WriteData))
{
BytesWritten = write(FileId, &WriteData, sizeof(WriteData));
if (BytesWritten != sizeof(WriteData))
{
Result = errno;
LxtLogError("Could not write to file! %d", Result);
goto ErrorExit;
}
}
//
// Run the test on file-backed memory.
//
Result = MadviseRunTest(FileId, BufferSize, 0);
if (Result != 0)
{
LxtLogError("Variation4 failed! %d", Result);
goto ErrorExit;
}
Result = LXT_RESULT_SUCCESS;
ErrorExit:
if (FileId != -1)
{
close(FileId);
}
return Result;
}
int MadviseVariation5(PLXT_ARGS Args)
/*++
--*/
{
int Result;
int FileId;
int BufferSize;
unsigned int WriteData;
int BytesWritten;
int ByteIndex;
//
// Initialize locals.
//
FileId = -1;
BufferSize = MADVISE_TEST_BUFFER_SIZE;
//
// Open the file and fill it to the appropriate size.
//
FileId = open("/data/test/madvise_test.bin", O_RDWR | O_CREAT, S_IRWXU);
if (FileId == -1)
{
Result = errno;
LxtLogError("Could not create test file! %d", Result);
goto ErrorExit;
}
WriteData = (unsigned int)-1;
for (ByteIndex = 0; ByteIndex < BufferSize; ByteIndex += sizeof(WriteData))
{
BytesWritten = write(FileId, &WriteData, sizeof(WriteData));
if (BytesWritten != sizeof(WriteData))
{
Result = errno;
LxtLogError("Could not write to file! %d", Result);
goto ErrorExit;
}
}
//
// Run the test on file-backed memory with fork.
//
Result = MadviseRunTest(FileId, BufferSize, 1);
if (Result != 0)
{
LxtLogError("Variation5 failed! %d", Result);
goto ErrorExit;
}
Result = LXT_RESULT_SUCCESS;
ErrorExit:
if (FileId != -1)
{
close(FileId);
}
return Result;
}