Skip to content

Commit d084a73

Browse files
melverPeter Zijlstra
authored andcommitted
compiler-context-analysis: Introduce scoped init guards
Add scoped init guard definitions for common synchronization primitives supported by context analysis. The scoped init guards treat the context as active within initialization scope of the underlying context lock, given initialization implies exclusive access to the underlying object. This allows initialization of guarded members without disabling context analysis, while documenting initialization from subsequent usage. The documentation is updated with the new recommendation. Where scoped init guards are not provided or cannot be implemented (ww_mutex omitted for lack of multi-arg guard initializers), the alternative is to just disable context analysis where guarded members are initialized. Suggested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Marco Elver <elver@google.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lore.kernel.org/all/20251212095943.GM3911114@noisy.programming.kicks-ass.net/ Link: https://patch.msgid.link/20260119094029.1344361-3-elver@google.com
1 parent 3b9ed30 commit d084a73

9 files changed

Lines changed: 70 additions & 18 deletions

File tree

Documentation/dev-tools/context-analysis.rst

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,33 @@ Currently the following synchronization primitives are supported:
8383
`bit_spinlock`, RCU, SRCU (`srcu_struct`), `rw_semaphore`, `local_lock_t`,
8484
`ww_mutex`.
8585

86-
For context locks with an initialization function (e.g., `spin_lock_init()`),
87-
calling this function before initializing any guarded members or globals
88-
prevents the compiler from issuing warnings about unguarded initialization.
86+
To initialize variables guarded by a context lock with an initialization
87+
function (``type_init(&lock)``), prefer using ``guard(type_init)(&lock)`` or
88+
``scoped_guard(type_init, &lock) { ... }`` to initialize such guarded members
89+
or globals in the enclosing scope. This initializes the context lock and treats
90+
the context as active within the initialization scope (initialization implies
91+
exclusive access to the underlying object).
92+
93+
For example::
94+
95+
struct my_data {
96+
spinlock_t lock;
97+
int counter __guarded_by(&lock);
98+
};
99+
100+
void init_my_data(struct my_data *d)
101+
{
102+
...
103+
guard(spinlock_init)(&d->lock);
104+
d->counter = 0;
105+
...
106+
}
107+
108+
Alternatively, initializing guarded variables can be done with context analysis
109+
disabled, preferably in the smallest possible scope (due to lack of any other
110+
checking): either with a ``context_unsafe(var = init)`` expression, or by
111+
marking small initialization functions with the ``__context_unsafe(init)``
112+
attribute.
89113

90114
Lockdep assertions, such as `lockdep_assert_held()`, inform the compiler's
91115
context analysis that the associated synchronization primitive is held after

include/linux/compiler-context-analysis.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,8 @@
3232
/*
3333
* The "assert_capability" attribute is a bit confusingly named. It does not
3434
* generate a check. Instead, it tells the analysis to *assume* the capability
35-
* is held. This is used for:
36-
*
37-
* 1. Augmenting runtime assertions, that can then help with patterns beyond the
38-
* compiler's static reasoning abilities.
39-
*
40-
* 2. Initialization of context locks, so we can access guarded variables right
41-
* after initialization (nothing else should access the same object yet).
35+
* is held. This is used for augmenting runtime assertions, that can then help
36+
* with patterns beyond the compiler's static reasoning abilities.
4237
*/
4338
# define __assumes_ctx_lock(...) __attribute__((assert_capability(__VA_ARGS__)))
4439
# define __assumes_shared_ctx_lock(...) __attribute__((assert_shared_capability(__VA_ARGS__)))

include/linux/local_lock.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ DEFINE_LOCK_GUARD_1(local_lock_nested_bh, local_lock_t __percpu,
104104
local_lock_nested_bh(_T->lock),
105105
local_unlock_nested_bh(_T->lock))
106106

107+
DEFINE_LOCK_GUARD_1(local_lock_init, local_lock_t, local_lock_init(_T->lock), /* */)
108+
107109
DECLARE_LOCK_GUARD_1_ATTRS(local_lock, __acquires(_T), __releases(*(local_lock_t __percpu **)_T))
108110
#define class_local_lock_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(local_lock, _T)
109111
DECLARE_LOCK_GUARD_1_ATTRS(local_lock_irq, __acquires(_T), __releases(*(local_lock_t __percpu **)_T))
@@ -112,5 +114,11 @@ DECLARE_LOCK_GUARD_1_ATTRS(local_lock_irqsave, __acquires(_T), __releases(*(loca
112114
#define class_local_lock_irqsave_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(local_lock_irqsave, _T)
113115
DECLARE_LOCK_GUARD_1_ATTRS(local_lock_nested_bh, __acquires(_T), __releases(*(local_lock_t __percpu **)_T))
114116
#define class_local_lock_nested_bh_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(local_lock_nested_bh, _T)
117+
DECLARE_LOCK_GUARD_1_ATTRS(local_lock_init, __acquires(_T), __releases(*(local_lock_t **)_T))
118+
#define class_local_lock_init_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(local_lock_init, _T)
119+
120+
DEFINE_LOCK_GUARD_1(local_trylock_init, local_trylock_t, local_trylock_init(_T->lock), /* */)
121+
DECLARE_LOCK_GUARD_1_ATTRS(local_trylock_init, __acquires(_T), __releases(*(local_trylock_t **)_T))
122+
#define class_local_trylock_init_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(local_trylock_init, _T)
115123

116124
#endif

include/linux/local_lock_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/percpu-defs.h>
77
#include <linux/irqflags.h>
88
#include <linux/lockdep.h>
9+
#include <linux/debug_locks.h>
910
#include <asm/current.h>
1011

1112
#ifndef CONFIG_PREEMPT_RT

include/linux/mutex.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,13 +254,16 @@ extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock) __cond_a
254254
DEFINE_LOCK_GUARD_1(mutex, struct mutex, mutex_lock(_T->lock), mutex_unlock(_T->lock))
255255
DEFINE_LOCK_GUARD_1_COND(mutex, _try, mutex_trylock(_T->lock))
256256
DEFINE_LOCK_GUARD_1_COND(mutex, _intr, mutex_lock_interruptible(_T->lock), _RET == 0)
257+
DEFINE_LOCK_GUARD_1(mutex_init, struct mutex, mutex_init(_T->lock), /* */)
257258

258259
DECLARE_LOCK_GUARD_1_ATTRS(mutex, __acquires(_T), __releases(*(struct mutex **)_T))
259260
#define class_mutex_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(mutex, _T)
260261
DECLARE_LOCK_GUARD_1_ATTRS(mutex_try, __acquires(_T), __releases(*(struct mutex **)_T))
261262
#define class_mutex_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(mutex_try, _T)
262263
DECLARE_LOCK_GUARD_1_ATTRS(mutex_intr, __acquires(_T), __releases(*(struct mutex **)_T))
263264
#define class_mutex_intr_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(mutex_intr, _T)
265+
DECLARE_LOCK_GUARD_1_ATTRS(mutex_init, __acquires(_T), __releases(*(struct mutex **)_T))
266+
#define class_mutex_init_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(mutex_init, _T)
264267

265268
extern unsigned long mutex_get_owner(struct mutex *lock);
266269

include/linux/rwsem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,10 @@ DECLARE_LOCK_GUARD_1_ATTRS(rwsem_write_try, __acquires(_T), __releases(*(struct
280280
DECLARE_LOCK_GUARD_1_ATTRS(rwsem_write_kill, __acquires(_T), __releases(*(struct rw_semaphore **)_T))
281281
#define class_rwsem_write_kill_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_write_kill, _T)
282282

283+
DEFINE_LOCK_GUARD_1(rwsem_init, struct rw_semaphore, init_rwsem(_T->lock), /* */)
284+
DECLARE_LOCK_GUARD_1_ATTRS(rwsem_init, __acquires(_T), __releases(*(struct rw_semaphore **)_T))
285+
#define class_rwsem_init_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_init, _T)
286+
283287
/*
284288
* downgrade write lock to read lock
285289
*/

include/linux/seqlock.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
#include <linux/compiler.h>
17+
#include <linux/cleanup.h>
1718
#include <linux/kcsan-checks.h>
1819
#include <linux/lockdep.h>
1920
#include <linux/mutex.h>
@@ -1358,4 +1359,8 @@ static __always_inline void __scoped_seqlock_cleanup_ctx(struct ss_tmp **s)
13581359
#define scoped_seqlock_read(_seqlock, _target) \
13591360
__scoped_seqlock_read(_seqlock, _target, __UNIQUE_ID(seqlock))
13601361

1362+
DEFINE_LOCK_GUARD_1(seqlock_init, seqlock_t, seqlock_init(_T->lock), /* */)
1363+
DECLARE_LOCK_GUARD_1_ATTRS(seqlock_init, __acquires(_T), __releases(*(seqlock_t **)_T))
1364+
#define class_seqlock_init_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(seqlock_init, _T)
1365+
13611366
#endif /* __LINUX_SEQLOCK_H */

include/linux/spinlock.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,10 @@ DEFINE_LOCK_GUARD_1_COND(raw_spinlock_irqsave, _try,
582582
DECLARE_LOCK_GUARD_1_ATTRS(raw_spinlock_irqsave_try, __acquires(_T), __releases(*(raw_spinlock_t **)_T))
583583
#define class_raw_spinlock_irqsave_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(raw_spinlock_irqsave_try, _T)
584584

585+
DEFINE_LOCK_GUARD_1(raw_spinlock_init, raw_spinlock_t, raw_spin_lock_init(_T->lock), /* */)
586+
DECLARE_LOCK_GUARD_1_ATTRS(raw_spinlock_init, __acquires(_T), __releases(*(raw_spinlock_t **)_T))
587+
#define class_raw_spinlock_init_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(raw_spinlock_init, _T)
588+
585589
DEFINE_LOCK_GUARD_1(spinlock, spinlock_t,
586590
spin_lock(_T->lock),
587591
spin_unlock(_T->lock))
@@ -626,6 +630,10 @@ DEFINE_LOCK_GUARD_1_COND(spinlock_irqsave, _try,
626630
DECLARE_LOCK_GUARD_1_ATTRS(spinlock_irqsave_try, __acquires(_T), __releases(*(spinlock_t **)_T))
627631
#define class_spinlock_irqsave_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(spinlock_irqsave_try, _T)
628632

633+
DEFINE_LOCK_GUARD_1(spinlock_init, spinlock_t, spin_lock_init(_T->lock), /* */)
634+
DECLARE_LOCK_GUARD_1_ATTRS(spinlock_init, __acquires(_T), __releases(*(spinlock_t **)_T))
635+
#define class_spinlock_init_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(spinlock_init, _T)
636+
629637
DEFINE_LOCK_GUARD_1(read_lock, rwlock_t,
630638
read_lock(_T->lock),
631639
read_unlock(_T->lock))
@@ -664,5 +672,9 @@ DEFINE_LOCK_GUARD_1(write_lock_irqsave, rwlock_t,
664672
DECLARE_LOCK_GUARD_1_ATTRS(write_lock_irqsave, __acquires(_T), __releases(*(rwlock_t **)_T))
665673
#define class_write_lock_irqsave_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(write_lock_irqsave, _T)
666674

675+
DEFINE_LOCK_GUARD_1(rwlock_init, rwlock_t, rwlock_init(_T->lock), /* */)
676+
DECLARE_LOCK_GUARD_1_ATTRS(rwlock_init, __acquires(_T), __releases(*(rwlock_t **)_T))
677+
#define class_rwlock_init_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwlock_init, _T)
678+
667679
#undef __LINUX_INSIDE_SPINLOCK_H
668680
#endif /* __LINUX_SPINLOCK_H */

lib/test_context-analysis.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ static void __used test_common_helpers(void)
3535
}; \
3636
static void __used test_##class##_init(struct test_##class##_data *d) \
3737
{ \
38-
type_init(&d->lock); \
38+
guard(type_init)(&d->lock); \
3939
d->counter = 0; \
4040
} \
4141
static void __used test_##class(struct test_##class##_data *d) \
@@ -83,7 +83,7 @@ static void __used test_common_helpers(void)
8383

8484
TEST_SPINLOCK_COMMON(raw_spinlock,
8585
raw_spinlock_t,
86-
raw_spin_lock_init,
86+
raw_spinlock_init,
8787
raw_spin_lock,
8888
raw_spin_unlock,
8989
raw_spin_trylock,
@@ -109,7 +109,7 @@ static void __used test_raw_spinlock_trylock_extra(struct test_raw_spinlock_data
109109

110110
TEST_SPINLOCK_COMMON(spinlock,
111111
spinlock_t,
112-
spin_lock_init,
112+
spinlock_init,
113113
spin_lock,
114114
spin_unlock,
115115
spin_trylock,
@@ -163,7 +163,7 @@ struct test_mutex_data {
163163

164164
static void __used test_mutex_init(struct test_mutex_data *d)
165165
{
166-
mutex_init(&d->mtx);
166+
guard(mutex_init)(&d->mtx);
167167
d->counter = 0;
168168
}
169169

@@ -226,7 +226,7 @@ struct test_seqlock_data {
226226

227227
static void __used test_seqlock_init(struct test_seqlock_data *d)
228228
{
229-
seqlock_init(&d->sl);
229+
guard(seqlock_init)(&d->sl);
230230
d->counter = 0;
231231
}
232232

@@ -275,7 +275,7 @@ struct test_rwsem_data {
275275

276276
static void __used test_rwsem_init(struct test_rwsem_data *d)
277277
{
278-
init_rwsem(&d->sem);
278+
guard(rwsem_init)(&d->sem);
279279
d->counter = 0;
280280
}
281281

@@ -475,7 +475,7 @@ static DEFINE_PER_CPU(struct test_local_lock_data, test_local_lock_data) = {
475475

476476
static void __used test_local_lock_init(struct test_local_lock_data *d)
477477
{
478-
local_lock_init(&d->lock);
478+
guard(local_lock_init)(&d->lock);
479479
d->counter = 0;
480480
}
481481

@@ -519,7 +519,7 @@ static DEFINE_PER_CPU(struct test_local_trylock_data, test_local_trylock_data) =
519519

520520
static void __used test_local_trylock_init(struct test_local_trylock_data *d)
521521
{
522-
local_trylock_init(&d->lock);
522+
guard(local_trylock_init)(&d->lock);
523523
d->counter = 0;
524524
}
525525

0 commit comments

Comments
 (0)