Implement function which allow to setup/remove hotplug state
callbacks.

The default behaviour for setup is to call the startup function for
this state for (or on) all cpus which have a hotplug state >= the
installed state.

The default behaviour for removal is to call the teardown function for
this state for (or on) all cpus which have a hotplug state >= the
installed state.

For both setup and remove helper functions are provided, which prevent
the core to issue the callbacks. This simplifies the conversion of
existing hotplug notifiers.

Signed-off-by: Thomas Gleixner <t...@linutronix.de>
---
 include/linux/cpu.h        |    1 
 include/linux/cpuhotplug.h |   70 +++++++++++++++++
 kernel/cpu.c               |  185 ++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 255 insertions(+), 1 deletion(-)

Index: linux-2.6/include/linux/cpu.h
===================================================================
--- linux-2.6.orig/include/linux/cpu.h
+++ linux-2.6/include/linux/cpu.h
@@ -17,6 +17,7 @@
 #include <linux/node.h>
 #include <linux/compiler.h>
 #include <linux/cpumask.h>
+#include <linux/cpuhotplug.h>
 
 struct device;
 
Index: linux-2.6/include/linux/cpuhotplug.h
===================================================================
--- linux-2.6.orig/include/linux/cpuhotplug.h
+++ linux-2.6/include/linux/cpuhotplug.h
@@ -17,4 +17,74 @@ enum cpuhp_states {
        CPUHP_NOTIFY_DOWN_PREPARE,
        CPUHP_MAX,
 };
+
+int __cpuhp_setup_state(enum cpuhp_states state, bool invoke,
+                       int (*startup)(unsigned int cpu),
+                       int (*teardown)(unsigned int cpu));
+
+/**
+ * cpuhp_setup_state - Setup hotplug state callbacks with calling the callbacks
+ * @state:     The state for which the calls are installed
+ * @startup:   startup callback function
+ * @teardown:  teardown callback function
+ *
+ * Installs the callback functions and invokes the startup callback on
+ * the present cpus which have already reached the @state.
+ */
+static inline int
+cpuhp_setup_state(enum cpuhp_states state, int (*startup)(unsigned int cpu),
+                 int (*teardown)(unsigned int cpu))
+{
+       return __cpuhp_setup_state(state, true, startup, teardown);
+}
+
+/**
+ * cpuhp_setup_state_nocalls - Setup hotplug state callbacks without calling 
the callbacks
+ * @state:     The state for which the calls are installed
+ * @startup:   startup callback function
+ * @teardown:  teardown callback function
+ *
+ * No calls are executed. NOP if SMP=n or HOTPLUG_CPU=n
+ */
+#if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_CPU)
+static inline int
+cpuhp_setup_state_nocalls(enum cpuhp_states state,
+                        int (*startup)(unsigned int cpu),
+                        int (*teardown)(unsigned int cpu))
+{
+       return __cpuhp_setup_state(state, false, startup, teardown);
+}
+#else
+static inline int
+cpuhp_setup_state_nocalls(enum cpuhp_states state,
+                        int (*startup)(unsigned int cpu),
+                        int (*teardown)(unsigned int cpu))
+{
+       return 0;
+}
+#endif
+
+void __cpuhp_remove_state(enum cpuhp_states state, bool invoke);
+
+/**
+ * cpuhp_remove_state - Remove hotplug state callbacks and invoke the teardown
+ * @state:     The state for which the calls are removed
+ *
+ * Removes the callback functions and invokes the teardown callback on
+ * the present cpus which have already reached the @state.
+ */
+static inline void cpuhp_remove_state(enum cpuhp_states state)
+{
+       __cpuhp_remove_state(state, true);
+}
+
+/**
+ * cpuhp_remove_state_nocalls - Remove hotplug state callbacks without 
invoking teardown
+ * @state:     The state for which the calls are removed
+ */
+static inline void cpuhp_remove_state_nocalls(enum cpuhp_states state)
+{
+       __cpuhp_remove_state(state, false);
+}
+
 #endif
Index: linux-2.6/kernel/cpu.c
===================================================================
--- linux-2.6.orig/kernel/cpu.c
+++ linux-2.6/kernel/cpu.c
@@ -19,7 +19,6 @@
 #include <linux/mutex.h>
 #include <linux/gfp.h>
 #include <linux/suspend.h>
-#include <linux/cpuhotplug.h>
 
 #include "smpboot.h"
 
@@ -804,6 +803,190 @@ static struct cpuhp_step cpuhp_ap_states
        },
 };
 
+/* Sanity check for callbacks */
+static int cpuhp_cb_check(enum cpuhp_states state)
+{
+       if (state <= CPUHP_OFFLINE || state >= CPUHP_MAX)
+               return -EINVAL;
+       return 0;
+}
+
+static bool cpuhp_is_ap_state(enum cpuhp_states state)
+{
+       return (state > CPUHP_AP_OFFLINE && state < CPUHP_AP_MAX);
+}
+
+static void cpuhp_store_callbacks(enum cpuhp_states state,
+                                 int (*startup)(unsigned int cpu),
+                                 int (*teardown)(unsigned int cpu))
+{
+       /* (Un)Install the callbacks for further cpu hotplug operations */
+       struct cpuhp_step *sp;
+
+       sp = cpuhp_is_ap_state(state) ? cpuhp_ap_states : cpuhp_bp_states;
+       sp[state].startup = startup;
+       sp[state].teardown = teardown;
+}
+
+static void *cpuhp_get_teardown_cb(enum cpuhp_states state)
+{
+       /* (Un)Install the callbacks for further cpu hotplug operations */
+       struct cpuhp_step *sp;
+
+       sp = cpuhp_is_ap_state(state) ? cpuhp_ap_states : cpuhp_bp_states;
+       return sp[state].teardown;
+}
+
+/* Helper function to run callback on the target cpu */
+static void cpuhp_on_cpu_cb(void *__cb)
+{
+       int (*cb)(unsigned int cpu) = __cb;
+
+       BUG_ON(cb(smp_processor_id()));
+}
+
+/*
+ * Call the startup/teardown function for a step either on the AP or
+ * on the current CPU.
+ */
+static int cpuhp_issue_call(int cpu, enum cpuhp_states state,
+                           int (*cb)(unsigned int), bool bringup)
+{
+       int ret;
+
+       if (!cb)
+               return 0;
+
+       if (cpuhp_is_ap_state(state)) {
+               /*
+                * Note, that a function called on the AP is not
+                * allowed to fail.
+                */
+               smp_call_function_single(cpu, cpuhp_on_cpu_cb, cb, 1);
+               return 0;
+       }
+
+       /*
+        * The non AP bound callbacks can fail on bringup. On teardown
+        * e.g. module removal we crash for now.
+        */
+       ret = cb(cpu);
+       BUG_ON(ret && !bringup);
+       return ret;
+}
+
+/*
+ * Called from __cpuhp_setup_state on a recoverable failure.
+ *
+ * Note: The teardown callbacks for rollback are not allowed to fail!
+ */
+static void cpuhp_rollback_install(int failedcpu, enum cpuhp_states state,
+                                  int (*teardown)(unsigned int cpu))
+{
+       int cpu;
+
+       if (!teardown)
+               return;
+
+       /* Roll back the already executed steps on the other cpus */
+       for_each_present_cpu(cpu) {
+               int cpustate = per_cpu(cpuhp_state, cpu);
+
+               if (cpu >= failedcpu)
+                       break;
+
+               /* Did we invoke the startup call on that cpu ? */
+               if (cpustate >= state)
+                       cpuhp_issue_call(cpu, state, teardown, false);
+       }
+}
+
+/**
+ * __cpuhp_setup_state - Setup the callbacks for an hotplug machine state
+ * @state:     The state to setup
+ * @invoke:    If true, the startup function is invoked for cpus where
+ *             cpu state >= @state
+ * @startup:   startup callback function
+ * @teardown:  teardown callback function
+ *
+ * Returns 0 if successful, otherwise a proper error code
+ */
+int __cpuhp_setup_state(enum cpuhp_states state, bool invoke,
+                       int (*startup)(unsigned int cpu),
+                       int (*teardown)(unsigned int cpu))
+{
+       int cpu, ret = 0;
+
+       if (cpuhp_cb_check(state))
+               return -EINVAL;
+
+       get_online_cpus();
+
+       if (!invoke || !startup)
+               goto install;
+
+       /*
+        * Try to call the startup callback for each present cpu
+        * depending on the hotplug state of the cpu.
+        */
+       for_each_present_cpu(cpu) {
+               int ret, cpustate = per_cpu(cpuhp_state, cpu);
+
+               if (cpustate < state)
+                       continue;
+
+               ret = cpuhp_issue_call(cpu, state, startup, true);
+               if (ret) {
+                       cpuhp_rollback_install(cpu, state, teardown);
+                       goto out;
+               }
+       }
+install:
+       cpuhp_store_callbacks(state, startup, teardown);
+out:
+       put_online_cpus();
+       return ret;
+}
+EXPORT_SYMBOL(__cpuhp_setup_state);
+
+/**
+ * __cpuhp_remove_state - Remove the callbacks for an hotplug machine state
+ * @state:     The state to remove
+ * @invoke:    If true, the teardown function is invoked for cpus where
+ *             cpu state >= @state
+ *
+ * The teardown callback is currently not allowed to fail. Think
+ * about module removal!
+ */
+void __cpuhp_remove_state(enum cpuhp_states state, bool invoke)
+{
+       int (*teardown)(unsigned int cpu) = cpuhp_get_teardown_cb(state);
+       int cpu;
+
+       BUG_ON(cpuhp_cb_check(state));
+
+       get_online_cpus();
+
+       if (!invoke || !teardown)
+               goto remove;
+
+       /*
+        * Call the teardown callback for each present cpu depending
+        * on the hotplug state of the cpu. This function is not
+        * allowed to fail currently!
+        */
+       for_each_present_cpu(cpu) {
+               int cpustate = per_cpu(cpuhp_state, cpu);
+
+               if (cpustate >= state)
+                       cpuhp_issue_call(cpu, state, teardown, false);
+       }
+remove:
+       cpuhp_store_callbacks(state, NULL, NULL);
+       put_online_cpus();
+}
+EXPORT_SYMBOL(__cpuhp_remove_state);
+
 /*
  * cpu_bit_bitmap[] is a special, "compressed" data structure that
  * represents all NR_CPUS bits binary values of 1<<nr.


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to