diff --git a/core/rcu.cc b/core/rcu.cc
index 6669ecafbcca9402e9abc29314d30bfa863a0942..1b27e2c798dba54359c13af10e845264f7a4c24e 100644
--- a/core/rcu.cc
+++ b/core/rcu.cc
@@ -68,9 +68,10 @@ void cpu_quiescent_state_thread::work()
     }
 }
 
-bool all_at_generation(uint64_t generation)
+bool all_at_generation(decltype(cpu_quiescent_state_threads)& cqsts,
+                       uint64_t generation)
 {
-    for (auto cqst : cpu_quiescent_state_threads) {
+    for (auto cqst : cqsts) {
         if (!cqst->check(generation)) {
             return false;
         }
@@ -82,10 +83,15 @@ void await_grace_period()
 {
     static uint64_t generation = 0;
     ++generation;
-    for (auto cqst : cpu_quiescent_state_threads) {
+    // copy cpu_quiescent_state_threads to prevent a hotplugged cpu
+    // from changing the number of cpus we request a new generation on,
+    // and the number of cpus we wait on
+    // FIXME: better locking
+    auto cqsts = cpu_quiescent_state_threads;
+    for (auto cqst : cqsts) {
         cqst->request(generation);
     }
-    sched::thread::wait_until([] { return all_at_generation(generation); });
+    sched::thread::wait_until([&cqsts] { return all_at_generation(cqsts, generation); });
 }
 
 void collect_garbage()