Merge tag 'irq-core-2021-06-29' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / kernel / time / tick-sched.c
index 828b091..6bffe5a 100644 (file)
@@ -230,6 +230,7 @@ static void tick_sched_handle(struct tick_sched *ts, struct pt_regs *regs)
 
 #ifdef CONFIG_NO_HZ_FULL
 cpumask_var_t tick_nohz_full_mask;
+EXPORT_SYMBOL_GPL(tick_nohz_full_mask);
 bool tick_nohz_full_running;
 EXPORT_SYMBOL_GPL(tick_nohz_full_running);
 static atomic_t tick_dep_mask;
@@ -322,6 +323,46 @@ void tick_nohz_full_kick_cpu(int cpu)
        irq_work_queue_on(&per_cpu(nohz_full_kick_work, cpu), cpu);
 }
 
+static void tick_nohz_kick_task(struct task_struct *tsk)
+{
+       int cpu;
+
+       /*
+        * If the task is not running, run_posix_cpu_timers()
+        * has nothing to elapse, IPI can then be spared.
+        *
+        * activate_task()                      STORE p->tick_dep_mask
+        *   STORE p->on_rq
+        * __schedule() (switch to task 'p')    smp_mb() (atomic_fetch_or())
+        *   LOCK rq->lock                      LOAD p->on_rq
+        *   smp_mb__after_spin_lock()
+        *   tick_nohz_task_switch()
+        *     LOAD p->tick_dep_mask
+        */
+       if (!sched_task_on_rq(tsk))
+               return;
+
+       /*
+        * If the task concurrently migrates to another CPU,
+        * we guarantee it sees the new tick dependency upon
+        * schedule.
+        *
+        * set_task_cpu(p, cpu);
+        *   STORE p->cpu = @cpu
+        * __schedule() (switch to task 'p')
+        *   LOCK rq->lock
+        *   smp_mb__after_spin_lock()          STORE p->tick_dep_mask
+        *   tick_nohz_task_switch()            smp_mb() (atomic_fetch_or())
+        *      LOAD p->tick_dep_mask           LOAD p->cpu
+        */
+       cpu = task_cpu(tsk);
+
+       preempt_disable();
+       if (cpu_online(cpu))
+               tick_nohz_full_kick_cpu(cpu);
+       preempt_enable();
+}
+
 /*
  * Kick all full dynticks CPUs in order to force these to re-evaluate
  * their dependency on the tick and restart it if necessary.
@@ -404,19 +445,8 @@ EXPORT_SYMBOL_GPL(tick_nohz_dep_clear_cpu);
  */
 void tick_nohz_dep_set_task(struct task_struct *tsk, enum tick_dep_bits bit)
 {
-       if (!atomic_fetch_or(BIT(bit), &tsk->tick_dep_mask)) {
-               if (tsk == current) {
-                       preempt_disable();
-                       tick_nohz_full_kick();
-                       preempt_enable();
-               } else {
-                       /*
-                        * Some future tick_nohz_full_kick_task()
-                        * should optimize this.
-                        */
-                       tick_nohz_full_kick_all();
-               }
-       }
+       if (!atomic_fetch_or(BIT(bit), &tsk->tick_dep_mask))
+               tick_nohz_kick_task(tsk);
 }
 EXPORT_SYMBOL_GPL(tick_nohz_dep_set_task);
 
@@ -430,9 +460,20 @@ EXPORT_SYMBOL_GPL(tick_nohz_dep_clear_task);
  * Set a per-taskgroup tick dependency. Posix CPU timers need this in order to elapse
  * per process timers.
  */
-void tick_nohz_dep_set_signal(struct signal_struct *sig, enum tick_dep_bits bit)
+void tick_nohz_dep_set_signal(struct task_struct *tsk,
+                             enum tick_dep_bits bit)
 {
-       tick_nohz_dep_set_all(&sig->tick_dep_mask, bit);
+       int prev;
+       struct signal_struct *sig = tsk->signal;
+
+       prev = atomic_fetch_or(BIT(bit), &sig->tick_dep_mask);
+       if (!prev) {
+               struct task_struct *t;
+
+               lockdep_assert_held(&tsk->sighand->siglock);
+               __for_each_thread(sig, t)
+                       tick_nohz_kick_task(t);
+       }
 }
 
 void tick_nohz_dep_clear_signal(struct signal_struct *sig, enum tick_dep_bits bit)
@@ -447,13 +488,10 @@ void tick_nohz_dep_clear_signal(struct signal_struct *sig, enum tick_dep_bits bi
  */
 void __tick_nohz_task_switch(void)
 {
-       unsigned long flags;
        struct tick_sched *ts;
 
-       local_irq_save(flags);
-
        if (!tick_nohz_full_cpu(smp_processor_id()))
-               goto out;
+               return;
 
        ts = this_cpu_ptr(&tick_cpu_sched);
 
@@ -462,8 +500,6 @@ void __tick_nohz_task_switch(void)
                    atomic_read(&current->signal->tick_dep_mask))
                        tick_nohz_full_kick();
        }
-out:
-       local_irq_restore(flags);
 }
 
 /* Get the boot-time nohz CPU list from the kernel parameters. */
@@ -921,27 +957,31 @@ static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now)
         * Cancel the scheduled timer and restore the tick
         */
        ts->tick_stopped  = 0;
-       ts->idle_exittime = now;
-
        tick_nohz_restart(ts, now);
 }
 
-static void tick_nohz_full_update_tick(struct tick_sched *ts)
+static void __tick_nohz_full_update_tick(struct tick_sched *ts,
+                                        ktime_t now)
 {
 #ifdef CONFIG_NO_HZ_FULL
        int cpu = smp_processor_id();
 
-       if (!tick_nohz_full_cpu(cpu))
+       if (can_stop_full_tick(cpu, ts))
+               tick_nohz_stop_sched_tick(ts, cpu);
+       else if (ts->tick_stopped)
+               tick_nohz_restart_sched_tick(ts, now);
+#endif
+}
+
+static void tick_nohz_full_update_tick(struct tick_sched *ts)
+{
+       if (!tick_nohz_full_cpu(smp_processor_id()))
                return;
 
        if (!ts->tick_stopped && ts->nohz_mode == NOHZ_MODE_INACTIVE)
                return;
 
-       if (can_stop_full_tick(cpu, ts))
-               tick_nohz_stop_sched_tick(ts, cpu);
-       else if (ts->tick_stopped)
-               tick_nohz_restart_sched_tick(ts, ktime_get());
-#endif
+       __tick_nohz_full_update_tick(ts, ktime_get());
 }
 
 static bool can_stop_idle_tick(int cpu, struct tick_sched *ts)
@@ -1188,11 +1228,13 @@ unsigned long tick_nohz_get_idle_calls(void)
        return ts->idle_calls;
 }
 
-static void tick_nohz_account_idle_ticks(struct tick_sched *ts)
+static void tick_nohz_account_idle_time(struct tick_sched *ts,
+                                       ktime_t now)
 {
-#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
        unsigned long ticks;
 
+       ts->idle_exittime = now;
+
        if (vtime_accounting_enabled_this_cpu())
                return;
        /*
@@ -1206,21 +1248,27 @@ static void tick_nohz_account_idle_ticks(struct tick_sched *ts)
         */
        if (ticks && ticks < LONG_MAX)
                account_idle_ticks(ticks);
-#endif
 }
 
-static void __tick_nohz_idle_restart_tick(struct tick_sched *ts, ktime_t now)
+void tick_nohz_idle_restart_tick(void)
 {
-       tick_nohz_restart_sched_tick(ts, now);
-       tick_nohz_account_idle_ticks(ts);
+       struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
+
+       if (ts->tick_stopped) {
+               ktime_t now = ktime_get();
+               tick_nohz_restart_sched_tick(ts, now);
+               tick_nohz_account_idle_time(ts, now);
+       }
 }
 
-void tick_nohz_idle_restart_tick(void)
+static void tick_nohz_idle_update_tick(struct tick_sched *ts, ktime_t now)
 {
-       struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
+       if (tick_nohz_full_cpu(smp_processor_id()))
+               __tick_nohz_full_update_tick(ts, now);
+       else
+               tick_nohz_restart_sched_tick(ts, now);
 
-       if (ts->tick_stopped)
-               __tick_nohz_idle_restart_tick(ts, ktime_get());
+       tick_nohz_account_idle_time(ts, now);
 }
 
 /**
@@ -1252,7 +1300,7 @@ void tick_nohz_idle_exit(void)
                tick_nohz_stop_idle(ts, now);
 
        if (tick_stopped)
-               __tick_nohz_idle_restart_tick(ts, now);
+               tick_nohz_idle_update_tick(ts, now);
 
        local_irq_enable();
 }