Skip to content

Commit 5c1806c

Browse files
melverPeter Zijlstra
authored and
Peter Zijlstra
committed
kcsan, seqlock: Support seqcount_latch_t
While fuzzing an arm64 kernel, Alexander Potapenko reported: | BUG: KCSAN: data-race in ktime_get_mono_fast_ns / timekeeping_update | | write to 0xffffffc082e74248 of 56 bytes by interrupt on cpu 0: | update_fast_timekeeper kernel/time/timekeeping.c:430 [inline] | timekeeping_update+0x1d8/0x2d8 kernel/time/timekeeping.c:768 | timekeeping_advance+0x9e8/0xb78 kernel/time/timekeeping.c:2344 | update_wall_time+0x18/0x38 kernel/time/timekeeping.c:2360 | [...] | | read to 0xffffffc082e74258 of 8 bytes by task 5260 on cpu 1: | __ktime_get_fast_ns kernel/time/timekeeping.c:372 [inline] | ktime_get_mono_fast_ns+0x88/0x174 kernel/time/timekeeping.c:489 | init_srcu_struct_fields+0x40c/0x530 kernel/rcu/srcutree.c:263 | init_srcu_struct+0x14/0x20 kernel/rcu/srcutree.c:311 | [...] | | value changed: 0x000002f875d33266 -> 0x000002f877416866 | | Reported by Kernel Concurrency Sanitizer on: | CPU: 1 UID: 0 PID: 5260 Comm: syz.2.7483 Not tainted 6.12.0-rc3-dirty #78 This is a false positive data race between a seqcount latch writer and a reader accessing stale data. Since its introduction, KCSAN has never understood the seqcount_latch interface (due to being unannotated). Unlike the regular seqlock interface, the seqcount_latch interface for latch writers never has had a well-defined critical section, making it difficult to teach tooling where the critical section starts and ends. Introduce an instrumentable (non-raw) seqcount_latch interface, with which we can clearly denote writer critical sections. This both helps readability and tooling like KCSAN to understand when the writer is done updating all latch copies. Fixes: 88ecd15 ("seqlock, kcsan: Add annotations for KCSAN") Reported-by: Alexander Potapenko <[email protected]> Co-developed-by: "Peter Zijlstra (Intel)" <[email protected]> Signed-off-by: "Peter Zijlstra (Intel)" <[email protected]> Signed-off-by: Marco Elver <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 8ab40fc commit 5c1806c

File tree

2 files changed

+72
-16
lines changed

2 files changed

+72
-16
lines changed

Documentation/locking/seqlock.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ Use seqcount_latch_t when the write side sections cannot be protected
153153
from interruption by readers. This is typically the case when the read
154154
side can be invoked from NMI handlers.
155155

156-
Check `raw_write_seqcount_latch()` for more information.
156+
Check `write_seqcount_latch()` for more information.
157157

158158

159159
.. _seqlock_t:

include/linux/seqlock.h

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,23 @@ static __always_inline unsigned raw_read_seqcount_latch(const seqcount_latch_t *
621621
return READ_ONCE(s->seqcount.sequence);
622622
}
623623

624+
/**
625+
* read_seqcount_latch() - pick even/odd latch data copy
626+
* @s: Pointer to seqcount_latch_t
627+
*
628+
* See write_seqcount_latch() for details and a full reader/writer usage
629+
* example.
630+
*
631+
* Return: sequence counter raw value. Use the lowest bit as an index for
632+
* picking which data copy to read. The full counter must then be checked
633+
* with read_seqcount_latch_retry().
634+
*/
635+
static __always_inline unsigned read_seqcount_latch(const seqcount_latch_t *s)
636+
{
637+
kcsan_atomic_next(KCSAN_SEQLOCK_REGION_MAX);
638+
return raw_read_seqcount_latch(s);
639+
}
640+
624641
/**
625642
* raw_read_seqcount_latch_retry() - end a seqcount_latch_t read section
626643
* @s: Pointer to seqcount_latch_t
@@ -635,9 +652,34 @@ raw_read_seqcount_latch_retry(const seqcount_latch_t *s, unsigned start)
635652
return unlikely(READ_ONCE(s->seqcount.sequence) != start);
636653
}
637654

655+
/**
656+
* read_seqcount_latch_retry() - end a seqcount_latch_t read section
657+
* @s: Pointer to seqcount_latch_t
658+
* @start: count, from read_seqcount_latch()
659+
*
660+
* Return: true if a read section retry is required, else false
661+
*/
662+
static __always_inline int
663+
read_seqcount_latch_retry(const seqcount_latch_t *s, unsigned start)
664+
{
665+
kcsan_atomic_next(0);
666+
return raw_read_seqcount_latch_retry(s, start);
667+
}
668+
638669
/**
639670
* raw_write_seqcount_latch() - redirect latch readers to even/odd copy
640671
* @s: Pointer to seqcount_latch_t
672+
*/
673+
static __always_inline void raw_write_seqcount_latch(seqcount_latch_t *s)
674+
{
675+
smp_wmb(); /* prior stores before incrementing "sequence" */
676+
s->seqcount.sequence++;
677+
smp_wmb(); /* increment "sequence" before following stores */
678+
}
679+
680+
/**
681+
* write_seqcount_latch_begin() - redirect latch readers to odd copy
682+
* @s: Pointer to seqcount_latch_t
641683
*
642684
* The latch technique is a multiversion concurrency control method that allows
643685
* queries during non-atomic modifications. If you can guarantee queries never
@@ -665,17 +707,11 @@ raw_read_seqcount_latch_retry(const seqcount_latch_t *s, unsigned start)
665707
*
666708
* void latch_modify(struct latch_struct *latch, ...)
667709
* {
668-
* smp_wmb(); // Ensure that the last data[1] update is visible
669-
* latch->seq.sequence++;
670-
* smp_wmb(); // Ensure that the seqcount update is visible
671-
*
710+
* write_seqcount_latch_begin(&latch->seq);
672711
* modify(latch->data[0], ...);
673-
*
674-
* smp_wmb(); // Ensure that the data[0] update is visible
675-
* latch->seq.sequence++;
676-
* smp_wmb(); // Ensure that the seqcount update is visible
677-
*
712+
* write_seqcount_latch(&latch->seq);
678713
* modify(latch->data[1], ...);
714+
* write_seqcount_latch_end(&latch->seq);
679715
* }
680716
*
681717
* The query will have a form like::
@@ -686,13 +722,13 @@ raw_read_seqcount_latch_retry(const seqcount_latch_t *s, unsigned start)
686722
* unsigned seq, idx;
687723
*
688724
* do {
689-
* seq = raw_read_seqcount_latch(&latch->seq);
725+
* seq = read_seqcount_latch(&latch->seq);
690726
*
691727
* idx = seq & 0x01;
692728
* entry = data_query(latch->data[idx], ...);
693729
*
694730
* // This includes needed smp_rmb()
695-
* } while (raw_read_seqcount_latch_retry(&latch->seq, seq));
731+
* } while (read_seqcount_latch_retry(&latch->seq, seq));
696732
*
697733
* return entry;
698734
* }
@@ -716,11 +752,31 @@ raw_read_seqcount_latch_retry(const seqcount_latch_t *s, unsigned start)
716752
* When data is a dynamic data structure; one should use regular RCU
717753
* patterns to manage the lifetimes of the objects within.
718754
*/
719-
static inline void raw_write_seqcount_latch(seqcount_latch_t *s)
755+
static __always_inline void write_seqcount_latch_begin(seqcount_latch_t *s)
720756
{
721-
smp_wmb(); /* prior stores before incrementing "sequence" */
722-
s->seqcount.sequence++;
723-
smp_wmb(); /* increment "sequence" before following stores */
757+
kcsan_nestable_atomic_begin();
758+
raw_write_seqcount_latch(s);
759+
}
760+
761+
/**
762+
* write_seqcount_latch() - redirect latch readers to even copy
763+
* @s: Pointer to seqcount_latch_t
764+
*/
765+
static __always_inline void write_seqcount_latch(seqcount_latch_t *s)
766+
{
767+
raw_write_seqcount_latch(s);
768+
}
769+
770+
/**
771+
* write_seqcount_latch_end() - end a seqcount_latch_t write section
772+
* @s: Pointer to seqcount_latch_t
773+
*
774+
* Marks the end of a seqcount_latch_t writer section, after all copies of the
775+
* latch-protected data have been updated.
776+
*/
777+
static __always_inline void write_seqcount_latch_end(seqcount_latch_t *s)
778+
{
779+
kcsan_nestable_atomic_end();
724780
}
725781

726782
#define __SEQLOCK_UNLOCKED(lockname) \

0 commit comments

Comments
 (0)