unsigned int redzone_size;
        int redzone_adjust;
 
+       if (!kasan_stack_collection_enabled()) {
+               *flags |= SLAB_KASAN;
+               return;
+       }
+
        /* Add alloc meta. */
        cache->kasan_info.alloc_meta_offset = *size;
        *size += sizeof(struct kasan_alloc_meta);
 
 size_t kasan_metadata_size(struct kmem_cache *cache)
 {
+       if (!kasan_stack_collection_enabled())
+               return 0;
        return (cache->kasan_info.alloc_meta_offset ?
                sizeof(struct kasan_alloc_meta) : 0) +
                (cache->kasan_info.free_meta_offset ?
 {
        struct kasan_alloc_meta *alloc_meta;
 
-       if (!(cache->flags & SLAB_KASAN))
-               return (void *)object;
+       if (kasan_stack_collection_enabled()) {
+               if (!(cache->flags & SLAB_KASAN))
+                       return (void *)object;
 
-       alloc_meta = kasan_get_alloc_meta(cache, object);
-       __memset(alloc_meta, 0, sizeof(*alloc_meta));
+               alloc_meta = kasan_get_alloc_meta(cache, object);
+               __memset(alloc_meta, 0, sizeof(*alloc_meta));
+       }
 
        if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) || IS_ENABLED(CONFIG_KASAN_HW_TAGS))
                object = set_tag(object, assign_tag(cache, object, true, false));
        rounded_up_size = round_up(cache->object_size, KASAN_GRANULE_SIZE);
        poison_range(object, rounded_up_size, KASAN_KMALLOC_FREE);
 
+       if (!kasan_stack_collection_enabled())
+               return false;
+
        if ((IS_ENABLED(CONFIG_KASAN_GENERIC) && !quarantine) ||
                        unlikely(!(cache->flags & SLAB_KASAN)))
                return false;
        poison_range((void *)redzone_start, redzone_end - redzone_start,
                     KASAN_KMALLOC_REDZONE);
 
-       if (cache->flags & SLAB_KASAN)
+       if (kasan_stack_collection_enabled() && (cache->flags & SLAB_KASAN))
                set_alloc_info(cache, (void *)object, flags);
 
        return set_tag(object, tag);
 
 
 #define pr_fmt(fmt) "kasan: " fmt
 
+#include <linux/init.h>
 #include <linux/kasan.h>
 #include <linux/kernel.h>
 #include <linux/memory.h>
 #include <linux/mm.h>
+#include <linux/static_key.h>
 #include <linux/string.h>
 #include <linux/types.h>
 
 #include "kasan.h"
 
+enum kasan_arg_mode {
+       KASAN_ARG_MODE_DEFAULT,
+       KASAN_ARG_MODE_OFF,
+       KASAN_ARG_MODE_PROD,
+       KASAN_ARG_MODE_FULL,
+};
+
+enum kasan_arg_stacktrace {
+       KASAN_ARG_STACKTRACE_DEFAULT,
+       KASAN_ARG_STACKTRACE_OFF,
+       KASAN_ARG_STACKTRACE_ON,
+};
+
+enum kasan_arg_fault {
+       KASAN_ARG_FAULT_DEFAULT,
+       KASAN_ARG_FAULT_REPORT,
+       KASAN_ARG_FAULT_PANIC,
+};
+
+static enum kasan_arg_mode kasan_arg_mode __ro_after_init;
+static enum kasan_arg_stacktrace kasan_arg_stacktrace __ro_after_init;
+static enum kasan_arg_fault kasan_arg_fault __ro_after_init;
+
+/* Whether KASAN is enabled at all. */
+DEFINE_STATIC_KEY_FALSE(kasan_flag_enabled);
+EXPORT_SYMBOL(kasan_flag_enabled);
+
+/* Whether to collect alloc/free stack traces. */
+DEFINE_STATIC_KEY_FALSE(kasan_flag_stacktrace);
+
+/* Whether panic or disable tag checking on fault. */
+bool kasan_flag_panic __ro_after_init;
+
+/* kasan.mode=off/prod/full */
+static int __init early_kasan_mode(char *arg)
+{
+       if (!arg)
+               return -EINVAL;
+
+       if (!strcmp(arg, "off"))
+               kasan_arg_mode = KASAN_ARG_MODE_OFF;
+       else if (!strcmp(arg, "prod"))
+               kasan_arg_mode = KASAN_ARG_MODE_PROD;
+       else if (!strcmp(arg, "full"))
+               kasan_arg_mode = KASAN_ARG_MODE_FULL;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+early_param("kasan.mode", early_kasan_mode);
+
+/* kasan.stack=off/on */
+static int __init early_kasan_flag_stacktrace(char *arg)
+{
+       if (!arg)
+               return -EINVAL;
+
+       if (!strcmp(arg, "off"))
+               kasan_arg_stacktrace = KASAN_ARG_STACKTRACE_OFF;
+       else if (!strcmp(arg, "on"))
+               kasan_arg_stacktrace = KASAN_ARG_STACKTRACE_ON;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+early_param("kasan.stacktrace", early_kasan_flag_stacktrace);
+
+/* kasan.fault=report/panic */
+static int __init early_kasan_fault(char *arg)
+{
+       if (!arg)
+               return -EINVAL;
+
+       if (!strcmp(arg, "report"))
+               kasan_arg_fault = KASAN_ARG_FAULT_REPORT;
+       else if (!strcmp(arg, "panic"))
+               kasan_arg_fault = KASAN_ARG_FAULT_PANIC;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+early_param("kasan.fault", early_kasan_fault);
+
 /* kasan_init_hw_tags_cpu() is called for each CPU. */
 void kasan_init_hw_tags_cpu(void)
 {
+       /*
+        * There's no need to check that the hardware is MTE-capable here,
+        * as this function is only called for MTE-capable hardware.
+        */
+
+       /* If KASAN is disabled, do nothing. */
+       if (kasan_arg_mode == KASAN_ARG_MODE_OFF)
+               return;
+
        hw_init_tags(KASAN_TAG_MAX);
        hw_enable_tagging();
 }
 /* kasan_init_hw_tags() is called once on boot CPU. */
 void __init kasan_init_hw_tags(void)
 {
+       /* If hardware doesn't support MTE, do nothing. */
+       if (!system_supports_mte())
+               return;
+
+       /* Choose KASAN mode if kasan boot parameter is not provided. */
+       if (kasan_arg_mode == KASAN_ARG_MODE_DEFAULT) {
+               if (IS_ENABLED(CONFIG_DEBUG_KERNEL))
+                       kasan_arg_mode = KASAN_ARG_MODE_FULL;
+               else
+                       kasan_arg_mode = KASAN_ARG_MODE_PROD;
+       }
+
+       /* Preset parameter values based on the mode. */
+       switch (kasan_arg_mode) {
+       case KASAN_ARG_MODE_DEFAULT:
+               /* Shouldn't happen as per the check above. */
+               WARN_ON(1);
+               return;
+       case KASAN_ARG_MODE_OFF:
+               /* If KASAN is disabled, do nothing. */
+               return;
+       case KASAN_ARG_MODE_PROD:
+               static_branch_enable(&kasan_flag_enabled);
+               break;
+       case KASAN_ARG_MODE_FULL:
+               static_branch_enable(&kasan_flag_enabled);
+               static_branch_enable(&kasan_flag_stacktrace);
+               break;
+       }
+
+       /* Now, optionally override the presets. */
+
+       switch (kasan_arg_stacktrace) {
+       case KASAN_ARG_STACKTRACE_DEFAULT:
+               break;
+       case KASAN_ARG_STACKTRACE_OFF:
+               static_branch_disable(&kasan_flag_stacktrace);
+               break;
+       case KASAN_ARG_STACKTRACE_ON:
+               static_branch_enable(&kasan_flag_stacktrace);
+               break;
+       }
+
+       switch (kasan_arg_fault) {
+       case KASAN_ARG_FAULT_DEFAULT:
+               break;
+       case KASAN_ARG_FAULT_REPORT:
+               kasan_flag_panic = false;
+               break;
+       case KASAN_ARG_FAULT_PANIC:
+               kasan_flag_panic = true;
+               break;
+       }
+
        pr_info("KernelAddressSanitizer initialized\n");
 }
 
 
                panic_on_warn = 0;
                panic("panic_on_warn set ...\n");
        }
+#ifdef CONFIG_KASAN_HW_TAGS
+       if (kasan_flag_panic)
+               panic("kasan.fault=panic set ...\n");
+#endif
        kasan_enable_current();
 }
 
                (void *)(object_addr + cache->object_size));
 }
 
-static void describe_object(struct kmem_cache *cache, void *object,
-                               const void *addr, u8 tag)
+static void describe_object_stacks(struct kmem_cache *cache, void *object,
+                                       const void *addr, u8 tag)
 {
        struct kasan_alloc_meta *alloc_meta = kasan_get_alloc_meta(cache, object);
 
                }
 #endif
        }
+}
 
+static void describe_object(struct kmem_cache *cache, void *object,
+                               const void *addr, u8 tag)
+{
+       if (kasan_stack_collection_enabled())
+               describe_object_stacks(cache, object, addr, tag);
        describe_object_addr(cache, object, addr);
 }