LCOV - code coverage report
Current view: top level - lib - percpu_counter.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 56 94 59.6 %
Date: 2022-04-01 14:58:12 Functions: 6 9 66.7 %
Branches: 17 44 38.6 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0
       2                 :            : /*
       3                 :            :  * Fast batching percpu counters.
       4                 :            :  */
       5                 :            : 
       6                 :            : #include <linux/percpu_counter.h>
       7                 :            : #include <linux/mutex.h>
       8                 :            : #include <linux/init.h>
       9                 :            : #include <linux/cpu.h>
      10                 :            : #include <linux/module.h>
      11                 :            : #include <linux/debugobjects.h>
      12                 :            : 
      13                 :            : #ifdef CONFIG_HOTPLUG_CPU
      14                 :            : static LIST_HEAD(percpu_counters);
      15                 :            : static DEFINE_SPINLOCK(percpu_counters_lock);
      16                 :            : #endif
      17                 :            : 
      18                 :            : #ifdef CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER
      19                 :            : 
      20                 :            : static struct debug_obj_descr percpu_counter_debug_descr;
      21                 :            : 
      22                 :            : static bool percpu_counter_fixup_free(void *addr, enum debug_obj_state state)
      23                 :            : {
      24                 :            :         struct percpu_counter *fbc = addr;
      25                 :            : 
      26                 :            :         switch (state) {
      27                 :            :         case ODEBUG_STATE_ACTIVE:
      28                 :            :                 percpu_counter_destroy(fbc);
      29                 :            :                 debug_object_free(fbc, &percpu_counter_debug_descr);
      30                 :            :                 return true;
      31                 :            :         default:
      32                 :            :                 return false;
      33                 :            :         }
      34                 :            : }
      35                 :            : 
      36                 :            : static struct debug_obj_descr percpu_counter_debug_descr = {
      37                 :            :         .name           = "percpu_counter",
      38                 :            :         .fixup_free     = percpu_counter_fixup_free,
      39                 :            : };
      40                 :            : 
      41                 :            : static inline void debug_percpu_counter_activate(struct percpu_counter *fbc)
      42                 :            : {
      43                 :            :         debug_object_init(fbc, &percpu_counter_debug_descr);
      44                 :            :         debug_object_activate(fbc, &percpu_counter_debug_descr);
      45                 :            : }
      46                 :            : 
      47                 :            : static inline void debug_percpu_counter_deactivate(struct percpu_counter *fbc)
      48                 :            : {
      49                 :            :         debug_object_deactivate(fbc, &percpu_counter_debug_descr);
      50                 :            :         debug_object_free(fbc, &percpu_counter_debug_descr);
      51                 :            : }
      52                 :            : 
      53                 :            : #else   /* CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER */
      54                 :        321 : static inline void debug_percpu_counter_activate(struct percpu_counter *fbc)
      55                 :        321 : { }
      56                 :          0 : static inline void debug_percpu_counter_deactivate(struct percpu_counter *fbc)
      57                 :          0 : { }
      58                 :            : #endif  /* CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER */
      59                 :            : 
      60                 :          0 : void percpu_counter_set(struct percpu_counter *fbc, s64 amount)
      61                 :            : {
      62                 :          0 :         int cpu;
      63                 :          0 :         unsigned long flags;
      64                 :            : 
      65                 :          0 :         raw_spin_lock_irqsave(&fbc->lock, flags);
      66         [ #  # ]:          0 :         for_each_possible_cpu(cpu) {
      67                 :          0 :                 s32 *pcount = per_cpu_ptr(fbc->counters, cpu);
      68                 :          0 :                 *pcount = 0;
      69                 :            :         }
      70                 :          0 :         fbc->count = amount;
      71                 :          0 :         raw_spin_unlock_irqrestore(&fbc->lock, flags);
      72                 :          0 : }
      73                 :            : EXPORT_SYMBOL(percpu_counter_set);
      74                 :            : 
      75                 :            : /**
      76                 :            :  * This function is both preempt and irq safe. The former is due to explicit
      77                 :            :  * preemption disable. The latter is guaranteed by the fact that the slow path
      78                 :            :  * is explicitly protected by an irq-safe spinlock whereas the fast patch uses
      79                 :            :  * this_cpu_add which is irq-safe by definition. Hence there is no need muck
      80                 :            :  * with irq state before calling this one
      81                 :            :  */
      82                 :    1354058 : void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch)
      83                 :            : {
      84                 :    1354058 :         s64 count;
      85                 :            : 
      86                 :    1354058 :         preempt_disable();
      87         [ +  + ]:    1354058 :         count = __this_cpu_read(*fbc->counters) + amount;
      88   [ +  +  +  + ]:    1359813 :         if (count >= batch || count <= -batch) {
      89                 :       5755 :                 unsigned long flags;
      90                 :       5755 :                 raw_spin_lock_irqsave(&fbc->lock, flags);
      91                 :       5755 :                 fbc->count += count;
      92   [ -  +  -  -  :       5755 :                 __this_cpu_sub(*fbc->counters, count - amount);
             -  -  -  + ]
      93                 :       5755 :                 raw_spin_unlock_irqrestore(&fbc->lock, flags);
      94                 :            :         } else {
      95   [ -  +  -  -  :    1348303 :                 this_cpu_add(*fbc->counters, amount);
             -  -  -  + ]
      96                 :            :         }
      97                 :    1354058 :         preempt_enable();
      98                 :    1354058 : }
      99                 :            : EXPORT_SYMBOL(percpu_counter_add_batch);
     100                 :            : 
     101                 :            : /*
     102                 :            :  * Add up all the per-cpu counts, return the result.  This is a more accurate
     103                 :            :  * but much slower version of percpu_counter_read_positive()
     104                 :            :  */
     105                 :       1851 : s64 __percpu_counter_sum(struct percpu_counter *fbc)
     106                 :            : {
     107                 :       1851 :         s64 ret;
     108                 :       1851 :         int cpu;
     109                 :       1851 :         unsigned long flags;
     110                 :            : 
     111                 :       1851 :         raw_spin_lock_irqsave(&fbc->lock, flags);
     112                 :       1851 :         ret = fbc->count;
     113         [ +  + ]:       3702 :         for_each_online_cpu(cpu) {
     114                 :       1851 :                 s32 *pcount = per_cpu_ptr(fbc->counters, cpu);
     115                 :       1851 :                 ret += *pcount;
     116                 :            :         }
     117                 :       1851 :         raw_spin_unlock_irqrestore(&fbc->lock, flags);
     118                 :       1851 :         return ret;
     119                 :            : }
     120                 :            : EXPORT_SYMBOL(__percpu_counter_sum);
     121                 :            : 
     122                 :        321 : int __percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp,
     123                 :            :                           struct lock_class_key *key)
     124                 :            : {
     125                 :        321 :         unsigned long flags __maybe_unused;
     126                 :            : 
     127                 :        321 :         raw_spin_lock_init(&fbc->lock);
     128                 :        321 :         lockdep_set_class(&fbc->lock, key);
     129                 :        321 :         fbc->count = amount;
     130                 :        321 :         fbc->counters = alloc_percpu_gfp(s32, gfp);
     131         [ +  - ]:        321 :         if (!fbc->counters)
     132                 :            :                 return -ENOMEM;
     133                 :            : 
     134                 :        321 :         debug_percpu_counter_activate(fbc);
     135                 :            : 
     136                 :            : #ifdef CONFIG_HOTPLUG_CPU
     137                 :        321 :         INIT_LIST_HEAD(&fbc->list);
     138                 :        321 :         spin_lock_irqsave(&percpu_counters_lock, flags);
     139                 :        321 :         list_add(&fbc->list, &percpu_counters);
     140                 :        321 :         spin_unlock_irqrestore(&percpu_counters_lock, flags);
     141                 :            : #endif
     142                 :        321 :         return 0;
     143                 :            : }
     144                 :            : EXPORT_SYMBOL(__percpu_counter_init);
     145                 :            : 
     146                 :          0 : void percpu_counter_destroy(struct percpu_counter *fbc)
     147                 :            : {
     148                 :          0 :         unsigned long flags __maybe_unused;
     149                 :            : 
     150         [ #  # ]:          0 :         if (!fbc->counters)
     151                 :            :                 return;
     152                 :            : 
     153                 :          0 :         debug_percpu_counter_deactivate(fbc);
     154                 :            : 
     155                 :            : #ifdef CONFIG_HOTPLUG_CPU
     156                 :          0 :         spin_lock_irqsave(&percpu_counters_lock, flags);
     157                 :          0 :         list_del(&fbc->list);
     158                 :          0 :         spin_unlock_irqrestore(&percpu_counters_lock, flags);
     159                 :            : #endif
     160                 :          0 :         free_percpu(fbc->counters);
     161                 :          0 :         fbc->counters = NULL;
     162                 :            : }
     163                 :            : EXPORT_SYMBOL(percpu_counter_destroy);
     164                 :            : 
     165                 :            : int percpu_counter_batch __read_mostly = 32;
     166                 :            : EXPORT_SYMBOL(percpu_counter_batch);
     167                 :            : 
     168                 :          3 : static int compute_batch_value(unsigned int cpu)
     169                 :            : {
     170                 :          3 :         int nr = num_online_cpus();
     171                 :            : 
     172                 :          3 :         percpu_counter_batch = max(32, nr*2);
     173                 :          3 :         return 0;
     174                 :            : }
     175                 :            : 
     176                 :          0 : static int percpu_counter_cpu_dead(unsigned int cpu)
     177                 :            : {
     178                 :            : #ifdef CONFIG_HOTPLUG_CPU
     179                 :          0 :         struct percpu_counter *fbc;
     180                 :            : 
     181                 :          0 :         compute_batch_value(cpu);
     182                 :            : 
     183                 :          0 :         spin_lock_irq(&percpu_counters_lock);
     184         [ #  # ]:          0 :         list_for_each_entry(fbc, &percpu_counters, list) {
     185                 :          0 :                 s32 *pcount;
     186                 :            : 
     187                 :          0 :                 raw_spin_lock(&fbc->lock);
     188                 :          0 :                 pcount = per_cpu_ptr(fbc->counters, cpu);
     189                 :          0 :                 fbc->count += *pcount;
     190                 :          0 :                 *pcount = 0;
     191                 :          0 :                 raw_spin_unlock(&fbc->lock);
     192                 :            :         }
     193                 :          0 :         spin_unlock_irq(&percpu_counters_lock);
     194                 :            : #endif
     195                 :          0 :         return 0;
     196                 :            : }
     197                 :            : 
     198                 :            : /*
     199                 :            :  * Compare counter against given value.
     200                 :            :  * Return 1 if greater, 0 if equal and -1 if less
     201                 :            :  */
     202                 :       3951 : int __percpu_counter_compare(struct percpu_counter *fbc, s64 rhs, s32 batch)
     203                 :            : {
     204                 :       3951 :         s64     count;
     205                 :            : 
     206                 :       3951 :         count = percpu_counter_read(fbc);
     207                 :            :         /* Check to see if rough count will be sufficient for comparison */
     208         [ +  - ]:       3951 :         if (abs(count - rhs) > (batch * num_online_cpus())) {
     209         [ +  - ]:       3951 :                 if (count > rhs)
     210                 :            :                         return 1;
     211                 :            :                 else
     212                 :       3951 :                         return -1;
     213                 :            :         }
     214                 :            :         /* Need to use precise count */
     215                 :          0 :         count = percpu_counter_sum(fbc);
     216         [ #  # ]:          0 :         if (count > rhs)
     217                 :            :                 return 1;
     218         [ #  # ]:          0 :         else if (count < rhs)
     219                 :            :                 return -1;
     220                 :            :         else
     221                 :          0 :                 return 0;
     222                 :            : }
     223                 :            : EXPORT_SYMBOL(__percpu_counter_compare);
     224                 :            : 
     225                 :          3 : static int __init percpu_counter_startup(void)
     226                 :            : {
     227                 :          3 :         int ret;
     228                 :            : 
     229                 :          3 :         ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "lib/percpu_cnt:online",
     230                 :            :                                 compute_batch_value, NULL);
     231         [ -  + ]:          3 :         WARN_ON(ret < 0);
     232                 :          3 :         ret = cpuhp_setup_state_nocalls(CPUHP_PERCPU_CNT_DEAD,
     233                 :            :                                         "lib/percpu_cnt:dead", NULL,
     234                 :            :                                         percpu_counter_cpu_dead);
     235         [ -  + ]:          3 :         WARN_ON(ret < 0);
     236                 :          3 :         return 0;
     237                 :            : }
     238                 :            : module_init(percpu_counter_startup);

Generated by: LCOV version 1.14