]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - kernel/smp.c
Merge branch 'next' of git://git.infradead.org/users/vkoul/slave-dma
[karo-tx-linux.git] / kernel / smp.c
index 69f38bd98b423a53ada73c7a6b7a2217e91a77b4..8e451f3ff51b818796bf5ca4e4b3189a215b367a 100644 (file)
 #include "smpboot.h"
 
 #ifdef CONFIG_USE_GENERIC_SMP_HELPERS
-static struct {
-       struct list_head        queue;
-       raw_spinlock_t          lock;
-} call_function __cacheline_aligned_in_smp =
-       {
-               .queue          = LIST_HEAD_INIT(call_function.queue),
-               .lock           = __RAW_SPIN_LOCK_UNLOCKED(call_function.lock),
-       };
-
 enum {
        CSD_FLAG_LOCK           = 0x01,
 };
 
 struct call_function_data {
-       struct call_single_data csd;
-       atomic_t                refs;
+       struct call_single_data __percpu *csd;
        cpumask_var_t           cpumask;
        cpumask_var_t           cpumask_ipi;
 };
@@ -60,6 +50,11 @@ hotplug_cfd(struct notifier_block *nfb, unsigned long action, void *hcpu)
                if (!zalloc_cpumask_var_node(&cfd->cpumask_ipi, GFP_KERNEL,
                                cpu_to_node(cpu)))
                        return notifier_from_errno(-ENOMEM);
+               cfd->csd = alloc_percpu(struct call_single_data);
+               if (!cfd->csd) {
+                       free_cpumask_var(cfd->cpumask);
+                       return notifier_from_errno(-ENOMEM);
+               }
                break;
 
 #ifdef CONFIG_HOTPLUG_CPU
@@ -70,6 +65,7 @@ hotplug_cfd(struct notifier_block *nfb, unsigned long action, void *hcpu)
        case CPU_DEAD_FROZEN:
                free_cpumask_var(cfd->cpumask);
                free_cpumask_var(cfd->cpumask_ipi);
+               free_percpu(cfd->csd);
                break;
 #endif
        };
@@ -170,85 +166,6 @@ void generic_exec_single(int cpu, struct call_single_data *data, int wait)
                csd_lock_wait(data);
 }
 
-/*
- * Invoked by arch to handle an IPI for call function. Must be called with
- * interrupts disabled.
- */
-void generic_smp_call_function_interrupt(void)
-{
-       struct call_function_data *data;
-       int cpu = smp_processor_id();
-
-       /*
-        * Shouldn't receive this interrupt on a cpu that is not yet online.
-        */
-       WARN_ON_ONCE(!cpu_online(cpu));
-
-       /*
-        * Ensure entry is visible on call_function_queue after we have
-        * entered the IPI. See comment in smp_call_function_many.
-        * If we don't have this, then we may miss an entry on the list
-        * and never get another IPI to process it.
-        */
-       smp_mb();
-
-       /*
-        * It's ok to use list_for_each_rcu() here even though we may
-        * delete 'pos', since list_del_rcu() doesn't clear ->next
-        */
-       list_for_each_entry_rcu(data, &call_function.queue, csd.list) {
-               int refs;
-               smp_call_func_t func;
-
-               /*
-                * Since we walk the list without any locks, we might
-                * see an entry that was completed, removed from the
-                * list and is in the process of being reused.
-                *
-                * We must check that the cpu is in the cpumask before
-                * checking the refs, and both must be set before
-                * executing the callback on this cpu.
-                */
-
-               if (!cpumask_test_cpu(cpu, data->cpumask))
-                       continue;
-
-               smp_rmb();
-
-               if (atomic_read(&data->refs) == 0)
-                       continue;
-
-               func = data->csd.func;          /* save for later warn */
-               func(data->csd.info);
-
-               /*
-                * If the cpu mask is not still set then func enabled
-                * interrupts (BUG), and this cpu took another smp call
-                * function interrupt and executed func(info) twice
-                * on this cpu.  That nested execution decremented refs.
-                */
-               if (!cpumask_test_and_clear_cpu(cpu, data->cpumask)) {
-                       WARN(1, "%pf enabled interrupts and double executed\n", func);
-                       continue;
-               }
-
-               refs = atomic_dec_return(&data->refs);
-               WARN_ON(refs < 0);
-
-               if (refs)
-                       continue;
-
-               WARN_ON(!cpumask_empty(data->cpumask));
-
-               raw_spin_lock(&call_function.lock);
-               list_del_rcu(&data->csd.list);
-               raw_spin_unlock(&call_function.lock);
-
-               csd_unlock(&data->csd);
-       }
-
-}
-
 /*
  * Invoked by arch to handle an IPI for call function single. Must be
  * called from the arch with interrupts disabled.
@@ -453,8 +370,7 @@ void smp_call_function_many(const struct cpumask *mask,
                            smp_call_func_t func, void *info, bool wait)
 {
        struct call_function_data *data;
-       unsigned long flags;
-       int refs, cpu, next_cpu, this_cpu = smp_processor_id();
+       int cpu, next_cpu, this_cpu = smp_processor_id();
 
        /*
         * Can deadlock when called with interrupts disabled.
@@ -486,50 +402,13 @@ void smp_call_function_many(const struct cpumask *mask,
        }
 
        data = &__get_cpu_var(cfd_data);
-       csd_lock(&data->csd);
-
-       /* This BUG_ON verifies our reuse assertions and can be removed */
-       BUG_ON(atomic_read(&data->refs) || !cpumask_empty(data->cpumask));
-
-       /*
-        * The global call function queue list add and delete are protected
-        * by a lock, but the list is traversed without any lock, relying
-        * on the rcu list add and delete to allow safe concurrent traversal.
-        * We reuse the call function data without waiting for any grace
-        * period after some other cpu removes it from the global queue.
-        * This means a cpu might find our data block as it is being
-        * filled out.
-        *
-        * We hold off the interrupt handler on the other cpu by
-        * ordering our writes to the cpu mask vs our setting of the
-        * refs counter.  We assert only the cpu owning the data block
-        * will set a bit in cpumask, and each bit will only be cleared
-        * by the subject cpu.  Each cpu must first find its bit is
-        * set and then check that refs is set indicating the element is
-        * ready to be processed, otherwise it must skip the entry.
-        *
-        * On the previous iteration refs was set to 0 by another cpu.
-        * To avoid the use of transitivity, set the counter to 0 here
-        * so the wmb will pair with the rmb in the interrupt handler.
-        */
-       atomic_set(&data->refs, 0);     /* convert 3rd to 1st party write */
-
-       data->csd.func = func;
-       data->csd.info = info;
 
-       /* Ensure 0 refs is visible before mask.  Also orders func and info */
-       smp_wmb();
-
-       /* We rely on the "and" being processed before the store */
        cpumask_and(data->cpumask, mask, cpu_online_mask);
        cpumask_clear_cpu(this_cpu, data->cpumask);
-       refs = cpumask_weight(data->cpumask);
 
        /* Some callers race with other cpus changing the passed mask */
-       if (unlikely(!refs)) {
-               csd_unlock(&data->csd);
+       if (unlikely(!cpumask_weight(data->cpumask)))
                return;
-       }
 
        /*
         * After we put an entry into the list, data->cpumask
@@ -537,34 +416,32 @@ void smp_call_function_many(const struct cpumask *mask,
         * a SMP function call, so data->cpumask will be zero.
         */
        cpumask_copy(data->cpumask_ipi, data->cpumask);
-       raw_spin_lock_irqsave(&call_function.lock, flags);
-       /*
-        * Place entry at the _HEAD_ of the list, so that any cpu still
-        * observing the entry in generic_smp_call_function_interrupt()
-        * will not miss any other list entries:
-        */
-       list_add_rcu(&data->csd.list, &call_function.queue);
-       /*
-        * We rely on the wmb() in list_add_rcu to complete our writes
-        * to the cpumask before this write to refs, which indicates
-        * data is on the list and is ready to be processed.
-        */
-       atomic_set(&data->refs, refs);
-       raw_spin_unlock_irqrestore(&call_function.lock, flags);
 
-       /*
-        * Make the list addition visible before sending the ipi.
-        * (IPIs must obey or appear to obey normal Linux cache
-        * coherency rules -- see comment in generic_exec_single).
-        */
-       smp_mb();
+       for_each_cpu(cpu, data->cpumask) {
+               struct call_single_data *csd = per_cpu_ptr(data->csd, cpu);
+               struct call_single_queue *dst =
+                                       &per_cpu(call_single_queue, cpu);
+               unsigned long flags;
+
+               csd_lock(csd);
+               csd->func = func;
+               csd->info = info;
+
+               raw_spin_lock_irqsave(&dst->lock, flags);
+               list_add_tail(&csd->list, &dst->list);
+               raw_spin_unlock_irqrestore(&dst->lock, flags);
+       }
 
        /* Send a message to all CPUs in the map */
        arch_send_call_function_ipi_mask(data->cpumask_ipi);
 
-       /* Optionally wait for the CPUs to complete */
-       if (wait)
-               csd_lock_wait(&data->csd);
+       if (wait) {
+               for_each_cpu(cpu, data->cpumask) {
+                       struct call_single_data *csd =
+                                       per_cpu_ptr(data->csd, cpu);
+                       csd_lock_wait(csd);
+               }
+       }
 }
 EXPORT_SYMBOL(smp_call_function_many);