diff --git a/core/sched.cc b/core/sched.cc
index 4bf07a023b62f14a77347c4d398945d470ecac36..1241651354d4a91a497c60dc3cb852d4e5b73b2f 100644
--- a/core/sched.cc
+++ b/core/sched.cc
@@ -189,9 +189,7 @@ thread::stack_info thread::get_stack_info()
     return _stack;
 }
 
-timer_list timers;
-
-timer_list::timer_list()
+timer_list::callback_dispatch::callback_dispatch()
 {
     clock_event->set_callback(this);
 }
@@ -210,6 +208,13 @@ void timer_list::fired()
     }
 }
 
+void timer_list::callback_dispatch::fired()
+{
+    cpu::current()->timers.fired();
+}
+
+timer_list::callback_dispatch timer_list::_dispatch;
+
 timer::timer(thread& t)
     : _t(t)
     , _expired()
@@ -232,6 +237,7 @@ void timer::set(u64 time)
 {
     _time = time;
     with_lock(irq_lock, [=] {
+        auto& timers = _t._cpu->timers;
         timers._list.insert(*this);
         _t._active_timers.push_back(*this);
         if (timers._list.iterator_to(*this) == timers._list.begin()) {
@@ -247,7 +253,7 @@ void timer::cancel()
             return;
         }
         _t._active_timers.erase(_t._active_timers.iterator_to(*this));
-        timers._list.erase(*this);
+        _t._cpu->timers._list.erase(*this);
         _expired = false;
     });
     // even if we remove the first timer, allow it to expire rather than
diff --git a/include/sched.hh b/include/sched.hh
index 7887470aa6fcd2d0174de3561dbb0cf562e9daf5..59cae1d39dc01a33b7457c8e77bbeb20525081af 100644
--- a/include/sched.hh
+++ b/include/sched.hh
@@ -107,13 +107,18 @@ public:
     bi::list_member_hook<> _thread_list_link;
 };
 
-class timer_list : private clock_event_callback {
+class timer_list {
 public:
-    timer_list();
-    virtual void fired();
+    void fired();
 private:
     friend class timer;
     bi::set<timer, bi::base_hook<bi::set_base_hook<>>> _list;
+    class callback_dispatch : private clock_event_callback {
+    public:
+        callback_dispatch();
+        virtual void fired();
+    };
+    static callback_dispatch _dispatch;
 };
 
 typedef bi::list<thread,
@@ -127,6 +132,7 @@ struct cpu {
     struct arch_cpu arch;
     thread* bringup_thread;
     runqueue_type runqueue;
+    timer_list timers;
     // for each cpu, a list of threads that are migrating into this cpu:
     typedef lockless_queue<thread, &thread::_wakeup_link> incoming_wakeup_queue;
     incoming_wakeup_queue* incoming_wakeups;