Blame view

include/linux/u64_stats_sync.h 4.17 KB
16b8a4761   Eric Dumazet   net: Introduce u6...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  #ifndef _LINUX_U64_STATS_SYNC_H
  #define _LINUX_U64_STATS_SYNC_H
  
  /*
   * To properly implement 64bits network statistics on 32bit and 64bit hosts,
   * we provide a synchronization point, that is a noop on 64bit or UP kernels.
   *
   * Key points :
   * 1) Use a seqcount on SMP 32bits, with low overhead.
   * 2) Whole thing is a noop on 64bit arches or UP kernels.
   * 3) Write side must ensure mutual exclusion or one seqcount update could
   *    be lost, thus blocking readers forever.
   *    If this synchronization point is not a mutex, but a spinlock or
   *    spinlock_bh() or disable_bh() :
   * 3.1) Write side should not sleep.
   * 3.2) Write side should not allow preemption.
   * 3.3) If applicable, interrupts should be disabled.
   *
   * 4) If reader fetches several counters, there is no guarantee the whole values
   *    are consistent (remember point 1) : this is a noop on 64bit arches anyway)
   *
   * 5) readers are allowed to sleep or be preempted/interrupted : They perform
   *    pure reads. But if they have to fetch many values, it's better to not allow
   *    preemptions/interruptions to avoid many retries.
   *
b6b3ecc71   Eric Dumazet   net: u64_stats_sy...
26
27
28
29
   * 6) If counter might be written by an interrupt, readers should block interrupts.
   *    (On UP, there is no seqcount_t protection, a reader allowing interrupts could
   *     read partial values)
   *
33d91f00c   Eric Dumazet   net: u64_stats_fe...
30
31
32
   * 7) For softirq uses, readers can use u64_stats_fetch_begin_bh() and
   *    u64_stats_fetch_retry_bh() helpers
   *
16b8a4761   Eric Dumazet   net: Introduce u6...
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
   * Usage :
   *
   * Stats producer (writer) should use following template granted it already got
   * an exclusive access to counters (a lock is already taken, or per cpu
   * data is used [in a non preemptable context])
   *
   *   spin_lock_bh(...) or other synchronization to get exclusive access
   *   ...
   *   u64_stats_update_begin(&stats->syncp);
   *   stats->bytes64 += len; // non atomic operation
   *   stats->packets64++;    // non atomic operation
   *   u64_stats_update_end(&stats->syncp);
   *
   * While a consumer (reader) should use following template to get consistent
   * snapshot for each variable (but no guarantee on several ones)
   *
   * u64 tbytes, tpackets;
   * unsigned int start;
   *
   * do {
   *         start = u64_stats_fetch_begin(&stats->syncp);
   *         tbytes = stats->bytes64; // non atomic operation
   *         tpackets = stats->packets64; // non atomic operation
b6b3ecc71   Eric Dumazet   net: u64_stats_sy...
56
   * } while (u64_stats_fetch_retry(&stats->syncp, start));
16b8a4761   Eric Dumazet   net: Introduce u6...
57
58
59
60
61
62
   *
   *
   * Example of use in drivers/net/loopback.c, using per_cpu containers,
   * in BH disabled context.
   */
  #include <linux/seqlock.h>
16b8a4761   Eric Dumazet   net: Introduce u6...
63
  struct u64_stats_sync {
33d91f00c   Eric Dumazet   net: u64_stats_fe...
64
  #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
16b8a4761   Eric Dumazet   net: Introduce u6...
65
  	seqcount_t	seq;
33d91f00c   Eric Dumazet   net: u64_stats_fe...
66
  #endif
16b8a4761   Eric Dumazet   net: Introduce u6...
67
  };
fa9f90be7   Jesper Juhl   Kill off a bunch ...
68
  static inline void u64_stats_update_begin(struct u64_stats_sync *syncp)
16b8a4761   Eric Dumazet   net: Introduce u6...
69
  {
33d91f00c   Eric Dumazet   net: u64_stats_fe...
70
  #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
16b8a4761   Eric Dumazet   net: Introduce u6...
71
  	write_seqcount_begin(&syncp->seq);
33d91f00c   Eric Dumazet   net: u64_stats_fe...
72
  #endif
16b8a4761   Eric Dumazet   net: Introduce u6...
73
  }
fa9f90be7   Jesper Juhl   Kill off a bunch ...
74
  static inline void u64_stats_update_end(struct u64_stats_sync *syncp)
16b8a4761   Eric Dumazet   net: Introduce u6...
75
  {
33d91f00c   Eric Dumazet   net: u64_stats_fe...
76
  #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
16b8a4761   Eric Dumazet   net: Introduce u6...
77
  	write_seqcount_end(&syncp->seq);
33d91f00c   Eric Dumazet   net: u64_stats_fe...
78
  #endif
16b8a4761   Eric Dumazet   net: Introduce u6...
79
  }
fa9f90be7   Jesper Juhl   Kill off a bunch ...
80
  static inline unsigned int u64_stats_fetch_begin(const struct u64_stats_sync *syncp)
16b8a4761   Eric Dumazet   net: Introduce u6...
81
  {
33d91f00c   Eric Dumazet   net: u64_stats_fe...
82
  #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
16b8a4761   Eric Dumazet   net: Introduce u6...
83
  	return read_seqcount_begin(&syncp->seq);
33d91f00c   Eric Dumazet   net: u64_stats_fe...
84
85
86
87
88
89
  #else
  #if BITS_PER_LONG==32
  	preempt_disable();
  #endif
  	return 0;
  #endif
16b8a4761   Eric Dumazet   net: Introduce u6...
90
  }
fa9f90be7   Jesper Juhl   Kill off a bunch ...
91
  static inline bool u64_stats_fetch_retry(const struct u64_stats_sync *syncp,
16b8a4761   Eric Dumazet   net: Introduce u6...
92
93
  					 unsigned int start)
  {
33d91f00c   Eric Dumazet   net: u64_stats_fe...
94
  #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
16b8a4761   Eric Dumazet   net: Introduce u6...
95
  	return read_seqcount_retry(&syncp->seq, start);
16b8a4761   Eric Dumazet   net: Introduce u6...
96
  #else
33d91f00c   Eric Dumazet   net: u64_stats_fe...
97
98
99
100
101
  #if BITS_PER_LONG==32
  	preempt_enable();
  #endif
  	return false;
  #endif
16b8a4761   Eric Dumazet   net: Introduce u6...
102
  }
33d91f00c   Eric Dumazet   net: u64_stats_fe...
103
104
105
106
107
108
  /*
   * In case softirq handlers can update u64 counters, readers can use following helpers
   * - SMP 32bit arches use seqcount protection, irq safe.
   * - UP 32bit must disable BH.
   * - 64bit have no problem atomically reading u64 values, irq safe.
   */
fa9f90be7   Jesper Juhl   Kill off a bunch ...
109
  static inline unsigned int u64_stats_fetch_begin_bh(const struct u64_stats_sync *syncp)
16b8a4761   Eric Dumazet   net: Introduce u6...
110
  {
33d91f00c   Eric Dumazet   net: u64_stats_fe...
111
112
113
114
115
116
  #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
  	return read_seqcount_begin(&syncp->seq);
  #else
  #if BITS_PER_LONG==32
  	local_bh_disable();
  #endif
16b8a4761   Eric Dumazet   net: Introduce u6...
117
  	return 0;
33d91f00c   Eric Dumazet   net: u64_stats_fe...
118
  #endif
16b8a4761   Eric Dumazet   net: Introduce u6...
119
  }
fa9f90be7   Jesper Juhl   Kill off a bunch ...
120
  static inline bool u64_stats_fetch_retry_bh(const struct u64_stats_sync *syncp,
16b8a4761   Eric Dumazet   net: Introduce u6...
121
122
  					 unsigned int start)
  {
33d91f00c   Eric Dumazet   net: u64_stats_fe...
123
124
125
126
127
128
  #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
  	return read_seqcount_retry(&syncp->seq, start);
  #else
  #if BITS_PER_LONG==32
  	local_bh_enable();
  #endif
16b8a4761   Eric Dumazet   net: Introduce u6...
129
  	return false;
16b8a4761   Eric Dumazet   net: Introduce u6...
130
  #endif
33d91f00c   Eric Dumazet   net: u64_stats_fe...
131
  }
16b8a4761   Eric Dumazet   net: Introduce u6...
132
133
  
  #endif /* _LINUX_U64_STATS_SYNC_H */