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

Configure Feed

Select the types of activity you want to include in your feed.

locking/seqlock: Support Clang's context analysis

Add support for Clang's context analysis for seqlock_t.

Signed-off-by: Marco Elver <elver@google.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/20251219154418.3592607-12-elver@google.com

authored by

Marco Elver and committed by
Peter Zijlstra
8f8a55f4 370f0a34

+91 -4
+1 -1
Documentation/dev-tools/context-analysis.rst
··· 79 79 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 80 80 81 81 Currently the following synchronization primitives are supported: 82 - `raw_spinlock_t`, `spinlock_t`, `rwlock_t`, `mutex`. 82 + `raw_spinlock_t`, `spinlock_t`, `rwlock_t`, `mutex`, `seqlock_t`. 83 83 84 84 For context locks with an initialization function (e.g., `spin_lock_init()`), 85 85 calling this function before initializing any guarded members or globals
+37 -1
include/linux/seqlock.h
··· 816 816 do { \ 817 817 spin_lock_init(&(sl)->lock); \ 818 818 seqcount_spinlock_init(&(sl)->seqcount, &(sl)->lock); \ 819 + __assume_ctx_lock(sl); \ 819 820 } while (0) 820 821 821 822 /** ··· 833 832 * Return: count, to be passed to read_seqretry() 834 833 */ 835 834 static inline unsigned read_seqbegin(const seqlock_t *sl) 835 + __acquires_shared(sl) __no_context_analysis 836 836 { 837 837 return read_seqcount_begin(&sl->seqcount); 838 838 } ··· 850 848 * Return: true if a read section retry is required, else false 851 849 */ 852 850 static inline unsigned read_seqretry(const seqlock_t *sl, unsigned start) 851 + __releases_shared(sl) __no_context_analysis 853 852 { 854 853 return read_seqcount_retry(&sl->seqcount, start); 855 854 } ··· 875 872 * _irqsave or _bh variants of this function instead. 876 873 */ 877 874 static inline void write_seqlock(seqlock_t *sl) 875 + __acquires(sl) __no_context_analysis 878 876 { 879 877 spin_lock(&sl->lock); 880 878 do_write_seqcount_begin(&sl->seqcount.seqcount); ··· 889 885 * critical section of given seqlock_t. 890 886 */ 891 887 static inline void write_sequnlock(seqlock_t *sl) 888 + __releases(sl) __no_context_analysis 892 889 { 893 890 do_write_seqcount_end(&sl->seqcount.seqcount); 894 891 spin_unlock(&sl->lock); ··· 903 898 * other write side sections, can be invoked from softirq contexts. 904 899 */ 905 900 static inline void write_seqlock_bh(seqlock_t *sl) 901 + __acquires(sl) __no_context_analysis 906 902 { 907 903 spin_lock_bh(&sl->lock); 908 904 do_write_seqcount_begin(&sl->seqcount.seqcount); ··· 918 912 * write_seqlock_bh(). 919 913 */ 920 914 static inline void write_sequnlock_bh(seqlock_t *sl) 915 + __releases(sl) __no_context_analysis 921 916 { 922 917 do_write_seqcount_end(&sl->seqcount.seqcount); 923 918 spin_unlock_bh(&sl->lock); ··· 932 925 * other write sections, can be invoked from hardirq contexts. 933 926 */ 934 927 static inline void write_seqlock_irq(seqlock_t *sl) 928 + __acquires(sl) __no_context_analysis 935 929 { 936 930 spin_lock_irq(&sl->lock); 937 931 do_write_seqcount_begin(&sl->seqcount.seqcount); ··· 946 938 * seqlock_t write side section opened with write_seqlock_irq(). 947 939 */ 948 940 static inline void write_sequnlock_irq(seqlock_t *sl) 941 + __releases(sl) __no_context_analysis 949 942 { 950 943 do_write_seqcount_end(&sl->seqcount.seqcount); 951 944 spin_unlock_irq(&sl->lock); 952 945 } 953 946 954 947 static inline unsigned long __write_seqlock_irqsave(seqlock_t *sl) 948 + __acquires(sl) __no_context_analysis 955 949 { 956 950 unsigned long flags; 957 951 ··· 986 976 */ 987 977 static inline void 988 978 write_sequnlock_irqrestore(seqlock_t *sl, unsigned long flags) 979 + __releases(sl) __no_context_analysis 989 980 { 990 981 do_write_seqcount_end(&sl->seqcount.seqcount); 991 982 spin_unlock_irqrestore(&sl->lock, flags); ··· 1009 998 * The opened read section must be closed with read_sequnlock_excl(). 1010 999 */ 1011 1000 static inline void read_seqlock_excl(seqlock_t *sl) 1001 + __acquires_shared(sl) __no_context_analysis 1012 1002 { 1013 1003 spin_lock(&sl->lock); 1014 1004 } ··· 1019 1007 * @sl: Pointer to seqlock_t 1020 1008 */ 1021 1009 static inline void read_sequnlock_excl(seqlock_t *sl) 1010 + __releases_shared(sl) __no_context_analysis 1022 1011 { 1023 1012 spin_unlock(&sl->lock); 1024 1013 } ··· 1034 1021 * from softirq contexts. 1035 1022 */ 1036 1023 static inline void read_seqlock_excl_bh(seqlock_t *sl) 1024 + __acquires_shared(sl) __no_context_analysis 1037 1025 { 1038 1026 spin_lock_bh(&sl->lock); 1039 1027 } ··· 1045 1031 * @sl: Pointer to seqlock_t 1046 1032 */ 1047 1033 static inline void read_sequnlock_excl_bh(seqlock_t *sl) 1034 + __releases_shared(sl) __no_context_analysis 1048 1035 { 1049 1036 spin_unlock_bh(&sl->lock); 1050 1037 } ··· 1060 1045 * hardirq context. 1061 1046 */ 1062 1047 static inline void read_seqlock_excl_irq(seqlock_t *sl) 1048 + __acquires_shared(sl) __no_context_analysis 1063 1049 { 1064 1050 spin_lock_irq(&sl->lock); 1065 1051 } ··· 1071 1055 * @sl: Pointer to seqlock_t 1072 1056 */ 1073 1057 static inline void read_sequnlock_excl_irq(seqlock_t *sl) 1058 + __releases_shared(sl) __no_context_analysis 1074 1059 { 1075 1060 spin_unlock_irq(&sl->lock); 1076 1061 } 1077 1062 1078 1063 static inline unsigned long __read_seqlock_excl_irqsave(seqlock_t *sl) 1064 + __acquires_shared(sl) __no_context_analysis 1079 1065 { 1080 1066 unsigned long flags; 1081 1067 ··· 1107 1089 */ 1108 1090 static inline void 1109 1091 read_sequnlock_excl_irqrestore(seqlock_t *sl, unsigned long flags) 1092 + __releases_shared(sl) __no_context_analysis 1110 1093 { 1111 1094 spin_unlock_irqrestore(&sl->lock, flags); 1112 1095 } ··· 1144 1125 * parameter of the next read_seqbegin_or_lock() iteration. 1145 1126 */ 1146 1127 static inline void read_seqbegin_or_lock(seqlock_t *lock, int *seq) 1128 + __acquires_shared(lock) __no_context_analysis 1147 1129 { 1148 1130 if (!(*seq & 1)) /* Even */ 1149 1131 *seq = read_seqbegin(lock); ··· 1160 1140 * Return: true if a read section retry is required, false otherwise 1161 1141 */ 1162 1142 static inline int need_seqretry(seqlock_t *lock, int seq) 1143 + __releases_shared(lock) __no_context_analysis 1163 1144 { 1164 1145 return !(seq & 1) && read_seqretry(lock, seq); 1165 1146 } ··· 1174 1153 * with read_seqbegin_or_lock() and validated by need_seqretry(). 1175 1154 */ 1176 1155 static inline void done_seqretry(seqlock_t *lock, int seq) 1156 + __no_context_analysis 1177 1157 { 1178 1158 if (seq & 1) 1179 1159 read_sequnlock_excl(lock); ··· 1202 1180 */ 1203 1181 static inline unsigned long 1204 1182 read_seqbegin_or_lock_irqsave(seqlock_t *lock, int *seq) 1183 + __acquires_shared(lock) __no_context_analysis 1205 1184 { 1206 1185 unsigned long flags = 0; 1207 1186 ··· 1228 1205 */ 1229 1206 static inline void 1230 1207 done_seqretry_irqrestore(seqlock_t *lock, int seq, unsigned long flags) 1208 + __no_context_analysis 1231 1209 { 1232 1210 if (seq & 1) 1233 1211 read_sequnlock_excl_irqrestore(lock, flags); ··· 1249 1225 }; 1250 1226 1251 1227 static __always_inline void __scoped_seqlock_cleanup(struct ss_tmp *sst) 1228 + __no_context_analysis 1252 1229 { 1253 1230 if (sst->lock) 1254 1231 spin_unlock(sst->lock); ··· 1279 1254 1280 1255 static __always_inline void 1281 1256 __scoped_seqlock_next(struct ss_tmp *sst, seqlock_t *lock, enum ss_state target) 1257 + __no_context_analysis 1282 1258 { 1283 1259 switch (sst->state) { 1284 1260 case ss_done: ··· 1322 1296 } 1323 1297 } 1324 1298 1299 + /* 1300 + * Context analysis no-op helper to release seqlock at the end of the for-scope; 1301 + * the alias analysis of the compiler will recognize that the pointer @s is an 1302 + * alias to @_seqlock passed to read_seqbegin(_seqlock) below. 1303 + */ 1304 + static __always_inline void __scoped_seqlock_cleanup_ctx(struct ss_tmp **s) 1305 + __releases_shared(*((seqlock_t **)s)) __no_context_analysis {} 1306 + 1325 1307 #define __scoped_seqlock_read(_seqlock, _target, _s) \ 1326 1308 for (struct ss_tmp _s __cleanup(__scoped_seqlock_cleanup) = \ 1327 - { .state = ss_lockless, .data = read_seqbegin(_seqlock) }; \ 1309 + { .state = ss_lockless, .data = read_seqbegin(_seqlock) }, \ 1310 + *__UNIQUE_ID(ctx) __cleanup(__scoped_seqlock_cleanup_ctx) =\ 1311 + (struct ss_tmp *)_seqlock; \ 1328 1312 _s.state != ss_done; \ 1329 1313 __scoped_seqlock_next(&_s, _seqlock, _target)) 1330 1314
+3 -2
include/linux/seqlock_types.h
··· 81 81 * - Comments on top of seqcount_t 82 82 * - Documentation/locking/seqlock.rst 83 83 */ 84 - typedef struct { 84 + context_lock_struct(seqlock) { 85 85 /* 86 86 * Make sure that readers don't starve writers on PREEMPT_RT: use 87 87 * seqcount_spinlock_t instead of seqcount_t. Check __SEQ_LOCK(). 88 88 */ 89 89 seqcount_spinlock_t seqcount; 90 90 spinlock_t lock; 91 - } seqlock_t; 91 + }; 92 + typedef struct seqlock seqlock_t; 92 93 93 94 #endif /* __LINUX_SEQLOCK_TYPES_H */
+50
lib/test_context-analysis.c
··· 6 6 7 7 #include <linux/build_bug.h> 8 8 #include <linux/mutex.h> 9 + #include <linux/seqlock.h> 9 10 #include <linux/spinlock.h> 10 11 11 12 /* ··· 207 206 } 208 207 scoped_cond_guard(mutex_intr, return, &d->mtx) { 209 208 d->counter++; 209 + } 210 + } 211 + 212 + struct test_seqlock_data { 213 + seqlock_t sl; 214 + int counter __guarded_by(&sl); 215 + }; 216 + 217 + static void __used test_seqlock_init(struct test_seqlock_data *d) 218 + { 219 + seqlock_init(&d->sl); 220 + d->counter = 0; 221 + } 222 + 223 + static void __used test_seqlock_reader(struct test_seqlock_data *d) 224 + { 225 + unsigned int seq; 226 + 227 + do { 228 + seq = read_seqbegin(&d->sl); 229 + (void)d->counter; 230 + } while (read_seqretry(&d->sl, seq)); 231 + } 232 + 233 + static void __used test_seqlock_writer(struct test_seqlock_data *d) 234 + { 235 + unsigned long flags; 236 + 237 + write_seqlock(&d->sl); 238 + d->counter++; 239 + write_sequnlock(&d->sl); 240 + 241 + write_seqlock_irq(&d->sl); 242 + d->counter++; 243 + write_sequnlock_irq(&d->sl); 244 + 245 + write_seqlock_bh(&d->sl); 246 + d->counter++; 247 + write_sequnlock_bh(&d->sl); 248 + 249 + write_seqlock_irqsave(&d->sl, flags); 250 + d->counter++; 251 + write_sequnlock_irqrestore(&d->sl, flags); 252 + } 253 + 254 + static void __used test_seqlock_scoped(struct test_seqlock_data *d) 255 + { 256 + scoped_seqlock_read (&d->sl, ss_lockless) { 257 + (void)d->counter; 210 258 } 211 259 }