| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Compile-only tests for common patterns that should not generate false
- * positive errors when compiled with Clang's context analysis.
- */
- #include <linux/bit_spinlock.h>
- #include <linux/build_bug.h>
- #include <linux/local_lock.h>
- #include <linux/mutex.h>
- #include <linux/percpu.h>
- #include <linux/rcupdate.h>
- #include <linux/rwsem.h>
- #include <linux/seqlock.h>
- #include <linux/spinlock.h>
- #include <linux/srcu.h>
- #include <linux/ww_mutex.h>
- /*
- * Test that helper macros work as expected.
- */
- static void __used test_common_helpers(void)
- {
- BUILD_BUG_ON(context_unsafe(3) != 3); /* plain expression */
- BUILD_BUG_ON(context_unsafe((void)2; 3) != 3); /* does not swallow semi-colon */
- BUILD_BUG_ON(context_unsafe((void)2, 3) != 3); /* does not swallow commas */
- context_unsafe(do { } while (0)); /* works with void statements */
- }
- #define TEST_SPINLOCK_COMMON(class, type, type_init, type_lock, type_unlock, type_trylock, op) \
- struct test_##class##_data { \
- type lock; \
- int counter __guarded_by(&lock); \
- int *pointer __pt_guarded_by(&lock); \
- }; \
- static void __used test_##class##_init(struct test_##class##_data *d) \
- { \
- guard(type_init)(&d->lock); \
- d->counter = 0; \
- } \
- static void __used test_##class(struct test_##class##_data *d) \
- { \
- unsigned long flags; \
- d->pointer++; \
- type_lock(&d->lock); \
- op(d->counter); \
- op(*d->pointer); \
- type_unlock(&d->lock); \
- type_lock##_irq(&d->lock); \
- op(d->counter); \
- op(*d->pointer); \
- type_unlock##_irq(&d->lock); \
- type_lock##_bh(&d->lock); \
- op(d->counter); \
- op(*d->pointer); \
- type_unlock##_bh(&d->lock); \
- type_lock##_irqsave(&d->lock, flags); \
- op(d->counter); \
- op(*d->pointer); \
- type_unlock##_irqrestore(&d->lock, flags); \
- } \
- static void __used test_##class##_trylock(struct test_##class##_data *d) \
- { \
- if (type_trylock(&d->lock)) { \
- op(d->counter); \
- type_unlock(&d->lock); \
- } \
- } \
- static void __used test_##class##_assert(struct test_##class##_data *d) \
- { \
- lockdep_assert_held(&d->lock); \
- op(d->counter); \
- } \
- static void __used test_##class##_guard(struct test_##class##_data *d) \
- { \
- { guard(class)(&d->lock); op(d->counter); } \
- { guard(class##_irq)(&d->lock); op(d->counter); } \
- { guard(class##_irqsave)(&d->lock); op(d->counter); } \
- }
- #define TEST_OP_RW(x) (x)++
- #define TEST_OP_RO(x) ((void)(x))
- TEST_SPINLOCK_COMMON(raw_spinlock,
- raw_spinlock_t,
- raw_spinlock_init,
- raw_spin_lock,
- raw_spin_unlock,
- raw_spin_trylock,
- TEST_OP_RW);
- static void __used test_raw_spinlock_trylock_extra(struct test_raw_spinlock_data *d)
- {
- unsigned long flags;
- data_race(d->counter++); /* no warning */
- if (raw_spin_trylock_irq(&d->lock)) {
- d->counter++;
- raw_spin_unlock_irq(&d->lock);
- }
- if (raw_spin_trylock_irqsave(&d->lock, flags)) {
- d->counter++;
- raw_spin_unlock_irqrestore(&d->lock, flags);
- }
- scoped_cond_guard(raw_spinlock_try, return, &d->lock) {
- d->counter++;
- }
- }
- TEST_SPINLOCK_COMMON(spinlock,
- spinlock_t,
- spinlock_init,
- spin_lock,
- spin_unlock,
- spin_trylock,
- TEST_OP_RW);
- static void __used test_spinlock_trylock_extra(struct test_spinlock_data *d)
- {
- unsigned long flags;
- if (spin_trylock_irq(&d->lock)) {
- d->counter++;
- spin_unlock_irq(&d->lock);
- }
- if (spin_trylock_irqsave(&d->lock, flags)) {
- d->counter++;
- spin_unlock_irqrestore(&d->lock, flags);
- }
- scoped_cond_guard(spinlock_try, return, &d->lock) {
- d->counter++;
- }
- }
- TEST_SPINLOCK_COMMON(write_lock,
- rwlock_t,
- rwlock_init,
- write_lock,
- write_unlock,
- write_trylock,
- TEST_OP_RW);
- static void __used test_write_trylock_extra(struct test_write_lock_data *d)
- {
- unsigned long flags;
- if (write_trylock_irqsave(&d->lock, flags)) {
- d->counter++;
- write_unlock_irqrestore(&d->lock, flags);
- }
- }
- TEST_SPINLOCK_COMMON(read_lock,
- rwlock_t,
- rwlock_init,
- read_lock,
- read_unlock,
- read_trylock,
- TEST_OP_RO);
- struct test_mutex_data {
- struct mutex mtx;
- int counter __guarded_by(&mtx);
- };
- static void __used test_mutex_init(struct test_mutex_data *d)
- {
- guard(mutex_init)(&d->mtx);
- d->counter = 0;
- }
- static void __used test_mutex_lock(struct test_mutex_data *d)
- {
- mutex_lock(&d->mtx);
- d->counter++;
- mutex_unlock(&d->mtx);
- mutex_lock_io(&d->mtx);
- d->counter++;
- mutex_unlock(&d->mtx);
- }
- static void __used test_mutex_trylock(struct test_mutex_data *d, atomic_t *a)
- {
- if (!mutex_lock_interruptible(&d->mtx)) {
- d->counter++;
- mutex_unlock(&d->mtx);
- }
- if (!mutex_lock_killable(&d->mtx)) {
- d->counter++;
- mutex_unlock(&d->mtx);
- }
- if (mutex_trylock(&d->mtx)) {
- d->counter++;
- mutex_unlock(&d->mtx);
- }
- if (atomic_dec_and_mutex_lock(a, &d->mtx)) {
- d->counter++;
- mutex_unlock(&d->mtx);
- }
- }
- static void __used test_mutex_assert(struct test_mutex_data *d)
- {
- lockdep_assert_held(&d->mtx);
- d->counter++;
- }
- static void __used test_mutex_guard(struct test_mutex_data *d)
- {
- guard(mutex)(&d->mtx);
- d->counter++;
- }
- static void __used test_mutex_cond_guard(struct test_mutex_data *d)
- {
- scoped_cond_guard(mutex_try, return, &d->mtx) {
- d->counter++;
- }
- scoped_cond_guard(mutex_intr, return, &d->mtx) {
- d->counter++;
- }
- }
- struct test_seqlock_data {
- seqlock_t sl;
- int counter __guarded_by(&sl);
- };
- static void __used test_seqlock_init(struct test_seqlock_data *d)
- {
- guard(seqlock_init)(&d->sl);
- d->counter = 0;
- }
- static void __used test_seqlock_reader(struct test_seqlock_data *d)
- {
- unsigned int seq;
- do {
- seq = read_seqbegin(&d->sl);
- (void)d->counter;
- } while (read_seqretry(&d->sl, seq));
- }
- static void __used test_seqlock_writer(struct test_seqlock_data *d)
- {
- unsigned long flags;
- write_seqlock(&d->sl);
- d->counter++;
- write_sequnlock(&d->sl);
- write_seqlock_irq(&d->sl);
- d->counter++;
- write_sequnlock_irq(&d->sl);
- write_seqlock_bh(&d->sl);
- d->counter++;
- write_sequnlock_bh(&d->sl);
- write_seqlock_irqsave(&d->sl, flags);
- d->counter++;
- write_sequnlock_irqrestore(&d->sl, flags);
- }
- static void __used test_seqlock_scoped(struct test_seqlock_data *d)
- {
- scoped_seqlock_read (&d->sl, ss_lockless) {
- (void)d->counter;
- }
- }
- struct test_rwsem_data {
- struct rw_semaphore sem;
- int counter __guarded_by(&sem);
- };
- static void __used test_rwsem_init(struct test_rwsem_data *d)
- {
- guard(rwsem_init)(&d->sem);
- d->counter = 0;
- }
- static void __used test_rwsem_reader(struct test_rwsem_data *d)
- {
- down_read(&d->sem);
- (void)d->counter;
- up_read(&d->sem);
- if (down_read_trylock(&d->sem)) {
- (void)d->counter;
- up_read(&d->sem);
- }
- }
- static void __used test_rwsem_writer(struct test_rwsem_data *d)
- {
- down_write(&d->sem);
- d->counter++;
- up_write(&d->sem);
- down_write(&d->sem);
- d->counter++;
- downgrade_write(&d->sem);
- (void)d->counter;
- up_read(&d->sem);
- if (down_write_trylock(&d->sem)) {
- d->counter++;
- up_write(&d->sem);
- }
- }
- static void __used test_rwsem_assert(struct test_rwsem_data *d)
- {
- rwsem_assert_held_nolockdep(&d->sem);
- d->counter++;
- }
- static void __used test_rwsem_guard(struct test_rwsem_data *d)
- {
- { guard(rwsem_read)(&d->sem); (void)d->counter; }
- { guard(rwsem_write)(&d->sem); d->counter++; }
- }
- static void __used test_rwsem_cond_guard(struct test_rwsem_data *d)
- {
- scoped_cond_guard(rwsem_read_try, return, &d->sem) {
- (void)d->counter;
- }
- scoped_cond_guard(rwsem_write_try, return, &d->sem) {
- d->counter++;
- }
- }
- struct test_bit_spinlock_data {
- unsigned long bits;
- int counter __guarded_by(__bitlock(3, &bits));
- };
- static void __used test_bit_spin_lock(struct test_bit_spinlock_data *d)
- {
- /*
- * Note, the analysis seems to have false negatives, because it won't
- * precisely recognize the bit of the fake __bitlock() token.
- */
- bit_spin_lock(3, &d->bits);
- d->counter++;
- bit_spin_unlock(3, &d->bits);
- bit_spin_lock(3, &d->bits);
- d->counter++;
- __bit_spin_unlock(3, &d->bits);
- if (bit_spin_trylock(3, &d->bits)) {
- d->counter++;
- bit_spin_unlock(3, &d->bits);
- }
- }
- /*
- * Test that we can mark a variable guarded by RCU, and we can dereference and
- * write to the pointer with RCU's primitives.
- */
- struct test_rcu_data {
- long __rcu_guarded *data;
- };
- static void __used test_rcu_guarded_reader(struct test_rcu_data *d)
- {
- rcu_read_lock();
- (void)rcu_dereference(d->data);
- rcu_read_unlock();
- rcu_read_lock_bh();
- (void)rcu_dereference(d->data);
- rcu_read_unlock_bh();
- rcu_read_lock_sched();
- (void)rcu_dereference(d->data);
- rcu_read_unlock_sched();
- }
- static void __used test_rcu_guard(struct test_rcu_data *d)
- {
- guard(rcu)();
- (void)rcu_dereference(d->data);
- }
- static void __used test_rcu_guarded_updater(struct test_rcu_data *d)
- {
- rcu_assign_pointer(d->data, NULL);
- RCU_INIT_POINTER(d->data, NULL);
- (void)unrcu_pointer(d->data);
- }
- static void wants_rcu_held(void) __must_hold_shared(RCU) { }
- static void wants_rcu_held_bh(void) __must_hold_shared(RCU_BH) { }
- static void wants_rcu_held_sched(void) __must_hold_shared(RCU_SCHED) { }
- static void __used test_rcu_lock_variants(void)
- {
- rcu_read_lock();
- wants_rcu_held();
- rcu_read_unlock();
- rcu_read_lock_bh();
- wants_rcu_held_bh();
- rcu_read_unlock_bh();
- rcu_read_lock_sched();
- wants_rcu_held_sched();
- rcu_read_unlock_sched();
- }
- static void __used test_rcu_lock_reentrant(void)
- {
- rcu_read_lock();
- rcu_read_lock();
- rcu_read_lock_bh();
- rcu_read_lock_bh();
- rcu_read_lock_sched();
- rcu_read_lock_sched();
- rcu_read_unlock_sched();
- rcu_read_unlock_sched();
- rcu_read_unlock_bh();
- rcu_read_unlock_bh();
- rcu_read_unlock();
- rcu_read_unlock();
- }
- static void __used test_rcu_assert_variants(void)
- {
- lockdep_assert_in_rcu_read_lock();
- wants_rcu_held();
- lockdep_assert_in_rcu_read_lock_bh();
- wants_rcu_held_bh();
- lockdep_assert_in_rcu_read_lock_sched();
- wants_rcu_held_sched();
- }
- struct test_srcu_data {
- struct srcu_struct srcu;
- long __rcu_guarded *data;
- };
- static void __used test_srcu(struct test_srcu_data *d)
- {
- init_srcu_struct(&d->srcu);
- int idx = srcu_read_lock(&d->srcu);
- long *data = srcu_dereference(d->data, &d->srcu);
- (void)data;
- srcu_read_unlock(&d->srcu, idx);
- rcu_assign_pointer(d->data, NULL);
- }
- static void __used test_srcu_guard(struct test_srcu_data *d)
- {
- { guard(srcu)(&d->srcu); (void)srcu_dereference(d->data, &d->srcu); }
- { guard(srcu_fast)(&d->srcu); (void)srcu_dereference(d->data, &d->srcu); }
- { guard(srcu_fast_notrace)(&d->srcu); (void)srcu_dereference(d->data, &d->srcu); }
- }
- struct test_local_lock_data {
- local_lock_t lock;
- int counter __guarded_by(&lock);
- };
- static DEFINE_PER_CPU(struct test_local_lock_data, test_local_lock_data) = {
- .lock = INIT_LOCAL_LOCK(lock),
- };
- static void __used test_local_lock_init(struct test_local_lock_data *d)
- {
- guard(local_lock_init)(&d->lock);
- d->counter = 0;
- }
- static void __used test_local_lock(void)
- {
- unsigned long flags;
- local_lock(&test_local_lock_data.lock);
- this_cpu_add(test_local_lock_data.counter, 1);
- local_unlock(&test_local_lock_data.lock);
- local_lock_irq(&test_local_lock_data.lock);
- this_cpu_add(test_local_lock_data.counter, 1);
- local_unlock_irq(&test_local_lock_data.lock);
- local_lock_irqsave(&test_local_lock_data.lock, flags);
- this_cpu_add(test_local_lock_data.counter, 1);
- local_unlock_irqrestore(&test_local_lock_data.lock, flags);
- local_lock_nested_bh(&test_local_lock_data.lock);
- this_cpu_add(test_local_lock_data.counter, 1);
- local_unlock_nested_bh(&test_local_lock_data.lock);
- }
- static void __used test_local_lock_guard(void)
- {
- { guard(local_lock)(&test_local_lock_data.lock); this_cpu_add(test_local_lock_data.counter, 1); }
- { guard(local_lock_irq)(&test_local_lock_data.lock); this_cpu_add(test_local_lock_data.counter, 1); }
- { guard(local_lock_irqsave)(&test_local_lock_data.lock); this_cpu_add(test_local_lock_data.counter, 1); }
- { guard(local_lock_nested_bh)(&test_local_lock_data.lock); this_cpu_add(test_local_lock_data.counter, 1); }
- }
- struct test_local_trylock_data {
- local_trylock_t lock;
- int counter __guarded_by(&lock);
- };
- static DEFINE_PER_CPU(struct test_local_trylock_data, test_local_trylock_data) = {
- .lock = INIT_LOCAL_TRYLOCK(lock),
- };
- static void __used test_local_trylock_init(struct test_local_trylock_data *d)
- {
- guard(local_trylock_init)(&d->lock);
- d->counter = 0;
- }
- static void __used test_local_trylock(void)
- {
- local_lock(&test_local_trylock_data.lock);
- this_cpu_add(test_local_trylock_data.counter, 1);
- local_unlock(&test_local_trylock_data.lock);
- if (local_trylock(&test_local_trylock_data.lock)) {
- this_cpu_add(test_local_trylock_data.counter, 1);
- local_unlock(&test_local_trylock_data.lock);
- }
- }
- static DEFINE_WD_CLASS(ww_class);
- struct test_ww_mutex_data {
- struct ww_mutex mtx;
- int counter __guarded_by(&mtx);
- };
- static void __used test_ww_mutex_lock_noctx(struct test_ww_mutex_data *d)
- {
- if (!ww_mutex_lock(&d->mtx, NULL)) {
- d->counter++;
- ww_mutex_unlock(&d->mtx);
- }
- if (!ww_mutex_lock_interruptible(&d->mtx, NULL)) {
- d->counter++;
- ww_mutex_unlock(&d->mtx);
- }
- if (ww_mutex_trylock(&d->mtx, NULL)) {
- d->counter++;
- ww_mutex_unlock(&d->mtx);
- }
- ww_mutex_lock_slow(&d->mtx, NULL);
- d->counter++;
- ww_mutex_unlock(&d->mtx);
- ww_mutex_destroy(&d->mtx);
- }
- static void __used test_ww_mutex_lock_ctx(struct test_ww_mutex_data *d)
- {
- struct ww_acquire_ctx ctx;
- ww_acquire_init(&ctx, &ww_class);
- if (!ww_mutex_lock(&d->mtx, &ctx)) {
- d->counter++;
- ww_mutex_unlock(&d->mtx);
- }
- if (!ww_mutex_lock_interruptible(&d->mtx, &ctx)) {
- d->counter++;
- ww_mutex_unlock(&d->mtx);
- }
- if (ww_mutex_trylock(&d->mtx, &ctx)) {
- d->counter++;
- ww_mutex_unlock(&d->mtx);
- }
- ww_mutex_lock_slow(&d->mtx, &ctx);
- d->counter++;
- ww_mutex_unlock(&d->mtx);
- ww_acquire_done(&ctx);
- ww_acquire_fini(&ctx);
- ww_mutex_destroy(&d->mtx);
- }
|