LCOV - code coverage report
Current view: top level - lib - flex_proportions.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 33 98 33.7 %
Date: 2014-02-18 Functions: 4 14 28.6 %
Branches: 49 570 8.6 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  *  Floating proportions with flexible aging period
       3                 :            :  *
       4                 :            :  *   Copyright (C) 2011, SUSE, Jan Kara <jack@suse.cz>
       5                 :            :  *
       6                 :            :  * The goal of this code is: Given different types of event, measure proportion
       7                 :            :  * of each type of event over time. The proportions are measured with
       8                 :            :  * exponentially decaying history to give smooth transitions. A formula
       9                 :            :  * expressing proportion of event of type 'j' is:
      10                 :            :  *
      11                 :            :  *   p_{j} = (\Sum_{i>=0} x_{i,j}/2^{i+1})/(\Sum_{i>=0} x_i/2^{i+1})
      12                 :            :  *
      13                 :            :  * Where x_{i,j} is j's number of events in i-th last time period and x_i is
      14                 :            :  * total number of events in i-th last time period.
      15                 :            :  *
      16                 :            :  * Note that p_{j}'s are normalised, i.e.
      17                 :            :  *
      18                 :            :  *   \Sum_{j} p_{j} = 1,
      19                 :            :  *
      20                 :            :  * This formula can be straightforwardly computed by maintaing denominator
      21                 :            :  * (let's call it 'd') and for each event type its numerator (let's call it
      22                 :            :  * 'n_j'). When an event of type 'j' happens, we simply need to do:
      23                 :            :  *   n_j++; d++;
      24                 :            :  *
      25                 :            :  * When a new period is declared, we could do:
      26                 :            :  *   d /= 2
      27                 :            :  *   for each j
      28                 :            :  *     n_j /= 2
      29                 :            :  *
      30                 :            :  * To avoid iteration over all event types, we instead shift numerator of event
      31                 :            :  * j lazily when someone asks for a proportion of event j or when event j
      32                 :            :  * occurs. This can bit trivially implemented by remembering last period in
      33                 :            :  * which something happened with proportion of type j.
      34                 :            :  */
      35                 :            : #include <linux/flex_proportions.h>
      36                 :            : 
      37                 :          0 : int fprop_global_init(struct fprop_global *p)
      38                 :            : {
      39                 :            :         int err;
      40                 :            : 
      41                 :          0 :         p->period = 0;
      42                 :            :         /* Use 1 to avoid dealing with periods with 0 events... */
      43                 :          0 :         err = percpu_counter_init(&p->events, 1);
      44         [ #  # ]:          0 :         if (err)
      45                 :            :                 return err;
      46                 :            :         seqcount_init(&p->sequence);
      47                 :          0 :         return 0;
      48                 :            : }
      49                 :            : 
      50                 :          0 : void fprop_global_destroy(struct fprop_global *p)
      51                 :            : {
      52                 :          0 :         percpu_counter_destroy(&p->events);
      53                 :          0 : }
      54                 :            : 
      55                 :            : /*
      56                 :            :  * Declare @periods new periods. It is upto the caller to make sure period
      57                 :            :  * transitions cannot happen in parallel.
      58                 :            :  *
      59                 :            :  * The function returns true if the proportions are still defined and false
      60                 :            :  * if aging zeroed out all events. This can be used to detect whether declaring
      61                 :            :  * further periods has any effect.
      62                 :            :  */
      63                 :          0 : bool fprop_new_period(struct fprop_global *p, int periods)
      64                 :            : {
      65                 :            :         s64 events;
      66                 :            :         unsigned long flags;
      67                 :            : 
      68                 :            :         local_irq_save(flags);
      69                 :       3260 :         events = percpu_counter_sum(&p->events);
      70                 :            :         /*
      71                 :            :          * Don't do anything if there are no events.
      72                 :            :          */
      73         [ +  + ]:       3260 :         if (events <= 1) {
      74         [ -  + ]:        200 :                 local_irq_restore(flags);
      75                 :            :                 return false;
      76                 :            :         }
      77                 :            :         write_seqcount_begin(&p->sequence);
      78         [ +  - ]:       3060 :         if (periods < 64)
      79                 :       3060 :                 events -= events >> periods;
      80                 :            :         /* Use addition to avoid losing events happening between sum and set */
      81                 :       3060 :         percpu_counter_add(&p->events, -events);
      82                 :       3060 :         p->period += periods;
      83                 :            :         write_seqcount_end(&p->sequence);
      84         [ -  + ]:       3060 :         local_irq_restore(flags);
      85                 :            : 
      86                 :            :         return true;
      87                 :            : }
      88                 :            : 
      89                 :            : /*
      90                 :            :  * ---- SINGLE ----
      91                 :            :  */
      92                 :            : 
      93                 :          0 : int fprop_local_init_single(struct fprop_local_single *pl)
      94                 :            : {
      95                 :          0 :         pl->events = 0;
      96                 :          0 :         pl->period = 0;
      97                 :          0 :         raw_spin_lock_init(&pl->lock);
      98                 :          0 :         return 0;
      99                 :            : }
     100                 :            : 
     101                 :          0 : void fprop_local_destroy_single(struct fprop_local_single *pl)
     102                 :            : {
     103                 :          0 : }
     104                 :            : 
     105                 :          0 : static void fprop_reflect_period_single(struct fprop_global *p,
     106                 :            :                                         struct fprop_local_single *pl)
     107                 :            : {
     108                 :          0 :         unsigned int period = p->period;
     109                 :            :         unsigned long flags;
     110                 :            : 
     111                 :            :         /* Fast path - period didn't change */
     112         [ #  # ]:          0 :         if (pl->period == period)
     113                 :            :                 return;
     114                 :          0 :         raw_spin_lock_irqsave(&pl->lock, flags);
     115                 :            :         /* Someone updated pl->period while we were spinning? */
     116         [ #  # ]:          0 :         if (pl->period >= period) {
     117                 :          0 :                 raw_spin_unlock_irqrestore(&pl->lock, flags);
     118                 :            :                 return;
     119                 :            :         }
     120                 :            :         /* Aging zeroed our fraction? */
     121         [ #  # ]:          0 :         if (period - pl->period < BITS_PER_LONG)
     122                 :          0 :                 pl->events >>= period - pl->period;
     123                 :            :         else
     124                 :          0 :                 pl->events = 0;
     125                 :          0 :         pl->period = period;
     126                 :          0 :         raw_spin_unlock_irqrestore(&pl->lock, flags);
     127                 :            : }
     128                 :            : 
     129                 :            : /* Event of type pl happened */
     130                 :          0 : void __fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl)
     131                 :            : {
     132                 :          0 :         fprop_reflect_period_single(p, pl);
     133                 :          0 :         pl->events++;
     134                 :          0 :         percpu_counter_add(&p->events, 1);
     135                 :          0 : }
     136                 :            : 
     137                 :            : /* Return fraction of events of type pl */
     138                 :          0 : void fprop_fraction_single(struct fprop_global *p,
     139                 :            :                            struct fprop_local_single *pl,
     140                 :            :                            unsigned long *numerator, unsigned long *denominator)
     141                 :            : {
     142                 :            :         unsigned int seq;
     143                 :            :         s64 num, den;
     144                 :            : 
     145                 :            :         do {
     146                 :            :                 seq = read_seqcount_begin(&p->sequence);
     147                 :          0 :                 fprop_reflect_period_single(p, pl);
     148                 :          0 :                 num = pl->events;
     149                 :          0 :                 den = percpu_counter_read_positive(&p->events);
     150         [ #  # ]:          0 :         } while (read_seqcount_retry(&p->sequence, seq));
     151                 :            : 
     152                 :            :         /*
     153                 :            :          * Make fraction <= 1 and denominator > 0 even in presence of percpu
     154                 :            :          * counter errors
     155                 :            :          */
     156         [ #  # ]:          0 :         if (den <= num) {
     157         [ #  # ]:          0 :                 if (num)
     158                 :            :                         den = num;
     159                 :            :                 else
     160                 :            :                         den = 1;
     161                 :            :         }
     162                 :          0 :         *denominator = den;
     163                 :          0 :         *numerator = num;
     164                 :          0 : }
     165                 :            : 
     166                 :            : /*
     167                 :            :  * ---- PERCPU ----
     168                 :            :  */
     169                 :            : #define PROP_BATCH (8*(1+ilog2(nr_cpu_ids)))
     170                 :            : 
     171                 :          0 : int fprop_local_init_percpu(struct fprop_local_percpu *pl)
     172                 :            : {
     173                 :            :         int err;
     174                 :            : 
     175                 :          0 :         err = percpu_counter_init(&pl->events, 0);
     176         [ #  # ]:          0 :         if (err)
     177                 :            :                 return err;
     178                 :          0 :         pl->period = 0;
     179                 :          0 :         raw_spin_lock_init(&pl->lock);
     180                 :          0 :         return 0;
     181                 :            : }
     182                 :            : 
     183                 :          0 : void fprop_local_destroy_percpu(struct fprop_local_percpu *pl)
     184                 :            : {
     185                 :          0 :         percpu_counter_destroy(&pl->events);
     186                 :          0 : }
     187                 :            : 
     188                 :          0 : static void fprop_reflect_period_percpu(struct fprop_global *p,
     189                 :            :                                         struct fprop_local_percpu *pl)
     190                 :            : {
     191                 :     997642 :         unsigned int period = p->period;
     192                 :            :         unsigned long flags;
     193                 :            : 
     194                 :            :         /* Fast path - period didn't change */
     195         [ +  + ]:     997642 :         if (pl->period == period)
     196                 :            :                 return;
     197                 :       2582 :         raw_spin_lock_irqsave(&pl->lock, flags);
     198                 :            :         /* Someone updated pl->period while we were spinning? */
     199         [ -  + ]:    1000224 :         if (pl->period >= period) {
     200                 :          0 :                 raw_spin_unlock_irqrestore(&pl->lock, flags);
     201                 :            :                 return;
     202                 :            :         }
     203                 :            :         /* Aging zeroed our fraction? */
     204         [ +  - ]:       2582 :         if (period - pl->period < BITS_PER_LONG) {
     205                 :       2582 :                 s64 val = percpu_counter_read(&pl->events);
     206                 :            : 
     207 [ -  + ][ #  # ]:       5164 :                 if (val < (nr_cpu_ids * PROP_BATCH))
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ +  + ]
     208                 :       1207 :                         val = percpu_counter_sum(&pl->events);
     209                 :            : 
     210         [ -  + ]:       5164 :                 __percpu_counter_add(&pl->events,
     211 [ #  # ][ #  # ]:          0 :                         -val + (val >> (period-pl->period)), PROP_BATCH);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     212                 :            :         } else
     213                 :          0 :                 percpu_counter_set(&pl->events, 0);
     214                 :       2582 :         pl->period = period;
     215                 :       2582 :         raw_spin_unlock_irqrestore(&pl->lock, flags);
     216                 :            : }
     217                 :            : 
     218                 :            : /* Event of type pl happened */
     219                 :          0 : void __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl)
     220                 :            : {
     221                 :          0 :         fprop_reflect_period_percpu(p, pl);
     222 [ #  # ][ #  # ]:          0 :         __percpu_counter_add(&pl->events, 1, PROP_BATCH);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     223                 :          0 :         percpu_counter_add(&p->events, 1);
     224                 :          0 : }
     225                 :            : 
     226                 :       8991 : void fprop_fraction_percpu(struct fprop_global *p,
     227                 :            :                            struct fprop_local_percpu *pl,
     228                 :            :                            unsigned long *numerator, unsigned long *denominator)
     229                 :            : {
     230                 :            :         unsigned int seq;
     231                 :            :         s64 num, den;
     232                 :            : 
     233                 :            :         do {
     234                 :            :                 seq = read_seqcount_begin(&p->sequence);
     235                 :       8991 :                 fprop_reflect_period_percpu(p, pl);
     236                 :       8991 :                 num = percpu_counter_read_positive(&pl->events);
     237                 :       8991 :                 den = percpu_counter_read_positive(&p->events);
     238         [ -  + ]:       8991 :         } while (read_seqcount_retry(&p->sequence, seq));
     239                 :            : 
     240                 :            :         /*
     241                 :            :          * Make fraction <= 1 and denominator > 0 even in presence of percpu
     242                 :            :          * counter errors
     243                 :            :          */
     244         [ +  + ]:       8991 :         if (den <= num) {
     245         [ +  + ]:       7975 :                 if (num)
     246                 :            :                         den = num;
     247                 :            :                 else
     248                 :            :                         den = 1;
     249                 :            :         }
     250                 :       8991 :         *denominator = den;
     251                 :       8991 :         *numerator = num;
     252                 :       8991 : }
     253                 :            : 
     254                 :            : /*
     255                 :            :  * Like __fprop_inc_percpu() except that event is counted only if the given
     256                 :            :  * type has fraction smaller than @max_frac/FPROP_FRAC_BASE
     257                 :            :  */
     258                 :          0 : void __fprop_inc_percpu_max(struct fprop_global *p,
     259                 :            :                             struct fprop_local_percpu *pl, int max_frac)
     260                 :            : {
     261         [ -  + ]:     988652 :         if (unlikely(max_frac < FPROP_FRAC_BASE)) {
     262                 :            :                 unsigned long numerator, denominator;
     263                 :            : 
     264                 :          0 :                 fprop_fraction_percpu(p, pl, &numerator, &denominator);
     265         [ #  # ]:          0 :                 if (numerator >
     266                 :          0 :                     (((u64)denominator) * max_frac) >> FPROP_FRAC_SHIFT)
     267                 :          0 :                         return;
     268                 :            :         } else
     269                 :     988652 :                 fprop_reflect_period_percpu(p, pl);
     270 [ +  + ][ -  + ]:    2965956 :         __percpu_counter_add(&pl->events, 1, PROP_BATCH);
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ -  - ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
     271                 :     988652 :         percpu_counter_add(&p->events, 1);
     272                 :            : }

Generated by: LCOV version 1.9