Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kernel: optimise seqlock

Add branch annotations for seqlock read fastpath, and introduce
__read_seqcount_begin and __read_seqcount_end functions, that can avoid the
smp_rmb() if used carefully. These will be used by store-free path walking
algorithm performance is critical and seqlocks are in use.

Signed-off-by: Nick Piggin <npiggin@kernel.dk>

+73 -7
+73 -7
include/linux/seqlock.h
··· 107 107 { 108 108 smp_rmb(); 109 109 110 - return (sl->sequence != start); 110 + return unlikely(sl->sequence != start); 111 111 } 112 112 113 113 ··· 125 125 #define SEQCNT_ZERO { 0 } 126 126 #define seqcount_init(x) do { *(x) = (seqcount_t) SEQCNT_ZERO; } while (0) 127 127 128 - /* Start of read using pointer to a sequence counter only. */ 129 - static inline unsigned read_seqcount_begin(const seqcount_t *s) 128 + /** 129 + * __read_seqcount_begin - begin a seq-read critical section (without barrier) 130 + * @s: pointer to seqcount_t 131 + * Returns: count to be passed to read_seqcount_retry 132 + * 133 + * __read_seqcount_begin is like read_seqcount_begin, but has no smp_rmb() 134 + * barrier. Callers should ensure that smp_rmb() or equivalent ordering is 135 + * provided before actually loading any of the variables that are to be 136 + * protected in this critical section. 137 + * 138 + * Use carefully, only in critical code, and comment how the barrier is 139 + * provided. 140 + */ 141 + static inline unsigned __read_seqcount_begin(const seqcount_t *s) 130 142 { 131 143 unsigned ret; 132 144 133 145 repeat: 134 146 ret = s->sequence; 135 - smp_rmb(); 136 147 if (unlikely(ret & 1)) { 137 148 cpu_relax(); 138 149 goto repeat; ··· 151 140 return ret; 152 141 } 153 142 154 - /* 155 - * Test if reader processed invalid data because sequence number has changed. 143 + /** 144 + * read_seqcount_begin - begin a seq-read critical section 145 + * @s: pointer to seqcount_t 146 + * Returns: count to be passed to read_seqcount_retry 147 + * 148 + * read_seqcount_begin opens a read critical section of the given seqcount. 149 + * Validity of the critical section is tested by checking read_seqcount_retry 150 + * function. 151 + */ 152 + static inline unsigned read_seqcount_begin(const seqcount_t *s) 153 + { 154 + unsigned ret = __read_seqcount_begin(s); 155 + smp_rmb(); 156 + return ret; 157 + } 158 + 159 + /** 160 + * __read_seqcount_retry - end a seq-read critical section (without barrier) 161 + * @s: pointer to seqcount_t 162 + * @start: count, from read_seqcount_begin 163 + * Returns: 1 if retry is required, else 0 164 + * 165 + * __read_seqcount_retry is like read_seqcount_retry, but has no smp_rmb() 166 + * barrier. Callers should ensure that smp_rmb() or equivalent ordering is 167 + * provided before actually loading any of the variables that are to be 168 + * protected in this critical section. 169 + * 170 + * Use carefully, only in critical code, and comment how the barrier is 171 + * provided. 172 + */ 173 + static inline int __read_seqcount_retry(const seqcount_t *s, unsigned start) 174 + { 175 + return unlikely(s->sequence != start); 176 + } 177 + 178 + /** 179 + * read_seqcount_retry - end a seq-read critical section 180 + * @s: pointer to seqcount_t 181 + * @start: count, from read_seqcount_begin 182 + * Returns: 1 if retry is required, else 0 183 + * 184 + * read_seqcount_retry closes a read critical section of the given seqcount. 185 + * If the critical section was invalid, it must be ignored (and typically 186 + * retried). 156 187 */ 157 188 static inline int read_seqcount_retry(const seqcount_t *s, unsigned start) 158 189 { 159 190 smp_rmb(); 160 191 161 - return s->sequence != start; 192 + return __read_seqcount_retry(s, start); 162 193 } 163 194 164 195 ··· 218 165 { 219 166 smp_wmb(); 220 167 s->sequence++; 168 + } 169 + 170 + /** 171 + * write_seqcount_barrier - invalidate in-progress read-side seq operations 172 + * @s: pointer to seqcount_t 173 + * 174 + * After write_seqcount_barrier, no read-side seq operations will complete 175 + * successfully and see data older than this. 176 + */ 177 + static inline void write_seqcount_barrier(seqcount_t *s) 178 + { 179 + smp_wmb(); 180 + s->sequence+=2; 221 181 } 222 182 223 183 /*