Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=f16b34aa13e8c55085f346bcf07afb2312c56c0a
Commit:     f16b34aa13e8c55085f346bcf07afb2312c56c0a
Parent:     cc295d0e95063809af31971e4aec1d809247f13b
Author:     Peter Zijlstra <[EMAIL PROTECTED]>
AuthorDate: Sat Dec 22 14:03:29 2007 -0800
Committer:  Linus Torvalds <[EMAIL PROTECTED]>
CommitDate: Sun Dec 23 12:54:37 2007 -0800

    lib: proportion: fix underflow in prop_norm_percpu()
    
    Zhe Jiang noticed that its possible to underflow pl->events in
    prop_norm_percpu() when the value returned by percpu_counter_read() is less
    than the error on that read and the period delay > 1.  In that case half 
might
    not trigger the batch increment and the value will be identical on the next
    iteration, causing the same half to be subtracted again and again.
    
    Fix this by rewriting the division as a single subtraction instead of a
    subtraction loop and using percpu_counter_sum() when the value returned by
    percpu_counter_read() is smaller than the error.
    
    The latter is still needed if we want pl->events to shrink properly in the
    error region.
    
    [EMAIL PROTECTED]: cleanups]
    Signed-off-by: Peter Zijlstra <[EMAIL PROTECTED]>
    Reviewed-by: Jiang Zhe <[EMAIL PROTECTED]>
    Signed-off-by: Andrew Morton <[EMAIL PROTECTED]>
    Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]>
---
 lib/proportions.c |   37 ++++++++++++++++---------------------
 1 files changed, 16 insertions(+), 21 deletions(-)

diff --git a/lib/proportions.c b/lib/proportions.c
index 332d8c5..9508d9a 100644
--- a/lib/proportions.c
+++ b/lib/proportions.c
@@ -190,6 +190,8 @@ prop_adjust_shift(int *pl_shift, unsigned long *pl_period, 
int new_shift)
  * PERCPU
  */
 
+#define PROP_BATCH (8*(1+ilog2(nr_cpu_ids)))
+
 int prop_local_init_percpu(struct prop_local_percpu *pl)
 {
        spin_lock_init(&pl->lock);
@@ -230,31 +232,24 @@ void prop_norm_percpu(struct prop_global *pg, struct 
prop_local_percpu *pl)
 
        spin_lock_irqsave(&pl->lock, flags);
        prop_adjust_shift(&pl->shift, &pl->period, pg->shift);
+
        /*
         * For each missed period, we half the local counter.
         * basically:
         *   pl->events >> (global_period - pl->period);
-        *
-        * but since the distributed nature of percpu counters make division
-        * rather hard, use a regular subtraction loop. This is safe, because
-        * the events will only every be incremented, hence the subtraction
-        * can never result in a negative number.
         */
-       while (pl->period != global_period) {
-               unsigned long val = percpu_counter_read(&pl->events);
-               unsigned long half = (val + 1) >> 1;
-
-               /*
-                * Half of zero won't be much less, break out.
-                * This limits the loop to shift iterations, even
-                * if we missed a million.
-                */
-               if (!val)
-                       break;
-
-               percpu_counter_add(&pl->events, -half);
-               pl->period += period;
-       }
+       period = (global_period - pl->period) >> (pg->shift - 1);
+       if (period < BITS_PER_LONG) {
+               s64 val = percpu_counter_read(&pl->events);
+
+               if (val < (nr_cpu_ids * PROP_BATCH))
+                       val = percpu_counter_sum(&pl->events);
+
+               __percpu_counter_add(&pl->events, -val + (val >> period),
+                                       PROP_BATCH);
+       } else
+               percpu_counter_set(&pl->events, 0);
+
        pl->period = global_period;
        spin_unlock_irqrestore(&pl->lock, flags);
 }
@@ -267,7 +262,7 @@ void __prop_inc_percpu(struct prop_descriptor *pd, struct 
prop_local_percpu *pl)
        struct prop_global *pg = prop_get_global(pd);
 
        prop_norm_percpu(pg, pl);
-       percpu_counter_add(&pl->events, 1);
+       __percpu_counter_add(&pl->events, 1, PROP_BATCH);
        percpu_counter_add(&pg->events, 1);
        prop_put_global(pd, pg);
 }
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to