cpuidle: coupled: add parallel barrier function
authorColin Cross <ccross@android.com>
Tue, 8 May 2012 00:57:42 +0000 (17:57 -0700)
committerLen Brown <len.brown@intel.com>
Sat, 2 Jun 2012 04:49:36 +0000 (00:49 -0400)
Adds cpuidle_coupled_parallel_barrier, which can be used by coupled
cpuidle state enter functions to handle resynchronization after
determining if any cpu needs to abort.  The normal use case will
be:

static bool abort_flag;
static atomic_t abort_barrier;

int arch_cpuidle_enter(struct cpuidle_device *dev, ...)
{
if (arch_turn_off_irq_controller()) {
    /* returns an error if an irq is pending and would be lost
   if idle continued and turned off power */
abort_flag = true;
}

cpuidle_coupled_parallel_barrier(dev, &abort_barrier);

if (abort_flag) {
    /* One of the cpus didn't turn off it's irq controller */
    arch_turn_on_irq_controller();
return -EINTR;
}

/* continue with idle */
...
}

This will cause all cpus to abort idle together if one of them needs
to abort.

Reviewed-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Tested-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
Tested-by: Kevin Hilman <khilman@ti.com>
Signed-off-by: Colin Cross <ccross@android.com>
Signed-off-by: Len Brown <len.brown@intel.com>
drivers/cpuidle/coupled.c
include/linux/cpuidle.h

index aab6bba8daec75842acf76ffb01f7099a2bd60ee..2c9bf2692232e51a66e5455368d9881383ccfc2f 100644 (file)
@@ -129,6 +129,43 @@ static DEFINE_PER_CPU(struct call_single_data, cpuidle_coupled_poke_cb);
  */
 static cpumask_t cpuidle_coupled_poked_mask;
 
+/**
+ * cpuidle_coupled_parallel_barrier - synchronize all online coupled cpus
+ * @dev: cpuidle_device of the calling cpu
+ * @a:   atomic variable to hold the barrier
+ *
+ * No caller to this function will return from this function until all online
+ * cpus in the same coupled group have called this function.  Once any caller
+ * has returned from this function, the barrier is immediately available for
+ * reuse.
+ *
+ * The atomic variable a must be initialized to 0 before any cpu calls
+ * this function, will be reset to 0 before any cpu returns from this function.
+ *
+ * Must only be called from within a coupled idle state handler
+ * (state.enter when state.flags has CPUIDLE_FLAG_COUPLED set).
+ *
+ * Provides full smp barrier semantics before and after calling.
+ */
+void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a)
+{
+       int n = dev->coupled->online_count;
+
+       smp_mb__before_atomic_inc();
+       atomic_inc(a);
+
+       while (atomic_read(a) < n)
+               cpu_relax();
+
+       if (atomic_inc_return(a) == n * 2) {
+               atomic_set(a, 0);
+               return;
+       }
+
+       while (atomic_read(a) > n)
+               cpu_relax();
+}
+
 /**
  * cpuidle_state_is_coupled - check if a state is part of a coupled set
  * @dev: struct cpuidle_device for the current cpu
index 603844835bc74eaf9d4d75fc4ddd529bc9ff1aab..5ab7183313ce2f361c8946cefc4442e6a6c15542 100644 (file)
@@ -183,6 +183,10 @@ static inline int cpuidle_play_dead(void) {return -ENODEV; }
 
 #endif
 
+#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
+void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a);
+#endif
+
 /******************************
  * CPUIDLE GOVERNOR INTERFACE *
  ******************************/