[POWERPC] Add smp_call_function_map and smp_call_function_single
authorwill schmidt <will_schmidt@vnet.ibm.com>
Wed, 2 May 2007 17:12:34 +0000 (03:12 +1000)
committerPaul Mackerras <paulus@samba.org>
Mon, 7 May 2007 10:31:13 +0000 (20:31 +1000)
Add a new function named smp_call_function_single().  This matches a generic
prototype from include/linux/smp.h.

Add a function smp_call_function_map().  This is, for the most part, a rename
of smp_call_function, with some added cpumask support.  smp_call_function and
smp_call_function_single call into smp_call_function_map.

Lightly tested on 970mp (blade), power4 and power5.

Signed-off-by: Will Schmidt <will_schmidt@vnet.ibm.com>
cc: Anton Blanchard <anton@samba.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
arch/powerpc/kernel/smp.c

index d8e503b2e1af8d6ed9ece6358b61f083eeb2e712..22f1ef1b3100ff8df55e8ab0f8256518b4c2987e 100644 (file)
@@ -176,10 +176,10 @@ static struct call_data_struct {
 #define SMP_CALL_TIMEOUT       8
 
 /*
- * This function sends a 'generic call function' IPI to all other CPUs
- * in the system.
+ * These functions send a 'generic call function' IPI to other online
+ * CPUS in the system.
  *
- * [SUMMARY] Run a function on all other CPUs.
+ * [SUMMARY] Run a function on other CPUs.
  * <func> The function to run. This must be fast and non-blocking.
  * <info> An arbitrary pointer to pass to the function.
  * <nonatomic> currently unused.
@@ -190,18 +190,26 @@ static struct call_data_struct {
  * You must not call this function with disabled interrupts or from a
  * hardware interrupt handler or from a bottom half handler.
  */
-int smp_call_function (void (*func) (void *info), void *info, int nonatomic,
-                      int wait)
-{ 
+int smp_call_function_map(void (*func) (void *info), void *info, int nonatomic,
+                       int wait, cpumask_t map)
+{
        struct call_data_struct data;
-       int ret = -1, cpus;
+       int ret = -1, num_cpus;
+       int cpu;
        u64 timeout;
 
        /* Can deadlock when called with interrupts disabled */
        WARN_ON(irqs_disabled());
 
+       /* remove 'self' from the map */
+       if (cpu_isset(smp_processor_id(), map))
+               cpu_clear(smp_processor_id(), map);
+
+       /* sanity check the map, remove any non-online processors. */
+       cpus_and(map, map, cpu_online_map);
+
        if (unlikely(smp_ops == NULL))
-               return -1;
+               return ret;
 
        data.func = func;
        data.info = info;
@@ -213,40 +221,42 @@ int smp_call_function (void (*func) (void *info), void *info, int nonatomic,
        spin_lock(&call_lock);
        /* Must grab online cpu count with preempt disabled, otherwise
         * it can change. */
-       cpus = num_online_cpus() - 1;
-       if (!cpus) {
+       num_cpus = num_online_cpus() - 1;
+       if (!num_cpus || cpus_empty(map)) {
                ret = 0;
                goto out;
        }
 
        call_data = &data;
        smp_wmb();
-       /* Send a message to all other CPUs and wait for them to respond */
-       smp_ops->message_pass(MSG_ALL_BUT_SELF, PPC_MSG_CALL_FUNCTION);
+       /* Send a message to all CPUs in the map */
+       for_each_cpu_mask(cpu, map)
+               smp_ops->message_pass(cpu, PPC_MSG_CALL_FUNCTION);
 
        timeout = get_tb() + (u64) SMP_CALL_TIMEOUT * tb_ticks_per_sec;
 
-       /* Wait for response */
-       while (atomic_read(&data.started) != cpus) {
+       /* Wait for indication that they have received the message */
+       while (atomic_read(&data.started) != num_cpus) {
                HMT_low();
                if (get_tb() >= timeout) {
                        printk("smp_call_function on cpu %d: other cpus not "
-                              "responding (%d)\n", smp_processor_id(),
-                              atomic_read(&data.started));
+                               "responding (%d)\n", smp_processor_id(),
+                               atomic_read(&data.started));
                        debugger(NULL);
                        goto out;
                }
        }
 
+       /* optionally wait for the CPUs to complete */
        if (wait) {
-               while (atomic_read(&data.finished) != cpus) {
+               while (atomic_read(&data.finished) != num_cpus) {
                        HMT_low();
                        if (get_tb() >= timeout) {
                                printk("smp_call_function on cpu %d: other "
-                                      "cpus not finishing (%d/%d)\n",
-                                      smp_processor_id(),
-                                      atomic_read(&data.finished),
-                                      atomic_read(&data.started));
+                                       "cpus not finishing (%d/%d)\n",
+                                       smp_processor_id(),
+                                       atomic_read(&data.finished),
+                                       atomic_read(&data.started));
                                debugger(NULL);
                                goto out;
                        }
@@ -262,8 +272,29 @@ int smp_call_function (void (*func) (void *info), void *info, int nonatomic,
        return ret;
 }
 
+int smp_call_function(void (*func) (void *info), void *info, int nonatomic,
+                       int wait)
+{
+       return smp_call_function_map(func,info,nonatomic,wait,cpu_online_map);
+}
 EXPORT_SYMBOL(smp_call_function);
 
+int smp_call_function_single(int cpu, void (*func) (void *info), void *info, int nonatomic,
+                       int wait)
+{
+       cpumask_t map=CPU_MASK_NONE;
+
+       if (!cpu_online(cpu))
+               return -EINVAL;
+
+       if (cpu == smp_processor_id())
+               return -EBUSY;
+
+       cpu_set(cpu, map);
+       return smp_call_function_map(func,info,nonatomic,wait,map);
+}
+EXPORT_SYMBOL(smp_call_function_single);
+
 void smp_call_function_interrupt(void)
 {
        void (*func) (void *info);