syscall_emul: [patch 13/22] add system call retry capability
authorBrandon Potter <brandon.potter@amd.com>
Mon, 20 Jul 2015 14:15:21 +0000 (09:15 -0500)
committerBrandon Potter <brandon.potter@amd.com>
Mon, 20 Jul 2015 14:15:21 +0000 (09:15 -0500)
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.

41 files changed:
src/arch/alpha/isa/decoder.isa
src/arch/arm/faults.cc
src/arch/mips/isa/decoder.isa
src/arch/power/isa/decoder.isa
src/arch/riscv/faults.cc
src/arch/sparc/faults.cc
src/arch/sparc/linux/process.cc
src/arch/sparc/linux/process.hh
src/arch/sparc/process.cc
src/arch/sparc/process.hh
src/arch/x86/isa/decoder/one_byte_opcodes.isa
src/arch/x86/isa/decoder/two_byte_opcodes.isa
src/arch/x86/process.cc
src/arch/x86/process.hh
src/arch/x86/pseudo_inst.cc
src/cpu/BaseCPU.py
src/cpu/base.cc
src/cpu/base.hh
src/cpu/checker/cpu.hh
src/cpu/checker/thread_context.hh
src/cpu/exec_context.hh
src/cpu/minor/exec_context.hh
src/cpu/o3/commit.hh
src/cpu/o3/commit_impl.hh
src/cpu/o3/cpu.cc
src/cpu/o3/cpu.hh
src/cpu/o3/dyn_inst.hh
src/cpu/o3/dyn_inst_impl.hh
src/cpu/o3/thread_context.hh
src/cpu/o3/thread_state.hh
src/cpu/simple/atomic.cc
src/cpu/simple/exec_context.hh
src/cpu/simple/timing.cc
src/cpu/simple_thread.hh
src/cpu/thread_context.hh
src/sim/faults.cc
src/sim/faults.hh
src/sim/process.cc
src/sim/process.hh
src/sim/syscall_desc.cc
src/sim/syscall_desc.hh

index d6de363a76708fae39f0e90368df798d9b8876f7..8789fa905506b11bd4abb0e85a5566d7332257bf 100644 (file)
@@ -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);
index b0d7700d16feb91470815c87098266b76a89181a..740d71d02a83f6044b1f6ffff573e80371f79fba 100644 (file)
@@ -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();
index 1f930f3f524cce01fcbc6f66e8b367db63b392ca..5c3c6f6b10062238c5e4729aac1ac75a2b5926b7 100644 (file)
@@ -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>(); }});
                     }
index 30002fe336eb4c02e5bdc91eea5124bbdcfe877d..71ef95b06709ede4a1bea907883fac3e10741ba9 100644 (file)
@@ -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 {
index f5ba5c7983b494f439e09d4320f8e8f8eea93f5a..58baa4e32de338e408ee51d163e33223118c25aa 100644 (file)
@@ -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);
 }
index 6c3b35c9af661d99323079f2f911bc8beb93184c..c09bd0da2bbbeb4c344af59340898df46f21c535 100644 (file)
@@ -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
index 0f72c69c7e002a4bcafe4bf0f9b4d3d2176ee0b0..d12f130483636ccdb8df0b3145b635fc9df56dff 100644 (file)
@@ -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);
     }
 }
index 1c38576fd2cb959d8ef3e4befe4d35af3e883eb1..778af1fda519af0fcdba7032286af8122a7832e8 100644 (file)
@@ -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,
index 5c4f43b67a3383b111ce2362b95153e17cdb03a3..a8359a9ce0cec2fb2a2c9ee3c5d293e2c54ce8c7 100644 (file)
@@ -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) {
index 69ea6b41fc8bfb00cb13f2c7a06d728fa951b585..efdc0f443e3fdc03db3d8f911fba5a24e4f5295c 100644 (file)
@@ -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; }
index 859d1f1b437cc593df2e0451f94b1e04e74dd1f8..95bc7a5c1a0350098ff5b5f93cc84969261eb197 100644 (file)
                         // 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);
index 772177d427acd5c1692c2f5fb94c787363dd09f3..97c3dd6882633bf91991aecb58f24719b59de7a7 100644 (file)
                 }
             }
             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();
             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();
index dfbd41e4eefaaf722e1c5b3e22e43fe38e67ec82..c1e4f710ada0f3a1369bdec0791b8e049fa82b3f 100644 (file)
@@ -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);
 }
 
 
index fa95b4ff4b4225e75ce90a9a23f7bc2b7da919ea..ef03293297a3cd0e09b83a9e3ba4203637dd05cc 100644 (file)
@@ -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);
index acf310631f0298821776fc887fc9a8911ffa186b..c0ec1105926904b972c49796d4ee7d96eb701302 100644 (file)
@@ -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);
index c85e5afda7ed3d806ca1c45abbdea2eca1ba1975..7b8a615ea13ae9b011b23d836f895894f183925b 100644 (file)
@@ -142,6 +142,8 @@ class BaseCPU(MemObject):
 
     checker = Param.BaseCPU(NULL, "checker CPU")
 
+    syscallRetryLatency = Param.Cycles(10000, "Cycles to wait until retry")
+
     do_checkpoint_insts = Param.Bool(True,
         "enable checkpoint pseudo instructions")
     do_statistics_insts = Param.Bool(True,
index 10b8ce297d90d15d8bb4b2f3cf5445e15601a2be..08f95ea493f3f469805e7521ba7ec7f4b2ea77d0 100644 (file)
@@ -135,7 +135,8 @@ BaseCPU::BaseCPU(Params *p, bool is_checker)
       numThreads(p->numThreads), system(p->system),
       functionTraceStream(nullptr), currentFunctionStart(0),
       currentFunctionEnd(0), functionEntryTick(0),
-      addressMonitor(p->numThreads)
+      addressMonitor(p->numThreads),
+      syscallRetryLatency(p->syscallRetryLatency)
 {
     // if Python did not provide a valid ID, do it here
     if (_cpuId == -1 ) {
index 6622339e087ea272c8376e70f7aded499439f492..14dfc260bf434b2436c9a8002a51c5ad9b87e9d9 100644 (file)
@@ -588,6 +588,8 @@ class BaseCPU : public MemObject
         assert(tid < numThreads);
         return &addressMonitor[tid];
     }
+
+    Cycles syscallRetryLatency;
 };
 
 #endif // THE_ISA == NULL_ISA
index 21ff9c7f74b9039b9b20fc258417e0fa7de13f8b..e47c88484194309a907ffd9bee26a03828ede677 100644 (file)
@@ -393,7 +393,7 @@ class CheckerCPU : public BaseCPU, public ExecContext
     void wakeup(ThreadID tid) override { }
     // Assume that the normal CPU's call to syscall was successful.
     // The checker's state would have already been updated by the syscall.
-    void syscall(int64_t callnum) override { }
+    void syscall(int64_t callnum, Fault *fault) override { }
 
     void handleError()
     {
index 5fcb82f6d61c4cc198dcf9fba47bb6acc66557d6..0313d079b9f85debb6b719b13f7dd9b26d530f5b 100644 (file)
@@ -146,8 +146,8 @@ class CheckerThreadContext : public ThreadContext
     SETranslatingPortProxy &getMemProxy() { return actualTC->getMemProxy(); }
 
     /** Executes a syscall in SE mode. */
-    void syscall(int64_t callnum)
-    { return actualTC->syscall(callnum); }
+    void syscall(int64_t callnum, Fault *fault)
+    { return actualTC->syscall(callnum, fault); }
 
     Status status() const { return actualTC->status(); }
 
index dd718b56a85f8374ed6a689a9f0fbfc095ee64d8..b21f0767a4d0973c203cdc5cfb8ed5272da10848 100644 (file)
@@ -228,7 +228,7 @@ class ExecContext {
     /**
      * Executes a syscall specified by the callnum.
      */
-    virtual void syscall(int64_t callnum) = 0;
+    virtual void syscall(int64_t callnum, Fault *fault) = 0;
 
     /** @} */
 
index 6235721cf5b445b7c0dbb3ef8d333d81719fc321..6b2eae0f1133c76598faf3bc2e48c1540d25507f 100644 (file)
@@ -241,12 +241,12 @@ class ExecContext : public ::ExecContext
     }
 
     void
-    syscall(int64_t callnum) override
+    syscall(int64_t callnum, Fault *fault) override
      {
         if (FullSystem)
             panic("Syscall emulation isn't available in FS mode.\n");
 
-        thread.syscall(callnum);
+        thread.syscall(callnum, fault);
     }
 
     ThreadContext *tcBase() override { return thread.getTC(); }
index 48c169389a1ada86f602fef16fa7067c406673f4..3cce7f69c00e8d6849cf3bf5c307c7c75ef51114 100644 (file)
@@ -235,7 +235,7 @@ class DefaultCommit
     size_t numROBFreeEntries(ThreadID tid);
 
     /** Generates an event to schedule a squash due to a trap. */
-    void generateTrapEvent(ThreadID tid);
+    void generateTrapEvent(ThreadID tid, Fault inst_fault);
 
     /** Records that commit needs to initiate a squash due to an
      * external state update through the TC.
index c6c6ea723fae508a253017b848a6de7adc9418c9..ea77f18fb54613c916130c09c55284f70aec3aba 100644 (file)
@@ -526,13 +526,16 @@ DefaultCommit<Impl>::numROBFreeEntries(ThreadID tid)
 
 template <class Impl>
 void
-DefaultCommit<Impl>::generateTrapEvent(ThreadID tid)
+DefaultCommit<Impl>::generateTrapEvent(ThreadID tid, Fault inst_fault)
 {
     DPRINTF(Commit, "Generating trap event for [tid:%i]\n", tid);
 
     TrapEvent *trap = new TrapEvent(this, tid);
 
-    cpu->schedule(trap, cpu->clockEdge(trapLatency));
+    Cycles latency = dynamic_pointer_cast<SyscallRetryFault>(inst_fault) ?
+                     cpu->syscallRetryLatency : trapLatency;
+
+    cpu->schedule(trap, cpu->clockEdge(latency));
     trapInFlight[tid] = true;
     thread[tid]->trapPending = true;
 }
@@ -767,10 +770,11 @@ DefaultCommit<Impl>::handleInterrupt()
 
         commitStatus[0] = TrapPending;
 
+        interrupt = NoFault;
+
         // Generate trap squash event.
-        generateTrapEvent(0);
+        generateTrapEvent(0, interrupt);
 
-        interrupt = NoFault;
         avoidQuiesceLiveLock = false;
     } else {
         DPRINTF(Commit, "Interrupt pending: instruction is %sin "
@@ -1240,7 +1244,7 @@ DefaultCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num)
         }
 
         // Generate trap squash event.
-        generateTrapEvent(tid);
+        generateTrapEvent(tid, inst_fault);
         return false;
     }
 
index d85895030f2fb5f208fe17f25970b15fd94cfa11..8d38ed1f2bbef26799571d687d612514e362082f 100644 (file)
@@ -972,7 +972,7 @@ FullO3CPU<Impl>::trap(const Fault &fault, ThreadID tid,
 
 template <class Impl>
 void
-FullO3CPU<Impl>::syscall(int64_t callnum, ThreadID tid)
+FullO3CPU<Impl>::syscall(int64_t callnum, ThreadID tid, Fault *fault)
 {
     DPRINTF(O3CPU, "[tid:%i] Executing syscall().\n\n", tid);
 
@@ -983,7 +983,7 @@ FullO3CPU<Impl>::syscall(int64_t callnum, ThreadID tid)
     ++(this->thread[tid]->funcExeInst);
 
     // Execute the actual syscall.
-    this->thread[tid]->syscall(callnum);
+    this->thread[tid]->syscall(callnum, fault);
 
     // Decrease funcExeInst by one as the normal commit will handle
     // incrementing it.
index 2065202f7a7b6df60c2f881a1a5418ad0b8973b6..abe036b09403b550577dc2d61d537229852f4d20 100644 (file)
@@ -344,7 +344,7 @@ class FullO3CPU : public BaseO3CPU
     /** Executes a syscall.
      * @todo: Determine if this needs to be virtual.
      */
-    void syscall(int64_t callnum, ThreadID tid);
+    void syscall(int64_t callnum, ThreadID tid, Fault *fault);
 
     /** Starts draining the CPU's pipeline of all instructions in
      * order to stop all memory accesses. */
index 6740c601dc7d5079ce6ec727c7edc5c3fec06fc1..8ab9979d2f691e1e378a94885387f4dcbc3fe8cb 100644 (file)
@@ -237,7 +237,7 @@ class BaseO3DynInst : public BaseDynInst<Impl>
     bool simPalCheck(int palFunc);
 
     /** Emulates a syscall. */
-    void syscall(int64_t callnum);
+    void syscall(int64_t callnum, Fault *fault);
 
   public:
 
index 06c0e15f32154667a29a075d0421204355b52d1e..00bcb334547c44eb97a93432d1f671d448fa8112 100644 (file)
@@ -242,7 +242,7 @@ BaseO3DynInst<Impl>::simPalCheck(int palFunc)
 
 template <class Impl>
 void
-BaseO3DynInst<Impl>::syscall(int64_t callnum)
+BaseO3DynInst<Impl>::syscall(int64_t callnum, Fault *fault)
 {
     if (FullSystem)
         panic("Syscall emulation isn't available in FS mode.\n");
@@ -251,7 +251,7 @@ BaseO3DynInst<Impl>::syscall(int64_t callnum)
     // changes, update this instruction's nextPC because the syscall
     // must have changed the nextPC.
     TheISA::PCState curPC = this->cpu->pcState(this->threadNumber);
-    this->cpu->syscall(callnum, this->threadNumber);
+    this->cpu->syscall(callnum, this->threadNumber, fault);
     TheISA::PCState newPC = this->cpu->pcState(this->threadNumber);
     if (!(curPC == newPC)) {
         this->pcState(newPC);
index 87b7d919837b0ddc0024a14b040c1ce2c46c3903..0321f57f7dff87eb54027e061720ca958bd9c624 100755 (executable)
@@ -258,8 +258,8 @@ class O3ThreadContext : public ThreadContext
     { thread->storeCondFailures = sc_failures; }
 
     /** Executes a syscall in SE mode. */
-    virtual void syscall(int64_t callnum)
-    { return cpu->syscall(callnum, thread->threadId()); }
+    virtual void syscall(int64_t callnum, Fault *fault)
+    { return cpu->syscall(callnum, thread->threadId(), fault); }
 
     /** Reads the funcExeInst counter. */
     virtual Counter readFuncExeInst() { return thread->funcExeInst; }
index 7765f86ead754d970528d4abaf7e7a5f55fc072a..4b4f51e8faeee0c6f307aecd097e16cce290a9ba 100644 (file)
@@ -140,7 +140,10 @@ struct O3ThreadState : public ThreadState {
     ThreadContext *getTC() { return tc; }
 
     /** Handles the syscall. */
-    void syscall(int64_t callnum) { process->syscall(callnum, tc); }
+    void syscall(int64_t callnum, Fault *fault)
+    {
+        process->syscall(callnum, tc, fault);
+    }
 
     void dumpFuncProfile()
     {
index c09f0c52635cebc03c9535bf029638fbbc5ab52c..6c31f1dddb4523d1e63a1efc81396ad89a96b9a0 100644 (file)
@@ -628,6 +628,7 @@ AtomicSimpleCPU::tick()
 
             preExecute();
 
+            Tick stall_ticks = 0;
             if (curStaticInst) {
                 fault = curStaticInst->execute(&t_info, traceData);
 
@@ -641,6 +642,13 @@ AtomicSimpleCPU::tick()
                     traceData = NULL;
                 }
 
+                if (dynamic_pointer_cast<SyscallRetryFault>(fault)) {
+                    // Retry execution of system calls after a delay.
+                    // Prevents immediate re-execution since conditions which
+                    // caused the retry are unlikely to change every tick.
+                    stall_ticks += clockEdge(syscallRetryLatency) - curTick();
+                }
+
                 postExecute();
             }
 
@@ -649,7 +657,6 @@ AtomicSimpleCPU::tick()
                         curStaticInst->isFirstMicroop()))
                 instCnt++;
 
-            Tick stall_ticks = 0;
             if (simulate_inst_stalls && icache_access)
                 stall_ticks += icache_latency;
 
index 430790c094fa5a11223486434a269ba9286bb158..bfae118adbcb116894601231bc706ea3819d97e9 100644 (file)
@@ -323,12 +323,12 @@ class SimpleExecContext : public ExecContext {
     /**
      * Executes a syscall specified by the callnum.
      */
-    void syscall(int64_t callnum) override
+    void syscall(int64_t callnum, Fault *fault) override
     {
         if (FullSystem)
             panic("Syscall emulation isn't available in FS mode.");
 
-        thread->syscall(callnum);
+        thread->syscall(callnum, fault);
     }
 
     /** Returns a pointer to the ThreadContext. */
index 43c50b948b008ca194dfd0f08ff9ceeab2e0fa3f..1c468dc99ff1db8e6bf123c5159d25005c28ebcc 100644 (file)
@@ -670,9 +670,15 @@ TimingSimpleCPU::advanceInst(const Fault &fault)
         return;
 
     if (fault != NoFault) {
-        advancePC(fault);
         DPRINTF(SimpleCPU, "Fault occured, scheduling fetch event\n");
-        reschedule(fetchEvent, clockEdge(), true);
+
+        advancePC(fault);
+
+        Tick stall = dynamic_pointer_cast<SyscallRetryFault>(fault) ?
+                     clockEdge(syscallRetryLatency) : clockEdge();
+
+        reschedule(fetchEvent, stall, true);
+
         _status = Faulting;
         return;
     }
index 631b8ccfc86cffd4f114ba09142cab96b5ffa8d4..9ef00ab3f242b821bd7ae75fddda9ed3826b1d1f 100644 (file)
@@ -424,9 +424,9 @@ class SimpleThread : public ThreadState
     void setStCondFailures(unsigned sc_failures)
     { storeCondFailures = sc_failures; }
 
-    void syscall(int64_t callnum)
+    void syscall(int64_t callnum, Fault *fault)
     {
-        process->syscall(callnum, tc);
+        process->syscall(callnum, tc, fault);
     }
 
     uint64_t readIntRegFlat(int idx) { return intRegs[idx]; }
index f966c0aa103d7d41e8f2c139ecd996935cc1d584..ecbd1a41ebe96daf3325dbcd7b9426626b52ee30 100644 (file)
@@ -264,7 +264,7 @@ class ThreadContext
     // Same with st cond failures.
     virtual Counter readFuncExeInst() = 0;
 
-    virtual void syscall(int64_t callnum) = 0;
+    virtual void syscall(int64_t callnum, Fault *fault) = 0;
 
     // This function exits the thread context in the CPU and returns
     // 1 if the CPU has no more active threads (meaning it's OK to exit);
@@ -471,8 +471,8 @@ class ProxyThreadContext : public ThreadContext
     void setStCondFailures(unsigned sc_failures)
     { actualTC->setStCondFailures(sc_failures); }
 
-    void syscall(int64_t callnum)
-    { actualTC->syscall(callnum); }
+    void syscall(int64_t callnum, Fault *fault)
+    { actualTC->syscall(callnum, fault); }
 
     Counter readFuncExeInst() { return actualTC->readFuncExeInst(); }
 
index 93e7665268fc01f97bc7d42138413d385324a91d..b0fa6fedb11f4f90ea26dc51637666f303b759e3 100644 (file)
@@ -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;
index da0ea22faabb95ae9c48bec731f6b35d7ba38b98..be7aab582ea5c5bd5137d3740acb175ad79e36c7 100644 (file)
@@ -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:
index 4c44f408625b0481d32f14575b1ac86ef70395ac..5acfa88b3ccf91ecec2512f0b82183fae807303d 100644 (file)
@@ -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
index 2191c3cd022a2a144edccbc71efeb967197935bc..101e4a31c529047de0ee40c68558fa60a5099616 100644 (file)
@@ -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,
index bb0e80f5fdc311de91ac558d5268c919d2a820c3..13b519081fc311cca71005aae0d723447e75e0c2 100644 (file)
 
 #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())
index 37aaad14d05afc9f81439d0e48b6c7e70e22f190..b4d24e67229cf1b11fa0d6e35053a8605d235c4d 100644 (file)
@@ -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.