rtc: hctosys: use function name in the error log
[linux-2.6-block.git] / kernel / smpboot.c
index 40190f28db3590140cb903d3f596883c61faaa74..c697f73d82d6a4157a15ef9b0039b007083f7857 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/cpu.h>
 #include <linux/err.h>
 #include <linux/smp.h>
+#include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/list.h>
 #include <linux/slab.h>
@@ -314,3 +315,158 @@ void smpboot_unregister_percpu_thread(struct smp_hotplug_thread *plug_thread)
        put_online_cpus();
 }
 EXPORT_SYMBOL_GPL(smpboot_unregister_percpu_thread);
+
+static DEFINE_PER_CPU(atomic_t, cpu_hotplug_state) = ATOMIC_INIT(CPU_POST_DEAD);
+
+/*
+ * Called to poll specified CPU's state, for example, when waiting for
+ * a CPU to come online.
+ */
+int cpu_report_state(int cpu)
+{
+       return atomic_read(&per_cpu(cpu_hotplug_state, cpu));
+}
+
+/*
+ * If CPU has died properly, set its state to CPU_UP_PREPARE and
+ * return success.  Otherwise, return -EBUSY if the CPU died after
+ * cpu_wait_death() timed out.  And yet otherwise again, return -EAGAIN
+ * if cpu_wait_death() timed out and the CPU still hasn't gotten around
+ * to dying.  In the latter two cases, the CPU might not be set up
+ * properly, but it is up to the arch-specific code to decide.
+ * Finally, -EIO indicates an unanticipated problem.
+ *
+ * Note that it is permissible to omit this call entirely, as is
+ * done in architectures that do no CPU-hotplug error checking.
+ */
+int cpu_check_up_prepare(int cpu)
+{
+       if (!IS_ENABLED(CONFIG_HOTPLUG_CPU)) {
+               atomic_set(&per_cpu(cpu_hotplug_state, cpu), CPU_UP_PREPARE);
+               return 0;
+       }
+
+       switch (atomic_read(&per_cpu(cpu_hotplug_state, cpu))) {
+
+       case CPU_POST_DEAD:
+
+               /* The CPU died properly, so just start it up again. */
+               atomic_set(&per_cpu(cpu_hotplug_state, cpu), CPU_UP_PREPARE);
+               return 0;
+
+       case CPU_DEAD_FROZEN:
+
+               /*
+                * Timeout during CPU death, so let caller know.
+                * The outgoing CPU completed its processing, but after
+                * cpu_wait_death() timed out and reported the error. The
+                * caller is free to proceed, in which case the state
+                * will be reset properly by cpu_set_state_online().
+                * Proceeding despite this -EBUSY return makes sense
+                * for systems where the outgoing CPUs take themselves
+                * offline, with no post-death manipulation required from
+                * a surviving CPU.
+                */
+               return -EBUSY;
+
+       case CPU_BROKEN:
+
+               /*
+                * The most likely reason we got here is that there was
+                * a timeout during CPU death, and the outgoing CPU never
+                * did complete its processing.  This could happen on
+                * a virtualized system if the outgoing VCPU gets preempted
+                * for more than five seconds, and the user attempts to
+                * immediately online that same CPU.  Trying again later
+                * might return -EBUSY above, hence -EAGAIN.
+                */
+               return -EAGAIN;
+
+       default:
+
+               /* Should not happen.  Famous last words. */
+               return -EIO;
+       }
+}
+
+/*
+ * Mark the specified CPU online.
+ *
+ * Note that it is permissible to omit this call entirely, as is
+ * done in architectures that do no CPU-hotplug error checking.
+ */
+void cpu_set_state_online(int cpu)
+{
+       (void)atomic_xchg(&per_cpu(cpu_hotplug_state, cpu), CPU_ONLINE);
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+/*
+ * Wait for the specified CPU to exit the idle loop and die.
+ */
+bool cpu_wait_death(unsigned int cpu, int seconds)
+{
+       int jf_left = seconds * HZ;
+       int oldstate;
+       bool ret = true;
+       int sleep_jf = 1;
+
+       might_sleep();
+
+       /* The outgoing CPU will normally get done quite quickly. */
+       if (atomic_read(&per_cpu(cpu_hotplug_state, cpu)) == CPU_DEAD)
+               goto update_state;
+       udelay(5);
+
+       /* But if the outgoing CPU dawdles, wait increasingly long times. */
+       while (atomic_read(&per_cpu(cpu_hotplug_state, cpu)) != CPU_DEAD) {
+               schedule_timeout_uninterruptible(sleep_jf);
+               jf_left -= sleep_jf;
+               if (jf_left <= 0)
+                       break;
+               sleep_jf = DIV_ROUND_UP(sleep_jf * 11, 10);
+       }
+update_state:
+       oldstate = atomic_read(&per_cpu(cpu_hotplug_state, cpu));
+       if (oldstate == CPU_DEAD) {
+               /* Outgoing CPU died normally, update state. */
+               smp_mb(); /* atomic_read() before update. */
+               atomic_set(&per_cpu(cpu_hotplug_state, cpu), CPU_POST_DEAD);
+       } else {
+               /* Outgoing CPU still hasn't died, set state accordingly. */
+               if (atomic_cmpxchg(&per_cpu(cpu_hotplug_state, cpu),
+                                  oldstate, CPU_BROKEN) != oldstate)
+                       goto update_state;
+               ret = false;
+       }
+       return ret;
+}
+
+/*
+ * Called by the outgoing CPU to report its successful death.  Return
+ * false if this report follows the surviving CPU's timing out.
+ *
+ * A separate "CPU_DEAD_FROZEN" is used when the surviving CPU
+ * timed out.  This approach allows architectures to omit calls to
+ * cpu_check_up_prepare() and cpu_set_state_online() without defeating
+ * the next cpu_wait_death()'s polling loop.
+ */
+bool cpu_report_death(void)
+{
+       int oldstate;
+       int newstate;
+       int cpu = smp_processor_id();
+
+       do {
+               oldstate = atomic_read(&per_cpu(cpu_hotplug_state, cpu));
+               if (oldstate != CPU_BROKEN)
+                       newstate = CPU_DEAD;
+               else
+                       newstate = CPU_DEAD_FROZEN;
+       } while (atomic_cmpxchg(&per_cpu(cpu_hotplug_state, cpu),
+                               oldstate, newstate) != oldstate);
+       return newstate == CPU_DEAD;
+}
+
+#endif /* #ifdef CONFIG_HOTPLUG_CPU */