#include <linux/delay.h>
 #include <linux/export.h>
 #include <linux/init.h>
+#include <linux/kernel.h>
 #include <linux/percpu.h>
 #include <linux/preempt.h>
 #include <linux/random.h>
        return in_task() ? ¤t->kcsan_ctx : raw_cpu_ptr(&kcsan_cpu_ctx);
 }
 
-static __always_inline bool is_atomic(const volatile void *ptr)
+static __always_inline bool
+is_atomic(const volatile void *ptr, size_t size, int type)
 {
-       struct kcsan_ctx *ctx = get_ctx();
+       struct kcsan_ctx *ctx;
+
+       if ((type & KCSAN_ACCESS_ATOMIC) != 0)
+               return true;
 
+       if (IS_ENABLED(CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC) &&
+           (type & KCSAN_ACCESS_WRITE) != 0 && size <= sizeof(long) &&
+           IS_ALIGNED((unsigned long)ptr, size))
+               return true; /* Assume aligned writes up to word size are atomic. */
+
+       ctx = get_ctx();
        if (unlikely(ctx->atomic_next > 0)) {
                /*
                 * Because we do not have separate contexts for nested
        return kcsan_is_atomic(ptr);
 }
 
-static __always_inline bool should_watch(const volatile void *ptr, int type)
+static __always_inline bool
+should_watch(const volatile void *ptr, size_t size, int type)
 {
        /*
         * Never set up watchpoints when memory operations are atomic.
         * should not count towards skipped instructions, and (2) to actually
         * decrement kcsan_atomic_next for consecutive instruction stream.
         */
-       if ((type & KCSAN_ACCESS_ATOMIC) != 0 || is_atomic(ptr))
+       if (is_atomic(ptr, size, type))
                return false;
 
        if (this_cpu_dec_return(kcsan_skip) >= 0)
        if (unlikely(watchpoint != NULL))
                kcsan_found_watchpoint(ptr, size, type, watchpoint,
                                       encoded_watchpoint);
-       else if (unlikely(should_watch(ptr, type)))
+       else if (unlikely(should_watch(ptr, size, type)))
                kcsan_setup_watchpoint(ptr, size, type);
 }
 
 
          limiting reporting to avoid flooding the console with reports.
          Setting this to 0 disables rate limiting.
 
-# Note that, while some of the below options could be turned into boot
-# parameters, to optimize for the common use-case, we avoid this because: (a)
-# it would impact performance (and we want to avoid static branch for all
-# {READ,WRITE}_ONCE, atomic_*, bitops, etc.), and (b) complicate the design
-# without real benefit. The main purpose of the below options is for use in
-# fuzzer configs to control reported data races, and they are not expected
-# to be switched frequently by a user.
+# The main purpose of the below options is to control reported data races (e.g.
+# in fuzzer configs), and are not expected to be switched frequently by other
+# users. We could turn some of them into boot parameters, but given they should
+# not be switched normally, let's keep them here to simplify configuration.
+#
+# The defaults below are chosen to be very conservative, and may miss certain
+# bugs.
 
 config KCSAN_REPORT_RACE_UNKNOWN_ORIGIN
        bool "Report races of unknown origin"
          the data value of the memory location was observed to remain
          unchanged, do not report the data race.
 
+config KCSAN_ASSUME_PLAIN_WRITES_ATOMIC
+       bool "Assume that plain aligned writes up to word size are atomic"
+       default y
+       help
+         Assume that plain aligned writes up to word size are atomic by
+         default, and also not subject to other unsafe compiler optimizations
+         resulting in data races. This will cause KCSAN to not report data
+         races due to conflicts where the only plain accesses are aligned
+         writes up to word size: conflicts between marked reads and plain
+         aligned writes up to word size will not be reported as data races;
+         notice that data races between two conflicting plain aligned writes
+         will also not be reported.
+
 config KCSAN_IGNORE_ATOMICS
        bool "Do not instrument marked atomic accesses"
        help