LCOV - code coverage report
Current view: top level - include/linux - u64_stats_sync.h (source / functions) Hit Total Coverage
Test: combined.info Lines: 15 25 60.0 %
Date: 2022-03-28 15:32:58 Functions: 0 0 -
Branches: 4 30 13.3 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: GPL-2.0 */
       2                 :            : #ifndef _LINUX_U64_STATS_SYNC_H
       3                 :            : #define _LINUX_U64_STATS_SYNC_H
       4                 :            : 
       5                 :            : /*
       6                 :            :  * To properly implement 64bits network statistics on 32bit and 64bit hosts,
       7                 :            :  * we provide a synchronization point, that is a noop on 64bit or UP kernels.
       8                 :            :  *
       9                 :            :  * Key points :
      10                 :            :  * 1) Use a seqcount on SMP 32bits, with low overhead.
      11                 :            :  * 2) Whole thing is a noop on 64bit arches or UP kernels.
      12                 :            :  * 3) Write side must ensure mutual exclusion or one seqcount update could
      13                 :            :  *    be lost, thus blocking readers forever.
      14                 :            :  *    If this synchronization point is not a mutex, but a spinlock or
      15                 :            :  *    spinlock_bh() or disable_bh() :
      16                 :            :  * 3.1) Write side should not sleep.
      17                 :            :  * 3.2) Write side should not allow preemption.
      18                 :            :  * 3.3) If applicable, interrupts should be disabled.
      19                 :            :  *
      20                 :            :  * 4) If reader fetches several counters, there is no guarantee the whole values
      21                 :            :  *    are consistent (remember point 1) : this is a noop on 64bit arches anyway)
      22                 :            :  *
      23                 :            :  * 5) readers are allowed to sleep or be preempted/interrupted : They perform
      24                 :            :  *    pure reads. But if they have to fetch many values, it's better to not allow
      25                 :            :  *    preemptions/interruptions to avoid many retries.
      26                 :            :  *
      27                 :            :  * 6) If counter might be written by an interrupt, readers should block interrupts.
      28                 :            :  *    (On UP, there is no seqcount_t protection, a reader allowing interrupts could
      29                 :            :  *     read partial values)
      30                 :            :  *
      31                 :            :  * 7) For irq and softirq uses, readers can use u64_stats_fetch_begin_irq() and
      32                 :            :  *    u64_stats_fetch_retry_irq() helpers
      33                 :            :  *
      34                 :            :  * Usage :
      35                 :            :  *
      36                 :            :  * Stats producer (writer) should use following template granted it already got
      37                 :            :  * an exclusive access to counters (a lock is already taken, or per cpu
      38                 :            :  * data is used [in a non preemptable context])
      39                 :            :  *
      40                 :            :  *   spin_lock_bh(...) or other synchronization to get exclusive access
      41                 :            :  *   ...
      42                 :            :  *   u64_stats_update_begin(&stats->syncp);
      43                 :            :  *   u64_stats_add(&stats->bytes64, len); // non atomic operation
      44                 :            :  *   u64_stats_inc(&stats->packets64);    // non atomic operation
      45                 :            :  *   u64_stats_update_end(&stats->syncp);
      46                 :            :  *
      47                 :            :  * While a consumer (reader) should use following template to get consistent
      48                 :            :  * snapshot for each variable (but no guarantee on several ones)
      49                 :            :  *
      50                 :            :  * u64 tbytes, tpackets;
      51                 :            :  * unsigned int start;
      52                 :            :  *
      53                 :            :  * do {
      54                 :            :  *         start = u64_stats_fetch_begin(&stats->syncp);
      55                 :            :  *         tbytes = u64_stats_read(&stats->bytes64); // non atomic operation
      56                 :            :  *         tpackets = u64_stats_read(&stats->packets64); // non atomic operation
      57                 :            :  * } while (u64_stats_fetch_retry(&stats->syncp, start));
      58                 :            :  *
      59                 :            :  *
      60                 :            :  * Example of use in drivers/net/loopback.c, using per_cpu containers,
      61                 :            :  * in BH disabled context.
      62                 :            :  */
      63                 :            : #include <linux/seqlock.h>
      64                 :            : 
      65                 :            : struct u64_stats_sync {
      66                 :            : #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
      67                 :            :         seqcount_t      seq;
      68                 :            : #endif
      69                 :            : };
      70                 :            : 
      71                 :            : #if BITS_PER_LONG == 64
      72                 :            : #include <asm/local64.h>
      73                 :            : 
      74                 :            : typedef struct {
      75                 :            :         local64_t       v;
      76                 :            : } u64_stats_t ;
      77                 :            : 
      78                 :        392 : static inline u64 u64_stats_read(const u64_stats_t *p)
      79                 :            : {
      80                 :        196 :         return local64_read(&p->v);
      81                 :            : }
      82                 :            : 
      83                 :          0 : static inline void u64_stats_add(u64_stats_t *p, unsigned long val)
      84                 :            : {
      85                 :          0 :         local64_add(val, &p->v);
      86                 :            : }
      87                 :            : 
      88                 :          0 : static inline void u64_stats_inc(u64_stats_t *p)
      89                 :            : {
      90                 :          0 :         local64_inc(&p->v);
      91                 :          0 : }
      92                 :            : 
      93                 :            : #else
      94                 :            : 
      95                 :            : typedef struct {
      96                 :            :         u64             v;
      97                 :            : } u64_stats_t;
      98                 :            : 
      99                 :            : static inline u64 u64_stats_read(const u64_stats_t *p)
     100                 :            : {
     101                 :            :         return p->v;
     102                 :            : }
     103                 :            : 
     104                 :            : static inline void u64_stats_add(u64_stats_t *p, unsigned long val)
     105                 :            : {
     106                 :            :         p->v += val;
     107                 :            : }
     108                 :            : 
     109                 :            : static inline void u64_stats_inc(u64_stats_t *p)
     110                 :            : {
     111                 :            :         p->v++;
     112                 :            : }
     113                 :            : #endif
     114                 :            : 
     115                 :       2660 : static inline void u64_stats_init(struct u64_stats_sync *syncp)
     116                 :            : {
     117                 :            : #if BITS_PER_LONG == 32 && defined(CONFIG_SMP)
     118                 :            :         seqcount_init(&syncp->seq);
     119                 :            : #endif
     120         [ #  # ]:          0 : }
     121                 :            : 
     122                 :     314245 : static inline void u64_stats_update_begin(struct u64_stats_sync *syncp)
     123                 :            : {
     124                 :            : #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
     125                 :            :         write_seqcount_begin(&syncp->seq);
     126                 :            : #endif
     127   [ +  +  -  #  :     314245 : }
          #  #  #  #  #  
                      # ]
     128                 :            : 
     129                 :     314245 : static inline void u64_stats_update_end(struct u64_stats_sync *syncp)
     130                 :            : {
     131                 :            : #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
     132                 :            :         write_seqcount_end(&syncp->seq);
     133                 :            : #endif
     134   [ #  #  #  #  :     314245 : }
                   #  # ]
     135                 :            : 
     136                 :            : static inline unsigned long
     137                 :            : u64_stats_update_begin_irqsave(struct u64_stats_sync *syncp)
     138                 :            : {
     139                 :            :         unsigned long flags = 0;
     140                 :            : 
     141                 :            : #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
     142                 :            :         local_irq_save(flags);
     143                 :            :         write_seqcount_begin(&syncp->seq);
     144                 :            : #endif
     145                 :            :         return flags;
     146                 :            : }
     147                 :            : 
     148                 :            : static inline void
     149                 :            : u64_stats_update_end_irqrestore(struct u64_stats_sync *syncp,
     150                 :            :                                 unsigned long flags)
     151                 :            : {
     152                 :            : #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
     153                 :            :         write_seqcount_end(&syncp->seq);
     154                 :            :         local_irq_restore(flags);
     155                 :            : #endif
     156                 :            : }
     157                 :            : 
     158                 :        756 : static inline unsigned int __u64_stats_fetch_begin(const struct u64_stats_sync *syncp)
     159                 :            : {
     160                 :            : #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
     161                 :            :         return read_seqcount_begin(&syncp->seq);
     162                 :            : #else
     163         [ +  - ]:        756 :         return 0;
     164                 :            : #endif
     165                 :            : }
     166                 :            : 
     167                 :          0 : static inline unsigned int u64_stats_fetch_begin(const struct u64_stats_sync *syncp)
     168                 :            : {
     169                 :            : #if BITS_PER_LONG==32 && !defined(CONFIG_SMP)
     170                 :            :         preempt_disable();
     171                 :            : #endif
     172         [ #  # ]:          0 :         return __u64_stats_fetch_begin(syncp);
     173                 :            : }
     174                 :            : 
     175                 :        756 : static inline bool __u64_stats_fetch_retry(const struct u64_stats_sync *syncp,
     176                 :            :                                          unsigned int start)
     177                 :            : {
     178                 :            : #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
     179                 :            :         return read_seqcount_retry(&syncp->seq, start);
     180                 :            : #else
     181         [ +  - ]:        756 :         return false;
     182                 :            : #endif
     183                 :            : }
     184                 :            : 
     185                 :          0 : static inline bool u64_stats_fetch_retry(const struct u64_stats_sync *syncp,
     186                 :            :                                          unsigned int start)
     187                 :            : {
     188                 :            : #if BITS_PER_LONG==32 && !defined(CONFIG_SMP)
     189                 :            :         preempt_enable();
     190                 :            : #endif
     191         [ #  # ]:          0 :         return __u64_stats_fetch_retry(syncp, start);
     192                 :            : }
     193                 :            : 
     194                 :            : /*
     195                 :            :  * In case irq handlers can update u64 counters, readers can use following helpers
     196                 :            :  * - SMP 32bit arches use seqcount protection, irq safe.
     197                 :            :  * - UP 32bit must disable irqs.
     198                 :            :  * - 64bit have no problem atomically reading u64 values, irq safe.
     199                 :            :  */
     200                 :        364 : static inline unsigned int u64_stats_fetch_begin_irq(const struct u64_stats_sync *syncp)
     201                 :            : {
     202                 :            : #if BITS_PER_LONG==32 && !defined(CONFIG_SMP)
     203                 :            :         local_irq_disable();
     204                 :            : #endif
     205         [ #  # ]:        364 :         return __u64_stats_fetch_begin(syncp);
     206                 :            : }
     207                 :            : 
     208                 :        364 : static inline bool u64_stats_fetch_retry_irq(const struct u64_stats_sync *syncp,
     209                 :            :                                              unsigned int start)
     210                 :            : {
     211                 :            : #if BITS_PER_LONG==32 && !defined(CONFIG_SMP)
     212                 :            :         local_irq_enable();
     213                 :            : #endif
     214         [ #  # ]:        364 :         return __u64_stats_fetch_retry(syncp, start);
     215                 :            : }
     216                 :            : 
     217                 :            : #endif /* _LINUX_U64_STATS_SYNC_H */

Generated by: LCOV version 1.14