8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/errors.h"
11#include "pedigree/kernel/process/Process.h"
12#include "pedigree/kernel/process/Scheduler.h"
13#include "pedigree/kernel/process/Thread.h"
14#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
15#include "pedigree/kernel/processor/Processor.h"
16#include "pedigree/kernel/utilities/ZombieQueue.h"
17#include "pedigree/kernel/utilities/lib.h"
21#include "modules/subsys/posix/PosixProcess.h"
22#include "modules/subsys/posix/PosixSubsystem.h"
23#include "modules/subsys/posix/linux-resource-abi.h"
24#include "modules/subsys/posix/system-syscalls.h"
26#include <sys/resource.h>
31constexpr size_t HostedAttempts = 10000;
32constexpr int PreservedErrno = 173;
34struct SeededExitContext {
35 SeededExitContext(Time::Timestamp user, Time::Timestamp kernel,
bool gated)
36 : user(user), kernel(kernel), ready(0), release(gated ? 0 : 1) {}
39 Time::Timestamp kernel;
44struct DescendantExitContext {
45 DescendantExitContext(Time::Timestamp childUser, Time::Timestamp childKernel,
46 Time::Timestamp descendantUser, Time::Timestamp descendantKernel)
47 : childUser(childUser),
48 childKernel(childKernel),
49 descendant(descendantUser, descendantKernel, false),
52 descendantReaped(false) {}
54 Time::Timestamp childUser;
55 Time::Timestamp childKernel;
56 SeededExitContext descendant;
59 bool descendantReaped;
62struct ConcurrentWaitContext {
63 explicit ConcurrentWaitContext(
int pid)
64 : pid(pid), ready(0), go(0), returned(0), result(0), error(0) {}
74struct ChildResourceContext {
76 : process(process), passed(false), returned(0) {}
84 for (
size_t attempt = 0; attempt < HostedAttempts; ++attempt) {
93bool waitForTermination(
Process* process) {
94 for (
size_t attempt = 0; attempt < HostedAttempts; ++attempt) {
95 if (process->isTerminationReapableForHostedTest()) {
103int seededExit(
void* parameter) {
104 SeededExitContext* context =
reinterpret_cast<SeededExitContext*
>(parameter);
106 while (!context->release) {
111 current->publishTimeAccountingForHostedTest(context->user, context->kernel);
116int reapDescendantAndExit(
void* parameter) {
117 DescendantExitContext* context =
reinterpret_cast<DescendantExitContext*
>(parameter);
119 while (!context->release) {
126 Thread* descendantThread =
127 new Thread(descendant, seededExit, &context->descendant,
nullptr,
false,
true,
true);
128 descendantThread->setName(
"hosted resource descendant");
130 const int descendantPid =
static_cast<int>(descendant->
getId());
131 if (descendantThread->
start()) {
132 context->descendantReaped = posix_waitpid(descendantPid,
nullptr, 0,
nullptr) == descendantPid;
134 delete descendantThread;
139 current->publishTimeAccountingForHostedTest(context->childUser, context->childKernel);
144int concurrentWait(
void* parameter) {
145 ConcurrentWaitContext* context =
reinterpret_cast<ConcurrentWaitContext*
>(parameter);
148 while (!context->go) {
153 context->result = posix_waitpid(context->pid,
nullptr, 0,
nullptr);
154 context->error = current->
getErrno();
155 context->returned += 1;
160 return static_cast<Time::Timestamp
>(
usage.userSeconds) * Time::Multiplier::Second +
161 static_cast<Time::Timestamp
>(
usage.userMicroseconds) * Time::Multiplier::Microsecond;
165 return static_cast<Time::Timestamp
>(
usage.systemSeconds) * Time::Multiplier::Second +
166 static_cast<Time::Timestamp
>(
usage.systemMicroseconds) * Time::Multiplier::Microsecond;
169bool transitiveChildAccounting(
PosixProcess* parent,
int* status,
struct tms* processTimes,
171 uint8_t* linuxCanary) {
172 constexpr Time::Timestamp clockTick = Time::Multiplier::Second / 100;
173 constexpr Time::Timestamp childUser = 29 * clockTick;
174 constexpr Time::Timestamp childKernel = 17 * clockTick;
175 constexpr Time::Timestamp descendantUser = 43 * clockTick;
176 constexpr Time::Timestamp descendantKernel = 23 * clockTick;
178 const Time::Timestamp userBefore = parent->getReapedChildrenUserTime();
179 const Time::Timestamp kernelBefore = parent->getReapedChildrenKernelTime();
180 DescendantExitContext context(childUser, childKernel, descendantUser, descendantKernel);
184 new Thread(child, reapDescendantAndExit, &context,
nullptr,
false,
true,
true);
185 childThread->setName(
"hosted transitive resource child");
187 const int childPid =
static_cast<int>(child->
getId());
188 if (!childThread->
start()) {
194 bool passed = waitForValue(context.ready);
196 *status = 0x5A5A5A5A;
198 MemoryCopy(untouched, linuxUsage,
sizeof(untouched));
201 passed &= posix_waitpid(childPid, status, WNOHANG, linuxUsage) == 0 && *status == 0x5A5A5A5A &&
202 !MemoryCompare(linuxUsage, untouched,
sizeof(untouched)) &&
203 current->
getErrno() == PreservedErrno;
205 context.release += 1;
208 const int waited = posix_waitpid(childPid, status, 0, linuxUsage);
209 passed &= waited == childPid && WIFEXITED(*status) && !WEXITSTATUS(*status) &&
210 context.descendantReaped && current->
getErrno() == PreservedErrno;
212 for (
size_t i = offsetof(
LinuxRusage64, maximumResidentSetSize); i <
sizeof(*linuxUsage); ++i) {
213 passed &= !
reinterpret_cast<uint8_t*
>(linuxUsage)[i];
215 for (
size_t i = 0; i < 32; ++i) {
216 passed &= linuxCanary[i] == 0xA5;
219 const Time::Timestamp userAfter = parent->getReapedChildrenUserTime();
220 const Time::Timestamp kernelAfter = parent->getReapedChildrenKernelTime();
221 const Time::Timestamp addedUser = userAfter - userBefore;
222 const Time::Timestamp addedKernel = kernelAfter - kernelBefore;
224 addedUser >= childUser + descendantUser && addedKernel >= childKernel + descendantKernel &&
225 linuxUserTime(*linuxUsage) == addedUser - (addedUser % Time::Multiplier::Microsecond) &&
226 linuxKernelTime(*linuxUsage) == addedKernel - (addedKernel % Time::Multiplier::Microsecond);
228 ByteSet(nativeUsage, 0xA5,
sizeof(*nativeUsage));
229 passed &= posix_getrusage(RUSAGE_CHILDREN, nativeUsage) == 0;
230 const Time::Timestamp nativeUser =
231 static_cast<Time::Timestamp
>(nativeUsage->ru_utime.tv_sec) * Time::Multiplier::Second +
232 static_cast<Time::Timestamp
>(nativeUsage->ru_utime.tv_usec) * Time::Multiplier::Microsecond;
233 const Time::Timestamp nativeKernel =
234 static_cast<Time::Timestamp
>(nativeUsage->ru_stime.tv_sec) * Time::Multiplier::Second +
235 static_cast<Time::Timestamp
>(nativeUsage->ru_stime.tv_usec) * Time::Multiplier::Microsecond;
236 passed &= nativeUser == userAfter - (userAfter % Time::Multiplier::Microsecond) &&
237 nativeKernel == kernelAfter - (kernelAfter % Time::Multiplier::Microsecond);
238 for (
size_t i = offsetof(
struct rusage, ru_maxrss); i <
sizeof(*nativeUsage); ++i) {
239 passed &= !
reinterpret_cast<uint8_t*
>(nativeUsage)[i];
244 posix_linux_getrusage(RUSAGE_CHILDREN, linuxUsage) == 0 &&
245 linuxUserTime(*linuxUsage) == userAfter - (userAfter % Time::Multiplier::Microsecond) &&
246 linuxKernelTime(*linuxUsage) == kernelAfter - (kernelAfter % Time::Multiplier::Microsecond);
247 for (
size_t i = offsetof(
LinuxRusage64, maximumResidentSetSize); i <
sizeof(*linuxUsage); ++i) {
248 passed &= !
reinterpret_cast<uint8_t*
>(linuxUsage)[i];
250 for (
size_t i = 0; i < 32; ++i) {
251 passed &= linuxCanary[i] == 0xA5;
254 passed &= posix_times(processTimes) >= 0 &&
255 processTimes->tms_cutime ==
static_cast<clock_t
>(userAfter / clockTick) &&
256 processTimes->tms_cstime ==
static_cast<clock_t
>(kernelAfter / clockTick);
258 *status = 0x5A5A5A5A;
260 MemoryCopy(untouched, linuxUsage,
sizeof(untouched));
262 passed &= posix_waitpid(childPid, status, WNOHANG, linuxUsage) == -1 &&
263 current->
getErrno() == Error::NoChildren && *status == 0x5A5A5A5A &&
264 !MemoryCompare(linuxUsage, untouched,
sizeof(untouched));
266 passed &= ZombieQueue::instance().
drain();
271 constexpr Time::Timestamp clockTick = Time::Multiplier::Second / 100;
272 SeededExitContext context(13 * clockTick, 7 * clockTick,
true);
275 Thread* childThread =
new Thread(child, seededExit, &context,
nullptr,
false,
true,
true);
276 childThread->setName(
"hosted transition resource child");
278 const int childPid =
static_cast<int>(child->
getId());
279 if (!childThread->
start()) {
285 bool passed = waitForValue(context.ready);
286 const Time::Timestamp userBefore = parent->getReapedChildrenUserTime();
287 const Time::Timestamp kernelBefore = parent->getReapedChildrenKernelTime();
289 ByteSet(linuxUsage, 0xA5,
sizeof(*linuxUsage));
290 passed &= posix_waitpid(childPid, status, WUNTRACED, linuxUsage) == childPid &&
291 WIFSTOPPED(*status) && WSTOPSIG(*status) == SIGSTOP &&
292 parent->getReapedChildrenUserTime() == userBefore &&
293 parent->getReapedChildrenKernelTime() == kernelBefore;
294 for (
size_t i = offsetof(
LinuxRusage64, maximumResidentSetSize); i <
sizeof(*linuxUsage); ++i) {
295 passed &= !
reinterpret_cast<uint8_t*
>(linuxUsage)[i];
299 ByteSet(linuxUsage, 0xA5,
sizeof(*linuxUsage));
300 passed &= posix_waitpid(childPid, status, WCONTINUED, linuxUsage) == childPid &&
301 WIFCONTINUED(*status) && parent->getReapedChildrenUserTime() == userBefore &&
302 parent->getReapedChildrenKernelTime() == kernelBefore;
303 for (
size_t i = offsetof(
LinuxRusage64, maximumResidentSetSize); i <
sizeof(*linuxUsage); ++i) {
304 passed &= !
reinterpret_cast<uint8_t*
>(linuxUsage)[i];
307 context.release += 1;
308 passed &= posix_waitpid(childPid, status, 0,
nullptr) == childPid && WIFEXITED(*status) &&
309 !WEXITSTATUS(*status) &&
310 parent->getReapedChildrenUserTime() >= userBefore + context.user &&
311 parent->getReapedChildrenKernelTime() >= kernelBefore + context.kernel;
312 passed &= ZombieQueue::instance().
drain();
317 constexpr Time::Timestamp clockTick = Time::Multiplier::Second / 100;
318 SeededExitContext exitContext(19 * clockTick, 11 * clockTick,
true);
321 Thread* childThread =
new Thread(child, seededExit, &exitContext,
nullptr,
false,
true,
true);
322 childThread->setName(
"hosted concurrent resource child");
324 const int childPid =
static_cast<int>(child->
getId());
328 const bool childStarted = leased && childThread->
start();
336 ConcurrentWaitContext firstContext(childPid);
337 ConcurrentWaitContext secondContext(childPid);
338 Thread* first =
new Thread(parent, concurrentWait, &firstContext,
nullptr,
false,
true,
true);
339 Thread* second =
new Thread(parent, concurrentWait, &secondContext,
nullptr,
false,
true,
true);
340 first->setName(
"hosted first child resource waiter");
341 second->setName(
"hosted second child resource waiter");
342 const bool firstStarted = first->
start();
343 const bool secondStarted = second->
start();
347 if (!secondStarted) {
351 bool passed = firstStarted && secondStarted && waitForValue(firstContext.ready) &&
352 waitForValue(secondContext.ready) && waitForValue(exitContext.ready);
353 const Time::Timestamp userBefore = parent->getReapedChildrenUserTime();
354 const Time::Timestamp kernelBefore = parent->getReapedChildrenKernelTime();
355 firstContext.go += 1;
356 secondContext.go += 1;
357 exitContext.release += 1;
359 const bool reapable = waitForTermination(child);
360 const Time::Timestamp childUser = child->
getUserTime();
361 const Time::Timestamp childKernel = child->getKernelTime();
364 const bool firstWon = firstContext.result == childPid;
365 const bool secondWon = secondContext.result == childPid;
368 if (!firstWon && !secondWon && child->getState() == Process::Terminated) {
370 if (child->getState() == Process::Terminated) {
376 Time::Timestamp ignoredUser = 0;
377 Time::Timestamp ignoredKernel = 0;
380 rescueReaper.publish();
383 passed &= reapable && firstJoined && secondJoined && firstContext.returned == 1 &&
384 secondContext.returned == 1 && firstWon != secondWon &&
385 (firstWon ? firstContext.error == PreservedErrno
386 : firstContext.result == -1 && firstContext.error == Error::NoChildren) &&
387 (secondWon ? secondContext.error == PreservedErrno
388 : secondContext.result == -1 && secondContext.error == Error::NoChildren) &&
389 parent->getReapedChildrenUserTime() == userBefore + childUser &&
390 parent->getReapedChildrenKernelTime() == kernelBefore + childKernel &&
391 child->getState() ==
Process::Reaped;
393 Process* childIdentity = child;
395 passed &= ZombieQueue::instance().
drain();
400int childResourceWorker(
void* parameter) {
401 ChildResourceContext* context =
reinterpret_cast<ChildResourceContext*
>(parameter);
403 uintptr_t address = 0;
404 if (!context->process->allocateUserRange(Process::UserRegion::Normal, pageSize, address)) {
405 context->returned += 1;
409 uintptr_t mappedAddress = address;
411 mappedAddress, pageSize, MemoryMappedObject::Read | MemoryMappedObject::Write);
412 if (!mapping || mappedAddress != address) {
416 context->process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
417 context->returned += 1;
421 int* status =
reinterpret_cast<int*
>(address + 64);
422 struct tms* processTimes =
reinterpret_cast<struct tms*
>(address + 128);
423 struct rusage* nativeUsage =
reinterpret_cast<struct rusage*
>(address + 256);
425 uint8_t* linuxCanary =
reinterpret_cast<uint8_t*
>(linuxUsage + 1);
426 const bool passed = transitiveChildAccounting(context->process, status, processTimes, nativeUsage,
427 linuxUsage, linuxCanary) &&
428 transitionAccounting(context->process, status, linuxUsage) &&
429 concurrentReaperAccounting(context->process);
432 context->process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
433 context->passed = passed;
434 context->returned += 1;
435 return passed ? 0 : 1;
439bool runHostedChildResourceRegressions(
Process* kernelProcess) {
442 ChildResourceContext context(process);
443 Thread*
worker =
new Thread(process, childResourceWorker, &context,
nullptr,
false,
true,
true);
444 worker->setName(
"hosted child resource semantics");
447 const bool started =
worker->start();
448 const bool joined = started &&
worker->joinForCompletion();
452 const bool passed = started && joined && context.returned == 1 && context.passed;
457 "HOSTED-SYSCALL-TEST: FAIL child-resource-accounting: "
458 "transitive, transition, concurrent reaper, or wait4 ABI semantics regressed");
462 NOTICE(
"HOSTED-SYSCALL-TEST: PASS child-resource-accounting");
Memory-mapped file interface.
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
size_t remove(uintptr_t base, size_t length)
static MemoryMapManager & instance()
static constexpr size_t getPageSize() PURE
void accountReapedChild(const Process *child, Time::Timestamp &user, Time::Timestamp &kernel)
WaitQueue::Guard acquireChildStateWait()
void suspend(int stopSignal=0)
bool waitUntilTerminationReapable()
Time::Timestamp getUserTime() const
ReaperClaim tryClaimReaper()
static ProcessorInformation & information()
static Scheduler & instance()
MUST_USE_RESULT bool acquireProcess(ProcessLease &lease, size_t n)
void waitUntilProcessRemoved(Process *expected)
void setErrno(size_t err)
static void threadExited() NORETURN
void deferProcessExit(int code)