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/arch | |
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/arch')
-rw-r--r-- | src/arch/alpha/isa/decoder.isa | 2 | ||||
-rw-r--r-- | src/arch/arm/faults.cc | 3 | ||||
-rw-r--r-- | src/arch/mips/isa/decoder.isa | 2 | ||||
-rw-r--r-- | src/arch/power/isa/decoder.isa | 2 | ||||
-rw-r--r-- | src/arch/riscv/faults.cc | 3 | ||||
-rw-r--r-- | src/arch/sparc/faults.cc | 3 | ||||
-rw-r--r-- | src/arch/sparc/linux/process.cc | 14 | ||||
-rw-r--r-- | src/arch/sparc/linux/process.hh | 4 | ||||
-rw-r--r-- | src/arch/sparc/process.cc | 2 | ||||
-rw-r--r-- | src/arch/sparc/process.hh | 2 | ||||
-rw-r--r-- | src/arch/x86/isa/decoder/one_byte_opcodes.isa | 5 | ||||
-rw-r--r-- | src/arch/x86/isa/decoder/two_byte_opcodes.isa | 10 | ||||
-rw-r--r-- | src/arch/x86/process.cc | 4 | ||||
-rw-r--r-- | src/arch/x86/process.hh | 2 | ||||
-rw-r--r-- | src/arch/x86/pseudo_inst.cc | 4 |
15 files changed, 36 insertions, 26 deletions
diff --git a/src/arch/alpha/isa/decoder.isa b/src/arch/alpha/isa/decoder.isa index d6de363a7..8789fa905 100644 --- a/src/arch/alpha/isa/decoder.isa +++ b/src/arch/alpha/isa/decoder.isa @@ -840,7 +840,7 @@ decode OPCODE default Unknown::unknown() { exitSimLoop("halt instruction encountered"); }}, IsNonSpeculative); 0x83: callsys({{ - xc->syscall(R0); + xc->syscall(R0, &fault); }}, IsSerializeAfter, IsNonSpeculative, IsSyscall); // Read uniq reg into ABI return value register (r0) 0x9e: rduniq({{ R0 = Runiq; }}, IsIprAccess); diff --git a/src/arch/arm/faults.cc b/src/arch/arm/faults.cc index b0d7700d1..740d71d02 100644 --- a/src/arch/arm/faults.cc +++ b/src/arch/arm/faults.cc @@ -784,7 +784,8 @@ SupervisorCall::invoke(ThreadContext *tc, const StaticInstPtr &inst) callNum = tc->readIntReg(INTREG_X8); else callNum = tc->readIntReg(INTREG_R7); - tc->syscall(callNum); + Fault fault; + tc->syscall(callNum, &fault); // Advance the PC since that won't happen automatically. PCState pc = tc->pcState(); diff --git a/src/arch/mips/isa/decoder.isa b/src/arch/mips/isa/decoder.isa index 1f930f3f5..5c3c6f6b1 100644 --- a/src/arch/mips/isa/decoder.isa +++ b/src/arch/mips/isa/decoder.isa @@ -164,7 +164,7 @@ decode OPCODE_HI default Unknown::unknown() { 0x2: movz({{ Rd = (Rt == 0) ? Rs : Rd; }}); 0x3: movn({{ Rd = (Rt != 0) ? Rs : Rd; }}); 0x4: decode FullSystemInt { - 0: syscall_se({{ xc->syscall(R2); }}, + 0: syscall_se({{ xc->syscall(R2, &fault); }}, IsSerializeAfter, IsNonSpeculative); default: syscall({{ fault = std::make_shared<SystemCallFault>(); }}); } diff --git a/src/arch/power/isa/decoder.isa b/src/arch/power/isa/decoder.isa index 30002fe33..71ef95b06 100644 --- a/src/arch/power/isa/decoder.isa +++ b/src/arch/power/isa/decoder.isa @@ -512,7 +512,7 @@ decode OPCODE default Unknown::unknown() { 55: stfdu({{ Mem_df = Fs; }}); } - 17: IntOp::sc({{ xc->syscall(R0); }}, + 17: IntOp::sc({{ xc->syscall(R0, &fault); }}, [ IsSyscall, IsNonSpeculative, IsSerializeAfter ]); format FloatArithOp { diff --git a/src/arch/riscv/faults.cc b/src/arch/riscv/faults.cc index f5ba5c798..58baa4e32 100644 --- a/src/arch/riscv/faults.cc +++ b/src/arch/riscv/faults.cc @@ -87,5 +87,6 @@ BreakpointFault::invoke_se(ThreadContext *tc, const StaticInstPtr &inst) void SyscallFault::invoke_se(ThreadContext *tc, const StaticInstPtr &inst) { - tc->syscall(tc->readIntReg(SyscallNumReg)); + Fault *fault = NoFault; + tc->syscall(tc->readIntReg(SyscallNumReg), fault); } diff --git a/src/arch/sparc/faults.cc b/src/arch/sparc/faults.cc index 6c3b35c9a..c09bd0da2 100644 --- a/src/arch/sparc/faults.cc +++ b/src/arch/sparc/faults.cc @@ -811,7 +811,8 @@ TrapInstruction::invoke(ThreadContext *tc, const StaticInstPtr &inst) SparcProcess *sp = dynamic_cast<SparcProcess *>(p); assert(sp); - sp->handleTrap(_n, tc); + Fault fault; + sp->handleTrap(_n, tc, &fault); // We need to explicitly advance the pc, since that's not done for us // on a faulting instruction diff --git a/src/arch/sparc/linux/process.cc b/src/arch/sparc/linux/process.cc index 0f72c69c7..d12f13048 100644 --- a/src/arch/sparc/linux/process.cc +++ b/src/arch/sparc/linux/process.cc @@ -65,14 +65,15 @@ Sparc32LinuxProcess::Sparc32LinuxProcess(ProcessParams * params, : Sparc32Process(params, objFile) {} -void Sparc32LinuxProcess::handleTrap(int trapNum, ThreadContext *tc) +void Sparc32LinuxProcess::handleTrap(int trapNum, ThreadContext *tc, + Fault *fault) { switch (trapNum) { case 0x10: //Linux 32 bit syscall trap - tc->syscall(tc->readIntReg(1)); + tc->syscall(tc->readIntReg(1), fault); break; default: - SparcProcess::handleTrap(trapNum, tc); + SparcProcess::handleTrap(trapNum, tc, fault); } } @@ -81,14 +82,15 @@ Sparc64LinuxProcess::Sparc64LinuxProcess(ProcessParams * params, : Sparc64Process(params, objFile) {} -void Sparc64LinuxProcess::handleTrap(int trapNum, ThreadContext *tc) +void Sparc64LinuxProcess::handleTrap(int trapNum, ThreadContext *tc, + Fault *fault) { switch (trapNum) { // case 0x10: // Linux 32 bit syscall trap case 0x6d: // Linux 64 bit syscall trap - tc->syscall(tc->readIntReg(1)); + tc->syscall(tc->readIntReg(1), fault); break; default: - SparcProcess::handleTrap(trapNum, tc); + SparcProcess::handleTrap(trapNum, tc, fault); } } diff --git a/src/arch/sparc/linux/process.hh b/src/arch/sparc/linux/process.hh index 1c38576fd..778af1fda 100644 --- a/src/arch/sparc/linux/process.hh +++ b/src/arch/sparc/linux/process.hh @@ -70,7 +70,7 @@ class Sparc32LinuxProcess : public SparcLinuxProcess, public Sparc32Process return SparcLinuxProcess::getDesc32(callnum); } - void handleTrap(int trapNum, ThreadContext *tc); + void handleTrap(int trapNum, ThreadContext *tc, Fault *fault); }; /// A process with emulated 32 bit SPARC/Linux syscalls. @@ -86,7 +86,7 @@ class Sparc64LinuxProcess : public SparcLinuxProcess, public Sparc64Process return SparcLinuxProcess::getDesc(callnum); } - void handleTrap(int trapNum, ThreadContext *tc); + void handleTrap(int trapNum, ThreadContext *tc, Fault *fault); }; SyscallReturn getresuidFunc(SyscallDesc *desc, int num, diff --git a/src/arch/sparc/process.cc b/src/arch/sparc/process.cc index 5c4f43b67..a8359a9ce 100644 --- a/src/arch/sparc/process.cc +++ b/src/arch/sparc/process.cc @@ -71,7 +71,7 @@ SparcProcess::SparcProcess(ProcessParams * params, ObjectFile *objFile, } void -SparcProcess::handleTrap(int trapNum, ThreadContext *tc) +SparcProcess::handleTrap(int trapNum, ThreadContext *tc, Fault *fault) { PCState pc = tc->pcState(); switch (trapNum) { diff --git a/src/arch/sparc/process.hh b/src/arch/sparc/process.hh index 69ea6b41f..efdc0f443 100644 --- a/src/arch/sparc/process.hh +++ b/src/arch/sparc/process.hh @@ -61,7 +61,7 @@ class SparcProcess : public Process public: // Handles traps which request services from the operating system - virtual void handleTrap(int trapNum, ThreadContext *tc); + virtual void handleTrap(int trapNum, ThreadContext *tc, Fault *fault); Addr readFillStart() { return fillStart; } Addr readSpillStart() { return spillStart; } diff --git a/src/arch/x86/isa/decoder/one_byte_opcodes.isa b/src/arch/x86/isa/decoder/one_byte_opcodes.isa index 859d1f1b4..95bc7a5c1 100644 --- a/src/arch/x86/isa/decoder/one_byte_opcodes.isa +++ b/src/arch/x86/isa/decoder/one_byte_opcodes.isa @@ -400,8 +400,9 @@ // will sign extend it, and there's no easy way to // specify only checking the first byte. 0xffffffffffffff80: - SyscallInst::int80('xc->syscall(Rax)', - IsSyscall, IsNonSpeculative, IsSerializeAfter); + SyscallInst::int80('xc->syscall(Rax, &fault)', + IsSyscall, IsNonSpeculative, + IsSerializeAfter); } default: Inst::INT(Ib); diff --git a/src/arch/x86/isa/decoder/two_byte_opcodes.isa b/src/arch/x86/isa/decoder/two_byte_opcodes.isa index 772177d42..97c3dd688 100644 --- a/src/arch/x86/isa/decoder/two_byte_opcodes.isa +++ b/src/arch/x86/isa/decoder/two_byte_opcodes.isa @@ -235,8 +235,9 @@ } } 0x05: decode FullSystemInt { - 0: SyscallInst::syscall('xc->syscall(Rax)', - IsSyscall, IsNonSpeculative, IsSerializeAfter); + 0: SyscallInst::syscall('xc->syscall(Rax, &fault)', + IsSyscall, IsNonSpeculative, + IsSerializeAfter); default: decode MODE_MODE { 0x0: decode MODE_SUBMODE { 0x0: Inst::SYSCALL_64(); @@ -422,8 +423,9 @@ 0x2: Inst::RDMSR(); 0x3: rdpmc(); 0x4: decode FullSystemInt { - 0: SyscallInst::sysenter('xc->syscall(Rax)', - IsSyscall, IsNonSpeculative, IsSerializeAfter); + 0: SyscallInst::sysenter('xc->syscall(Rax, &fault)', + IsSyscall, IsNonSpeculative, + IsSerializeAfter); default: sysenter(); } 0x5: sysexit(); diff --git a/src/arch/x86/process.cc b/src/arch/x86/process.cc index dfbd41e4e..c1e4f710a 100644 --- a/src/arch/x86/process.cc +++ b/src/arch/x86/process.cc @@ -134,7 +134,7 @@ X86_64Process::X86_64Process(ProcessParams *params, ObjectFile *objFile, } void -I386Process::syscall(int64_t callnum, ThreadContext *tc) +I386Process::syscall(int64_t callnum, ThreadContext *tc, Fault *fault) { TheISA::PCState pc = tc->pcState(); Addr eip = pc.pc(); @@ -143,7 +143,7 @@ I386Process::syscall(int64_t callnum, ThreadContext *tc) pc.npc(vsyscallPage.base + vsyscallPage.vsysexitOffset); tc->pcState(pc); } - X86Process::syscall(callnum, tc); + X86Process::syscall(callnum, tc, fault); } diff --git a/src/arch/x86/process.hh b/src/arch/x86/process.hh index fa95b4ff4..ef0329329 100644 --- a/src/arch/x86/process.hh +++ b/src/arch/x86/process.hh @@ -130,7 +130,7 @@ namespace X86ISA void argsInit(int intSize, int pageSize); void initState(); - void syscall(int64_t callnum, ThreadContext *tc); + void syscall(int64_t callnum, ThreadContext *tc, Fault *fault); X86ISA::IntReg getSyscallArg(ThreadContext *tc, int &i); X86ISA::IntReg getSyscallArg(ThreadContext *tc, int &i, int width); void setSyscallArg(ThreadContext *tc, int i, X86ISA::IntReg val); diff --git a/src/arch/x86/pseudo_inst.cc b/src/arch/x86/pseudo_inst.cc index acf310631..c0ec11059 100644 --- a/src/arch/x86/pseudo_inst.cc +++ b/src/arch/x86/pseudo_inst.cc @@ -49,7 +49,9 @@ m5Syscall(ThreadContext *tc) { DPRINTF(PseudoInst, "PseudoInst::m5Syscall()\n"); - tc->syscall(tc->readIntReg(INTREG_RAX)); + Fault fault; + tc->syscall(tc->readIntReg(INTREG_RAX), &fault); + MiscReg rflags = tc->readMiscReg(MISCREG_RFLAGS); rflags &= ~(1 << 16); tc->setMiscReg(MISCREG_RFLAGS, rflags); |