while ((node = timerqueue_getnext(&group->events))) {
                evt = container_of(node, struct tmigr_event, nextevt);
 
-               if (!evt->ignore) {
+               if (!READ_ONCE(evt->ignore)) {
                        WRITE_ONCE(group->next_expiry, evt->nextevt.expires);
                        return evt;
                }
         * lock is held while updating the ignore flag in idle path. So this
         * state change will not be lost.
         */
-       group->groupevt.ignore = true;
+       WRITE_ONCE(group->groupevt.ignore, true);
 
        return walk_done;
 }
        union tmigr_state childstate, groupstate;
        bool remote = data->remote;
        bool walk_done = false;
+       bool ignore;
        u64 nextexp;
 
        if (child) {
                nextexp = child->next_expiry;
                evt = &child->groupevt;
 
-               evt->ignore = (nextexp == KTIME_MAX) ? true : false;
+               /*
+                * This can race with concurrent idle exit (activate).
+                * If the current writer wins, a useless remote expiration may
+                * be scheduled. If the activate wins, the event is properly
+                * ignored.
+                */
+               ignore = (nextexp == KTIME_MAX) ? true : false;
+               WRITE_ONCE(evt->ignore, ignore);
        } else {
                nextexp = data->nextexp;
 
                first_childevt = evt = data->evt;
+               ignore = evt->ignore;
 
                /*
                 * Walking the hierarchy is required in any case when a
                 * first event information of the group is updated properly and
                 * also handled properly, so skip this fast return path.
                 */
-               if (evt->ignore && !remote && group->parent)
+               if (ignore && !remote && group->parent)
                        return true;
 
                raw_spin_lock(&group->lock);
         * queue when the expiry time changed only or when it could be ignored.
         */
        if (timerqueue_node_queued(&evt->nextevt)) {
-               if ((evt->nextevt.expires == nextexp) && !evt->ignore) {
+               if ((evt->nextevt.expires == nextexp) && !ignore) {
                        /* Make sure not to miss a new CPU event with the same expiry */
                        evt->cpu = first_childevt->cpu;
                        goto check_toplvl;
                        WRITE_ONCE(group->next_expiry, KTIME_MAX);
        }
 
-       if (evt->ignore) {
+       if (ignore) {
                /*
                 * When the next child event could be ignored (nextexp is
                 * KTIME_MAX) and there was no remote timer handling before or