- ``kasan=off`` or ``=on`` controls whether KASAN is enabled (default: ``on``).
 
-- ``kasan.mode=sync`` or ``=async`` controls whether KASAN is configured in
-  synchronous or asynchronous mode of execution (default: ``sync``).
+- ``kasan.mode=sync``, ``=async`` or ``=asymm`` controls whether KASAN
+  is configured in synchronous, asynchronous or asymmetric mode of
+  execution (default: ``sync``).
   Synchronous mode: a bad access is detected immediately when a tag
   check fault occurs.
   Asynchronous mode: a bad access detection is delayed. When a tag check
   fault occurs, the information is stored in hardware (in the TFSR_EL1
   register for arm64). The kernel periodically checks the hardware and
   only reports tag faults during these checks.
+  Asymmetric mode: a bad access is detected synchronously on reads and
+  asynchronously on writes.
 
 - ``kasan.stacktrace=off`` or ``=on`` disables or enables alloc and free stack
   traces collection (default: ``on``).
 
  */
 #define KUNIT_EXPECT_KASAN_FAIL(test, expression) do {                 \
        if (IS_ENABLED(CONFIG_KASAN_HW_TAGS) &&                         \
-           !kasan_async_mode_enabled())                                \
+           kasan_sync_fault_possible())                                \
                migrate_disable();                                      \
        KUNIT_EXPECT_FALSE(test, READ_ONCE(fail_data.report_found));    \
        barrier();                                                      \
 
        KASAN_ARG_MODE_DEFAULT,
        KASAN_ARG_MODE_SYNC,
        KASAN_ARG_MODE_ASYNC,
+       KASAN_ARG_MODE_ASYMM,
 };
 
 enum kasan_arg_stacktrace {
 DEFINE_STATIC_KEY_FALSE(kasan_flag_enabled);
 EXPORT_SYMBOL(kasan_flag_enabled);
 
-/* Whether the asynchronous mode is enabled. */
-bool kasan_flag_async __ro_after_init;
-EXPORT_SYMBOL_GPL(kasan_flag_async);
+/* Whether the selected mode is synchronous/asynchronous/asymmetric.*/
+enum kasan_mode kasan_mode __ro_after_init;
+EXPORT_SYMBOL_GPL(kasan_mode);
 
 /* Whether to collect alloc/free stack traces. */
 DEFINE_STATIC_KEY_FALSE(kasan_flag_stacktrace);
 }
 early_param("kasan", early_kasan_flag);
 
-/* kasan.mode=sync/async */
+/* kasan.mode=sync/async/asymm */
 static int __init early_kasan_mode(char *arg)
 {
        if (!arg)
                kasan_arg_mode = KASAN_ARG_MODE_SYNC;
        else if (!strcmp(arg, "async"))
                kasan_arg_mode = KASAN_ARG_MODE_ASYNC;
+       else if (!strcmp(arg, "asymm"))
+               kasan_arg_mode = KASAN_ARG_MODE_ASYMM;
        else
                return -EINVAL;
 
                return;
 
        /*
-        * Enable async mode only when explicitly requested through
-        * the command line.
+        * Enable async or asymm modes only when explicitly requested
+        * through the command line.
         */
        if (kasan_arg_mode == KASAN_ARG_MODE_ASYNC)
                hw_enable_tagging_async();
+       else if (kasan_arg_mode == KASAN_ARG_MODE_ASYMM)
+               hw_enable_tagging_asymm();
        else
                hw_enable_tagging_sync();
 }
        case KASAN_ARG_MODE_DEFAULT:
                /*
                 * Default to sync mode.
-                * Do nothing, kasan_flag_async keeps its default value.
                 */
-               break;
+               fallthrough;
        case KASAN_ARG_MODE_SYNC:
-               /* Do nothing, kasan_flag_async keeps its default value. */
+               /* Sync mode enabled. */
+               kasan_mode = KASAN_MODE_SYNC;
                break;
        case KASAN_ARG_MODE_ASYNC:
                /* Async mode enabled. */
-               kasan_flag_async = true;
+               kasan_mode = KASAN_MODE_ASYNC;
+               break;
+       case KASAN_ARG_MODE_ASYMM:
+               /* Asymm mode enabled. */
+               kasan_mode = KASAN_MODE_ASYMM;
                break;
        }
 
 
 #include "../slab.h"
 
 DECLARE_STATIC_KEY_FALSE(kasan_flag_stacktrace);
-extern bool kasan_flag_async __ro_after_init;
+
+enum kasan_mode {
+       KASAN_MODE_SYNC,
+       KASAN_MODE_ASYNC,
+       KASAN_MODE_ASYMM,
+};
+
+extern enum kasan_mode kasan_mode __ro_after_init;
 
 static inline bool kasan_stack_collection_enabled(void)
 {
        return static_branch_unlikely(&kasan_flag_stacktrace);
 }
 
-static inline bool kasan_async_mode_enabled(void)
+static inline bool kasan_async_fault_possible(void)
+{
+       return kasan_mode == KASAN_MODE_ASYNC || kasan_mode == KASAN_MODE_ASYMM;
+}
+
+static inline bool kasan_sync_fault_possible(void)
 {
-       return kasan_flag_async;
+       return kasan_mode == KASAN_MODE_SYNC || kasan_mode == KASAN_MODE_ASYMM;
 }
 #else
 
        return true;
 }
 
-static inline bool kasan_async_mode_enabled(void)
+static inline bool kasan_async_fault_possible(void)
 {
        return false;
 }
 
+static inline bool kasan_sync_fault_possible(void)
+{
+       return true;
+}
+
 #endif
 
 #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 #ifndef arch_enable_tagging_async
 #define arch_enable_tagging_async()
 #endif
+#ifndef arch_enable_tagging_asymm
+#define arch_enable_tagging_asymm()
+#endif
 #ifndef arch_force_async_tag_fault
 #define arch_force_async_tag_fault()
 #endif
 
 #define hw_enable_tagging_sync()               arch_enable_tagging_sync()
 #define hw_enable_tagging_async()              arch_enable_tagging_async()
+#define hw_enable_tagging_asymm()              arch_enable_tagging_asymm()
 #define hw_force_async_tag_fault()             arch_force_async_tag_fault()
 #define hw_get_random_tag()                    arch_get_random_tag()
 #define hw_get_mem_tag(addr)                   arch_get_mem_tag(addr)
 
 #define hw_enable_tagging_sync()
 #define hw_enable_tagging_async()
+#define hw_enable_tagging_asymm()
 
 #endif /* CONFIG_KASAN_HW_TAGS */
 
 
 
 static void end_report(unsigned long *flags, unsigned long addr)
 {
-       if (!kasan_async_mode_enabled())
+       if (!kasan_async_fault_possible())
                trace_error_report_end(ERROR_DETECTOR_KASAN, addr);
        pr_err("==================================================================\n");
        add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);