diff options
author | Brandon Potter <brandon.potter@amd.com> | 2015-07-20 09:15:21 -0500 |
---|---|---|
committer | Brandon Potter <brandon.potter@amd.com> | 2015-07-20 09:15:21 -0500 |
commit | a5802c823f4f6ec2bd97c953494551e31faa2cf8 (patch) | |
tree | b150d317b84b2168c09381d304919e86efcc9ab6 /src/sim | |
parent | a7a0fd2c58cbd0e8e7e83ad8e7f7729a32527c02 (diff) | |
download | gem5-a5802c823f4f6ec2bd97c953494551e31faa2cf8.tar.xz |
syscall_emul: [patch 13/22] add system call retry capability
This changeset adds functionality that allows system calls to retry without
affecting thread context state such as the program counter or register values
for the associated thread context (when system calls return with a retry
fault).
This functionality is needed to solve problems with blocking system calls
in multi-process or multi-threaded simulations where information is passed
between processes/threads. Blocking system calls can cause deadlock because
the simulator itself is single threaded. There is only a single thread
servicing the event queue which can cause deadlock if the thread hits a
blocking system call instruction.
To illustrate the problem, consider two processes using the producer/consumer
sharing model. The processes can use file descriptors and the read and write
calls to pass information to one another. If the consumer calls the blocking
read system call before the producer has produced anything, the call will
block the event queue (while executing the system call instruction) and
deadlock the simulation.
The solution implemented in this changeset is to recognize that the system
calls will block and then generate a special retry fault. The fault will
be sent back up through the function call chain until it is exposed to the
cpu model's pipeline where the fault becomes visible. The fault will trigger
the cpu model to replay the instruction at a future tick where the call has
a chance to succeed without actually going into a blocking state.
In subsequent patches, we recognize that a syscall will block by calling a
non-blocking poll (from inside the system call implementation) and checking
for events. When events show up during the poll, it signifies that the call
would not have blocked and the syscall is allowed to proceed (calling an
underlying host system call if necessary). If no events are returned from the
poll, we generate the fault and try the instruction for the thread context
at a distant tick. Note that retrying every tick is not efficient.
As an aside, the simulator has some multi-threading support for the event
queue, but it is not used by default and needs work. Even if the event queue
was completely multi-threaded, meaning that there is a hardware thread on
the host servicing a single simulator thread contexts with a 1:1 mapping
between them, it's still possible to run into deadlock due to the event queue
barriers on quantum boundaries. The solution of replaying at a later tick
is the simplest solution and solves the problem generally.
Diffstat (limited to 'src/sim')
-rw-r--r-- | src/sim/faults.cc | 5 | ||||
-rw-r--r-- | src/sim/faults.hh | 16 | ||||
-rw-r--r-- | src/sim/process.cc | 4 | ||||
-rw-r--r-- | src/sim/process.hh | 2 | ||||
-rw-r--r-- | src/sim/syscall_desc.cc | 12 | ||||
-rw-r--r-- | src/sim/syscall_desc.hh | 5 |
6 files changed, 37 insertions, 7 deletions
diff --git a/src/sim/faults.cc b/src/sim/faults.cc index 93e766526..b0fa6fedb 100644 --- a/src/sim/faults.cc +++ b/src/sim/faults.cc @@ -59,6 +59,11 @@ void ReExec::invoke(ThreadContext *tc, const StaticInstPtr &inst) tc->pcState(tc->pcState()); } +void SyscallRetryFault::invoke(ThreadContext *tc, const StaticInstPtr &inst) +{ + tc->pcState(tc->pcState()); +} + void GenericPageTableFault::invoke(ThreadContext *tc, const StaticInstPtr &inst) { bool handled = false; diff --git a/src/sim/faults.hh b/src/sim/faults.hh index da0ea22fa..be7aab582 100644 --- a/src/sim/faults.hh +++ b/src/sim/faults.hh @@ -72,6 +72,22 @@ class ReExec : public FaultBase StaticInst::nullStaticInstPtr); }; +/* + * This class is needed to allow system call retries to occur for blocking + * system calls in SE mode. A retry fault will be generated by the system call + * emulation code if blocking conditions arise; the fault is passed up the + * function call chain into the CPU model where it is handled by retrying the + * syscall instruction on a later tick. + */ +class SyscallRetryFault : public FaultBase +{ + public: + virtual FaultName name() const { return "System call retry fault"; } + SyscallRetryFault() {} + void invoke(ThreadContext *tc, const StaticInstPtr &inst = + StaticInst::nullStaticInstPtr); +}; + class GenericPageTableFault : public FaultBase { private: diff --git a/src/sim/process.cc b/src/sim/process.cc index 4c44f4086..5acfa88b3 100644 --- a/src/sim/process.cc +++ b/src/sim/process.cc @@ -269,7 +269,7 @@ Process::map(Addr vaddr, Addr paddr, int size, bool cacheable) } void -Process::syscall(int64_t callnum, ThreadContext *tc) +Process::syscall(int64_t callnum, ThreadContext *tc, Fault *fault) { num_syscalls++; @@ -277,7 +277,7 @@ Process::syscall(int64_t callnum, ThreadContext *tc) if (desc == NULL) fatal("Syscall %d out of range", callnum); - desc->doSyscall(callnum, this, tc); + desc->doSyscall(callnum, this, tc, fault); } IntReg diff --git a/src/sim/process.hh b/src/sim/process.hh index 2191c3cd0..101e4a31c 100644 --- a/src/sim/process.hh +++ b/src/sim/process.hh @@ -80,7 +80,7 @@ class Process : public SimObject void initState() override; DrainState drain() override; - void syscall(int64_t callnum, ThreadContext *tc); + void syscall(int64_t callnum, ThreadContext *tc, Fault *fault); virtual TheISA::IntReg getSyscallArg(ThreadContext *tc, int &i) = 0; virtual TheISA::IntReg getSyscallArg(ThreadContext *tc, int &i, int width); virtual void setSyscallArg(ThreadContext *tc, int i, diff --git a/src/sim/syscall_desc.cc b/src/sim/syscall_desc.cc index bb0e80f5f..13b519081 100644 --- a/src/sim/syscall_desc.cc +++ b/src/sim/syscall_desc.cc @@ -33,16 +33,21 @@ #include "sim/syscall_desc.hh" +#include <memory> + #include "base/trace.hh" +#include "base/types.hh" #include "config/the_isa.hh" #include "cpu/base.hh" #include "cpu/thread_context.hh" +#include "sim/faults.hh" #include "sim/process.hh" #include "sim/syscall_debug_macros.hh" #include "sim/syscall_return.hh" void -SyscallDesc::doSyscall(int callnum, Process *process, ThreadContext *tc) +SyscallDesc::doSyscall(int callnum, Process *process, ThreadContext *tc, + Fault *fault) { TheISA::IntReg arg[6] M5_VAR_USED; @@ -71,9 +76,10 @@ SyscallDesc::doSyscall(int callnum, Process *process, ThreadContext *tc) * blocking behavior, warn that the system call will retry; * alternatively, print the return value. */ - if (retval.needsRetry()) + if (retval.needsRetry()) { + *fault = std::make_shared<SyscallRetryFault>(); DPRINTF_SYSCALL(Base, "%s needs retry\n", _name); - else + } else DPRINTF_SYSCALL(Base, "%s returns %d\n", _name, retval.encodedValue()); if (!(_flags & SyscallDesc::SuppressReturnValue) && !retval.needsRetry()) diff --git a/src/sim/syscall_desc.hh b/src/sim/syscall_desc.hh index 37aaad14d..b4d24e672 100644 --- a/src/sim/syscall_desc.hh +++ b/src/sim/syscall_desc.hh @@ -48,6 +48,8 @@ #include <string> +#include "base/types.hh" + class Process; class SyscallReturn; class ThreadContext; @@ -91,7 +93,8 @@ class SyscallDesc { * @param proc Handle for the owning Process to pass information * @param tc Handle for owning ThreadContext to pass information */ - void doSyscall(int callnum, Process *proc, ThreadContext *tc); + void doSyscall(int callnum, Process *proc, ThreadContext *tc, + Fault *fault); /** * Return false if WarnOnce is set and a warning has already been issued. |