changeset b0b94a7b7c1f in /z/repo/m5
details: http://repo.m5sim.org/m5?cmd=changeset;node=b0b94a7b7c1f
description:
        ARM: Detect and skip udelay() functions in linux kernel.

        This change speeds up booting, especially in MP cases, by not executing
        udelay() on the core but instead skipping ahead tha amount of time that 
is being
        delayed.

diffstat:

 src/arch/arm/linux/system.cc |  24 ++++++++++++++++++++++++
 src/arch/arm/linux/system.hh |  13 +++++++++++++
 src/cpu/simple/atomic.cc     |   3 +++
 src/cpu/simple/timing.cc     |   4 ++++
 src/kern/linux/events.cc     |  25 +++++++++++++++++++++++++
 src/kern/linux/events.hh     |  27 +++++++++++++++++++++++++++
 6 files changed, 96 insertions(+), 0 deletions(-)

diffs (183 lines):

diff -r e08035e1a1f6 -r b0b94a7b7c1f src/arch/arm/linux/system.cc
--- a/src/arch/arm/linux/system.cc      Thu Mar 17 19:20:20 2011 -0500
+++ b/src/arch/arm/linux/system.cc      Thu Mar 17 19:20:20 2011 -0500
@@ -47,9 +47,11 @@
 #include "base/loader/object_file.hh"
 #include "base/loader/symtab.hh"
 #include "cpu/thread_context.hh"
+#include "kern/linux/events.hh"
 #include "mem/physical.hh"
 
 using namespace ArmISA;
+using namespace Linux;
 
 LinuxArmSystem::LinuxArmSystem(Params *p)
     : ArmSystem(p)
@@ -96,6 +98,24 @@
     if (!kernelPanicEvent)
         panic("could not find kernel symbol \'panic\'");
 #endif
+
+    // With ARM udelay() is #defined to __udelay
+    Addr addr = 0;
+    if (kernelSymtab->findAddress("__udelay", addr)) {
+        uDelaySkipEvent = new UDelayEvent(&pcEventQueue, "__udelay",
+                fixFuncEventAddr(addr), 1000, 0);
+    } else {
+        panic("couldn't find kernel symbol \'udelay\'");
+    }
+
+    // constant arguments to udelay() have some precomputation done ahead of
+    // time. Constant comes from code.
+    if (kernelSymtab->findAddress("__const_udelay", addr)) {
+        constUDelaySkipEvent = new UDelayEvent(&pcEventQueue, "__const_udelay",
+                fixFuncEventAddr(addr), 1000, 107374);
+    } else {
+        panic("couldn't find kernel symbol \'udelay\'");
+    }
 }
 
 void
@@ -115,6 +135,10 @@
 
 LinuxArmSystem::~LinuxArmSystem()
 {
+    if (uDelaySkipEvent)
+        delete uDelaySkipEvent;
+    if (constUDelaySkipEvent)
+        delete constUDelaySkipEvent;
 }
 
 LinuxArmSystem *
diff -r e08035e1a1f6 -r b0b94a7b7c1f src/arch/arm/linux/system.hh
--- a/src/arch/arm/linux/system.hh      Thu Mar 17 19:20:20 2011 -0500
+++ b/src/arch/arm/linux/system.hh      Thu Mar 17 19:20:20 2011 -0500
@@ -74,6 +74,19 @@
     /** Event to halt the simulator if the kernel calls panic()  */
     BreakPCEvent *kernelPanicEvent;
 #endif
+    /**
+     * PC based event to skip udelay(<time>) calls and quiesce the
+     * processor for the appropriate amount of time. This is not functionally
+     * required but does speed up simulation.
+     */
+    Linux::UDelayEvent *uDelaySkipEvent;
+
+    /** Another PC based skip event for const_udelay(). Similar to the udelay
+     * skip, but this function precomputes the first multiply that is done
+     * in the generic case since the parameter is known at compile time.
+     * Thus we need to do some division to get back to us.
+     */
+    Linux::UDelayEvent *constUDelaySkipEvent;
 };
 
 #endif // __ARCH_ARM_LINUX_SYSTEM_HH__
diff -r e08035e1a1f6 -r b0b94a7b7c1f src/cpu/simple/atomic.cc
--- a/src/cpu/simple/atomic.cc  Thu Mar 17 19:20:20 2011 -0500
+++ b/src/cpu/simple/atomic.cc  Thu Mar 17 19:20:20 2011 -0500
@@ -641,6 +641,9 @@
             checkForInterrupts();
 
         checkPcEventQueue();
+        // We must have just got suspended by a PC event
+        if (_status == Idle)
+            return;
 
         Fault fault = NoFault;
 
diff -r e08035e1a1f6 -r b0b94a7b7c1f src/cpu/simple/timing.cc
--- a/src/cpu/simple/timing.cc  Thu Mar 17 19:20:20 2011 -0500
+++ b/src/cpu/simple/timing.cc  Thu Mar 17 19:20:20 2011 -0500
@@ -714,6 +714,10 @@
 
     checkPcEventQueue();
 
+    // We must have just got suspended by a PC event
+    if (_status == Idle)
+        return;
+
     TheISA::PCState pcState = thread->pcState();
     bool needToFetch = !isRomMicroPC(pcState.microPC()) && !curMacroStaticInst;
 
diff -r e08035e1a1f6 -r b0b94a7b7c1f src/kern/linux/events.cc
--- a/src/kern/linux/events.cc  Thu Mar 17 19:20:20 2011 -0500
+++ b/src/kern/linux/events.cc  Thu Mar 17 19:20:20 2011 -0500
@@ -44,11 +44,13 @@
 #include <sstream>
 
 #include "base/trace.hh"
+#include "arch/utility.hh"
 #include "cpu/thread_context.hh"
 #include "kern/linux/events.hh"
 #include "kern/linux/printk.hh"
 #include "kern/system_events.hh"
 #include "sim/arguments.hh"
+#include "sim/pseudo_inst.hh"
 #include "sim/system.hh"
 
 namespace Linux {
@@ -66,4 +68,27 @@
     SkipFuncEvent::process(tc);
 }
 
+void
+UDelayEvent::process(ThreadContext *tc)
+{
+    int arg_num  = 0;
+
+    // Get the time in native size
+    uint64_t time = TheISA::getArgument(tc, arg_num,  (uint16_t)-1, false);
+
+    // convert parameter to ns
+    if (argDivToNs)
+        time /= argDivToNs;
+
+    time *= argMultToNs;
+
+    // Convert ns to ticks
+    time *= SimClock::Int::ns;
+
+    SkipFuncEvent::process(tc);
+
+    PseudoInst::quiesceNs(tc, time);
+}
+
+
 } // namespace linux
diff -r e08035e1a1f6 -r b0b94a7b7c1f src/kern/linux/events.hh
--- a/src/kern/linux/events.hh  Thu Mar 17 19:20:20 2011 -0500
+++ b/src/kern/linux/events.hh  Thu Mar 17 19:20:20 2011 -0500
@@ -44,6 +44,33 @@
     virtual void process(ThreadContext *xc);
 };
 
+/** A class to skip udelay() and related calls in the kernel.
+ * This class has two additional parameters that take the argument to udelay 
and
+ * manipulated it to come up with ns and eventually ticks to quiesce for.
+ * See descriptions of argDivToNs and argMultToNs below.
+ */
+class UDelayEvent : public SkipFuncEvent
+{
+  private:
+    /** value to divide arg by to create ns. This is present beacues the linux
+     * kernel code sometime precomputes the first multiply that is done in
+     * udelay() if the parameter is a constant. We need to undo it so here is
+     * how. */
+    uint64_t argDivToNs;
+
+    /** value to multiple arg by to create ns. Nominally, this is 1000 to
+     * convert us to ns, but since linux can do some preprocessing of constant
+     * values something else might be required. */
+    uint64_t argMultToNs;
+
+  public:
+    UDelayEvent(PCEventQueue *q, const std::string &desc, Addr addr,
+            uint64_t mult, uint64_t div)
+        : SkipFuncEvent(q, desc, addr), argDivToNs(div), argMultToNs(mult) {}
+    virtual void process(ThreadContext *xc);
+};
+
+
 }
 
 #endif
_______________________________________________
m5-dev mailing list
m5-dev@m5sim.org
http://m5sim.org/mailman/listinfo/m5-dev

Reply via email to