return false;
 }
 
-static inline bool rcu_running_nocb_timer(struct rcu_data *rdp)
-{
-       return (timer_curr_running(&rdp->nocb_timer) && !in_irq());
-}
 #else
 static inline int rcu_lockdep_is_held_nocb(struct rcu_data *rdp)
 {
        return false;
 }
 
-static inline bool rcu_running_nocb_timer(struct rcu_data *rdp)
-{
-       return false;
-}
-
 #endif /* #ifdef CONFIG_RCU_NOCB_CPU */
 
 static bool rcu_rdp_is_offloaded(struct rcu_data *rdp)
                  rcu_lockdep_is_held_nocb(rdp) ||
                  (rdp == this_cpu_ptr(&rcu_data) &&
                   !(IS_ENABLED(CONFIG_PREEMPT_COUNT) && preemptible())) ||
-                 rcu_current_is_nocb_kthread(rdp) ||
-                 rcu_running_nocb_timer(rdp)),
+                 rcu_current_is_nocb_kthread(rdp)),
                "Unsafe read of RCU_NOCB offloaded state"
        );
 
        return false;
 }
 
-/*
- * Kick the GP kthread for this NOCB group.  Caller holds ->nocb_lock
- * and this function releases it.
- */
-static bool wake_nocb_gp(struct rcu_data *rdp, bool force,
-                        unsigned long flags)
-       __releases(rdp->nocb_lock)
+static bool __wake_nocb_gp(struct rcu_data *rdp_gp,
+                          struct rcu_data *rdp,
+                          bool force, unsigned long flags)
+       __releases(rdp_gp->nocb_gp_lock)
 {
        bool needwake = false;
-       struct rcu_data *rdp_gp = rdp->nocb_gp_rdp;
 
-       lockdep_assert_held(&rdp->nocb_lock);
        if (!READ_ONCE(rdp_gp->nocb_gp_kthread)) {
-               rcu_nocb_unlock_irqrestore(rdp, flags);
+               raw_spin_unlock_irqrestore(&rdp_gp->nocb_gp_lock, flags);
                trace_rcu_nocb_wake(rcu_state.name, rdp->cpu,
                                    TPS("AlreadyAwake"));
                return false;
        }
 
-       if (READ_ONCE(rdp->nocb_defer_wakeup) > RCU_NOCB_WAKE_NOT) {
-               WRITE_ONCE(rdp->nocb_defer_wakeup, RCU_NOCB_WAKE_NOT);
-               del_timer(&rdp->nocb_timer);
+       if (rdp_gp->nocb_defer_wakeup > RCU_NOCB_WAKE_NOT) {
+               WRITE_ONCE(rdp_gp->nocb_defer_wakeup, RCU_NOCB_WAKE_NOT);
+               del_timer(&rdp_gp->nocb_timer);
        }
-       rcu_nocb_unlock_irqrestore(rdp, flags);
-       raw_spin_lock_irqsave(&rdp_gp->nocb_gp_lock, flags);
+
        if (force || READ_ONCE(rdp_gp->nocb_gp_sleep)) {
                WRITE_ONCE(rdp_gp->nocb_gp_sleep, false);
                needwake = true;
-               trace_rcu_nocb_wake(rcu_state.name, rdp->cpu, TPS("DoWake"));
        }
        raw_spin_unlock_irqrestore(&rdp_gp->nocb_gp_lock, flags);
-       if (needwake)
+       if (needwake) {
+               trace_rcu_nocb_wake(rcu_state.name, rdp->cpu, TPS("DoWake"));
                wake_up_process(rdp_gp->nocb_gp_kthread);
+       }
 
        return needwake;
 }
 
+/*
+ * Kick the GP kthread for this NOCB group.
+ */
+static bool wake_nocb_gp(struct rcu_data *rdp, bool force)
+{
+       unsigned long flags;
+       struct rcu_data *rdp_gp = rdp->nocb_gp_rdp;
+
+       raw_spin_lock_irqsave(&rdp_gp->nocb_gp_lock, flags);
+       return __wake_nocb_gp(rdp_gp, rdp, force, flags);
+}
+
 /*
  * Arrange to wake the GP kthread for this NOCB group at some future
  * time when it is safe to do so.
 static void wake_nocb_gp_defer(struct rcu_data *rdp, int waketype,
                               const char *reason)
 {
-       if (rdp->nocb_defer_wakeup == RCU_NOCB_WAKE_OFF)
-               return;
-       if (rdp->nocb_defer_wakeup == RCU_NOCB_WAKE_NOT)
-               mod_timer(&rdp->nocb_timer, jiffies + 1);
-       if (rdp->nocb_defer_wakeup < waketype)
-               WRITE_ONCE(rdp->nocb_defer_wakeup, waketype);
+       unsigned long flags;
+       struct rcu_data *rdp_gp = rdp->nocb_gp_rdp;
+
+       raw_spin_lock_irqsave(&rdp_gp->nocb_gp_lock, flags);
+
+       if (rdp_gp->nocb_defer_wakeup == RCU_NOCB_WAKE_NOT)
+               mod_timer(&rdp_gp->nocb_timer, jiffies + 1);
+       if (rdp_gp->nocb_defer_wakeup < waketype)
+               WRITE_ONCE(rdp_gp->nocb_defer_wakeup, waketype);
+
+       raw_spin_unlock_irqrestore(&rdp_gp->nocb_gp_lock, flags);
+
        trace_rcu_nocb_wake(rcu_state.name, rdp->cpu, reason);
 }
 
                rdp->qlen_last_fqs_check = len;
                if (!irqs_disabled_flags(flags)) {
                        /* ... if queue was empty ... */
-                       wake_nocb_gp(rdp, false, flags);
+                       rcu_nocb_unlock_irqrestore(rdp, flags);
+                       wake_nocb_gp(rdp, false);
                        trace_rcu_nocb_wake(rcu_state.name, rdp->cpu,
                                            TPS("WakeEmpty"));
                } else {
+                       rcu_nocb_unlock_irqrestore(rdp, flags);
                        wake_nocb_gp_defer(rdp, RCU_NOCB_WAKE,
                                           TPS("WakeEmptyIsDeferred"));
-                       rcu_nocb_unlock_irqrestore(rdp, flags);
                }
        } else if (len > rdp->qlen_last_fqs_check + qhimark) {
                /* ... or if many callbacks queued. */
                smp_mb(); /* Enqueue before timer_pending(). */
                if ((rdp->nocb_cb_sleep ||
                     !rcu_segcblist_ready_cbs(&rdp->cblist)) &&
-                   !timer_pending(&rdp->nocb_bypass_timer))
+                   !timer_pending(&rdp->nocb_bypass_timer)) {
+                       rcu_nocb_unlock_irqrestore(rdp, flags);
                        wake_nocb_gp_defer(rdp, RCU_NOCB_WAKE_FORCE,
                                           TPS("WakeOvfIsDeferred"));
-               rcu_nocb_unlock_irqrestore(rdp, flags);
+               } else {
+                       rcu_nocb_unlock_irqrestore(rdp, flags);
+                       trace_rcu_nocb_wake(rcu_state.name, rdp->cpu, TPS("WakeNot"));
+               }
        } else {
                rcu_nocb_unlock_irqrestore(rdp, flags);
                trace_rcu_nocb_wake(rcu_state.name, rdp->cpu, TPS("WakeNot"));
                        bypass = true;
                }
                rnp = rdp->mynode;
-               if (bypass) {  // Avoid race with first bypass CB.
-                       WRITE_ONCE(my_rdp->nocb_defer_wakeup,
-                                  RCU_NOCB_WAKE_NOT);
-                       del_timer(&my_rdp->nocb_timer);
-               }
+
                // Advance callbacks if helpful and low contention.
                needwake_gp = false;
                if (!rcu_segcblist_restempty(&rdp->cblist,
        my_rdp->nocb_gp_bypass = bypass;
        my_rdp->nocb_gp_gp = needwait_gp;
        my_rdp->nocb_gp_seq = needwait_gp ? wait_gp_seq : 0;
-       if (bypass && !rcu_nocb_poll) {
-               // At least one child with non-empty ->nocb_bypass, so set
-               // timer in order to avoid stranding its callbacks.
+       if (bypass) {
                raw_spin_lock_irqsave(&my_rdp->nocb_gp_lock, flags);
-               mod_timer(&my_rdp->nocb_bypass_timer, j + 2);
+               // Avoid race with first bypass CB.
+               if (my_rdp->nocb_defer_wakeup > RCU_NOCB_WAKE_NOT) {
+                       WRITE_ONCE(my_rdp->nocb_defer_wakeup, RCU_NOCB_WAKE_NOT);
+                       del_timer(&my_rdp->nocb_timer);
+               }
+               if (!rcu_nocb_poll) {
+                       // At least one child with non-empty ->nocb_bypass, so set
+                       // timer in order to avoid stranding its callbacks.
+                       mod_timer(&my_rdp->nocb_bypass_timer, j + 2);
+               }
                raw_spin_unlock_irqrestore(&my_rdp->nocb_gp_lock, flags);
        }
        if (rcu_nocb_poll) {
 {
        unsigned long flags;
        int ndw;
+       struct rcu_data *rdp_gp = rdp->nocb_gp_rdp;
        int ret;
 
-       rcu_nocb_lock_irqsave(rdp, flags);
-       if (!rcu_nocb_need_deferred_wakeup(rdp)) {
-               rcu_nocb_unlock_irqrestore(rdp, flags);
+       raw_spin_lock_irqsave(&rdp_gp->nocb_gp_lock, flags);
+
+       if (!rcu_nocb_need_deferred_wakeup(rdp_gp)) {
+               raw_spin_unlock_irqrestore(&rdp_gp->nocb_gp_lock, flags);
                return false;
        }
-       ndw = READ_ONCE(rdp->nocb_defer_wakeup);
-       ret = wake_nocb_gp(rdp, ndw == RCU_NOCB_WAKE_FORCE, flags);
+
+       ndw = rdp_gp->nocb_defer_wakeup;
+       ret = __wake_nocb_gp(rdp_gp, rdp, ndw == RCU_NOCB_WAKE_FORCE, flags);
        trace_rcu_nocb_wake(rcu_state.name, rdp->cpu, TPS("DeferredWake"));
 
        return ret;
  */
 static bool do_nocb_deferred_wakeup(struct rcu_data *rdp)
 {
-       if (rcu_nocb_need_deferred_wakeup(rdp))
+       if (!rdp->nocb_gp_rdp)
+               return false;
+
+       if (rcu_nocb_need_deferred_wakeup(rdp->nocb_gp_rdp))
                return do_nocb_deferred_wakeup_common(rdp);
        return false;
 }
        swait_event_exclusive(rdp->nocb_state_wq,
                              !rcu_segcblist_test_flags(cblist, SEGCBLIST_KTHREAD_CB |
                                                        SEGCBLIST_KTHREAD_GP));
-       rcu_nocb_lock_irqsave(rdp, flags);
-       /* Make sure nocb timer won't stay around */
-       WRITE_ONCE(rdp->nocb_defer_wakeup, RCU_NOCB_WAKE_OFF);
-       rcu_nocb_unlock_irqrestore(rdp, flags);
-       del_timer_sync(&rdp->nocb_timer);
-
        /*
-        * Theoretically we could set SEGCBLIST_SOFTIRQ_ONLY with CB unlocked
-        * and IRQs disabled but let's be paranoid.
+        * Lock one last time to acquire latest callback updates from kthreads
+        * so we can later handle callbacks locally without locking.
         */
        rcu_nocb_lock_irqsave(rdp, flags);
+       /*
+        * Theoretically we could set SEGCBLIST_SOFTIRQ_ONLY after the nocb
+        * lock is released but how about being paranoid for once?
+        */
        rcu_segcblist_set_flags(cblist, SEGCBLIST_SOFTIRQ_ONLY);
        /*
         * With SEGCBLIST_SOFTIRQ_ONLY, we can't use
         * SEGCBLIST_SOFTIRQ_ONLY mode.
         */
        raw_spin_lock_irqsave(&rdp->nocb_lock, flags);
-       /* Re-enable nocb timer */
-       WRITE_ONCE(rdp->nocb_defer_wakeup, RCU_NOCB_WAKE_NOT);
+
        /*
         * We didn't take the nocb lock while working on the
         * rdp->cblist in SEGCBLIST_SOFTIRQ_ONLY mode.