diff --git a/sys-kernel/vanilla-kernel/Manifest b/sys-kernel/vanilla-kernel/Manifest
new file mode 100644
index 0000000..8a516f9
--- /dev/null
+++ b/sys-kernel/vanilla-kernel/Manifest
@@ -0,0 +1,7 @@
+DIST gentoo-kernel-config-g15.tar.gz 5746 BLAKE2B 2baef40e18cbf3bb975362d71ad674604ad80338d1e6914ffa2fc03af26f1b9c9cec66d39a56077693a7327060bc29bd5eb5787549857dd3779e841192cb2016 SHA512 45d609ee25a529988868b8c99c808f4e6abbde34ae501bf35523431ee85c9f3657663d6f938f2036a5f012a55a68965b32fd41fc44d8f6ca606a6fd38f952445
+DIST kernel-aarch64-fedora.config.6.13.6-gentoo 289529 BLAKE2B c7a8719c9d50c59fd9008e1e7f7e48d3f28b0d9b6be1fadfb10b48150b926550bfc2b57d6f5974be7d3cac3e4048f4747fb71c932a9503c97b45e0ea4be1ced0 SHA512 9da213c08991db8414f0b7497beeb73de999f7c16c455e24fa84f9ea95af7cfa3ddc13685a53271fb08ad2c1e122d030f38b3110e28073ddf5207f886da86095
+DIST kernel-i686-fedora.config.6.13.6-gentoo 256980 BLAKE2B 4e31d185ff5d1e7e1e906b26bc8379629cc1ebe2d7a7794c7fb72b63e13c6e44f8826f08b9af6da4edea5c498bf034663545f795b0740c469bd4b5366d559962 SHA512 8e4b39f056515c9d28d46706602c320f5a128b16dc177974463f74075881345471134957da7f4bd2f91ab818835319dd3f6560fcb8045ad6b3e8e7be4ec9017f
+DIST kernel-ppc64le-fedora.config.6.13.6-gentoo 242376 BLAKE2B fb6efa38ebd36971f3d156ff248fd14e150ce06d464805f823237590b22b4b0c7acc8df4d8ae74aa1731fcd76365fecc6de42736ca0a92fc330926a07263d3ab SHA512 710336dfc76fd8a3939fb682acc095bba6a7175dc24def4241437d383bdd133353738f1ee637df1fb41040fa2f4c03cdd95eac83e486d0fd0aaa07f9fc7d2671
+DIST kernel-x86_64-fedora.config.6.13.6-gentoo 258071 BLAKE2B 7a1ea7f365b5e817073393b01a0b9ba0164b39a43f42c601b4547891c76e2ba47878e00b2d91e936b9502ecfad32e6cc5755cf3f245395a209408667dc9c13ed SHA512 c343e9909726173eebccd2f71ed9d1c346ed7cb6d103a5cedf541e2fe8ac5b6646354c7244bbcca76b017e4db4badab89185e1f37bfb47858aef768376f6ca44
+DIST linux-6.14.tar.sign 985 BLAKE2B 95c87fcbbb86d0fb557f7910e47a04c5d83967f31af13ac551ef36610f4bb6e4a5c7690072e960fd348303c1593ca0e57c420e16cd5bb1c7ede385357dd51581 SHA512 5c43d24e7f45cccdc48a88f0130a631db91bcabf6d3ff963250679eaa0227c99fa18f94533d247573bbf1d429937009ab017f541fcaa6bae348ec5032cf35a8a
+DIST linux-6.14.tar.xz 149408504 BLAKE2B 11835719804b406fe281ea1c276a84dc0cbaa808552ddcca9233d3eaeb1c001d0455c7205379b02de8e8db758c1bae6fe7ceb6697e63e3cf9ae7187dc7a9715e SHA512 71dcaa3772d8d9797c3ae30cae9c582b11a7047a3bbcb8dfd479a4dffb40ff0da74cf3d45175f50cc9992e338bcadd46c9c570f54054ca3bde6661768d3d22eb
diff --git a/sys-kernel/vanilla-kernel/files/linux-6.14.amd64.config b/sys-kernel/vanilla-kernel/files/linux-6.14.amd64.config
new file mode 100644
index 0000000..98add8b
--- /dev/null
+++ b/sys-kernel/vanilla-kernel/files/linux-6.14.amd64.config
@@ -0,0 +1,7129 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Linux/x86 6.14.0 Kernel Configuration
+#
+CONFIG_CC_VERSION_TEXT="gcc (Gentoo Hardened 14.2.1_p20241221 p7) 14.2.1 20241221"
+CONFIG_CC_IS_GCC=y
+CONFIG_GCC_VERSION=140201
+CONFIG_CLANG_VERSION=0
+CONFIG_AS_IS_GNU=y
+CONFIG_AS_VERSION=24301
+CONFIG_LD_IS_BFD=y
+CONFIG_LD_VERSION=24301
+CONFIG_LLD_VERSION=0
+CONFIG_RUSTC_VERSION=108300
+CONFIG_RUSTC_LLVM_VERSION=190101
+CONFIG_CC_CAN_LINK=y
+CONFIG_CC_CAN_LINK_STATIC=y
+CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y
+CONFIG_CC_HAS_ASM_GOTO_TIED_OUTPUT=y
+CONFIG_TOOLS_SUPPORT_RELR=y
+CONFIG_CC_HAS_ASM_INLINE=y
+CONFIG_CC_HAS_NO_PROFILE_FN_ATTR=y
+CONFIG_PAHOLE_VERSION=127
+CONFIG_IRQ_WORK=y
+CONFIG_BUILDTIME_TABLE_SORT=y
+CONFIG_THREAD_INFO_IN_TASK=y
+
+#
+# General setup
+#
+CONFIG_INIT_ENV_ARG_LIMIT=32
+# CONFIG_COMPILE_TEST is not set
+CONFIG_WERROR=y
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_BUILD_SALT=""
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_BZIP2=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_XZ=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_HAVE_KERNEL_LZ4=y
+CONFIG_HAVE_KERNEL_ZSTD=y
+# CONFIG_KERNEL_GZIP is not set
+# CONFIG_KERNEL_BZIP2 is not set
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_XZ is not set
+# CONFIG_KERNEL_LZO is not set
+# CONFIG_KERNEL_LZ4 is not set
+CONFIG_KERNEL_ZSTD=y
+CONFIG_DEFAULT_INIT=""
+CONFIG_DEFAULT_HOSTNAME="gentoo"
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_SYSVIPC_COMPAT=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_POSIX_MQUEUE_SYSCTL=y
+CONFIG_WATCH_QUEUE=y
+CONFIG_CROSS_MEMORY_ATTACH=y
+# CONFIG_USELIB is not set
+CONFIG_AUDIT=y
+CONFIG_HAVE_ARCH_AUDITSYSCALL=y
+CONFIG_AUDITSYSCALL=y
+
+#
+# IRQ subsystem
+#
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
+CONFIG_GENERIC_PENDING_IRQ=y
+CONFIG_GENERIC_IRQ_MIGRATION=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_DOMAIN_HIERARCHY=y
+CONFIG_GENERIC_MSI_IRQ=y
+CONFIG_IRQ_MSI_IOMMU=y
+CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR=y
+CONFIG_GENERIC_IRQ_RESERVATION_MODE=y
+CONFIG_GENERIC_IRQ_STAT_SNAPSHOT=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_SPARSE_IRQ=y
+# CONFIG_GENERIC_IRQ_DEBUGFS is not set
+# end of IRQ subsystem
+
+CONFIG_CLOCKSOURCE_WATCHDOG=y
+CONFIG_ARCH_CLOCKSOURCE_INIT=y
+CONFIG_GENERIC_TIME_VSYSCALL=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST_IDLE=y
+CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+CONFIG_HAVE_POSIX_CPU_TIMERS_TASK_WORK=y
+CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
+CONFIG_CONTEXT_TRACKING=y
+CONFIG_CONTEXT_TRACKING_IDLE=y
+
+#
+# Timers subsystem
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ_COMMON=y
+# CONFIG_HZ_PERIODIC is not set
+CONFIG_NO_HZ_IDLE=y
+# CONFIG_NO_HZ_FULL is not set
+CONFIG_CONTEXT_TRACKING_USER=y
+CONFIG_CONTEXT_TRACKING_USER_FORCE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CLOCKSOURCE_WATCHDOG_MAX_SKEW_US=100
+# end of Timers subsystem
+
+CONFIG_BPF=y
+CONFIG_HAVE_EBPF_JIT=y
+CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y
+
+#
+# BPF subsystem
+#
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF_JIT=y
+CONFIG_BPF_JIT_ALWAYS_ON=y
+CONFIG_BPF_JIT_DEFAULT_ON=y
+CONFIG_BPF_UNPRIV_DEFAULT_OFF=y
+# CONFIG_BPF_PRELOAD is not set
+CONFIG_BPF_LSM=y
+# end of BPF subsystem
+
+CONFIG_PREEMPT_BUILD=y
+CONFIG_ARCH_HAS_PREEMPT_LAZY=y
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_PREEMPT_LAZY=y
+CONFIG_PREEMPT_COUNT=y
+CONFIG_PREEMPTION=y
+CONFIG_PREEMPT_DYNAMIC=y
+CONFIG_SCHED_CORE=y
+
+#
+# CPU/Task time and stats accounting
+#
+CONFIG_VIRT_CPU_ACCOUNTING=y
+# CONFIG_TICK_CPU_ACCOUNTING is not set
+CONFIG_VIRT_CPU_ACCOUNTING_GEN=y
+CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_SCHED_AVG_IRQ=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_PSI=y
+# CONFIG_PSI_DEFAULT_DISABLED is not set
+# end of CPU/Task time and stats accounting
+
+CONFIG_CPU_ISOLATION=y
+
+#
+# RCU Subsystem
+#
+CONFIG_TREE_RCU=y
+CONFIG_PREEMPT_RCU=y
+# CONFIG_RCU_EXPERT is not set
+CONFIG_TREE_SRCU=y
+CONFIG_TASKS_RCU_GENERIC=y
+CONFIG_NEED_TASKS_RCU=y
+CONFIG_TASKS_RCU=y
+CONFIG_TASKS_RUDE_RCU=y
+CONFIG_TASKS_TRACE_RCU=y
+CONFIG_RCU_STALL_COMMON=y
+CONFIG_RCU_NEED_SEGCBLIST=y
+# end of RCU Subsystem
+
+CONFIG_IKCONFIG=m
+CONFIG_IKCONFIG_PROC=y
+CONFIG_IKHEADERS=m
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=12
+CONFIG_PRINTK_INDEX=y
+CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y
+
+#
+# Scheduler features
+#
+# CONFIG_UCLAMP_TASK is not set
+# end of Scheduler features
+
+CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y
+CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y
+CONFIG_CC_HAS_INT128=y
+CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5"
+CONFIG_GCC10_NO_ARRAY_BOUNDS=y
+CONFIG_CC_NO_ARRAY_BOUNDS=y
+CONFIG_GCC_NO_STRINGOP_OVERFLOW=y
+CONFIG_CC_NO_STRINGOP_OVERFLOW=y
+CONFIG_ARCH_SUPPORTS_INT128=y
+# CONFIG_NUMA_BALANCING is not set
+CONFIG_SLAB_OBJ_EXT=y
+CONFIG_CGROUPS=y
+CONFIG_PAGE_COUNTER=y
+# CONFIG_CGROUP_FAVOR_DYNMODS is not set
+CONFIG_MEMCG=y
+# CONFIG_MEMCG_V1 is not set
+CONFIG_BLK_CGROUP=y
+CONFIG_CGROUP_WRITEBACK=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_GROUP_SCHED_WEIGHT=y
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_CFS_BANDWIDTH=y
+# CONFIG_RT_GROUP_SCHED is not set
+CONFIG_SCHED_MM_CID=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_RDMA=y
+CONFIG_CGROUP_DMEM=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_HUGETLB=y
+CONFIG_CPUSETS=y
+# CONFIG_CPUSETS_V1 is not set
+CONFIG_PROC_PID_CPUSET=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_CGROUP_MISC=y
+# CONFIG_CGROUP_DEBUG is not set
+CONFIG_SOCK_CGROUP_DATA=y
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_TIME_NS=y
+CONFIG_IPC_NS=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
+CONFIG_NET_NS=y
+CONFIG_CHECKPOINT_RESTORE=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_RD_GZIP is not set
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+CONFIG_RD_XZ=y
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_RD_ZSTD=y
+CONFIG_BOOT_CONFIG=y
+# CONFIG_BOOT_CONFIG_FORCE is not set
+# CONFIG_BOOT_CONFIG_EMBED is not set
+CONFIG_INITRAMFS_PRESERVE_MTIME=y
+CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_LD_ORPHAN_WARN=y
+CONFIG_LD_ORPHAN_WARN_LEVEL="error"
+CONFIG_SYSCTL=y
+CONFIG_HAVE_UID16=y
+CONFIG_SYSCTL_EXCEPTION_TRACE=y
+CONFIG_HAVE_PCSPKR_PLATFORM=y
+# CONFIG_EXPERT is not set
+CONFIG_UID16=y
+CONFIG_MULTIUSER=y
+CONFIG_SGETMASK_SYSCALL=y
+CONFIG_SYSFS_SYSCALL=y
+CONFIG_FHANDLE=y
+CONFIG_POSIX_TIMERS=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_PCSPKR_PLATFORM=y
+CONFIG_FUTEX=y
+CONFIG_FUTEX_PI=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_IO_URING=y
+CONFIG_ADVISE_SYSCALLS=y
+CONFIG_MEMBARRIER=y
+CONFIG_KCMP=y
+CONFIG_RSEQ=y
+CONFIG_CACHESTAT_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_SELFTEST is not set
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y
+CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_GUEST_PERF_EVENTS=y
+
+#
+# Kernel Performance Events And Counters
+#
+CONFIG_PERF_EVENTS=y
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+# end of Kernel Performance Events And Counters
+
+CONFIG_SYSTEM_DATA_VERIFICATION=y
+CONFIG_PROFILING=y
+CONFIG_TRACEPOINTS=y
+
+#
+# Kexec and crash features
+#
+# CONFIG_KEXEC is not set
+# CONFIG_KEXEC_FILE is not set
+# end of Kexec and crash features
+# end of General setup
+
+CONFIG_64BIT=y
+CONFIG_X86_64=y
+CONFIG_X86=y
+CONFIG_INSTRUCTION_DECODER=y
+CONFIG_OUTPUT_FORMAT="elf64-x86-64"
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_MMU=y
+CONFIG_ARCH_MMAP_RND_BITS_MIN=28
+CONFIG_ARCH_MMAP_RND_BITS_MAX=32
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
+CONFIG_ARCH_MAY_HAVE_PC_FDC=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_HAS_CPU_RELAX=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_AUDIT_ARCH=y
+CONFIG_HAVE_INTEL_TXT=y
+CONFIG_X86_64_SMP=y
+CONFIG_ARCH_SUPPORTS_UPROBES=y
+CONFIG_FIX_EARLYCON_MEM=y
+CONFIG_PGTABLE_LEVELS=4
+CONFIG_CC_HAS_SANE_STACKPROTECTOR=y
+
+#
+# Processor type and features
+#
+CONFIG_SMP=y
+CONFIG_X86_X2APIC=y
+CONFIG_X86_POSTED_MSI=y
+CONFIG_X86_MPPARSE=y
+CONFIG_X86_CPU_RESCTRL=y
+CONFIG_X86_FRED=y
+# CONFIG_X86_EXTENDED_PLATFORM is not set
+CONFIG_X86_INTEL_LPSS=y
+CONFIG_X86_AMD_PLATFORM_DEVICE=y
+CONFIG_IOSF_MBI=y
+# CONFIG_IOSF_MBI_DEBUG is not set
+CONFIG_X86_SUPPORTS_MEMORY_FAILURE=y
+CONFIG_SCHED_OMIT_FRAME_POINTER=y
+CONFIG_HYPERVISOR_GUEST=y
+CONFIG_PARAVIRT=y
+# CONFIG_PARAVIRT_DEBUG is not set
+CONFIG_PARAVIRT_SPINLOCKS=y
+CONFIG_X86_HV_CALLBACK_VECTOR=y
+# CONFIG_XEN is not set
+CONFIG_KVM_GUEST=y
+CONFIG_ARCH_CPUIDLE_HALTPOLL=y
+CONFIG_PVH=y
+CONFIG_PARAVIRT_TIME_ACCOUNTING=y
+CONFIG_PARAVIRT_CLOCK=y
+# CONFIG_JAILHOUSE_GUEST is not set
+# CONFIG_ACRN_GUEST is not set
+# CONFIG_INTEL_TDX_GUEST is not set
+# CONFIG_MK8 is not set
+# CONFIG_MPSC is not set
+# CONFIG_MCORE2 is not set
+# CONFIG_MATOM is not set
+CONFIG_GENERIC_CPU=y
+CONFIG_X86_INTERNODE_CACHE_SHIFT=6
+CONFIG_X86_L1_CACHE_SHIFT=6
+CONFIG_X86_TSC=y
+CONFIG_X86_HAVE_PAE=y
+CONFIG_X86_CMPXCHG64=y
+CONFIG_X86_CMOV=y
+CONFIG_X86_MINIMUM_CPU_FAMILY=64
+CONFIG_X86_DEBUGCTLMSR=y
+CONFIG_IA32_FEAT_CTL=y
+CONFIG_X86_VMX_FEATURE_NAMES=y
+CONFIG_CPU_SUP_INTEL=y
+CONFIG_CPU_SUP_AMD=y
+CONFIG_CPU_SUP_HYGON=y
+CONFIG_CPU_SUP_CENTAUR=y
+CONFIG_CPU_SUP_ZHAOXIN=y
+CONFIG_HPET_TIMER=y
+CONFIG_HPET_EMULATE_RTC=y
+CONFIG_DMI=y
+# CONFIG_GART_IOMMU is not set
+CONFIG_BOOT_VESA_SUPPORT=y
+# CONFIG_MAXSMP is not set
+CONFIG_NR_CPUS_RANGE_BEGIN=2
+CONFIG_NR_CPUS_RANGE_END=512
+CONFIG_NR_CPUS_DEFAULT=64
+CONFIG_NR_CPUS=8
+CONFIG_SCHED_CLUSTER=y
+CONFIG_SCHED_SMT=y
+CONFIG_SCHED_MC=y
+CONFIG_SCHED_MC_PRIO=y
+CONFIG_X86_LOCAL_APIC=y
+CONFIG_ACPI_MADT_WAKEUP=y
+CONFIG_X86_IO_APIC=y
+CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y
+CONFIG_X86_MCE=y
+# CONFIG_X86_MCELOG_LEGACY is not set
+CONFIG_X86_MCE_INTEL=y
+CONFIG_X86_MCE_AMD=y
+CONFIG_X86_MCE_THRESHOLD=y
+# CONFIG_X86_MCE_INJECT is not set
+
+#
+# Performance monitoring
+#
+CONFIG_PERF_EVENTS_INTEL_UNCORE=y
+CONFIG_PERF_EVENTS_INTEL_RAPL=y
+CONFIG_PERF_EVENTS_INTEL_CSTATE=y
+# CONFIG_PERF_EVENTS_AMD_POWER is not set
+CONFIG_PERF_EVENTS_AMD_UNCORE=m
+# CONFIG_PERF_EVENTS_AMD_BRS is not set
+# end of Performance monitoring
+
+CONFIG_X86_16BIT=y
+CONFIG_X86_ESPFIX64=y
+CONFIG_X86_VSYSCALL_EMULATION=y
+# CONFIG_X86_IOPL_IOPERM is not set
+CONFIG_MICROCODE=y
+# CONFIG_MICROCODE_LATE_LOADING is not set
+CONFIG_X86_MSR=m
+CONFIG_X86_CPUID=m
+# CONFIG_X86_5LEVEL is not set
+CONFIG_X86_DIRECT_GBPAGES=y
+# CONFIG_X86_CPA_STATISTICS is not set
+# CONFIG_AMD_MEM_ENCRYPT is not set
+CONFIG_NUMA=y
+# CONFIG_AMD_NUMA is not set
+CONFIG_X86_64_ACPI_NUMA=y
+CONFIG_NODES_SHIFT=2
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+# CONFIG_ARCH_MEMORY_PROBE is not set
+CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
+CONFIG_X86_PMEM_LEGACY_DEVICE=y
+CONFIG_X86_PMEM_LEGACY=m
+CONFIG_X86_CHECK_BIOS_CORRUPTION=y
+CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK=y
+CONFIG_MTRR=y
+CONFIG_MTRR_SANITIZER=y
+CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=1
+CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT=0
+CONFIG_X86_PAT=y
+CONFIG_X86_UMIP=y
+CONFIG_CC_HAS_IBT=y
+CONFIG_X86_CET=y
+CONFIG_X86_KERNEL_IBT=y
+CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS=y
+CONFIG_ARCH_PKEY_BITS=4
+# CONFIG_X86_INTEL_TSX_MODE_OFF is not set
+# CONFIG_X86_INTEL_TSX_MODE_ON is not set
+CONFIG_X86_INTEL_TSX_MODE_AUTO=y
+# CONFIG_X86_SGX is not set
+# CONFIG_X86_USER_SHADOW_STACK is not set
+# CONFIG_INTEL_TDX_HOST is not set
+CONFIG_EFI=y
+CONFIG_EFI_STUB=y
+# CONFIG_EFI_HANDOVER_PROTOCOL is not set
+# CONFIG_EFI_MIXED is not set
+# CONFIG_HZ_100 is not set
+# CONFIG_HZ_250 is not set
+CONFIG_HZ_300=y
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=300
+CONFIG_SCHED_HRTICK=y
+CONFIG_ARCH_SUPPORTS_KEXEC=y
+CONFIG_ARCH_SUPPORTS_KEXEC_FILE=y
+CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY=y
+CONFIG_ARCH_SUPPORTS_KEXEC_SIG=y
+CONFIG_ARCH_SUPPORTS_KEXEC_SIG_FORCE=y
+CONFIG_ARCH_SUPPORTS_KEXEC_BZIMAGE_VERIFY_SIG=y
+CONFIG_ARCH_SUPPORTS_KEXEC_JUMP=y
+CONFIG_ARCH_SUPPORTS_CRASH_DUMP=y
+CONFIG_ARCH_DEFAULT_CRASH_DUMP=y
+CONFIG_ARCH_SUPPORTS_CRASH_HOTPLUG=y
+CONFIG_PHYSICAL_START=0x1000000
+CONFIG_RELOCATABLE=y
+CONFIG_RANDOMIZE_BASE=y
+CONFIG_X86_NEED_RELOCS=y
+CONFIG_PHYSICAL_ALIGN=0x1000000
+CONFIG_DYNAMIC_MEMORY_LAYOUT=y
+CONFIG_RANDOMIZE_MEMORY=y
+CONFIG_RANDOMIZE_MEMORY_PHYSICAL_PADDING=0xa
+CONFIG_HOTPLUG_CPU=y
+# CONFIG_COMPAT_VDSO is not set
+# CONFIG_LEGACY_VSYSCALL_XONLY is not set
+CONFIG_LEGACY_VSYSCALL_NONE=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="vdso32=0 page_poison=1 page_alloc.shuffle=1 slab_nomerge pti=on"
+# CONFIG_CMDLINE_OVERRIDE is not set
+CONFIG_MODIFY_LDT_SYSCALL=y
+# CONFIG_STRICT_SIGALTSTACK_SIZE is not set
+CONFIG_HAVE_LIVEPATCH=y
+CONFIG_X86_BUS_LOCK_DETECT=y
+# end of Processor type and features
+
+CONFIG_CC_HAS_NAMED_AS=y
+CONFIG_CC_HAS_NAMED_AS_FIXED_SANITIZERS=y
+CONFIG_USE_X86_SEG_SUPPORT=y
+CONFIG_CC_HAS_SLS=y
+CONFIG_CC_HAS_RETURN_THUNK=y
+CONFIG_CC_HAS_ENTRY_PADDING=y
+CONFIG_FUNCTION_PADDING_CFI=11
+CONFIG_FUNCTION_PADDING_BYTES=16
+CONFIG_CALL_PADDING=y
+CONFIG_HAVE_CALL_THUNKS=y
+CONFIG_CALL_THUNKS=y
+CONFIG_PREFIX_SYMBOLS=y
+CONFIG_CPU_MITIGATIONS=y
+CONFIG_MITIGATION_PAGE_TABLE_ISOLATION=y
+CONFIG_MITIGATION_RETPOLINE=y
+CONFIG_MITIGATION_RETHUNK=y
+CONFIG_MITIGATION_UNRET_ENTRY=y
+CONFIG_MITIGATION_CALL_DEPTH_TRACKING=y
+# CONFIG_CALL_THUNKS_DEBUG is not set
+CONFIG_MITIGATION_IBPB_ENTRY=y
+CONFIG_MITIGATION_IBRS_ENTRY=y
+CONFIG_MITIGATION_SRSO=y
+CONFIG_MITIGATION_SLS=y
+CONFIG_MITIGATION_GDS=y
+CONFIG_MITIGATION_RFDS=y
+CONFIG_MITIGATION_SPECTRE_BHI=y
+CONFIG_MITIGATION_MDS=y
+CONFIG_MITIGATION_TAA=y
+CONFIG_MITIGATION_MMIO_STALE_DATA=y
+CONFIG_MITIGATION_L1TF=y
+CONFIG_MITIGATION_RETBLEED=y
+CONFIG_MITIGATION_SPECTRE_V1=y
+CONFIG_MITIGATION_SPECTRE_V2=y
+CONFIG_MITIGATION_SRBDS=y
+CONFIG_MITIGATION_SSB=y
+CONFIG_ARCH_HAS_ADD_PAGES=y
+
+#
+# Power management and ACPI options
+#
+CONFIG_ARCH_HIBERNATION_HEADER=y
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_HIBERNATE_CALLBACKS=y
+CONFIG_HIBERNATION=y
+CONFIG_HIBERNATION_SNAPSHOT_DEV=y
+CONFIG_HIBERNATION_COMP_LZO=y
+# CONFIG_HIBERNATION_COMP_LZ4 is not set
+CONFIG_HIBERNATION_DEF_COMP="lzo"
+CONFIG_PM_STD_PARTITION=""
+CONFIG_PM_SLEEP=y
+CONFIG_PM_SLEEP_SMP=y
+CONFIG_PM_AUTOSLEEP=y
+# CONFIG_PM_USERSPACE_AUTOSLEEP is not set
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=100
+CONFIG_PM_WAKELOCKS_GC=y
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_CLK=y
+CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y
+CONFIG_ENERGY_MODEL=y
+CONFIG_ARCH_SUPPORTS_ACPI=y
+CONFIG_ACPI=y
+CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y
+CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC=y
+CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y
+CONFIG_ACPI_THERMAL_LIB=y
+# CONFIG_ACPI_DEBUGGER is not set
+CONFIG_ACPI_SPCR_TABLE=y
+CONFIG_ACPI_FPDT=y
+CONFIG_ACPI_LPIT=y
+CONFIG_ACPI_SLEEP=y
+CONFIG_ACPI_REV_OVERRIDE_POSSIBLE=y
+CONFIG_ACPI_EC=y
+CONFIG_ACPI_EC_DEBUGFS=m
+CONFIG_ACPI_AC=y
+CONFIG_ACPI_BATTERY=m
+CONFIG_ACPI_BUTTON=y
+CONFIG_ACPI_VIDEO=m
+CONFIG_ACPI_FAN=y
+CONFIG_ACPI_TAD=m
+# CONFIG_ACPI_DOCK is not set
+CONFIG_ACPI_CPU_FREQ_PSS=y
+CONFIG_ACPI_PROCESSOR_CSTATE=y
+CONFIG_ACPI_PROCESSOR_IDLE=y
+CONFIG_ACPI_CPPC_LIB=y
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_HOTPLUG_CPU=y
+CONFIG_ACPI_PROCESSOR_AGGREGATOR=y
+CONFIG_ACPI_THERMAL=y
+CONFIG_ACPI_PLATFORM_PROFILE=m
+CONFIG_ARCH_HAS_ACPI_TABLE_UPGRADE=y
+# CONFIG_ACPI_TABLE_UPGRADE is not set
+# CONFIG_ACPI_DEBUG is not set
+CONFIG_ACPI_PCI_SLOT=y
+CONFIG_ACPI_CONTAINER=y
+CONFIG_ACPI_HOTPLUG_MEMORY=y
+CONFIG_ACPI_HOTPLUG_IOAPIC=y
+CONFIG_ACPI_SBS=m
+CONFIG_ACPI_HED=y
+CONFIG_ACPI_BGRT=y
+CONFIG_ACPI_NHLT=y
+# CONFIG_ACPI_NFIT is not set
+CONFIG_ACPI_NUMA=y
+# CONFIG_ACPI_HMAT is not set
+CONFIG_HAVE_ACPI_APEI=y
+CONFIG_HAVE_ACPI_APEI_NMI=y
+CONFIG_ACPI_APEI=y
+CONFIG_ACPI_APEI_GHES=y
+CONFIG_ACPI_APEI_PCIEAER=y
+CONFIG_ACPI_APEI_MEMORY_FAILURE=y
+# CONFIG_ACPI_APEI_EINJ is not set
+# CONFIG_ACPI_APEI_ERST_DEBUG is not set
+# CONFIG_ACPI_DPTF is not set
+CONFIG_ACPI_EXTLOG=m
+CONFIG_ACPI_CONFIGFS=m
+# CONFIG_ACPI_PFRUT is not set
+CONFIG_ACPI_PCC=y
+# CONFIG_ACPI_FFH is not set
+CONFIG_PMIC_OPREGION=y
+CONFIG_ACPI_VIOT=y
+CONFIG_ACPI_PRMT=y
+CONFIG_X86_PM_TIMER=y
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_ATTR_SET=y
+CONFIG_CPU_FREQ_GOV_COMMON=y
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+
+#
+# CPU frequency scaling drivers
+#
+CONFIG_X86_INTEL_PSTATE=y
+CONFIG_X86_PCC_CPUFREQ=y
+CONFIG_X86_AMD_PSTATE=y
+CONFIG_X86_AMD_PSTATE_DEFAULT_MODE=3
+CONFIG_X86_AMD_PSTATE_UT=m
+CONFIG_X86_ACPI_CPUFREQ=y
+# CONFIG_X86_ACPI_CPUFREQ_CPB is not set
+CONFIG_X86_POWERNOW_K8=m
+CONFIG_X86_AMD_FREQ_SENSITIVITY=m
+# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
+# CONFIG_X86_P4_CLOCKMOD is not set
+
+#
+# shared options
+#
+# end of CPU Frequency scaling
+
+#
+# CPU Idle
+#
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+CONFIG_CPU_IDLE_GOV_TEO=y
+CONFIG_CPU_IDLE_GOV_HALTPOLL=y
+CONFIG_HALTPOLL_CPUIDLE=y
+# end of CPU Idle
+
+CONFIG_INTEL_IDLE=y
+# end of Power management and ACPI options
+
+#
+# Bus options (PCI etc.)
+#
+CONFIG_PCI_DIRECT=y
+CONFIG_PCI_MMCONFIG=y
+CONFIG_MMCONF_FAM10H=y
+CONFIG_ISA_DMA_API=y
+CONFIG_AMD_NB=y
+CONFIG_AMD_NODE=y
+# end of Bus options (PCI etc.)
+
+#
+# Binary Emulations
+#
+CONFIG_IA32_EMULATION=y
+# CONFIG_IA32_EMULATION_DEFAULT_DISABLED is not set
+# CONFIG_X86_X32_ABI is not set
+CONFIG_COMPAT_32=y
+CONFIG_COMPAT=y
+CONFIG_COMPAT_FOR_U64_ALIGNMENT=y
+# end of Binary Emulations
+
+CONFIG_KVM_COMMON=y
+CONFIG_HAVE_KVM_PFNCACHE=y
+CONFIG_HAVE_KVM_IRQCHIP=y
+CONFIG_HAVE_KVM_IRQ_ROUTING=y
+CONFIG_HAVE_KVM_DIRTY_RING=y
+CONFIG_HAVE_KVM_DIRTY_RING_TSO=y
+CONFIG_HAVE_KVM_DIRTY_RING_ACQ_REL=y
+CONFIG_KVM_MMIO=y
+CONFIG_KVM_ASYNC_PF=y
+CONFIG_HAVE_KVM_MSI=y
+CONFIG_HAVE_KVM_READONLY_MEM=y
+CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT=y
+CONFIG_KVM_VFIO=y
+CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT=y
+CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY=y
+CONFIG_KVM_COMPAT=y
+CONFIG_HAVE_KVM_IRQ_BYPASS=y
+CONFIG_HAVE_KVM_NO_POLL=y
+CONFIG_KVM_XFER_TO_GUEST_WORK=y
+CONFIG_HAVE_KVM_PM_NOTIFIER=y
+CONFIG_KVM_GENERIC_HARDWARE_ENABLING=y
+CONFIG_KVM_GENERIC_MMU_NOTIFIER=y
+CONFIG_KVM_ELIDE_TLB_FLUSH_IF_YOUNG=y
+CONFIG_KVM_GENERIC_MEMORY_ATTRIBUTES=y
+CONFIG_KVM_PRIVATE_MEM=y
+CONFIG_KVM_GENERIC_PRIVATE_MEM=y
+CONFIG_HAVE_KVM_ARCH_GMEM_PREPARE=y
+CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE=y
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM_X86=m
+CONFIG_KVM=m
+CONFIG_KVM_WERROR=y
+CONFIG_KVM_INTEL=m
+CONFIG_KVM_AMD=m
+CONFIG_KVM_AMD_SEV=y
+CONFIG_KVM_SMM=y
+# CONFIG_KVM_HYPERV is not set
+# CONFIG_KVM_XEN is not set
+CONFIG_KVM_EXTERNAL_WRITE_TRACKING=y
+CONFIG_KVM_MAX_NR_VCPUS=1024
+CONFIG_AS_AVX512=y
+CONFIG_AS_SHA1_NI=y
+CONFIG_AS_SHA256_NI=y
+CONFIG_AS_TPAUSE=y
+CONFIG_AS_GFNI=y
+CONFIG_AS_VAES=y
+CONFIG_AS_VPCLMULQDQ=y
+CONFIG_AS_WRUSS=y
+CONFIG_ARCH_CONFIGURES_CPU_MITIGATIONS=y
+
+#
+# General architecture-dependent options
+#
+CONFIG_HOTPLUG_SMT=y
+CONFIG_HOTPLUG_CORE_SYNC=y
+CONFIG_HOTPLUG_CORE_SYNC_DEAD=y
+CONFIG_HOTPLUG_CORE_SYNC_FULL=y
+CONFIG_HOTPLUG_SPLIT_STARTUP=y
+CONFIG_HOTPLUG_PARALLEL=y
+CONFIG_GENERIC_ENTRY=y
+CONFIG_KPROBES=y
+CONFIG_JUMP_LABEL=y
+# CONFIG_STATIC_KEYS_SELFTEST is not set
+# CONFIG_STATIC_CALL_SELFTEST is not set
+CONFIG_OPTPROBES=y
+CONFIG_KPROBES_ON_FTRACE=y
+CONFIG_UPROBES=y
+CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_KRETPROBES=y
+CONFIG_KRETPROBE_ON_RETHOOK=y
+CONFIG_USER_RETURN_NOTIFIER=y
+CONFIG_HAVE_IOREMAP_PROT=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_OPTPROBES=y
+CONFIG_HAVE_KPROBES_ON_FTRACE=y
+CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y
+CONFIG_HAVE_FUNCTION_ERROR_INJECTION=y
+CONFIG_HAVE_NMI=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_DMA_CONTIGUOUS=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_ARCH_HAS_FORTIFY_SOURCE=y
+CONFIG_ARCH_HAS_SET_MEMORY=y
+CONFIG_ARCH_HAS_SET_DIRECT_MAP=y
+CONFIG_ARCH_HAS_CPU_FINALIZE_INIT=y
+CONFIG_ARCH_HAS_CPU_PASID=y
+CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y
+CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT=y
+CONFIG_ARCH_WANTS_NO_INSTR=y
+CONFIG_HAVE_ASM_MODVERSIONS=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_RSEQ=y
+CONFIG_HAVE_RUST=y
+CONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y
+CONFIG_HAVE_USER_RETURN_NOTIFIER=y
+CONFIG_HAVE_PERF_EVENTS_NMI=y
+CONFIG_HAVE_HARDLOCKUP_DETECTOR_PERF=y
+CONFIG_HAVE_PERF_REGS=y
+CONFIG_HAVE_PERF_USER_STACK_DUMP=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y
+CONFIG_MMU_GATHER_TABLE_FREE=y
+CONFIG_MMU_GATHER_RCU_TABLE_FREE=y
+CONFIG_MMU_GATHER_MERGE_VMAS=y
+CONFIG_MMU_LAZY_TLB_REFCOUNT=y
+CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y
+CONFIG_ARCH_HAVE_EXTRA_ELF_NOTES=y
+CONFIG_ARCH_HAS_NMI_SAFE_THIS_CPU_OPS=y
+CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y
+CONFIG_HAVE_CMPXCHG_LOCAL=y
+CONFIG_HAVE_CMPXCHG_DOUBLE=y
+CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION=y
+CONFIG_ARCH_WANT_OLD_COMPAT_IPC=y
+CONFIG_HAVE_ARCH_SECCOMP=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_SECCOMP=y
+CONFIG_SECCOMP_FILTER=y
+# CONFIG_SECCOMP_CACHE_DEBUG is not set
+CONFIG_HAVE_ARCH_STACKLEAK=y
+CONFIG_HAVE_STACKPROTECTOR=y
+CONFIG_STACKPROTECTOR=y
+CONFIG_STACKPROTECTOR_STRONG=y
+CONFIG_ARCH_SUPPORTS_LTO_CLANG=y
+CONFIG_ARCH_SUPPORTS_LTO_CLANG_THIN=y
+CONFIG_LTO_NONE=y
+CONFIG_ARCH_SUPPORTS_AUTOFDO_CLANG=y
+CONFIG_ARCH_SUPPORTS_PROPELLER_CLANG=y
+CONFIG_ARCH_SUPPORTS_CFI_CLANG=y
+CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES=y
+CONFIG_HAVE_CONTEXT_TRACKING_USER=y
+CONFIG_HAVE_CONTEXT_TRACKING_USER_OFFSTACK=y
+CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_MOVE_PUD=y
+CONFIG_HAVE_MOVE_PMD=y
+CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y
+CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD=y
+CONFIG_HAVE_ARCH_HUGE_VMAP=y
+CONFIG_HAVE_ARCH_HUGE_VMALLOC=y
+CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y
+CONFIG_ARCH_WANT_PMD_MKWRITE=y
+CONFIG_HAVE_ARCH_SOFT_DIRTY=y
+CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
+CONFIG_MODULES_USE_ELF_RELA=y
+CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=y
+CONFIG_HAVE_SOFTIRQ_ON_OWN_STACK=y
+CONFIG_SOFTIRQ_ON_OWN_STACK=y
+CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
+CONFIG_HAVE_ARCH_MMAP_RND_BITS=y
+CONFIG_HAVE_EXIT_THREAD=y
+CONFIG_ARCH_MMAP_RND_BITS=28
+CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS=y
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS=8
+CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES=y
+CONFIG_HAVE_PAGE_SIZE_4KB=y
+CONFIG_PAGE_SIZE_4KB=y
+CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
+CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
+CONFIG_PAGE_SHIFT=12
+CONFIG_HAVE_OBJTOOL=y
+CONFIG_HAVE_JUMP_LABEL_HACK=y
+CONFIG_HAVE_NOINSTR_HACK=y
+CONFIG_HAVE_NOINSTR_VALIDATION=y
+CONFIG_HAVE_UACCESS_VALIDATION=y
+CONFIG_HAVE_STACK_VALIDATION=y
+CONFIG_HAVE_RELIABLE_STACKTRACE=y
+CONFIG_OLD_SIGSUSPEND3=y
+CONFIG_COMPAT_OLD_SIGACTION=y
+CONFIG_COMPAT_32BIT_TIME=y
+CONFIG_ARCH_SUPPORTS_RT=y
+CONFIG_HAVE_ARCH_VMAP_STACK=y
+CONFIG_VMAP_STACK=y
+CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET=y
+CONFIG_RANDOMIZE_KSTACK_OFFSET=y
+CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y
+CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y
+CONFIG_STRICT_KERNEL_RWX=y
+CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y
+CONFIG_STRICT_MODULE_RWX=y
+CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y
+CONFIG_ARCH_USE_MEMREMAP_PROT=y
+CONFIG_LOCK_EVENT_COUNTS=y
+CONFIG_ARCH_HAS_MEM_ENCRYPT=y
+CONFIG_ARCH_HAS_CC_PLATFORM=y
+CONFIG_HAVE_STATIC_CALL=y
+CONFIG_HAVE_STATIC_CALL_INLINE=y
+CONFIG_HAVE_PREEMPT_DYNAMIC=y
+CONFIG_HAVE_PREEMPT_DYNAMIC_CALL=y
+CONFIG_ARCH_WANT_LD_ORPHAN_WARN=y
+CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y
+CONFIG_ARCH_SUPPORTS_PAGE_TABLE_CHECK=y
+CONFIG_ARCH_HAS_ELFCORE_COMPAT=y
+CONFIG_ARCH_HAS_PARANOID_L1D_FLUSH=y
+CONFIG_DYNAMIC_SIGFRAME=y
+CONFIG_ARCH_HAS_HW_PTE_YOUNG=y
+CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG=y
+CONFIG_ARCH_HAS_KERNEL_FPU_SUPPORT=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
+# end of GCOV-based kernel profiling
+
+CONFIG_HAVE_GCC_PLUGINS=y
+CONFIG_GCC_PLUGINS=y
+CONFIG_GCC_PLUGIN_LATENT_ENTROPY=y
+CONFIG_FUNCTION_ALIGNMENT_4B=y
+CONFIG_FUNCTION_ALIGNMENT_16B=y
+CONFIG_FUNCTION_ALIGNMENT=16
+CONFIG_CC_HAS_MIN_FUNCTION_ALIGNMENT=y
+CONFIG_CC_HAS_SANE_FUNCTION_ALIGNMENT=y
+# end of General architecture-dependent options
+
+CONFIG_RT_MUTEXES=y
+CONFIG_MODULE_SIG_FORMAT=y
+CONFIG_MODULES=y
+CONFIG_MODULE_DEBUGFS=y
+# CONFIG_MODULE_DEBUG is not set
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODULE_UNLOAD_TAINT_TRACKING=y
+CONFIG_MODVERSIONS=y
+CONFIG_GENKSYMS=y
+CONFIG_ASM_MODVERSIONS=y
+# CONFIG_EXTENDED_MODVERSIONS is not set
+CONFIG_BASIC_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_MODULE_SIG=y
+# CONFIG_MODULE_SIG_FORCE is not set
+CONFIG_MODULE_SIG_ALL=y
+CONFIG_MODULE_SIG_SHA1=y
+# CONFIG_MODULE_SIG_SHA256 is not set
+# CONFIG_MODULE_SIG_SHA384 is not set
+# CONFIG_MODULE_SIG_SHA512 is not set
+# CONFIG_MODULE_SIG_SHA3_256 is not set
+# CONFIG_MODULE_SIG_SHA3_384 is not set
+# CONFIG_MODULE_SIG_SHA3_512 is not set
+CONFIG_MODULE_SIG_HASH="sha1"
+CONFIG_MODULE_COMPRESS=y
+# CONFIG_MODULE_COMPRESS_GZIP is not set
+# CONFIG_MODULE_COMPRESS_XZ is not set
+CONFIG_MODULE_COMPRESS_ZSTD=y
+CONFIG_MODULE_COMPRESS_ALL=y
+CONFIG_MODULE_DECOMPRESS=y
+# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set
+CONFIG_MODPROBE_PATH="/sbin/modprobe"
+CONFIG_TRIM_UNUSED_KSYMS=y
+CONFIG_UNUSED_KSYMS_WHITELIST=""
+CONFIG_MODULES_TREE_LOOKUP=y
+CONFIG_BLOCK=y
+CONFIG_BLOCK_LEGACY_AUTOLOAD=y
+CONFIG_BLK_RQ_ALLOC_TIME=y
+CONFIG_BLK_CGROUP_RWSTAT=y
+CONFIG_BLK_CGROUP_PUNT_BIO=y
+CONFIG_BLK_DEV_BSG_COMMON=y
+CONFIG_BLK_ICQ=y
+CONFIG_BLK_DEV_BSGLIB=y
+CONFIG_BLK_DEV_INTEGRITY=y
+CONFIG_BLK_DEV_WRITE_MOUNTED=y
+CONFIG_BLK_DEV_ZONED=y
+CONFIG_BLK_DEV_THROTTLING=y
+CONFIG_BLK_WBT=y
+CONFIG_BLK_WBT_MQ=y
+CONFIG_BLK_CGROUP_IOLATENCY=y
+CONFIG_BLK_CGROUP_IOCOST=y
+CONFIG_BLK_CGROUP_IOPRIO=y
+CONFIG_BLK_DEBUG_FS=y
+# CONFIG_BLK_SED_OPAL is not set
+CONFIG_BLK_INLINE_ENCRYPTION=y
+CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_AIX_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+CONFIG_EFI_PARTITION=y
+# CONFIG_SYSV68_PARTITION is not set
+# CONFIG_CMDLINE_PARTITION is not set
+# end of Partition Types
+
+CONFIG_BLK_MQ_PCI=y
+CONFIG_BLK_MQ_VIRTIO=y
+CONFIG_BLK_PM=y
+CONFIG_BLOCK_HOLDER_DEPRECATED=y
+CONFIG_BLK_MQ_STACKING=y
+
+#
+# IO Schedulers
+#
+CONFIG_MQ_IOSCHED_DEADLINE=y
+CONFIG_MQ_IOSCHED_KYBER=y
+CONFIG_IOSCHED_BFQ=y
+CONFIG_BFQ_GROUP_IOSCHED=y
+# CONFIG_BFQ_CGROUP_DEBUG is not set
+# end of IO Schedulers
+
+CONFIG_PREEMPT_NOTIFIERS=y
+CONFIG_PADATA=y
+CONFIG_ASN1=y
+CONFIG_UNINLINE_SPIN_UNLOCK=y
+CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+CONFIG_RWSEM_SPIN_ON_OWNER=y
+CONFIG_LOCK_SPIN_ON_OWNER=y
+CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y
+CONFIG_QUEUED_SPINLOCKS=y
+CONFIG_ARCH_USE_QUEUED_RWLOCKS=y
+CONFIG_QUEUED_RWLOCKS=y
+CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE=y
+CONFIG_ARCH_HAS_SYNC_CORE_BEFORE_USERMODE=y
+CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y
+CONFIG_FREEZER=y
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_COMPAT_BINFMT_ELF=y
+CONFIG_ELFCORE=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_SCRIPT=y
+# CONFIG_BINFMT_MISC is not set
+CONFIG_COREDUMP=y
+# end of Executable file formats
+
+#
+# Memory Management options
+#
+CONFIG_ZPOOL=y
+CONFIG_SWAP=y
+CONFIG_ZSWAP=y
+CONFIG_ZSWAP_DEFAULT_ON=y
+CONFIG_ZSWAP_SHRINKER_DEFAULT_ON=y
+# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_DEFLATE is not set
+# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZO is not set
+# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_842 is not set
+# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZ4 is not set
+# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZ4HC is not set
+CONFIG_ZSWAP_COMPRESSOR_DEFAULT_ZSTD=y
+CONFIG_ZSWAP_COMPRESSOR_DEFAULT="zstd"
+# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD is not set
+# CONFIG_ZSWAP_ZPOOL_DEFAULT_Z3FOLD_DEPRECATED is not set
+CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC=y
+CONFIG_ZSWAP_ZPOOL_DEFAULT="zsmalloc"
+CONFIG_ZBUD=y
+# CONFIG_Z3FOLD_DEPRECATED is not set
+CONFIG_ZSMALLOC=y
+# CONFIG_ZSMALLOC_STAT is not set
+CONFIG_ZSMALLOC_CHAIN_SIZE=8
+
+#
+# Slab allocator options
+#
+CONFIG_SLUB=y
+# CONFIG_SLAB_MERGE_DEFAULT is not set
+CONFIG_SLAB_FREELIST_RANDOM=y
+CONFIG_SLAB_FREELIST_HARDENED=y
+CONFIG_SLAB_BUCKETS=y
+# CONFIG_SLUB_STATS is not set
+CONFIG_SLUB_CPU_PARTIAL=y
+CONFIG_RANDOM_KMALLOC_CACHES=y
+# end of Slab allocator options
+
+CONFIG_SHUFFLE_PAGE_ALLOCATOR=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SPARSEMEM=y
+CONFIG_SPARSEMEM_EXTREME=y
+CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
+CONFIG_SPARSEMEM_VMEMMAP=y
+CONFIG_ARCH_WANT_OPTIMIZE_DAX_VMEMMAP=y
+CONFIG_ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP=y
+CONFIG_HAVE_GUP_FAST=y
+CONFIG_NUMA_KEEP_MEMINFO=y
+CONFIG_MEMORY_ISOLATION=y
+CONFIG_EXCLUSIVE_SYSTEM_RAM=y
+CONFIG_HAVE_BOOTMEM_INFO_NODE=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y
+CONFIG_MEMORY_HOTPLUG=y
+# CONFIG_MHP_DEFAULT_ONLINE_TYPE_OFFLINE is not set
+CONFIG_MHP_DEFAULT_ONLINE_TYPE_ONLINE_AUTO=y
+# CONFIG_MHP_DEFAULT_ONLINE_TYPE_ONLINE_KERNEL is not set
+# CONFIG_MHP_DEFAULT_ONLINE_TYPE_ONLINE_MOVABLE is not set
+CONFIG_MEMORY_HOTREMOVE=y
+CONFIG_MHP_MEMMAP_ON_MEMORY=y
+CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y
+CONFIG_SPLIT_PTE_PTLOCKS=y
+CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y
+CONFIG_SPLIT_PMD_PTLOCKS=y
+CONFIG_MEMORY_BALLOON=y
+CONFIG_BALLOON_COMPACTION=y
+CONFIG_COMPACTION=y
+CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
+CONFIG_PAGE_REPORTING=y
+CONFIG_MIGRATION=y
+CONFIG_DEVICE_MIGRATION=y
+CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y
+CONFIG_ARCH_ENABLE_THP_MIGRATION=y
+CONFIG_CONTIG_ALLOC=y
+CONFIG_PCP_BATCH_SCALE_MAX=5
+CONFIG_PHYS_ADDR_T_64BIT=y
+CONFIG_MMU_NOTIFIER=y
+CONFIG_KSM=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=65536
+CONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y
+CONFIG_MEMORY_FAILURE=y
+# CONFIG_HWPOISON_INJECT is not set
+CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
+CONFIG_ARCH_WANTS_THP_SWAP=y
+CONFIG_TRANSPARENT_HUGEPAGE=y
+# CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS is not set
+CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
+# CONFIG_TRANSPARENT_HUGEPAGE_NEVER is not set
+CONFIG_THP_SWAP=y
+CONFIG_READ_ONLY_THP_FOR_FS=y
+CONFIG_PGTABLE_HAS_HUGE_LEAVES=y
+CONFIG_ARCH_SUPPORTS_HUGE_PFNMAP=y
+CONFIG_ARCH_SUPPORTS_PMD_PFNMAP=y
+CONFIG_ARCH_SUPPORTS_PUD_PFNMAP=y
+CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y
+CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y
+CONFIG_USE_PERCPU_NUMA_NODE_ID=y
+CONFIG_HAVE_SETUP_PER_CPU_AREA=y
+# CONFIG_CMA is not set
+# CONFIG_MEM_SOFT_DIRTY is not set
+CONFIG_GENERIC_EARLY_IOREMAP=y
+# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set
+CONFIG_PAGE_IDLE_FLAG=y
+CONFIG_IDLE_PAGE_TRACKING=y
+CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y
+CONFIG_ARCH_HAS_CURRENT_STACK_POINTER=y
+CONFIG_ARCH_HAS_PTE_DEVMAP=y
+CONFIG_ZONE_DMA=y
+CONFIG_ZONE_DMA32=y
+CONFIG_ZONE_DEVICE=y
+CONFIG_HMM_MIRROR=y
+CONFIG_GET_FREE_REGION=y
+CONFIG_DEVICE_PRIVATE=y
+CONFIG_VMAP_PFN=y
+CONFIG_ARCH_USES_HIGH_VMA_FLAGS=y
+CONFIG_ARCH_HAS_PKEYS=y
+CONFIG_ARCH_USES_PG_ARCH_2=y
+CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_PERCPU_STATS is not set
+# CONFIG_GUP_TEST is not set
+# CONFIG_DMAPOOL_TEST is not set
+CONFIG_ARCH_HAS_PTE_SPECIAL=y
+CONFIG_MEMFD_CREATE=y
+CONFIG_SECRETMEM=y
+CONFIG_ANON_VMA_NAME=y
+# CONFIG_USERFAULTFD is not set
+CONFIG_LRU_GEN=y
+CONFIG_LRU_GEN_ENABLED=y
+# CONFIG_LRU_GEN_STATS is not set
+CONFIG_LRU_GEN_WALKS_MMU=y
+CONFIG_ARCH_SUPPORTS_PER_VMA_LOCK=y
+CONFIG_PER_VMA_LOCK=y
+CONFIG_LOCK_MM_AND_FIND_VMA=y
+CONFIG_IOMMU_MM_DATA=y
+CONFIG_EXECMEM=y
+CONFIG_NUMA_MEMBLKS=y
+# CONFIG_NUMA_EMU is not set
+CONFIG_ARCH_SUPPORTS_PT_RECLAIM=y
+CONFIG_PT_RECLAIM=y
+
+#
+# Data Access Monitoring
+#
+# CONFIG_DAMON is not set
+# end of Data Access Monitoring
+# end of Memory Management options
+
+CONFIG_NET=y
+CONFIG_NET_INGRESS=y
+CONFIG_NET_EGRESS=y
+CONFIG_NET_XGRESS=y
+CONFIG_SKB_DECRYPTED=y
+CONFIG_SKB_EXTENSIONS=y
+CONFIG_NET_DEVMEM=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_PACKET_DIAG=m
+CONFIG_UNIX=y
+CONFIG_AF_UNIX_OOB=y
+CONFIG_UNIX_DIAG=y
+CONFIG_TLS=m
+CONFIG_TLS_DEVICE=y
+# CONFIG_TLS_TOE is not set
+CONFIG_XFRM=y
+CONFIG_XFRM_OFFLOAD=y
+CONFIG_XFRM_ALGO=m
+CONFIG_XFRM_USER=m
+# CONFIG_XFRM_USER_COMPAT is not set
+CONFIG_XFRM_INTERFACE=m
+CONFIG_XFRM_SUB_POLICY=y
+CONFIG_XFRM_MIGRATE=y
+CONFIG_XFRM_STATISTICS=y
+CONFIG_XFRM_AH=m
+CONFIG_XFRM_ESP=m
+CONFIG_XFRM_IPCOMP=m
+CONFIG_NET_KEY=m
+# CONFIG_NET_KEY_MIGRATE is not set
+# CONFIG_XFRM_IPTFS is not set
+CONFIG_XDP_SOCKETS=y
+CONFIG_XDP_SOCKETS_DIAG=m
+CONFIG_NET_HANDSHAKE=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+# CONFIG_IP_FIB_TRIE_STATS is not set
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+# CONFIG_IP_ROUTE_VERBOSE is not set
+CONFIG_IP_ROUTE_CLASSID=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+CONFIG_NET_IPIP=m
+# CONFIG_NET_IPGRE_DEMUX is not set
+CONFIG_NET_IP_TUNNEL=m
+CONFIG_IP_MROUTE_COMMON=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_SYN_COOKIES=y
+# CONFIG_NET_IPVTI is not set
+CONFIG_NET_UDP_TUNNEL=m
+# CONFIG_NET_FOU is not set
+# CONFIG_NET_FOU_IP_TUNNELS is not set
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_ESP_OFFLOAD=m
+# CONFIG_INET_ESPINTCP is not set
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TABLE_PERTURB_ORDER=16
+CONFIG_INET_XFRM_TUNNEL=m
+CONFIG_INET_TUNNEL=m
+# CONFIG_INET_DIAG is not set
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BIC=m
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_TCP_CONG_WESTWOOD=m
+CONFIG_TCP_CONG_HTCP=m
+CONFIG_TCP_CONG_HSTCP=m
+CONFIG_TCP_CONG_HYBLA=m
+CONFIG_TCP_CONG_VEGAS=y
+# CONFIG_TCP_CONG_NV is not set
+CONFIG_TCP_CONG_SCALABLE=m
+CONFIG_TCP_CONG_LP=m
+CONFIG_TCP_CONG_VENO=m
+CONFIG_TCP_CONG_YEAH=y
+CONFIG_TCP_CONG_ILLINOIS=m
+# CONFIG_TCP_CONG_DCTCP is not set
+# CONFIG_TCP_CONG_CDG is not set
+CONFIG_TCP_CONG_BBR=y
+# CONFIG_DEFAULT_CUBIC is not set
+# CONFIG_DEFAULT_VEGAS is not set
+CONFIG_DEFAULT_BBR=y
+# CONFIG_DEFAULT_RENO is not set
+CONFIG_DEFAULT_TCP_CONG="bbr"
+CONFIG_TCP_SIGPOOL=y
+# CONFIG_TCP_AO is not set
+CONFIG_TCP_MD5SIG=y
+CONFIG_IPV6=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_ESP_OFFLOAD=m
+# CONFIG_INET6_ESPINTCP is not set
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_MIP6=m
+CONFIG_IPV6_ILA=m
+CONFIG_INET6_XFRM_TUNNEL=m
+CONFIG_INET6_TUNNEL=m
+# CONFIG_IPV6_VTI is not set
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_SIT_6RD=y
+CONFIG_IPV6_NDISC_NODETYPE=y
+CONFIG_IPV6_TUNNEL=m
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y
+# CONFIG_IPV6_PIMSM_V2 is not set
+CONFIG_IPV6_SEG6_LWTUNNEL=y
+CONFIG_IPV6_SEG6_HMAC=y
+CONFIG_IPV6_SEG6_BPF=y
+# CONFIG_IPV6_RPL_LWTUNNEL is not set
+CONFIG_IPV6_IOAM6_LWTUNNEL=y
+# CONFIG_NETLABEL is not set
+# CONFIG_MPTCP is not set
+CONFIG_NETWORK_SECMARK=y
+CONFIG_NET_PTP_CLASSIFY=y
+# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_ADVANCED=y
+CONFIG_BRIDGE_NETFILTER=m
+
+#
+# Core Netfilter Configuration
+#
+CONFIG_NETFILTER_INGRESS=y
+CONFIG_NETFILTER_EGRESS=y
+CONFIG_NETFILTER_SKIP_EGRESS=y
+CONFIG_NETFILTER_NETLINK=m
+CONFIG_NETFILTER_FAMILY_BRIDGE=y
+CONFIG_NETFILTER_FAMILY_ARP=y
+CONFIG_NETFILTER_BPF_LINK=y
+CONFIG_NETFILTER_NETLINK_HOOK=m
+CONFIG_NETFILTER_NETLINK_ACCT=m
+CONFIG_NETFILTER_NETLINK_QUEUE=m
+CONFIG_NETFILTER_NETLINK_LOG=m
+CONFIG_NETFILTER_NETLINK_OSF=m
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_SYSLOG=m
+CONFIG_NETFILTER_CONNCOUNT=m
+CONFIG_NF_CONNTRACK_MARK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_ZONES=y
+# CONFIG_NF_CONNTRACK_PROCFS is not set
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_TIMEOUT=y
+CONFIG_NF_CONNTRACK_TIMESTAMP=y
+CONFIG_NF_CONNTRACK_LABELS=y
+CONFIG_NF_CONNTRACK_OVS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_GRE=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_BROADCAST=m
+CONFIG_NF_CONNTRACK_NETBIOS_NS=m
+CONFIG_NF_CONNTRACK_SNMP=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SANE=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NF_CT_NETLINK_TIMEOUT=m
+CONFIG_NF_CT_NETLINK_HELPER=m
+CONFIG_NETFILTER_NETLINK_GLUE_CT=y
+CONFIG_NF_NAT=m
+CONFIG_NF_NAT_AMANDA=m
+CONFIG_NF_NAT_FTP=m
+CONFIG_NF_NAT_IRC=m
+CONFIG_NF_NAT_SIP=m
+CONFIG_NF_NAT_TFTP=m
+CONFIG_NF_NAT_REDIRECT=y
+CONFIG_NF_NAT_MASQUERADE=y
+CONFIG_NF_NAT_OVS=y
+CONFIG_NETFILTER_SYNPROXY=m
+CONFIG_NF_TABLES=m
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_NETDEV=y
+CONFIG_NFT_NUMGEN=m
+CONFIG_NFT_CT=m
+CONFIG_NFT_FLOW_OFFLOAD=m
+CONFIG_NFT_CONNLIMIT=m
+CONFIG_NFT_LOG=m
+CONFIG_NFT_LIMIT=m
+CONFIG_NFT_MASQ=m
+CONFIG_NFT_REDIR=m
+CONFIG_NFT_NAT=m
+CONFIG_NFT_TUNNEL=m
+CONFIG_NFT_QUEUE=m
+CONFIG_NFT_QUOTA=m
+CONFIG_NFT_REJECT=m
+CONFIG_NFT_REJECT_INET=m
+CONFIG_NFT_COMPAT=m
+CONFIG_NFT_HASH=m
+CONFIG_NFT_FIB=m
+CONFIG_NFT_FIB_INET=m
+CONFIG_NFT_XFRM=m
+CONFIG_NFT_SOCKET=m
+CONFIG_NFT_OSF=m
+CONFIG_NFT_TPROXY=m
+CONFIG_NFT_SYNPROXY=m
+CONFIG_NF_DUP_NETDEV=m
+CONFIG_NFT_DUP_NETDEV=m
+CONFIG_NFT_FWD_NETDEV=m
+CONFIG_NFT_FIB_NETDEV=m
+CONFIG_NFT_REJECT_NETDEV=m
+CONFIG_NF_FLOW_TABLE_INET=m
+CONFIG_NF_FLOW_TABLE=m
+CONFIG_NF_FLOW_TABLE_PROCFS=y
+CONFIG_NETFILTER_XTABLES=m
+CONFIG_NETFILTER_XTABLES_COMPAT=y
+
+#
+# Xtables combined modules
+#
+CONFIG_NETFILTER_XT_MARK=m
+CONFIG_NETFILTER_XT_CONNMARK=m
+CONFIG_NETFILTER_XT_SET=m
+
+#
+# Xtables targets
+#
+CONFIG_NETFILTER_XT_TARGET_AUDIT=m
+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m
+CONFIG_NETFILTER_XT_TARGET_CT=m
+CONFIG_NETFILTER_XT_TARGET_DSCP=m
+CONFIG_NETFILTER_XT_TARGET_HL=m
+CONFIG_NETFILTER_XT_TARGET_HMARK=m
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m
+CONFIG_NETFILTER_XT_TARGET_LED=m
+CONFIG_NETFILTER_XT_TARGET_LOG=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_NAT=m
+CONFIG_NETFILTER_XT_TARGET_NETMAP=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set
+CONFIG_NETFILTER_XT_TARGET_RATEEST=m
+CONFIG_NETFILTER_XT_TARGET_REDIRECT=m
+CONFIG_NETFILTER_XT_TARGET_MASQUERADE=m
+CONFIG_NETFILTER_XT_TARGET_TEE=m
+CONFIG_NETFILTER_XT_TARGET_TPROXY=m
+CONFIG_NETFILTER_XT_TARGET_TRACE=m
+CONFIG_NETFILTER_XT_TARGET_SECMARK=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m
+
+#
+# Xtables matches
+#
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m
+CONFIG_NETFILTER_XT_MATCH_BPF=m
+CONFIG_NETFILTER_XT_MATCH_CGROUP=m
+CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_CPU=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ECN=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_HL=m
+CONFIG_NETFILTER_XT_MATCH_IPCOMP=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_L2TP=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_NFACCT=m
+CONFIG_NETFILTER_XT_MATCH_OSF=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+CONFIG_NETFILTER_XT_MATCH_SCTP=m
+CONFIG_NETFILTER_XT_MATCH_SOCKET=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+# end of Core Netfilter Configuration
+
+CONFIG_IP_SET=m
+CONFIG_IP_SET_MAX=256
+CONFIG_IP_SET_BITMAP_IP=m
+CONFIG_IP_SET_BITMAP_IPMAC=m
+CONFIG_IP_SET_BITMAP_PORT=m
+CONFIG_IP_SET_HASH_IP=m
+CONFIG_IP_SET_HASH_IPMARK=m
+CONFIG_IP_SET_HASH_IPPORT=m
+CONFIG_IP_SET_HASH_IPPORTIP=m
+CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_IPMAC=m
+CONFIG_IP_SET_HASH_MAC=m
+CONFIG_IP_SET_HASH_NETPORTNET=m
+CONFIG_IP_SET_HASH_NET=m
+CONFIG_IP_SET_HASH_NETNET=m
+CONFIG_IP_SET_HASH_NETPORT=m
+CONFIG_IP_SET_HASH_NETIFACE=m
+CONFIG_IP_SET_LIST_SET=m
+# CONFIG_IP_VS is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_NF_DEFRAG_IPV4=m
+CONFIG_IP_NF_IPTABLES_LEGACY=m
+CONFIG_NF_SOCKET_IPV4=m
+CONFIG_NF_TPROXY_IPV4=m
+CONFIG_NF_TABLES_IPV4=y
+CONFIG_NFT_REJECT_IPV4=m
+CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
+CONFIG_NF_TABLES_ARP=y
+CONFIG_NF_DUP_IPV4=m
+CONFIG_NF_LOG_ARP=m
+CONFIG_NF_LOG_IPV4=m
+CONFIG_NF_REJECT_IPV4=m
+CONFIG_NF_NAT_SNMP_BASIC=m
+CONFIG_NF_NAT_PPTP=m
+CONFIG_NF_NAT_H323=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_RPFILTER=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_SYNPROXY=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_SECURITY=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_NFT_COMPAT_ARP=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+# end of IP: Netfilter Configuration
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_IPTABLES_LEGACY=m
+CONFIG_NF_SOCKET_IPV6=m
+CONFIG_NF_TPROXY_IPV6=m
+CONFIG_NF_TABLES_IPV6=y
+CONFIG_NFT_REJECT_IPV6=m
+CONFIG_NFT_DUP_IPV6=m
+CONFIG_NFT_FIB_IPV6=m
+CONFIG_NF_DUP_IPV6=m
+CONFIG_NF_REJECT_IPV6=m
+CONFIG_NF_LOG_IPV6=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_AH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_MH=m
+CONFIG_IP6_NF_MATCH_RPFILTER=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_SRH=m
+CONFIG_IP6_NF_TARGET_HL=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_TARGET_SYNPROXY=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_IP6_NF_SECURITY=m
+CONFIG_IP6_NF_NAT=m
+CONFIG_IP6_NF_TARGET_MASQUERADE=m
+CONFIG_IP6_NF_TARGET_NPT=m
+# end of IPv6: Netfilter Configuration
+
+CONFIG_NF_DEFRAG_IPV6=m
+CONFIG_NF_TABLES_BRIDGE=m
+CONFIG_NFT_BRIDGE_META=m
+CONFIG_NFT_BRIDGE_REJECT=m
+CONFIG_NF_CONNTRACK_BRIDGE=m
+CONFIG_BRIDGE_NF_EBTABLES_LEGACY=m
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_IP6=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_NFLOG=m
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_RDS is not set
+# CONFIG_TIPC is not set
+CONFIG_ATM=m
+CONFIG_ATM_CLIP=m
+# CONFIG_ATM_CLIP_NO_ICMP is not set
+CONFIG_ATM_LANE=m
+CONFIG_ATM_MPOA=m
+CONFIG_ATM_BR2684=m
+# CONFIG_ATM_BR2684_IPFILTER is not set
+CONFIG_L2TP=m
+# CONFIG_L2TP_DEBUGFS is not set
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=m
+CONFIG_L2TP_ETH=m
+CONFIG_STP=m
+CONFIG_BRIDGE=m
+CONFIG_BRIDGE_IGMP_SNOOPING=y
+# CONFIG_BRIDGE_VLAN_FILTERING is not set
+# CONFIG_BRIDGE_MRP is not set
+CONFIG_BRIDGE_CFM=y
+# CONFIG_NET_DSA is not set
+CONFIG_VLAN_8021Q=m
+# CONFIG_VLAN_8021Q_GVRP is not set
+# CONFIG_VLAN_8021Q_MVRP is not set
+CONFIG_LLC=m
+# CONFIG_LLC2 is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_PHONET is not set
+# CONFIG_6LOWPAN is not set
+# CONFIG_IEEE802154 is not set
+CONFIG_NET_SCHED=y
+
+#
+# Queueing/Scheduling
+#
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_PRIO=m
+# CONFIG_NET_SCH_MULTIQ is not set
+CONFIG_NET_SCH_RED=m
+# CONFIG_NET_SCH_SFB is not set
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+# CONFIG_NET_SCH_CBS is not set
+CONFIG_NET_SCH_ETF=m
+CONFIG_NET_SCH_MQPRIO_LIB=m
+CONFIG_NET_SCH_TAPRIO=m
+CONFIG_NET_SCH_GRED=m
+# CONFIG_NET_SCH_NETEM is not set
+# CONFIG_NET_SCH_DRR is not set
+# CONFIG_NET_SCH_MQPRIO is not set
+CONFIG_NET_SCH_SKBPRIO=m
+# CONFIG_NET_SCH_CHOKE is not set
+CONFIG_NET_SCH_QFQ=m
+# CONFIG_NET_SCH_CODEL is not set
+CONFIG_NET_SCH_FQ_CODEL=y
+CONFIG_NET_SCH_CAKE=m
+CONFIG_NET_SCH_FQ=m
+# CONFIG_NET_SCH_HHF is not set
+# CONFIG_NET_SCH_PIE is not set
+CONFIG_NET_SCH_INGRESS=m
+# CONFIG_NET_SCH_PLUG is not set
+# CONFIG_NET_SCH_ETS is not set
+CONFIG_NET_SCH_DEFAULT=y
+# CONFIG_DEFAULT_FQ is not set
+# CONFIG_DEFAULT_FQ_CODEL is not set
+# CONFIG_DEFAULT_SFQ is not set
+CONFIG_DEFAULT_PFIFO_FAST=y
+CONFIG_DEFAULT_NET_SCH="pfifo_fast"
+
+#
+# Classification
+#
+CONFIG_NET_CLS=y
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_PERF=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=m
+CONFIG_NET_CLS_CGROUP=y
+CONFIG_NET_CLS_BPF=m
+# CONFIG_NET_CLS_FLOWER is not set
+# CONFIG_NET_CLS_MATCHALL is not set
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_STACK=32
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_NBYTE=m
+CONFIG_NET_EMATCH_U32=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_EMATCH_TEXT=m
+# CONFIG_NET_EMATCH_IPSET is not set
+CONFIG_NET_EMATCH_IPT=m
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=m
+CONFIG_NET_ACT_GACT=m
+CONFIG_GACT_PROB=y
+CONFIG_NET_ACT_MIRRED=m
+# CONFIG_NET_ACT_SAMPLE is not set
+CONFIG_NET_ACT_NAT=m
+CONFIG_NET_ACT_PEDIT=m
+# CONFIG_NET_ACT_SIMP is not set
+# CONFIG_NET_ACT_SKBEDIT is not set
+# CONFIG_NET_ACT_CSUM is not set
+# CONFIG_NET_ACT_MPLS is not set
+# CONFIG_NET_ACT_VLAN is not set
+CONFIG_NET_ACT_BPF=m
+# CONFIG_NET_ACT_CONNMARK is not set
+CONFIG_NET_ACT_CTINFO=m
+# CONFIG_NET_ACT_SKBMOD is not set
+# CONFIG_NET_ACT_IFE is not set
+# CONFIG_NET_ACT_TUNNEL_KEY is not set
+CONFIG_NET_ACT_CT=m
+# CONFIG_NET_ACT_GATE is not set
+# CONFIG_NET_TC_SKB_EXT is not set
+CONFIG_NET_SCH_FIFO=y
+# CONFIG_DCB is not set
+CONFIG_DNS_RESOLVER=m
+CONFIG_BATMAN_ADV=m
+# CONFIG_BATMAN_ADV_BATMAN_V is not set
+CONFIG_BATMAN_ADV_BLA=y
+CONFIG_BATMAN_ADV_DAT=y
+CONFIG_BATMAN_ADV_NC=y
+CONFIG_BATMAN_ADV_MCAST=y
+# CONFIG_BATMAN_ADV_DEBUG is not set
+# CONFIG_BATMAN_ADV_TRACING is not set
+CONFIG_OPENVSWITCH=m
+CONFIG_OPENVSWITCH_VXLAN=m
+# CONFIG_VSOCKETS is not set
+CONFIG_NETLINK_DIAG=y
+CONFIG_MPLS=y
+CONFIG_NET_MPLS_GSO=m
+# CONFIG_MPLS_ROUTING is not set
+CONFIG_NET_NSH=m
+# CONFIG_HSR is not set
+# CONFIG_NET_SWITCHDEV is not set
+# CONFIG_NET_L3_MASTER_DEV is not set
+# CONFIG_QRTR is not set
+# CONFIG_NET_NCSI is not set
+CONFIG_PCPU_DEV_REFCNT=y
+CONFIG_MAX_SKB_FRAGS=17
+CONFIG_RPS=y
+CONFIG_RFS_ACCEL=y
+CONFIG_SOCK_RX_QUEUE_MAPPING=y
+CONFIG_XPS=y
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_CGROUP_NET_CLASSID=y
+CONFIG_NET_RX_BUSY_POLL=y
+CONFIG_BQL=y
+CONFIG_BPF_STREAM_PARSER=y
+CONFIG_NET_FLOW_LIMIT=y
+
+#
+# Network testing
+#
+CONFIG_NET_PKTGEN=m
+# CONFIG_NET_DROP_MONITOR is not set
+# end of Network testing
+# end of Networking options
+
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+CONFIG_BT=m
+CONFIG_BT_BREDR=y
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+# CONFIG_BT_BNEP is not set
+# CONFIG_BT_HIDP is not set
+CONFIG_BT_LE=y
+CONFIG_BT_LE_L2CAP_ECRED=y
+# CONFIG_BT_LEDS is not set
+CONFIG_BT_MSFTEXT=y
+CONFIG_BT_AOSPEXT=y
+CONFIG_BT_DEBUGFS=y
+# CONFIG_BT_SELFTEST is not set
+# CONFIG_BT_FEATURE_DEBUG is not set
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_INTEL=m
+CONFIG_BT_BCM=m
+CONFIG_BT_RTL=m
+CONFIG_BT_MTK=m
+CONFIG_BT_HCIBTUSB=m
+CONFIG_BT_HCIBTUSB_AUTOSUSPEND=y
+CONFIG_BT_HCIBTUSB_POLL_SYNC=y
+# CONFIG_BT_HCIBTUSB_AUTO_ISOC_ALT is not set
+CONFIG_BT_HCIBTUSB_BCM=y
+CONFIG_BT_HCIBTUSB_MTK=y
+CONFIG_BT_HCIBTUSB_RTL=y
+# CONFIG_BT_HCIBTSDIO is not set
+# CONFIG_BT_HCIUART is not set
+# CONFIG_BT_HCIBCM203X is not set
+# CONFIG_BT_HCIBCM4377 is not set
+# CONFIG_BT_HCIBPA10X is not set
+# CONFIG_BT_HCIBFUSB is not set
+# CONFIG_BT_HCIVHCI is not set
+# CONFIG_BT_MRVL is not set
+CONFIG_BT_ATH3K=m
+# CONFIG_BT_MTKSDIO is not set
+CONFIG_BT_VIRTIO=m
+# CONFIG_BT_INTEL_PCIE is not set
+# end of Bluetooth device drivers
+
+CONFIG_AF_RXRPC=m
+# CONFIG_AF_RXRPC_IPV6 is not set
+# CONFIG_AF_RXRPC_INJECT_LOSS is not set
+# CONFIG_AF_RXRPC_INJECT_RX_DELAY is not set
+# CONFIG_AF_RXRPC_DEBUG is not set
+# CONFIG_RXKAD is not set
+# CONFIG_RXPERF is not set
+# CONFIG_AF_KCM is not set
+CONFIG_STREAM_PARSER=y
+CONFIG_MCTP=y
+CONFIG_FIB_RULES=y
+CONFIG_WIRELESS=y
+CONFIG_CFG80211=m
+# CONFIG_NL80211_TESTMODE is not set
+# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set
+CONFIG_CFG80211_REQUIRE_SIGNED_REGDB=y
+CONFIG_CFG80211_USE_KERNEL_REGDB_KEYS=y
+CONFIG_CFG80211_DEFAULT_PS=y
+# CONFIG_CFG80211_DEBUGFS is not set
+CONFIG_CFG80211_CRDA_SUPPORT=y
+# CONFIG_CFG80211_WEXT is not set
+CONFIG_MAC80211=m
+CONFIG_MAC80211_HAS_RC=y
+CONFIG_MAC80211_RC_MINSTREL=y
+CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
+CONFIG_MAC80211_RC_DEFAULT="minstrel_ht"
+CONFIG_MAC80211_MESH=y
+CONFIG_MAC80211_LEDS=y
+# CONFIG_MAC80211_MESSAGE_TRACING is not set
+# CONFIG_MAC80211_DEBUG_MENU is not set
+CONFIG_MAC80211_STA_HASH_MAX_SIZE=0
+CONFIG_RFKILL=m
+CONFIG_RFKILL_LEDS=y
+CONFIG_RFKILL_INPUT=y
+# CONFIG_NET_9P is not set
+# CONFIG_CAIF is not set
+CONFIG_CEPH_LIB=m
+# CONFIG_CEPH_LIB_PRETTYDEBUG is not set
+# CONFIG_CEPH_LIB_USE_DNS_RESOLVER is not set
+# CONFIG_NFC is not set
+# CONFIG_PSAMPLE is not set
+# CONFIG_NET_IFE is not set
+CONFIG_LWTUNNEL=y
+CONFIG_LWTUNNEL_BPF=y
+CONFIG_DST_CACHE=y
+CONFIG_GRO_CELLS=y
+CONFIG_SOCK_VALIDATE_XMIT=y
+CONFIG_NET_SELFTESTS=m
+CONFIG_NET_SOCK_MSG=y
+CONFIG_PAGE_POOL=y
+# CONFIG_PAGE_POOL_STATS is not set
+CONFIG_FAILOVER=m
+CONFIG_ETHTOOL_NETLINK=y
+
+#
+# Device Drivers
+#
+CONFIG_HAVE_EISA=y
+# CONFIG_EISA is not set
+CONFIG_HAVE_PCI=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_HOTPLUG_PCI_PCIE=y
+CONFIG_PCIEAER=y
+# CONFIG_PCIEAER_INJECT is not set
+CONFIG_PCIE_ECRC=y
+CONFIG_PCIEASPM=y
+CONFIG_PCIEASPM_DEFAULT=y
+# CONFIG_PCIEASPM_POWERSAVE is not set
+# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set
+# CONFIG_PCIEASPM_PERFORMANCE is not set
+CONFIG_PCIE_PME=y
+CONFIG_PCIE_DPC=y
+CONFIG_PCIE_PTM=y
+# CONFIG_PCIE_EDR is not set
+CONFIG_PCI_MSI=y
+CONFIG_PCI_QUIRKS=y
+# CONFIG_PCI_DEBUG is not set
+CONFIG_PCI_REALLOC_ENABLE_AUTO=y
+CONFIG_PCI_STUB=m
+# CONFIG_PCI_PF_STUB is not set
+CONFIG_PCI_ATS=y
+CONFIG_PCI_LOCKLESS_CONFIG=y
+CONFIG_PCI_IOV=y
+CONFIG_PCI_PRI=y
+CONFIG_PCI_PASID=y
+# CONFIG_PCIE_TPH is not set
+# CONFIG_PCI_P2PDMA is not set
+CONFIG_PCI_LABEL=y
+CONFIG_VGA_ARB=y
+CONFIG_VGA_ARB_MAX_GPUS=16
+CONFIG_HOTPLUG_PCI=y
+CONFIG_HOTPLUG_PCI_ACPI=y
+CONFIG_HOTPLUG_PCI_ACPI_IBM=m
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+# CONFIG_HOTPLUG_PCI_OCTEONEP is not set
+# CONFIG_HOTPLUG_PCI_SHPC is not set
+
+#
+# PCI controller drivers
+#
+# CONFIG_VMD is not set
+
+#
+# Cadence-based PCIe controllers
+#
+# end of Cadence-based PCIe controllers
+
+#
+# DesignWare-based PCIe controllers
+#
+# CONFIG_PCI_MESON is not set
+# CONFIG_PCIE_DW_PLAT_HOST is not set
+# end of DesignWare-based PCIe controllers
+
+#
+# Mobiveil-based PCIe controllers
+#
+# end of Mobiveil-based PCIe controllers
+
+#
+# PLDA-based PCIe controllers
+#
+# end of PLDA-based PCIe controllers
+# end of PCI controller drivers
+
+#
+# PCI Endpoint
+#
+# CONFIG_PCI_ENDPOINT is not set
+# end of PCI Endpoint
+
+#
+# PCI switch controller drivers
+#
+# CONFIG_PCI_SW_SWITCHTEC is not set
+# end of PCI switch controller drivers
+
+# CONFIG_CXL_BUS is not set
+# CONFIG_PCCARD is not set
+# CONFIG_RAPIDIO is not set
+
+#
+# Generic Driver Options
+#
+CONFIG_AUXILIARY_BUS=y
+CONFIG_UEVENT_HELPER=y
+CONFIG_UEVENT_HELPER_PATH=""
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DEVTMPFS_SAFE=y
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+
+#
+# Firmware loader
+#
+CONFIG_FW_LOADER=y
+CONFIG_FW_LOADER_PAGED_BUF=y
+CONFIG_FW_LOADER_SYSFS=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_FW_LOADER_USER_HELPER is not set
+CONFIG_FW_LOADER_COMPRESS=y
+CONFIG_FW_LOADER_COMPRESS_XZ=y
+CONFIG_FW_LOADER_COMPRESS_ZSTD=y
+CONFIG_FW_CACHE=y
+CONFIG_FW_UPLOAD=y
+# end of Firmware loader
+
+CONFIG_WANT_DEV_COREDUMP=y
+CONFIG_ALLOW_DEV_COREDUMP=y
+CONFIG_DEV_COREDUMP=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set
+# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set
+CONFIG_GENERIC_CPU_DEVICES=y
+CONFIG_GENERIC_CPU_AUTOPROBE=y
+CONFIG_GENERIC_CPU_VULNERABILITIES=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_I2C=y
+CONFIG_REGMAP_SPI=y
+CONFIG_DMA_SHARED_BUFFER=y
+# CONFIG_DMA_FENCE_TRACE is not set
+# CONFIG_FW_DEVLINK_SYNC_STATE_TIMEOUT is not set
+# end of Generic Driver Options
+
+#
+# Bus devices
+#
+# CONFIG_MHI_BUS is not set
+# CONFIG_MHI_BUS_EP is not set
+# end of Bus devices
+
+#
+# Cache Drivers
+#
+# end of Cache Drivers
+
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
+
+#
+# Firmware Drivers
+#
+
+#
+# ARM System Control and Management Interface Protocol
+#
+# end of ARM System Control and Management Interface Protocol
+
+# CONFIG_EDD is not set
+CONFIG_FIRMWARE_MEMMAP=y
+CONFIG_DMIID=y
+CONFIG_DMI_SYSFS=m
+CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK=y
+# CONFIG_ISCSI_IBFT is not set
+# CONFIG_FW_CFG_SYSFS is not set
+CONFIG_SYSFB=y
+CONFIG_SYSFB_SIMPLEFB=y
+# CONFIG_GOOGLE_FIRMWARE is not set
+
+#
+# EFI (Extensible Firmware Interface) Support
+#
+CONFIG_EFI_ESRT=y
+CONFIG_EFI_VARS_PSTORE=y
+# CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE is not set
+CONFIG_EFI_DXE_MEM_ATTRIBUTES=y
+CONFIG_EFI_RUNTIME_WRAPPERS=y
+CONFIG_EFI_BOOTLOADER_CONTROL=m
+CONFIG_EFI_CAPSULE_LOADER=m
+# CONFIG_EFI_TEST is not set
+CONFIG_EFI_DEV_PATH_PARSER=y
+CONFIG_APPLE_PROPERTIES=y
+CONFIG_RESET_ATTACK_MITIGATION=y
+CONFIG_EFI_RCI2_TABLE=y
+# CONFIG_EFI_DISABLE_PCI_DMA is not set
+CONFIG_EFI_EARLYCON=y
+# CONFIG_EFI_CUSTOM_SSDT_OVERLAYS is not set
+# CONFIG_EFI_DISABLE_RUNTIME is not set
+CONFIG_EFI_COCO_SECRET=y
+# end of EFI (Extensible Firmware Interface) Support
+
+CONFIG_UEFI_CPER=y
+CONFIG_UEFI_CPER_X86=y
+
+#
+# Qualcomm firmware drivers
+#
+# end of Qualcomm firmware drivers
+
+#
+# Tegra firmware driver
+#
+# end of Tegra firmware driver
+# end of Firmware Drivers
+
+# CONFIG_GNSS is not set
+# CONFIG_MTD is not set
+# CONFIG_OF is not set
+CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
+# CONFIG_PARPORT is not set
+CONFIG_PNP=y
+CONFIG_PNP_DEBUG_MESSAGES=y
+
+#
+# Protocols
+#
+CONFIG_PNPACPI=y
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_NULL_BLK is not set
+# CONFIG_BLK_DEV_FD is not set
+CONFIG_CDROM=m
+# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set
+CONFIG_ZRAM=m
+CONFIG_ZRAM_BACKEND_LZ4=y
+CONFIG_ZRAM_BACKEND_LZ4HC=y
+CONFIG_ZRAM_BACKEND_ZSTD=y
+# CONFIG_ZRAM_BACKEND_DEFLATE is not set
+# CONFIG_ZRAM_BACKEND_842 is not set
+CONFIG_ZRAM_BACKEND_LZO=y
+# CONFIG_ZRAM_DEF_COMP_LZORLE is not set
+# CONFIG_ZRAM_DEF_COMP_LZO is not set
+# CONFIG_ZRAM_DEF_COMP_LZ4 is not set
+# CONFIG_ZRAM_DEF_COMP_LZ4HC is not set
+CONFIG_ZRAM_DEF_COMP_ZSTD=y
+CONFIG_ZRAM_DEF_COMP="zstd"
+CONFIG_ZRAM_WRITEBACK=y
+CONFIG_ZRAM_TRACK_ENTRY_ACTIME=y
+CONFIG_ZRAM_MEMORY_TRACKING=y
+CONFIG_ZRAM_MULTI_COMP=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
+CONFIG_BLK_DEV_DRBD=m
+# CONFIG_DRBD_FAULT_INJECTION is not set
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=8192
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_VIRTIO_BLK=m
+CONFIG_BLK_DEV_RBD=m
+CONFIG_BLK_DEV_UBLK=m
+# CONFIG_BLKDEV_UBLK_LEGACY_OPCODES is not set
+
+#
+# NVME Support
+#
+CONFIG_NVME_CORE=y
+CONFIG_BLK_DEV_NVME=y
+# CONFIG_NVME_MULTIPATH is not set
+# CONFIG_NVME_VERBOSE_ERRORS is not set
+CONFIG_NVME_HWMON=y
+# CONFIG_NVME_FC is not set
+# CONFIG_NVME_TCP is not set
+# CONFIG_NVME_HOST_AUTH is not set
+# CONFIG_NVME_TARGET is not set
+# end of NVME Support
+
+#
+# Misc devices
+#
+# CONFIG_AD525X_DPOT is not set
+# CONFIG_DUMMY_IRQ is not set
+# CONFIG_IBM_ASM is not set
+# CONFIG_PHANTOM is not set
+# CONFIG_RPMB is not set
+# CONFIG_TIFM_CORE is not set
+# CONFIG_ICS932S401 is not set
+CONFIG_ENCLOSURE_SERVICES=m
+# CONFIG_HP_ILO is not set
+# CONFIG_APDS9802ALS is not set
+# CONFIG_ISL29003 is not set
+# CONFIG_ISL29020 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_SENSORS_BH1770 is not set
+# CONFIG_SENSORS_APDS990X is not set
+# CONFIG_HMC6352 is not set
+# CONFIG_DS1682 is not set
+# CONFIG_LATTICE_ECP3_CONFIG is not set
+# CONFIG_SRAM is not set
+# CONFIG_DW_XDATA_PCIE is not set
+# CONFIG_PCI_ENDPOINT_TEST is not set
+# CONFIG_XILINX_SDFEC is not set
+# CONFIG_NTSYNC is not set
+# CONFIG_NSM is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+CONFIG_EEPROM_AT24=m
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EEPROM_MAX6875 is not set
+CONFIG_EEPROM_93CX6=y
+# CONFIG_EEPROM_93XX46 is not set
+# CONFIG_EEPROM_IDT_89HPESX is not set
+CONFIG_EEPROM_EE1004=m
+# end of EEPROM support
+
+# CONFIG_CB710_CORE is not set
+# CONFIG_SENSORS_LIS3_I2C is not set
+# CONFIG_ALTERA_STAPL is not set
+# CONFIG_INTEL_MEI is not set
+# CONFIG_VMWARE_VMCI is not set
+# CONFIG_GENWQE is not set
+# CONFIG_ECHO is not set
+# CONFIG_BCM_VK is not set
+# CONFIG_MISC_ALCOR_PCI is not set
+# CONFIG_MISC_RTSX_PCI is not set
+# CONFIG_MISC_RTSX_USB is not set
+# CONFIG_UACCE is not set
+# CONFIG_PVPANIC is not set
+# CONFIG_KEBA_CP500 is not set
+# end of Misc devices
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI_COMMON=y
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+CONFIG_BLK_DEV_SR=m
+CONFIG_CHR_DEV_SG=y
+CONFIG_BLK_DEV_BSG=y
+# CONFIG_CHR_DEV_SCH is not set
+CONFIG_SCSI_ENCLOSURE=m
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+CONFIG_SCSI_SCAN_ASYNC=y
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# end of SCSI Transports
+
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_ISCSI_BOOT_SYSFS is not set
+# CONFIG_SCSI_CXGB3_ISCSI is not set
+# CONFIG_SCSI_CXGB4_ISCSI is not set
+# CONFIG_SCSI_BNX2_ISCSI is not set
+# CONFIG_BE2ISCSI is not set
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_HPSA is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_3W_SAS is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC94XX is not set
+# CONFIG_SCSI_MVSAS is not set
+# CONFIG_SCSI_MVUMI is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_ARCMSR is not set
+# CONFIG_SCSI_ESAS2R is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_MEGARAID_SAS is not set
+# CONFIG_SCSI_MPT3SAS is not set
+# CONFIG_SCSI_MPT2SAS is not set
+# CONFIG_SCSI_MPI3MR is not set
+# CONFIG_SCSI_SMARTPQI is not set
+# CONFIG_SCSI_HPTIOP is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_MYRB is not set
+# CONFIG_SCSI_MYRS is not set
+# CONFIG_VMWARE_PVSCSI is not set
+# CONFIG_SCSI_SNIC is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_FDOMAIN_PCI is not set
+# CONFIG_SCSI_ISCI is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_STEX is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_QLA_ISCSI is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_WD719X is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_PMCRAID is not set
+# CONFIG_SCSI_PM8001 is not set
+CONFIG_SCSI_VIRTIO=m
+# CONFIG_SCSI_DH is not set
+# end of SCSI device support
+
+CONFIG_ATA=y
+CONFIG_SATA_HOST=y
+CONFIG_PATA_TIMINGS=y
+CONFIG_ATA_VERBOSE_ERROR=y
+CONFIG_ATA_FORCE=y
+CONFIG_ATA_ACPI=y
+# CONFIG_SATA_ZPODD is not set
+CONFIG_SATA_PMP=y
+
+#
+# Controllers with non-SFF native interface
+#
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_MOBILE_LPM_POLICY=0
+CONFIG_SATA_AHCI_PLATFORM=m
+# CONFIG_AHCI_DWC is not set
+# CONFIG_SATA_INIC162X is not set
+# CONFIG_SATA_ACARD_AHCI is not set
+# CONFIG_SATA_SIL24 is not set
+# CONFIG_ATA_SFF is not set
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+# CONFIG_MD_BITMAP_FILE is not set
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+CONFIG_MD_RAID10=m
+CONFIG_MD_RAID456=m
+CONFIG_BCACHE=m
+# CONFIG_BCACHE_DEBUG is not set
+CONFIG_BCACHE_ASYNC_REGISTRATION=y
+CONFIG_BLK_DEV_DM_BUILTIN=y
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_BUFIO=m
+# CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING is not set
+CONFIG_DM_BIO_PRISON=m
+CONFIG_DM_PERSISTENT_DATA=m
+CONFIG_DM_UNSTRIPED=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_THIN_PROVISIONING=m
+CONFIG_DM_CACHE=m
+CONFIG_DM_CACHE_SMQ=m
+CONFIG_DM_WRITECACHE=m
+# CONFIG_DM_EBS is not set
+# CONFIG_DM_ERA is not set
+# CONFIG_DM_CLONE is not set
+CONFIG_DM_MIRROR=m
+# CONFIG_DM_LOG_USERSPACE is not set
+CONFIG_DM_RAID=m
+# CONFIG_DM_ZERO is not set
+# CONFIG_DM_MULTIPATH is not set
+# CONFIG_DM_DELAY is not set
+CONFIG_DM_DUST=m
+CONFIG_DM_UEVENT=y
+# CONFIG_DM_FLAKEY is not set
+CONFIG_DM_VERITY=m
+CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_DM_SWITCH=m
+# CONFIG_DM_LOG_WRITES is not set
+CONFIG_DM_INTEGRITY=m
+CONFIG_DM_ZONED=m
+CONFIG_DM_AUDIT=y
+CONFIG_DM_VDO=m
+# CONFIG_TARGET_CORE is not set
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_FIREWIRE is not set
+# CONFIG_FIREWIRE_NOSY is not set
+# end of IEEE 1394 (FireWire) support
+
+# CONFIG_MACINTOSH_DRIVERS is not set
+CONFIG_NETDEVICES=y
+CONFIG_MII=m
+CONFIG_NET_CORE=y
+# CONFIG_BONDING is not set
+CONFIG_DUMMY=m
+CONFIG_WIREGUARD=m
+# CONFIG_WIREGUARD_DEBUG is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_NET_FC is not set
+# CONFIG_IFB is not set
+# CONFIG_NET_TEAM is not set
+CONFIG_MACVLAN=m
+CONFIG_MACVTAP=m
+# CONFIG_IPVLAN is not set
+CONFIG_VXLAN=m
+# CONFIG_GENEVE is not set
+# CONFIG_BAREUDP is not set
+# CONFIG_GTP is not set
+CONFIG_PFCP=m
+# CONFIG_AMT is not set
+# CONFIG_MACSEC is not set
+CONFIG_NETCONSOLE=m
+CONFIG_NETCONSOLE_DYNAMIC=y
+# CONFIG_NETCONSOLE_EXTENDED_LOG is not set
+CONFIG_NETPOLL=y
+CONFIG_NET_POLL_CONTROLLER=y
+CONFIG_TUN=m
+CONFIG_TAP=m
+# CONFIG_TUN_VNET_CROSS_LE is not set
+CONFIG_VETH=m
+CONFIG_VIRTIO_NET=m
+# CONFIG_NLMON is not set
+CONFIG_NETKIT=y
+# CONFIG_ARCNET is not set
+# CONFIG_ATM_DRIVERS is not set
+CONFIG_ETHERNET=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ADAPTEC is not set
+# CONFIG_NET_VENDOR_AGERE is not set
+# CONFIG_NET_VENDOR_ALACRITECH is not set
+# CONFIG_NET_VENDOR_ALTEON is not set
+# CONFIG_ALTERA_TSE is not set
+CONFIG_NET_VENDOR_AMAZON=y
+# CONFIG_ENA_ETHERNET is not set
+# CONFIG_NET_VENDOR_AMD is not set
+# CONFIG_NET_VENDOR_AQUANTIA is not set
+# CONFIG_NET_VENDOR_ARC is not set
+CONFIG_NET_VENDOR_ASIX=y
+# CONFIG_NET_VENDOR_ATHEROS is not set
+# CONFIG_CX_ECAT is not set
+CONFIG_NET_VENDOR_BROADCOM=y
+# CONFIG_B44 is not set
+# CONFIG_BCMGENET is not set
+# CONFIG_BNX2 is not set
+# CONFIG_CNIC is not set
+CONFIG_TIGON3=m
+CONFIG_TIGON3_HWMON=y
+# CONFIG_BNX2X is not set
+# CONFIG_SYSTEMPORT is not set
+# CONFIG_BNXT is not set
+# CONFIG_NET_VENDOR_CADENCE is not set
+# CONFIG_NET_VENDOR_CAVIUM is not set
+# CONFIG_NET_VENDOR_CHELSIO is not set
+# CONFIG_NET_VENDOR_CISCO is not set
+# CONFIG_NET_VENDOR_CORTINA is not set
+# CONFIG_NET_VENDOR_DAVICOM is not set
+# CONFIG_DNET is not set
+# CONFIG_NET_VENDOR_DEC is not set
+# CONFIG_NET_VENDOR_DLINK is not set
+# CONFIG_NET_VENDOR_EMULEX is not set
+# CONFIG_NET_VENDOR_ENGLEDER is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_FUNGIBLE is not set
+# CONFIG_NET_VENDOR_GOOGLE is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_HUAWEI is not set
+# CONFIG_NET_VENDOR_I825XX is not set
+CONFIG_NET_VENDOR_INTEL=y
+# CONFIG_E100 is not set
+# CONFIG_E1000 is not set
+CONFIG_E1000E=m
+CONFIG_E1000E_HWTS=y
+CONFIG_IGB=m
+CONFIG_IGB_HWMON=y
+CONFIG_IGB_DCA=y
+CONFIG_IGBVF=m
+# CONFIG_IXGBE is not set
+# CONFIG_IXGBEVF is not set
+# CONFIG_I40E is not set
+# CONFIG_I40EVF is not set
+# CONFIG_ICE is not set
+# CONFIG_FM10K is not set
+# CONFIG_IGC is not set
+# CONFIG_IDPF is not set
+# CONFIG_JME is not set
+# CONFIG_NET_VENDOR_ADI is not set
+# CONFIG_NET_VENDOR_LITEX is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_META is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_MICROSEMI is not set
+# CONFIG_NET_VENDOR_MICROSOFT is not set
+# CONFIG_NET_VENDOR_MYRI is not set
+# CONFIG_FEALNX is not set
+# CONFIG_NET_VENDOR_NI is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_NETERION is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+CONFIG_NET_VENDOR_NVIDIA=y
+CONFIG_FORCEDETH=m
+# CONFIG_NET_VENDOR_OKI is not set
+# CONFIG_ETHOC is not set
+# CONFIG_NET_VENDOR_PACKET_ENGINES is not set
+# CONFIG_NET_VENDOR_PENSANDO is not set
+# CONFIG_NET_VENDOR_QLOGIC is not set
+# CONFIG_NET_VENDOR_BROCADE is not set
+# CONFIG_NET_VENDOR_QUALCOMM is not set
+# CONFIG_NET_VENDOR_RDC is not set
+# CONFIG_NET_VENDOR_REALTEK is not set
+# CONFIG_NET_VENDOR_RENESAS is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SILAN is not set
+# CONFIG_NET_VENDOR_SIS is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_SOCIONEXT is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_SUN is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
+# CONFIG_NET_VENDOR_TEHUTI is not set
+# CONFIG_NET_VENDOR_TI is not set
+# CONFIG_NET_VENDOR_VERTEXCOM is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WANGXUN is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_NET_VENDOR_XILINX is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+CONFIG_PHYLINK=m
+CONFIG_PHYLIB=m
+CONFIG_SWPHY=y
+# CONFIG_LED_TRIGGER_PHY is not set
+CONFIG_FIXED_PHY=m
+# CONFIG_SFP is not set
+
+#
+# MII PHY device drivers
+#
+# CONFIG_AIR_EN8811H_PHY is not set
+# CONFIG_AMD_PHY is not set
+# CONFIG_ADIN_PHY is not set
+# CONFIG_ADIN1100_PHY is not set
+# CONFIG_AQUANTIA_PHY is not set
+CONFIG_AX88796B_PHY=m
+CONFIG_BROADCOM_PHY=m
+# CONFIG_BCM54140_PHY is not set
+# CONFIG_BCM7XXX_PHY is not set
+# CONFIG_BCM84881_PHY is not set
+# CONFIG_BCM87XX_PHY is not set
+CONFIG_BCM_NET_PHYLIB=m
+# CONFIG_CICADA_PHY is not set
+# CONFIG_CORTINA_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_INTEL_XWAY_PHY is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_MARVELL_10G_PHY is not set
+# CONFIG_MARVELL_88Q2XXX_PHY is not set
+# CONFIG_MARVELL_88X2222_PHY is not set
+# CONFIG_MAXLINEAR_GPHY is not set
+# CONFIG_MEDIATEK_GE_PHY is not set
+# CONFIG_MICREL_PHY is not set
+# CONFIG_MICROCHIP_T1S_PHY is not set
+# CONFIG_MICROCHIP_PHY is not set
+# CONFIG_MICROCHIP_T1_PHY is not set
+# CONFIG_MICROSEMI_PHY is not set
+# CONFIG_MOTORCOMM_PHY is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_NXP_CBTX_PHY is not set
+# CONFIG_NXP_C45_TJA11XX_PHY is not set
+# CONFIG_NXP_TJA11XX_PHY is not set
+# CONFIG_NCN26000_PHY is not set
+# CONFIG_QCA83XX_PHY is not set
+# CONFIG_QCA808X_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_RENESAS_PHY is not set
+# CONFIG_ROCKCHIP_PHY is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_STE10XP is not set
+# CONFIG_TERANETICS_PHY is not set
+# CONFIG_DP83822_PHY is not set
+# CONFIG_DP83TC811_PHY is not set
+# CONFIG_DP83848_PHY is not set
+# CONFIG_DP83867_PHY is not set
+# CONFIG_DP83869_PHY is not set
+# CONFIG_DP83TD510_PHY is not set
+# CONFIG_DP83TG720_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_XILINX_GMII2RGMII is not set
+# CONFIG_MICREL_KS8995MA is not set
+
+#
+# MCTP Device Drivers
+#
+# CONFIG_MCTP_SERIAL is not set
+# CONFIG_MCTP_TRANSPORT_I3C is not set
+# end of MCTP Device Drivers
+
+CONFIG_MDIO_DEVICE=m
+CONFIG_MDIO_BUS=m
+CONFIG_FWNODE_MDIO=m
+CONFIG_ACPI_MDIO=m
+CONFIG_MDIO_DEVRES=m
+# CONFIG_MDIO_BITBANG is not set
+# CONFIG_MDIO_BCM_UNIMAC is not set
+# CONFIG_MDIO_MVUSB is not set
+# CONFIG_MDIO_THUNDER is not set
+
+#
+# MDIO Multiplexers
+#
+
+#
+# PCS device drivers
+#
+# CONFIG_PCS_XPCS is not set
+# end of PCS device drivers
+
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOATM=m
+CONFIG_PPPOE=m
+# CONFIG_PPPOE_HASH_BITS_1 is not set
+# CONFIG_PPPOE_HASH_BITS_2 is not set
+CONFIG_PPPOE_HASH_BITS_4=y
+# CONFIG_PPPOE_HASH_BITS_8 is not set
+CONFIG_PPPOE_HASH_BITS=4
+CONFIG_PPPOL2TP=m
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+
+#
+# Host-side USB support is needed for USB Network Adapter support
+#
+CONFIG_USB_NET_DRIVERS=m
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_RTL8152 is not set
+# CONFIG_USB_LAN78XX is not set
+CONFIG_USB_USBNET=m
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_AX88179_178A=m
+CONFIG_USB_NET_CDCETHER=m
+CONFIG_USB_NET_CDC_EEM=m
+CONFIG_USB_NET_CDC_NCM=m
+# CONFIG_USB_NET_HUAWEI_CDC_NCM is not set
+# CONFIG_USB_NET_CDC_MBIM is not set
+# CONFIG_USB_NET_DM9601 is not set
+# CONFIG_USB_NET_SR9700 is not set
+# CONFIG_USB_NET_SR9800 is not set
+# CONFIG_USB_NET_SMSC75XX is not set
+# CONFIG_USB_NET_SMSC95XX is not set
+# CONFIG_USB_NET_GL620A is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+CONFIG_USB_NET_RNDIS_HOST=m
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_USB_NET_CX82310_ETH is not set
+# CONFIG_USB_NET_KALMIA is not set
+# CONFIG_USB_NET_QMI_WWAN is not set
+# CONFIG_USB_HSO is not set
+# CONFIG_USB_NET_INT51X1 is not set
+# CONFIG_USB_IPHETH is not set
+# CONFIG_USB_SIERRA_NET is not set
+# CONFIG_USB_VL600 is not set
+# CONFIG_USB_NET_CH9200 is not set
+# CONFIG_USB_NET_AQC111 is not set
+CONFIG_USB_RTL8153_ECM=m
+CONFIG_WLAN=y
+# CONFIG_WLAN_VENDOR_ADMTEK is not set
+CONFIG_ATH_COMMON=m
+CONFIG_WLAN_VENDOR_ATH=y
+# CONFIG_ATH_DEBUG is not set
+# CONFIG_ATH5K is not set
+# CONFIG_ATH5K_PCI is not set
+# CONFIG_ATH9K is not set
+# CONFIG_ATH9K_HTC is not set
+# CONFIG_CARL9170 is not set
+# CONFIG_ATH6KL is not set
+# CONFIG_AR5523 is not set
+# CONFIG_WIL6210 is not set
+CONFIG_ATH10K=m
+CONFIG_ATH10K_CE=y
+CONFIG_ATH10K_PCI=m
+# CONFIG_ATH10K_SDIO is not set
+# CONFIG_ATH10K_USB is not set
+# CONFIG_ATH10K_DEBUG is not set
+# CONFIG_ATH10K_DEBUGFS is not set
+CONFIG_ATH10K_LEDS=y
+# CONFIG_ATH10K_TRACING is not set
+# CONFIG_WCN36XX is not set
+# CONFIG_ATH11K is not set
+# CONFIG_ATH12K is not set
+# CONFIG_WLAN_VENDOR_ATMEL is not set
+# CONFIG_WLAN_VENDOR_BROADCOM is not set
+CONFIG_WLAN_VENDOR_INTEL=y
+# CONFIG_IPW2100 is not set
+# CONFIG_IPW2200 is not set
+# CONFIG_IWL4965 is not set
+# CONFIG_IWL3945 is not set
+CONFIG_IWLWIFI=m
+CONFIG_IWLWIFI_LEDS=y
+CONFIG_IWLDVM=m
+CONFIG_IWLMVM=m
+CONFIG_IWLWIFI_OPMODE_MODULAR=y
+
+#
+# Debugging Options
+#
+# CONFIG_IWLWIFI_DEBUG is not set
+# CONFIG_IWLWIFI_DEVICE_TRACING is not set
+# end of Debugging Options
+
+# CONFIG_WLAN_VENDOR_INTERSIL is not set
+# CONFIG_WLAN_VENDOR_MARVELL is not set
+CONFIG_WLAN_VENDOR_MEDIATEK=y
+# CONFIG_MT7601U is not set
+CONFIG_MT76_CORE=m
+CONFIG_MT76_LEDS=y
+CONFIG_MT76_USB=m
+CONFIG_MT76x02_LIB=m
+CONFIG_MT76x02_USB=m
+CONFIG_MT76_CONNAC_LIB=m
+CONFIG_MT792x_LIB=m
+# CONFIG_MT76x0U is not set
+# CONFIG_MT76x0E is not set
+CONFIG_MT76x2_COMMON=m
+# CONFIG_MT76x2E is not set
+CONFIG_MT76x2U=m
+# CONFIG_MT7603E is not set
+# CONFIG_MT7615E is not set
+# CONFIG_MT7663U is not set
+# CONFIG_MT7663S is not set
+# CONFIG_MT7915E is not set
+CONFIG_MT7921_COMMON=m
+CONFIG_MT7921E=m
+# CONFIG_MT7921S is not set
+# CONFIG_MT7921U is not set
+# CONFIG_MT7996E is not set
+# CONFIG_MT7925E is not set
+# CONFIG_MT7925U is not set
+# CONFIG_WLAN_VENDOR_MICROCHIP is not set
+# CONFIG_WLAN_VENDOR_PURELIFI is not set
+# CONFIG_WLAN_VENDOR_RALINK is not set
+CONFIG_WLAN_VENDOR_REALTEK=y
+# CONFIG_RTL8180 is not set
+# CONFIG_RTL8187 is not set
+CONFIG_RTL_CARDS=m
+# CONFIG_RTL8192CE is not set
+CONFIG_RTL8192SE=m
+# CONFIG_RTL8192DE is not set
+# CONFIG_RTL8723AE is not set
+# CONFIG_RTL8723BE is not set
+# CONFIG_RTL8188EE is not set
+# CONFIG_RTL8192EE is not set
+# CONFIG_RTL8821AE is not set
+# CONFIG_RTL8192CU is not set
+# CONFIG_RTL8192DU is not set
+CONFIG_RTLWIFI=m
+CONFIG_RTLWIFI_PCI=m
+# CONFIG_RTLWIFI_DEBUG is not set
+# CONFIG_RTL8XXXU is not set
+# CONFIG_RTW88 is not set
+# CONFIG_RTW89 is not set
+# CONFIG_WLAN_VENDOR_RSI is not set
+# CONFIG_WLAN_VENDOR_SILABS is not set
+# CONFIG_WLAN_VENDOR_ST is not set
+# CONFIG_WLAN_VENDOR_TI is not set
+# CONFIG_WLAN_VENDOR_ZYDAS is not set
+# CONFIG_WLAN_VENDOR_QUANTENNA is not set
+# CONFIG_MAC80211_HWSIM is not set
+# CONFIG_VIRT_WIFI is not set
+# CONFIG_WAN is not set
+
+#
+# Wireless WAN
+#
+# CONFIG_WWAN is not set
+# end of Wireless WAN
+
+# CONFIG_VMXNET3 is not set
+# CONFIG_FUJITSU_ES is not set
+CONFIG_USB4_NET=m
+# CONFIG_NETDEVSIM is not set
+CONFIG_NET_FAILOVER=m
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+CONFIG_INPUT_LEDS=m
+CONFIG_INPUT_FF_MEMLESS=y
+CONFIG_INPUT_SPARSEKMAP=m
+# CONFIG_INPUT_MATRIXKMAP is not set
+CONFIG_INPUT_VIVALDIFMAP=y
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+# CONFIG_KEYBOARD_APPLESPI is not set
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_QT1050 is not set
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_DLINK_DIR685 is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_TCA8418 is not set
+# CONFIG_KEYBOARD_LM8323 is not set
+# CONFIG_KEYBOARD_LM8333 is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_SAMSUNG is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_TM2_TOUCHKEY is not set
+CONFIG_KEYBOARD_XTKBD=m
+# CONFIG_KEYBOARD_CYPRESS_SF is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_BYD=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS=y
+CONFIG_MOUSE_PS2_CYPRESS=y
+CONFIG_MOUSE_PS2_LIFEBOOK=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_ELANTECH is not set
+# CONFIG_MOUSE_PS2_SENTELIC is not set
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+CONFIG_MOUSE_PS2_FOCALTECH=y
+# CONFIG_MOUSE_PS2_VMMOUSE is not set
+CONFIG_MOUSE_PS2_SMBUS=y
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_BCM5974 is not set
+# CONFIG_MOUSE_CYAPA is not set
+# CONFIG_MOUSE_ELAN_I2C is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+CONFIG_MOUSE_SYNAPTICS_I2C=m
+CONFIG_MOUSE_SYNAPTICS_USB=m
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_AD714X is not set
+# CONFIG_INPUT_BMA150 is not set
+# CONFIG_INPUT_E3X0_BUTTON is not set
+# CONFIG_INPUT_PCSPKR is not set
+# CONFIG_INPUT_MMA8450 is not set
+# CONFIG_INPUT_APANEL is not set
+# CONFIG_INPUT_ATLAS_BTNS is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_KXTJ9 is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INPUT_CM109 is not set
+CONFIG_INPUT_UINPUT=m
+# CONFIG_INPUT_PCF8574 is not set
+# CONFIG_INPUT_PWM_BEEPER is not set
+# CONFIG_INPUT_PWM_VIBRA is not set
+# CONFIG_INPUT_DA7280_HAPTICS is not set
+# CONFIG_INPUT_ADXL34X is not set
+# CONFIG_INPUT_IMS_PCU is not set
+# CONFIG_INPUT_IQS269A is not set
+# CONFIG_INPUT_IQS626A is not set
+# CONFIG_INPUT_IQS7222 is not set
+# CONFIG_INPUT_CMA3000 is not set
+# CONFIG_INPUT_IDEAPAD_SLIDEBAR is not set
+# CONFIG_INPUT_DRV2665_HAPTICS is not set
+# CONFIG_INPUT_DRV2667_HAPTICS is not set
+CONFIG_RMI4_CORE=m
+# CONFIG_RMI4_I2C is not set
+# CONFIG_RMI4_SPI is not set
+# CONFIG_RMI4_SMB is not set
+CONFIG_RMI4_F03=y
+CONFIG_RMI4_F03_SERIO=m
+CONFIG_RMI4_2D_SENSOR=y
+CONFIG_RMI4_F11=y
+CONFIG_RMI4_F12=y
+CONFIG_RMI4_F30=y
+# CONFIG_RMI4_F34 is not set
+CONFIG_RMI4_F3A=y
+# CONFIG_RMI4_F54 is not set
+# CONFIG_RMI4_F55 is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y
+CONFIG_SERIO_I8042=y
+CONFIG_SERIO_SERPORT=m
+CONFIG_SERIO_CT82C710=m
+CONFIG_SERIO_PCIPS2=m
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_SERIO_ALTERA_PS2 is not set
+# CONFIG_SERIO_PS2MULT is not set
+# CONFIG_SERIO_ARC_PS2 is not set
+# CONFIG_USERIO is not set
+# CONFIG_GAMEPORT is not set
+# end of Hardware I/O ports
+# end of Input device support
+
+#
+# Character devices
+#
+CONFIG_TTY=y
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_VT_CONSOLE_SLEEP=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_LEGACY_TIOCSTI is not set
+# CONFIG_LDISC_AUTOLOAD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_EARLYCON=y
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_PNP=y
+# CONFIG_SERIAL_8250_16550A_VARIANTS is not set
+# CONFIG_SERIAL_8250_FINTEK is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_DMA=y
+CONFIG_SERIAL_8250_PCILIB=y
+CONFIG_SERIAL_8250_PCI=y
+CONFIG_SERIAL_8250_EXAR=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+# CONFIG_SERIAL_8250_PCI1XXXX is not set
+CONFIG_SERIAL_8250_DWLIB=y
+# CONFIG_SERIAL_8250_DW is not set
+# CONFIG_SERIAL_8250_RT288X is not set
+CONFIG_SERIAL_8250_LPSS=y
+CONFIG_SERIAL_8250_MID=y
+CONFIG_SERIAL_8250_PERICOM=y
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_MAX3100=m
+CONFIG_SERIAL_MAX310X=y
+# CONFIG_SERIAL_UARTLITE is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_JSM is not set
+# CONFIG_SERIAL_LANTIQ is not set
+# CONFIG_SERIAL_SCCNXP is not set
+# CONFIG_SERIAL_SC16IS7XX is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_ARC is not set
+# CONFIG_SERIAL_RP2 is not set
+# CONFIG_SERIAL_FSL_LPUART is not set
+# CONFIG_SERIAL_FSL_LINFLEXUART is not set
+# CONFIG_SERIAL_SPRD is not set
+# end of Serial drivers
+
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_N_GSM is not set
+# CONFIG_NOZOMI is not set
+# CONFIG_NULL_TTY is not set
+CONFIG_HVC_DRIVER=y
+# CONFIG_SERIAL_DEV_BUS is not set
+CONFIG_VIRTIO_CONSOLE=m
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_HW_RANDOM_TIMERIOMEM is not set
+CONFIG_HW_RANDOM_INTEL=m
+CONFIG_HW_RANDOM_AMD=m
+# CONFIG_HW_RANDOM_BA431 is not set
+# CONFIG_HW_RANDOM_VIA is not set
+CONFIG_HW_RANDOM_VIRTIO=m
+# CONFIG_HW_RANDOM_XIPHERA is not set
+# CONFIG_APPLICOM is not set
+CONFIG_MWAVE=m
+# CONFIG_DEVMEM is not set
+CONFIG_NVRAM=m
+# CONFIG_DEVPORT is not set
+CONFIG_HPET=y
+CONFIG_HPET_MMAP=y
+CONFIG_HPET_MMAP_DEFAULT=y
+# CONFIG_HANGCHECK_TIMER is not set
+CONFIG_TCG_TPM=m
+CONFIG_TCG_TPM2_HMAC=y
+CONFIG_HW_RANDOM_TPM=y
+CONFIG_TCG_TIS_CORE=m
+CONFIG_TCG_TIS=m
+CONFIG_TCG_TIS_SPI=m
+CONFIG_TCG_TIS_SPI_CR50=y
+CONFIG_TCG_TIS_I2C=m
+CONFIG_TCG_TIS_I2C_CR50=m
+CONFIG_TCG_TIS_I2C_ATMEL=m
+CONFIG_TCG_TIS_I2C_INFINEON=m
+CONFIG_TCG_TIS_I2C_NUVOTON=m
+CONFIG_TCG_NSC=m
+CONFIG_TCG_ATMEL=m
+CONFIG_TCG_INFINEON=m
+CONFIG_TCG_CRB=m
+CONFIG_TCG_VTPM_PROXY=m
+CONFIG_TCG_TIS_ST33ZP24=m
+CONFIG_TCG_TIS_ST33ZP24_I2C=m
+CONFIG_TCG_TIS_ST33ZP24_SPI=m
+# CONFIG_TELCLOCK is not set
+# CONFIG_XILLYBUS is not set
+# CONFIG_XILLYUSB is not set
+# end of Character devices
+
+#
+# I2C support
+#
+CONFIG_I2C=y
+CONFIG_ACPI_I2C_OPREGION=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_MUX=m
+
+#
+# Multiplexer I2C Chip support
+#
+# CONFIG_I2C_MUX_LTC4306 is not set
+# CONFIG_I2C_MUX_PCA9541 is not set
+# CONFIG_I2C_MUX_REG is not set
+# CONFIG_I2C_MUX_MLXCPLD is not set
+# end of Multiplexer I2C Chip support
+
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_SMBUS=m
+CONFIG_I2C_ALGOBIT=m
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# PC SMBus host controller drivers
+#
+CONFIG_I2C_CCGX_UCSI=m
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+# CONFIG_I2C_AMD756 is not set
+# CONFIG_I2C_AMD8111 is not set
+# CONFIG_I2C_AMD_MP2 is not set
+# CONFIG_I2C_AMD_ASF is not set
+CONFIG_I2C_I801=m
+CONFIG_I2C_ISCH=m
+CONFIG_I2C_ISMT=m
+CONFIG_I2C_PIIX4=m
+# CONFIG_I2C_NFORCE2 is not set
+CONFIG_I2C_NVIDIA_GPU=m
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+# CONFIG_I2C_SIS96X is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+# CONFIG_I2C_ZHAOXIN is not set
+
+#
+# ACPI drivers
+#
+# CONFIG_I2C_SCMI is not set
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_DESIGNWARE_CORE is not set
+# CONFIG_I2C_EMEV2 is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_XILINX is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_DIOLAN_U2C is not set
+# CONFIG_I2C_CP2615 is not set
+# CONFIG_I2C_PCI1XXXX is not set
+# CONFIG_I2C_ROBOTFUZZ_OSIF is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_MLXCPLD is not set
+CONFIG_I2C_VIRTIO=m
+# end of I2C Hardware Bus support
+
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_SLAVE is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# end of I2C support
+
+CONFIG_I3C=m
+CONFIG_CDNS_I3C_MASTER=m
+CONFIG_DW_I3C_MASTER=m
+# CONFIG_SVC_I3C_MASTER is not set
+# CONFIG_MIPI_I3C_HCI is not set
+CONFIG_SPI=y
+# CONFIG_SPI_DEBUG is not set
+CONFIG_SPI_MASTER=y
+# CONFIG_SPI_MEM is not set
+
+#
+# SPI Master Controller Drivers
+#
+# CONFIG_SPI_ALTERA is not set
+# CONFIG_SPI_AXI_SPI_ENGINE is not set
+# CONFIG_SPI_BITBANG is not set
+# CONFIG_SPI_CADENCE is not set
+# CONFIG_SPI_CH341 is not set
+# CONFIG_SPI_DESIGNWARE is not set
+# CONFIG_SPI_MICROCHIP_CORE is not set
+# CONFIG_SPI_MICROCHIP_CORE_QSPI is not set
+# CONFIG_SPI_LANTIQ_SSC is not set
+# CONFIG_SPI_PCI1XXXX is not set
+# CONFIG_SPI_PXA2XX is not set
+# CONFIG_SPI_SC18IS602 is not set
+# CONFIG_SPI_SIFIVE is not set
+# CONFIG_SPI_MXIC is not set
+# CONFIG_SPI_XCOMM is not set
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_AMD is not set
+
+#
+# SPI Multiplexer support
+#
+# CONFIG_SPI_MUX is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_LOOPBACK_TEST is not set
+# CONFIG_SPI_TLE62X0 is not set
+# CONFIG_SPI_SLAVE is not set
+CONFIG_SPI_DYNAMIC=y
+CONFIG_SPMI=m
+# CONFIG_SPMI_HISI3670 is not set
+# CONFIG_HSI is not set
+CONFIG_PPS=m
+# CONFIG_PPS_DEBUG is not set
+
+#
+# PPS clients support
+#
+# CONFIG_PPS_CLIENT_KTIMER is not set
+# CONFIG_PPS_CLIENT_LDISC is not set
+# CONFIG_PPS_CLIENT_GPIO is not set
+# CONFIG_PPS_GENERATOR is not set
+
+#
+# PTP clock support
+#
+CONFIG_PTP_1588_CLOCK=m
+CONFIG_PTP_1588_CLOCK_OPTIONAL=m
+
+#
+# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.
+#
+CONFIG_PTP_1588_CLOCK_KVM=m
+CONFIG_PTP_1588_CLOCK_VMCLOCK=m
+# CONFIG_PTP_1588_CLOCK_IDT82P33 is not set
+# CONFIG_PTP_1588_CLOCK_IDTCM is not set
+# CONFIG_PTP_1588_CLOCK_FC3W is not set
+# CONFIG_PTP_1588_CLOCK_MOCK is not set
+# CONFIG_PTP_1588_CLOCK_VMW is not set
+# end of PTP clock support
+
+CONFIG_PINCTRL=y
+# CONFIG_DEBUG_PINCTRL is not set
+# CONFIG_PINCTRL_AMD is not set
+# CONFIG_PINCTRL_CY8C95X0 is not set
+# CONFIG_PINCTRL_MCP23S08 is not set
+# CONFIG_PINCTRL_SX150X is not set
+
+#
+# Intel pinctrl drivers
+#
+# CONFIG_PINCTRL_BAYTRAIL is not set
+# CONFIG_PINCTRL_CHERRYVIEW is not set
+# CONFIG_PINCTRL_LYNXPOINT is not set
+# CONFIG_PINCTRL_INTEL_PLATFORM is not set
+# CONFIG_PINCTRL_ALDERLAKE is not set
+# CONFIG_PINCTRL_BROXTON is not set
+# CONFIG_PINCTRL_CANNONLAKE is not set
+# CONFIG_PINCTRL_CEDARFORK is not set
+# CONFIG_PINCTRL_DENVERTON is not set
+# CONFIG_PINCTRL_ELKHARTLAKE is not set
+# CONFIG_PINCTRL_EMMITSBURG is not set
+# CONFIG_PINCTRL_GEMINILAKE is not set
+# CONFIG_PINCTRL_ICELAKE is not set
+# CONFIG_PINCTRL_JASPERLAKE is not set
+# CONFIG_PINCTRL_LAKEFIELD is not set
+# CONFIG_PINCTRL_LEWISBURG is not set
+# CONFIG_PINCTRL_METEORLAKE is not set
+# CONFIG_PINCTRL_METEORPOINT is not set
+# CONFIG_PINCTRL_SUNRISEPOINT is not set
+# CONFIG_PINCTRL_TIGERLAKE is not set
+# end of Intel pinctrl drivers
+
+#
+# Renesas pinctrl drivers
+#
+# end of Renesas pinctrl drivers
+
+# CONFIG_GPIOLIB is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_RESET is not set
+# CONFIG_POWER_SEQUENCING is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+CONFIG_POWER_SUPPLY_HWMON=y
+# CONFIG_IP5XXX_POWER is not set
+# CONFIG_TEST_POWER is not set
+# CONFIG_CHARGER_ADP5061 is not set
+# CONFIG_BATTERY_CW2015 is not set
+# CONFIG_BATTERY_DS2780 is not set
+# CONFIG_BATTERY_DS2781 is not set
+# CONFIG_BATTERY_DS2782 is not set
+# CONFIG_BATTERY_SAMSUNG_SDI is not set
+# CONFIG_BATTERY_SBS is not set
+# CONFIG_CHARGER_SBS is not set
+# CONFIG_BATTERY_BQ27XXX is not set
+# CONFIG_BATTERY_MAX17042 is not set
+# CONFIG_BATTERY_MAX1720X is not set
+# CONFIG_CHARGER_ISP1704 is not set
+# CONFIG_CHARGER_MAX8903 is not set
+# CONFIG_CHARGER_LP8727 is not set
+# CONFIG_CHARGER_LTC4162L is not set
+# CONFIG_CHARGER_MAX77976 is not set
+# CONFIG_CHARGER_BQ2415X is not set
+# CONFIG_BATTERY_GAUGE_LTC2941 is not set
+# CONFIG_BATTERY_GOLDFISH is not set
+# CONFIG_BATTERY_RT5033 is not set
+# CONFIG_FUEL_GAUGE_STC3117 is not set
+# CONFIG_CHARGER_BD99954 is not set
+# CONFIG_BATTERY_UG3105 is not set
+# CONFIG_FUEL_GAUGE_MM8013 is not set
+CONFIG_HWMON=y
+CONFIG_HWMON_VID=m
+# CONFIG_HWMON_DEBUG_CHIP is not set
+
+#
+# Native drivers
+#
+# CONFIG_SENSORS_ABITUGURU is not set
+# CONFIG_SENSORS_ABITUGURU3 is not set
+# CONFIG_SENSORS_AD7314 is not set
+# CONFIG_SENSORS_AD7414 is not set
+# CONFIG_SENSORS_AD7418 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1029 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM1177 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ADT7310 is not set
+# CONFIG_SENSORS_ADT7410 is not set
+# CONFIG_SENSORS_ADT7411 is not set
+# CONFIG_SENSORS_ADT7462 is not set
+# CONFIG_SENSORS_ADT7470 is not set
+# CONFIG_SENSORS_ADT7475 is not set
+# CONFIG_SENSORS_AHT10 is not set
+# CONFIG_SENSORS_AQUACOMPUTER_D5NEXT is not set
+# CONFIG_SENSORS_AS370 is not set
+# CONFIG_SENSORS_ASC7621 is not set
+# CONFIG_SENSORS_ASUS_ROG_RYUJIN is not set
+# CONFIG_SENSORS_AXI_FAN_CONTROL is not set
+# CONFIG_SENSORS_K8TEMP is not set
+CONFIG_SENSORS_K10TEMP=m
+CONFIG_SENSORS_FAM15H_POWER=m
+# CONFIG_SENSORS_APPLESMC is not set
+# CONFIG_SENSORS_ASB100 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_CHIPCAP2 is not set
+# CONFIG_SENSORS_CORSAIR_CPRO is not set
+# CONFIG_SENSORS_CORSAIR_PSU is not set
+CONFIG_SENSORS_DRIVETEMP=m
+# CONFIG_SENSORS_DS620 is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_DELL_SMM is not set
+# CONFIG_SENSORS_I5K_AMB is not set
+# CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_F71882FG is not set
+# CONFIG_SENSORS_F75375S is not set
+# CONFIG_SENSORS_FSCHMD is not set
+# CONFIG_SENSORS_FTSTEUTATES is not set
+# CONFIG_SENSORS_GIGABYTE_WATERFORCE is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_G760A is not set
+# CONFIG_SENSORS_G762 is not set
+# CONFIG_SENSORS_HIH6130 is not set
+# CONFIG_SENSORS_HS3001 is not set
+# CONFIG_SENSORS_I5500 is not set
+CONFIG_SENSORS_CORETEMP=m
+# CONFIG_SENSORS_ISL28022 is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_JC42 is not set
+# CONFIG_SENSORS_POWERZ is not set
+# CONFIG_SENSORS_POWR1220 is not set
+CONFIG_SENSORS_LENOVO_EC=m
+# CONFIG_SENSORS_LINEAGE is not set
+# CONFIG_SENSORS_LTC2945 is not set
+# CONFIG_SENSORS_LTC2947_I2C is not set
+# CONFIG_SENSORS_LTC2947_SPI is not set
+# CONFIG_SENSORS_LTC2990 is not set
+# CONFIG_SENSORS_LTC2991 is not set
+# CONFIG_SENSORS_LTC4151 is not set
+# CONFIG_SENSORS_LTC4215 is not set
+# CONFIG_SENSORS_LTC4222 is not set
+# CONFIG_SENSORS_LTC4245 is not set
+# CONFIG_SENSORS_LTC4260 is not set
+# CONFIG_SENSORS_LTC4261 is not set
+# CONFIG_SENSORS_LTC4282 is not set
+# CONFIG_SENSORS_MAX1111 is not set
+# CONFIG_SENSORS_MAX127 is not set
+# CONFIG_SENSORS_MAX16065 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_MAX1668 is not set
+# CONFIG_SENSORS_MAX197 is not set
+# CONFIG_SENSORS_MAX31722 is not set
+# CONFIG_SENSORS_MAX31730 is not set
+# CONFIG_SENSORS_MAX31760 is not set
+# CONFIG_MAX31827 is not set
+# CONFIG_SENSORS_MAX6620 is not set
+# CONFIG_SENSORS_MAX6621 is not set
+# CONFIG_SENSORS_MAX6639 is not set
+# CONFIG_SENSORS_MAX6650 is not set
+# CONFIG_SENSORS_MAX6697 is not set
+# CONFIG_SENSORS_MAX31790 is not set
+# CONFIG_SENSORS_MC34VR500 is not set
+# CONFIG_SENSORS_MCP3021 is not set
+# CONFIG_SENSORS_TC654 is not set
+# CONFIG_SENSORS_TPS23861 is not set
+# CONFIG_SENSORS_MR75203 is not set
+# CONFIG_SENSORS_ADCXX is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 is not set
+# CONFIG_SENSORS_LM73 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+# CONFIG_SENSORS_LM85 is not set
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_LM93 is not set
+# CONFIG_SENSORS_LM95234 is not set
+# CONFIG_SENSORS_LM95241 is not set
+# CONFIG_SENSORS_LM95245 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 is not set
+# CONFIG_SENSORS_NCT6683 is not set
+CONFIG_SENSORS_NCT6775_CORE=m
+CONFIG_SENSORS_NCT6775=m
+CONFIG_SENSORS_NCT6775_I2C=m
+# CONFIG_SENSORS_NCT7363 is not set
+# CONFIG_SENSORS_NCT7802 is not set
+# CONFIG_SENSORS_NCT7904 is not set
+# CONFIG_SENSORS_NPCM7XX is not set
+# CONFIG_SENSORS_NZXT_KRAKEN2 is not set
+# CONFIG_SENSORS_NZXT_KRAKEN3 is not set
+# CONFIG_SENSORS_NZXT_SMART2 is not set
+# CONFIG_SENSORS_OCC_P8_I2C is not set
+# CONFIG_SENSORS_OXP is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_PMBUS is not set
+# CONFIG_SENSORS_PT5161L is not set
+CONFIG_SENSORS_PWM_FAN=m
+# CONFIG_SENSORS_SBTSI is not set
+# CONFIG_SENSORS_SBRMI is not set
+# CONFIG_SENSORS_SHT21 is not set
+# CONFIG_SENSORS_SHT3x is not set
+# CONFIG_SENSORS_SHT4x is not set
+# CONFIG_SENSORS_SHTC1 is not set
+# CONFIG_SENSORS_SIS5595 is not set
+# CONFIG_SENSORS_DME1737 is not set
+# CONFIG_SENSORS_EMC1403 is not set
+# CONFIG_SENSORS_EMC2103 is not set
+# CONFIG_SENSORS_EMC2305 is not set
+# CONFIG_SENSORS_EMC6W201 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47M192 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_SCH5627 is not set
+# CONFIG_SENSORS_SCH5636 is not set
+# CONFIG_SENSORS_STTS751 is not set
+# CONFIG_SENSORS_ADC128D818 is not set
+# CONFIG_SENSORS_ADS7828 is not set
+# CONFIG_SENSORS_ADS7871 is not set
+# CONFIG_SENSORS_AMC6821 is not set
+# CONFIG_SENSORS_INA209 is not set
+# CONFIG_SENSORS_INA2XX is not set
+# CONFIG_SENSORS_INA238 is not set
+# CONFIG_SENSORS_INA3221 is not set
+# CONFIG_SENSORS_SPD5118 is not set
+# CONFIG_SENSORS_TC74 is not set
+# CONFIG_SENSORS_THMC50 is not set
+# CONFIG_SENSORS_TMP102 is not set
+# CONFIG_SENSORS_TMP103 is not set
+# CONFIG_SENSORS_TMP108 is not set
+# CONFIG_SENSORS_TMP401 is not set
+# CONFIG_SENSORS_TMP421 is not set
+# CONFIG_SENSORS_TMP464 is not set
+# CONFIG_SENSORS_TMP513 is not set
+# CONFIG_SENSORS_VIA_CPUTEMP is not set
+# CONFIG_SENSORS_VIA686A is not set
+# CONFIG_SENSORS_VT1211 is not set
+# CONFIG_SENSORS_VT8231 is not set
+# CONFIG_SENSORS_W83773G is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83791D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83793 is not set
+# CONFIG_SENSORS_W83795 is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83L786NG is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_SENSORS_XGENE is not set
+
+#
+# ACPI drivers
+#
+CONFIG_SENSORS_ACPI_POWER=m
+# CONFIG_SENSORS_ATK0110 is not set
+# CONFIG_SENSORS_ASUS_WMI is not set
+# CONFIG_SENSORS_ASUS_EC is not set
+# CONFIG_SENSORS_HP_WMI is not set
+CONFIG_THERMAL=y
+CONFIG_THERMAL_NETLINK=y
+# CONFIG_THERMAL_STATISTICS is not set
+# CONFIG_THERMAL_DEBUGFS is not set
+# CONFIG_THERMAL_CORE_TESTING is not set
+CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
+CONFIG_THERMAL_HWMON=y
+# CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE is not set
+CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE=y
+# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set
+CONFIG_THERMAL_GOV_FAIR_SHARE=y
+CONFIG_THERMAL_GOV_STEP_WISE=y
+# CONFIG_THERMAL_GOV_BANG_BANG is not set
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y
+# CONFIG_DEVFREQ_THERMAL is not set
+# CONFIG_PCIE_THERMAL is not set
+# CONFIG_THERMAL_EMULATION is not set
+
+#
+# Intel thermal drivers
+#
+# CONFIG_INTEL_POWERCLAMP is not set
+CONFIG_X86_THERMAL_VECTOR=y
+CONFIG_INTEL_TCC=y
+CONFIG_X86_PKG_TEMP_THERMAL=m
+CONFIG_INTEL_SOC_DTS_IOSF_CORE=m
+# CONFIG_INTEL_SOC_DTS_THERMAL is not set
+
+#
+# ACPI INT340X thermal drivers
+#
+CONFIG_INT340X_THERMAL=m
+CONFIG_ACPI_THERMAL_REL=m
+CONFIG_INT3406_THERMAL=m
+CONFIG_PROC_THERMAL_MMIO_RAPL=m
+# end of ACPI INT340X thermal drivers
+
+CONFIG_INTEL_PCH_THERMAL=m
+CONFIG_INTEL_TCC_COOLING=m
+CONFIG_INTEL_HFI_THERMAL=y
+# end of Intel thermal drivers
+
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_CORE=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y
+CONFIG_WATCHDOG_OPEN_TIMEOUT=0
+CONFIG_WATCHDOG_SYSFS=y
+# CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT is not set
+
+#
+# Watchdog Pretimeout Governors
+#
+# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+# CONFIG_LENOVO_SE10_WDT is not set
+# CONFIG_WDAT_WDT is not set
+# CONFIG_XILINX_WATCHDOG is not set
+# CONFIG_ZIIRAVE_WATCHDOG is not set
+# CONFIG_CADENCE_WATCHDOG is not set
+# CONFIG_DW_WATCHDOG is not set
+# CONFIG_MAX63XX_WATCHDOG is not set
+# CONFIG_ACQUIRE_WDT is not set
+# CONFIG_ADVANTECH_WDT is not set
+# CONFIG_ADVANTECH_EC_WDT is not set
+# CONFIG_ALIM1535_WDT is not set
+# CONFIG_ALIM7101_WDT is not set
+# CONFIG_EBC_C384_WDT is not set
+# CONFIG_EXAR_WDT is not set
+# CONFIG_F71808E_WDT is not set
+# CONFIG_SP5100_TCO is not set
+# CONFIG_SBC_FITPC2_WATCHDOG is not set
+# CONFIG_EUROTECH_WDT is not set
+# CONFIG_IB700_WDT is not set
+# CONFIG_IBMASR is not set
+# CONFIG_WAFER_WDT is not set
+# CONFIG_I6300ESB_WDT is not set
+# CONFIG_IE6XX_WDT is not set
+CONFIG_ITCO_WDT=m
+CONFIG_ITCO_VENDOR_SUPPORT=y
+CONFIG_IT8712F_WDT=m
+CONFIG_IT87_WDT=m
+# CONFIG_HP_WATCHDOG is not set
+# CONFIG_SC1200_WDT is not set
+# CONFIG_PC87413_WDT is not set
+# CONFIG_NV_TCO is not set
+# CONFIG_60XX_WDT is not set
+# CONFIG_SMSC_SCH311X_WDT is not set
+# CONFIG_SMSC37B787_WDT is not set
+# CONFIG_TQMX86_WDT is not set
+# CONFIG_VIA_WDT is not set
+# CONFIG_W83627HF_WDT is not set
+# CONFIG_W83877F_WDT is not set
+# CONFIG_W83977F_WDT is not set
+# CONFIG_MACHZ_WDT is not set
+# CONFIG_SBC_EPX_C3_WATCHDOG is not set
+# CONFIG_NI903X_WDT is not set
+# CONFIG_NIC7018_WDT is not set
+
+#
+# PCI-based Watchdog Cards
+#
+# CONFIG_PCIPCWATCHDOG is not set
+# CONFIG_WDTPCI is not set
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+CONFIG_SSB=m
+CONFIG_SSB_SPROM=y
+CONFIG_SSB_PCIHOST_POSSIBLE=y
+CONFIG_SSB_PCIHOST=y
+CONFIG_SSB_SDIOHOST_POSSIBLE=y
+# CONFIG_SSB_SDIOHOST is not set
+CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y
+CONFIG_SSB_DRIVER_PCICORE=y
+CONFIG_BCMA_POSSIBLE=y
+# CONFIG_BCMA is not set
+
+#
+# Multifunction device drivers
+#
+CONFIG_MFD_CORE=m
+# CONFIG_MFD_AS3711 is not set
+# CONFIG_MFD_SMPRO is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_MFD_BCM590XX is not set
+# CONFIG_MFD_BD9571MWV is not set
+# CONFIG_MFD_AXP20X_I2C is not set
+# CONFIG_MFD_CGBC is not set
+# CONFIG_MFD_CS42L43_I2C is not set
+# CONFIG_MFD_MADERA is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_DA9052_SPI is not set
+# CONFIG_MFD_DA9052_I2C is not set
+# CONFIG_MFD_DA9055 is not set
+# CONFIG_MFD_DA9062 is not set
+# CONFIG_MFD_DA9063 is not set
+# CONFIG_MFD_DA9150 is not set
+# CONFIG_MFD_DLN2 is not set
+# CONFIG_MFD_MC13XXX_SPI is not set
+# CONFIG_MFD_MC13XXX_I2C is not set
+# CONFIG_MFD_MP2629 is not set
+# CONFIG_MFD_INTEL_QUARK_I2C_GPIO is not set
+CONFIG_LPC_ICH=m
+CONFIG_LPC_SCH=m
+CONFIG_MFD_INTEL_LPSS=m
+CONFIG_MFD_INTEL_LPSS_ACPI=m
+CONFIG_MFD_INTEL_LPSS_PCI=m
+# CONFIG_MFD_INTEL_PMC_BXT is not set
+# CONFIG_MFD_IQS62X is not set
+# CONFIG_MFD_JANZ_CMODIO is not set
+# CONFIG_MFD_KEMPLD is not set
+# CONFIG_MFD_88PM800 is not set
+# CONFIG_MFD_88PM805 is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_MAX14577 is not set
+# CONFIG_MFD_MAX77541 is not set
+# CONFIG_MFD_MAX77693 is not set
+# CONFIG_MFD_MAX77843 is not set
+# CONFIG_MFD_MAX8907 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_MFD_MT6360 is not set
+# CONFIG_MFD_MT6370 is not set
+# CONFIG_MFD_MT6397 is not set
+# CONFIG_MFD_MENF21BMC is not set
+# CONFIG_MFD_OCELOT is not set
+# CONFIG_EZX_PCAP is not set
+# CONFIG_MFD_VIPERBOARD is not set
+# CONFIG_MFD_RETU is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_MFD_SY7636A is not set
+# CONFIG_MFD_RDC321X is not set
+# CONFIG_MFD_RT4831 is not set
+# CONFIG_MFD_RT5033 is not set
+# CONFIG_MFD_RT5120 is not set
+# CONFIG_MFD_RC5T583 is not set
+# CONFIG_MFD_SI476X_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_SKY81452 is not set
+# CONFIG_MFD_SYSCON is not set
+# CONFIG_MFD_LP3943 is not set
+# CONFIG_MFD_LP8788 is not set
+# CONFIG_MFD_TI_LMU is not set
+# CONFIG_MFD_PALMAS is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_MFD_TPS65086 is not set
+# CONFIG_MFD_TPS65090 is not set
+# CONFIG_MFD_TI_LP873X is not set
+# CONFIG_MFD_TPS6586X is not set
+# CONFIG_MFD_TPS65912_I2C is not set
+# CONFIG_MFD_TPS65912_SPI is not set
+# CONFIG_MFD_TPS6594_I2C is not set
+# CONFIG_MFD_TPS6594_SPI is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_TWL6040_CORE is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_MFD_LM3533 is not set
+# CONFIG_MFD_TQMX86 is not set
+# CONFIG_MFD_VX855 is not set
+# CONFIG_MFD_ARIZONA_I2C is not set
+# CONFIG_MFD_ARIZONA_SPI is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM831X_SPI is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8994 is not set
+# CONFIG_MFD_ATC260X_I2C is not set
+# CONFIG_MFD_CS40L50_I2C is not set
+# CONFIG_MFD_CS40L50_SPI is not set
+# CONFIG_MFD_INTEL_M10_BMC_SPI is not set
+# CONFIG_MFD_UPBOARD_FPGA is not set
+# end of Multifunction device drivers
+
+# CONFIG_REGULATOR is not set
+# CONFIG_RC_CORE is not set
+CONFIG_CEC_CORE=m
+CONFIG_CEC_NOTIFIER=y
+
+#
+# CEC support
+#
+# CONFIG_MEDIA_CEC_SUPPORT is not set
+# end of CEC support
+
+CONFIG_MEDIA_SUPPORT=m
+CONFIG_MEDIA_SUPPORT_FILTER=y
+CONFIG_MEDIA_SUBDRV_AUTOSELECT=y
+
+#
+# Media device types
+#
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set
+# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set
+# CONFIG_MEDIA_RADIO_SUPPORT is not set
+# CONFIG_MEDIA_SDR_SUPPORT is not set
+# CONFIG_MEDIA_PLATFORM_SUPPORT is not set
+# CONFIG_MEDIA_TEST_SUPPORT is not set
+# end of Media device types
+
+CONFIG_VIDEO_DEV=m
+CONFIG_MEDIA_CONTROLLER=y
+
+#
+# Video4Linux options
+#
+CONFIG_VIDEO_V4L2_I2C=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+CONFIG_V4L2_FWNODE=m
+CONFIG_V4L2_ASYNC=m
+# end of Video4Linux options
+
+#
+# Media controller options
+#
+# end of Media controller options
+
+#
+# Media drivers
+#
+
+#
+# Drivers filtered as selected at 'Filter media drivers'
+#
+
+#
+# Media drivers
+#
+CONFIG_MEDIA_USB_SUPPORT=y
+
+#
+# Webcam devices
+#
+# CONFIG_USB_GSPCA is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_S2255 is not set
+# CONFIG_VIDEO_USBTV is not set
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y
+
+#
+# Webcam, TV (analog/digital) USB devices
+#
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_MEDIA_PCI_SUPPORT is not set
+CONFIG_UVC_COMMON=m
+CONFIG_VIDEOBUF2_CORE=m
+CONFIG_VIDEOBUF2_V4L2=m
+CONFIG_VIDEOBUF2_MEMOPS=m
+CONFIG_VIDEOBUF2_VMALLOC=m
+# end of Media drivers
+
+CONFIG_MEDIA_HIDE_ANCILLARY_SUBDRV=y
+
+#
+# Media ancillary drivers
+#
+CONFIG_VIDEO_CAMERA_SENSOR=y
+# CONFIG_VIDEO_ALVIUM_CSI2 is not set
+# CONFIG_VIDEO_AR0521 is not set
+# CONFIG_VIDEO_GC0308 is not set
+# CONFIG_VIDEO_GC05A2 is not set
+# CONFIG_VIDEO_GC08A3 is not set
+# CONFIG_VIDEO_GC2145 is not set
+# CONFIG_VIDEO_HI556 is not set
+# CONFIG_VIDEO_HI846 is not set
+# CONFIG_VIDEO_HI847 is not set
+# CONFIG_VIDEO_IMX208 is not set
+# CONFIG_VIDEO_IMX219 is not set
+# CONFIG_VIDEO_IMX258 is not set
+# CONFIG_VIDEO_IMX274 is not set
+# CONFIG_VIDEO_IMX283 is not set
+# CONFIG_VIDEO_IMX290 is not set
+# CONFIG_VIDEO_IMX296 is not set
+# CONFIG_VIDEO_IMX319 is not set
+# CONFIG_VIDEO_IMX355 is not set
+# CONFIG_VIDEO_MT9M001 is not set
+# CONFIG_VIDEO_MT9M111 is not set
+# CONFIG_VIDEO_MT9M114 is not set
+# CONFIG_VIDEO_MT9P031 is not set
+# CONFIG_VIDEO_MT9T112 is not set
+# CONFIG_VIDEO_MT9V011 is not set
+# CONFIG_VIDEO_MT9V032 is not set
+# CONFIG_VIDEO_MT9V111 is not set
+# CONFIG_VIDEO_OG01A1B is not set
+# CONFIG_VIDEO_OV01A10 is not set
+# CONFIG_VIDEO_OV02A10 is not set
+# CONFIG_VIDEO_OV08D10 is not set
+# CONFIG_VIDEO_OV08X40 is not set
+# CONFIG_VIDEO_OV13858 is not set
+# CONFIG_VIDEO_OV13B10 is not set
+# CONFIG_VIDEO_OV2640 is not set
+# CONFIG_VIDEO_OV2680 is not set
+# CONFIG_VIDEO_OV2685 is not set
+# CONFIG_VIDEO_OV2740 is not set
+# CONFIG_VIDEO_OV5647 is not set
+# CONFIG_VIDEO_OV5648 is not set
+# CONFIG_VIDEO_OV5670 is not set
+# CONFIG_VIDEO_OV5675 is not set
+# CONFIG_VIDEO_OV5693 is not set
+# CONFIG_VIDEO_OV5695 is not set
+# CONFIG_VIDEO_OV64A40 is not set
+# CONFIG_VIDEO_OV6650 is not set
+# CONFIG_VIDEO_OV7251 is not set
+# CONFIG_VIDEO_OV7640 is not set
+# CONFIG_VIDEO_OV7670 is not set
+# CONFIG_VIDEO_OV772X is not set
+# CONFIG_VIDEO_OV7740 is not set
+# CONFIG_VIDEO_OV8856 is not set
+# CONFIG_VIDEO_OV8858 is not set
+# CONFIG_VIDEO_OV8865 is not set
+# CONFIG_VIDEO_OV9640 is not set
+# CONFIG_VIDEO_OV9650 is not set
+# CONFIG_VIDEO_OV9734 is not set
+# CONFIG_VIDEO_RDACM20 is not set
+# CONFIG_VIDEO_RDACM21 is not set
+# CONFIG_VIDEO_RJ54N1 is not set
+# CONFIG_VIDEO_S5C73M3 is not set
+# CONFIG_VIDEO_S5K5BAF is not set
+# CONFIG_VIDEO_S5K6A3 is not set
+# CONFIG_VIDEO_CCS is not set
+# CONFIG_VIDEO_ET8EK8 is not set
+
+#
+# Camera ISPs
+#
+# CONFIG_VIDEO_THP7312 is not set
+# end of Camera ISPs
+
+#
+# Lens drivers
+#
+# CONFIG_VIDEO_AK7375 is not set
+# CONFIG_VIDEO_DW9714 is not set
+# CONFIG_VIDEO_DW9719 is not set
+# CONFIG_VIDEO_DW9768 is not set
+# CONFIG_VIDEO_DW9807_VCM is not set
+# end of Lens drivers
+
+#
+# Flash devices
+#
+# CONFIG_VIDEO_ADP1653 is not set
+# CONFIG_VIDEO_LM3560 is not set
+# CONFIG_VIDEO_LM3646 is not set
+# end of Flash devices
+
+#
+# audio, video and radio I2C drivers auto-selected by 'Autoselect ancillary drivers'
+#
+
+#
+# Video and audio decoders
+#
+
+#
+# Video serializers and deserializers
+#
+# end of Video serializers and deserializers
+
+#
+# SPI I2C drivers auto-selected by 'Autoselect ancillary drivers'
+#
+
+#
+# Media SPI Adapters
+#
+# CONFIG_VIDEO_GS1662 is not set
+# end of Media SPI Adapters
+# end of Media ancillary drivers
+
+#
+# Graphics support
+#
+CONFIG_APERTURE_HELPERS=y
+CONFIG_SCREEN_INFO=y
+CONFIG_VIDEO=y
+# CONFIG_AUXDISPLAY is not set
+CONFIG_AGP=y
+CONFIG_AGP_AMD64=m
+CONFIG_AGP_INTEL=m
+# CONFIG_AGP_SIS is not set
+# CONFIG_AGP_VIA is not set
+CONFIG_INTEL_GTT=m
+CONFIG_VGA_SWITCHEROO=y
+CONFIG_DRM=y
+CONFIG_DRM_MIPI_DSI=y
+# CONFIG_DRM_DEBUG_MM is not set
+CONFIG_DRM_KMS_HELPER=y
+CONFIG_DRM_DRAW=y
+CONFIG_DRM_PANIC=y
+CONFIG_DRM_PANIC_FOREGROUND_COLOR=0xffffff
+CONFIG_DRM_PANIC_BACKGROUND_COLOR=0x000000
+# CONFIG_DRM_PANIC_DEBUG is not set
+CONFIG_DRM_PANIC_SCREEN="user"
+CONFIG_DRM_CLIENT=y
+CONFIG_DRM_CLIENT_LIB=y
+CONFIG_DRM_CLIENT_SELECTION=y
+CONFIG_DRM_CLIENT_SETUP=y
+
+#
+# Supported DRM clients
+#
+CONFIG_DRM_FBDEV_EMULATION=y
+CONFIG_DRM_FBDEV_OVERALLOC=100
+# CONFIG_DRM_CLIENT_LOG is not set
+CONFIG_DRM_CLIENT_DEFAULT_FBDEV=y
+CONFIG_DRM_CLIENT_DEFAULT="fbdev"
+# end of Supported DRM clients
+
+# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set
+CONFIG_DRM_DISPLAY_HELPER=m
+CONFIG_DRM_DISPLAY_DP_AUX_CEC=y
+CONFIG_DRM_DISPLAY_DP_AUX_CHARDEV=y
+CONFIG_DRM_DISPLAY_DP_HELPER=y
+CONFIG_DRM_DISPLAY_DP_TUNNEL=y
+CONFIG_DRM_DISPLAY_DSC_HELPER=y
+CONFIG_DRM_DISPLAY_HDCP_HELPER=y
+CONFIG_DRM_DISPLAY_HDMI_HELPER=y
+CONFIG_DRM_TTM=m
+CONFIG_DRM_EXEC=m
+CONFIG_DRM_GPUVM=m
+CONFIG_DRM_BUDDY=m
+CONFIG_DRM_TTM_HELPER=m
+CONFIG_DRM_GEM_SHMEM_HELPER=y
+CONFIG_DRM_SUBALLOC_HELPER=m
+CONFIG_DRM_SCHED=m
+
+#
+# I2C encoder or helper chips
+#
+# CONFIG_DRM_I2C_CH7006 is not set
+# CONFIG_DRM_I2C_SIL164 is not set
+# CONFIG_DRM_I2C_NXP_TDA998X is not set
+# CONFIG_DRM_I2C_NXP_TDA9950 is not set
+# end of I2C encoder or helper chips
+
+#
+# ARM devices
+#
+# end of ARM devices
+
+# CONFIG_DRM_RADEON is not set
+CONFIG_DRM_AMDGPU=m
+CONFIG_DRM_AMDGPU_SI=y
+CONFIG_DRM_AMDGPU_CIK=y
+CONFIG_DRM_AMDGPU_USERPTR=y
+# CONFIG_DRM_AMD_ISP is not set
+
+#
+# ACP (Audio CoProcessor) Configuration
+#
+# CONFIG_DRM_AMD_ACP is not set
+# end of ACP (Audio CoProcessor) Configuration
+
+#
+# Display Engine Configuration
+#
+CONFIG_DRM_AMD_DC=y
+CONFIG_DRM_AMD_DC_FP=y
+CONFIG_DRM_AMD_DC_SI=y
+CONFIG_DRM_AMD_SECURE_DISPLAY=y
+# end of Display Engine Configuration
+
+CONFIG_HSA_AMD=y
+CONFIG_HSA_AMD_SVM=y
+CONFIG_DRM_NOUVEAU=m
+CONFIG_NOUVEAU_DEBUG=5
+CONFIG_NOUVEAU_DEBUG_DEFAULT=3
+# CONFIG_NOUVEAU_DEBUG_MMU is not set
+# CONFIG_NOUVEAU_DEBUG_PUSH is not set
+CONFIG_DRM_NOUVEAU_BACKLIGHT=y
+# CONFIG_DRM_NOUVEAU_GSP_DEFAULT is not set
+CONFIG_DRM_I915=m
+CONFIG_DRM_I915_FORCE_PROBE=""
+CONFIG_DRM_I915_CAPTURE_ERROR=y
+CONFIG_DRM_I915_COMPRESS_ERROR=y
+CONFIG_DRM_I915_USERPTR=y
+CONFIG_DRM_I915_GVT_KVMGT=m
+CONFIG_DRM_I915_DP_TUNNEL=y
+CONFIG_DRM_I915_REQUEST_TIMEOUT=20000
+CONFIG_DRM_I915_FENCE_TIMEOUT=10000
+CONFIG_DRM_I915_USERFAULT_AUTOSUSPEND=250
+CONFIG_DRM_I915_HEARTBEAT_INTERVAL=2500
+CONFIG_DRM_I915_PREEMPT_TIMEOUT=640
+CONFIG_DRM_I915_PREEMPT_TIMEOUT_COMPUTE=7500
+CONFIG_DRM_I915_MAX_REQUEST_BUSYWAIT=8000
+CONFIG_DRM_I915_STOP_TIMEOUT=100
+CONFIG_DRM_I915_TIMESLICE_DURATION=1
+CONFIG_DRM_I915_GVT=y
+# CONFIG_DRM_XE is not set
+CONFIG_DRM_VGEM=m
+CONFIG_DRM_VKMS=m
+# CONFIG_DRM_VMWGFX is not set
+# CONFIG_DRM_GMA500 is not set
+CONFIG_DRM_UDL=m
+# CONFIG_DRM_AST is not set
+# CONFIG_DRM_MGAG200 is not set
+CONFIG_DRM_QXL=m
+CONFIG_DRM_VIRTIO_GPU=m
+CONFIG_DRM_VIRTIO_GPU_KMS=y
+CONFIG_DRM_PANEL=y
+
+#
+# Display Panels
+#
+# CONFIG_DRM_PANEL_AUO_A030JTN01 is not set
+# CONFIG_DRM_PANEL_ILITEK_ILI9341 is not set
+# CONFIG_DRM_PANEL_ORISETECH_OTA5601A is not set
+# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set
+# end of Display Panels
+
+CONFIG_DRM_BRIDGE=y
+CONFIG_DRM_PANEL_BRIDGE=y
+
+#
+# Display Interface Bridges
+#
+# CONFIG_DRM_ANALOGIX_ANX78XX is not set
+# end of Display Interface Bridges
+
+# CONFIG_DRM_ETNAVIV is not set
+# CONFIG_DRM_HISI_HIBMC is not set
+CONFIG_DRM_BOCHS=m
+# CONFIG_DRM_CIRRUS_QEMU is not set
+# CONFIG_DRM_GM12U320 is not set
+# CONFIG_DRM_PANEL_MIPI_DBI is not set
+CONFIG_DRM_SIMPLEDRM=y
+# CONFIG_TINYDRM_HX8357D is not set
+# CONFIG_TINYDRM_ILI9163 is not set
+# CONFIG_TINYDRM_ILI9225 is not set
+# CONFIG_TINYDRM_ILI9341 is not set
+# CONFIG_TINYDRM_ILI9486 is not set
+# CONFIG_TINYDRM_MI0283QT is not set
+# CONFIG_TINYDRM_REPAPER is not set
+# CONFIG_TINYDRM_SHARP_MEMORY is not set
+# CONFIG_TINYDRM_ST7586 is not set
+# CONFIG_TINYDRM_ST7735R is not set
+# CONFIG_DRM_VBOXVIDEO is not set
+CONFIG_DRM_GUD=m
+# CONFIG_DRM_SSD130X is not set
+CONFIG_DRM_PANEL_BACKLIGHT_QUIRKS=m
+CONFIG_DRM_PRIVACY_SCREEN=y
+CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y
+
+#
+# Frame buffer Devices
+#
+CONFIG_FB=y
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_ARC is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_VGA16 is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_VESA is not set
+CONFIG_FB_EFI=y
+# CONFIG_FB_N411 is not set
+# CONFIG_FB_HGA is not set
+# CONFIG_FB_OPENCORES is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_I740 is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_CARMINE is not set
+# CONFIG_FB_SMSCUFX is not set
+# CONFIG_FB_IBM_GXT4500 is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_SM712 is not set
+CONFIG_FB_CORE=y
+CONFIG_FB_NOTIFY=y
+CONFIG_FIRMWARE_EDID=y
+# CONFIG_FB_DEVICE is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+CONFIG_FB_SYS_FILLRECT=y
+CONFIG_FB_SYS_COPYAREA=y
+CONFIG_FB_SYS_IMAGEBLIT=y
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+CONFIG_FB_SYSMEM_FOPS=y
+CONFIG_FB_DEFERRED_IO=y
+CONFIG_FB_IOMEM_FOPS=y
+CONFIG_FB_IOMEM_HELPERS=y
+CONFIG_FB_SYSMEM_HELPERS=y
+CONFIG_FB_SYSMEM_HELPERS_DEFERRED=y
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+# end of Frame buffer Devices
+
+#
+# Backlight & LCD device support
+#
+CONFIG_LCD_CLASS_DEVICE=m
+# CONFIG_LCD_LTV350QV is not set
+# CONFIG_LCD_ILI922X is not set
+# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_TDO24M is not set
+# CONFIG_LCD_VGG2432A4 is not set
+CONFIG_LCD_PLATFORM=m
+# CONFIG_LCD_AMS369FG06 is not set
+# CONFIG_LCD_LMS501KF03 is not set
+# CONFIG_LCD_HX8357 is not set
+# CONFIG_LCD_OTM3225A is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_KTD2801 is not set
+# CONFIG_BACKLIGHT_KTZ8866 is not set
+CONFIG_BACKLIGHT_PWM=m
+# CONFIG_BACKLIGHT_APPLE is not set
+# CONFIG_BACKLIGHT_QCOM_WLED is not set
+# CONFIG_BACKLIGHT_SAHARA is not set
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+# CONFIG_BACKLIGHT_LM3509 is not set
+# CONFIG_BACKLIGHT_LM3630A is not set
+CONFIG_BACKLIGHT_LM3639=m
+# CONFIG_BACKLIGHT_LP855X is not set
+# CONFIG_BACKLIGHT_MP3309C is not set
+# CONFIG_BACKLIGHT_LV5207LP is not set
+# CONFIG_BACKLIGHT_BD6107 is not set
+# CONFIG_BACKLIGHT_ARCXCNN is not set
+# end of Backlight & LCD device support
+
+CONFIG_HDMI=y
+
+#
+# Console display driver support
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_DUMMY_CONSOLE_COLUMNS=80
+CONFIG_DUMMY_CONSOLE_ROWS=25
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set
+CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y
+# end of Console display driver support
+
+# CONFIG_LOGO is not set
+# end of Graphics support
+
+# CONFIG_DRM_ACCEL is not set
+CONFIG_SOUND=y
+CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_HWDEP=m
+CONFIG_SND_SEQ_DEVICE=m
+CONFIG_SND_RAWMIDI=m
+CONFIG_SND_UMP=m
+# CONFIG_SND_UMP_LEGACY_RAWMIDI is not set
+CONFIG_SND_COMPRESS_OFFLOAD=m
+CONFIG_SND_JACK=y
+CONFIG_SND_JACK_INPUT_DEV=y
+# CONFIG_SND_OSSEMUL is not set
+CONFIG_SND_PCM_TIMER=y
+CONFIG_SND_HRTIMER=m
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_MAX_CARDS=6
+# CONFIG_SND_SUPPORT_OLD_API is not set
+CONFIG_SND_PROC_FS=y
+# CONFIG_SND_VERBOSE_PROCFS is not set
+CONFIG_SND_CTL_FAST_LOOKUP=y
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_CTL_INPUT_VALIDATION=y
+CONFIG_SND_UTIMER=y
+CONFIG_SND_VMASTER=y
+CONFIG_SND_DMA_SGBUF=y
+CONFIG_SND_CTL_LED=m
+CONFIG_SND_SEQUENCER=m
+# CONFIG_SND_SEQ_DUMMY is not set
+CONFIG_SND_SEQ_HRTIMER_DEFAULT=y
+CONFIG_SND_SEQ_MIDI_EVENT=m
+CONFIG_SND_SEQ_MIDI=m
+# CONFIG_SND_SEQ_UMP is not set
+CONFIG_SND_MPU401_UART=m
+CONFIG_SND_AC97_CODEC=m
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_PCSP is not set
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_ALOOP is not set
+# CONFIG_SND_PCMTEST is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+CONFIG_SND_MPU401=m
+# CONFIG_SND_AC97_POWER_SAVE is not set
+CONFIG_SND_PCI=y
+# CONFIG_SND_AD1889 is not set
+# CONFIG_SND_ALS300 is not set
+# CONFIG_SND_ALS4000 is not set
+# CONFIG_SND_ALI5451 is not set
+# CONFIG_SND_ASIHPI is not set
+# CONFIG_SND_ATIIXP is not set
+# CONFIG_SND_ATIIXP_MODEM is not set
+# CONFIG_SND_AU8810 is not set
+# CONFIG_SND_AU8820 is not set
+# CONFIG_SND_AU8830 is not set
+# CONFIG_SND_AW2 is not set
+# CONFIG_SND_AZT3328 is not set
+# CONFIG_SND_BT87X is not set
+# CONFIG_SND_CA0106 is not set
+# CONFIG_SND_CMIPCI is not set
+# CONFIG_SND_OXYGEN is not set
+# CONFIG_SND_CS4281 is not set
+# CONFIG_SND_CS46XX is not set
+# CONFIG_SND_CTXFI is not set
+# CONFIG_SND_DARLA20 is not set
+# CONFIG_SND_GINA20 is not set
+# CONFIG_SND_LAYLA20 is not set
+# CONFIG_SND_DARLA24 is not set
+# CONFIG_SND_GINA24 is not set
+# CONFIG_SND_LAYLA24 is not set
+# CONFIG_SND_MONA is not set
+# CONFIG_SND_MIA is not set
+# CONFIG_SND_ECHO3G is not set
+# CONFIG_SND_INDIGO is not set
+# CONFIG_SND_INDIGOIO is not set
+# CONFIG_SND_INDIGODJ is not set
+# CONFIG_SND_INDIGOIOX is not set
+# CONFIG_SND_INDIGODJX is not set
+# CONFIG_SND_EMU10K1 is not set
+# CONFIG_SND_EMU10K1X is not set
+# CONFIG_SND_ENS1370 is not set
+# CONFIG_SND_ENS1371 is not set
+# CONFIG_SND_ES1938 is not set
+# CONFIG_SND_ES1968 is not set
+# CONFIG_SND_FM801 is not set
+# CONFIG_SND_HDSP is not set
+# CONFIG_SND_HDSPM is not set
+# CONFIG_SND_ICE1712 is not set
+# CONFIG_SND_ICE1724 is not set
+CONFIG_SND_INTEL8X0=m
+# CONFIG_SND_INTEL8X0M is not set
+# CONFIG_SND_KORG1212 is not set
+# CONFIG_SND_LOLA is not set
+# CONFIG_SND_LX6464ES is not set
+# CONFIG_SND_MAESTRO3 is not set
+# CONFIG_SND_MIXART is not set
+# CONFIG_SND_NM256 is not set
+# CONFIG_SND_PCXHR is not set
+# CONFIG_SND_RIPTIDE is not set
+# CONFIG_SND_RME32 is not set
+# CONFIG_SND_RME96 is not set
+# CONFIG_SND_RME9652 is not set
+# CONFIG_SND_SE6X is not set
+# CONFIG_SND_SONICVIBES is not set
+# CONFIG_SND_TRIDENT is not set
+# CONFIG_SND_VIA82XX is not set
+# CONFIG_SND_VIA82XX_MODEM is not set
+# CONFIG_SND_VIRTUOSO is not set
+# CONFIG_SND_VX222 is not set
+# CONFIG_SND_YMFPCI is not set
+
+#
+# HD-Audio
+#
+CONFIG_SND_HDA=m
+CONFIG_SND_HDA_GENERIC_LEDS=y
+CONFIG_SND_HDA_INTEL=m
+CONFIG_SND_HDA_HWDEP=y
+CONFIG_SND_HDA_RECONFIG=y
+# CONFIG_SND_HDA_INPUT_BEEP is not set
+CONFIG_SND_HDA_PATCH_LOADER=y
+CONFIG_SND_HDA_SCODEC_COMPONENT=m
+# CONFIG_SND_HDA_SCODEC_CS35L41_I2C is not set
+# CONFIG_SND_HDA_SCODEC_CS35L41_SPI is not set
+# CONFIG_SND_HDA_SCODEC_CS35L56_I2C is not set
+# CONFIG_SND_HDA_SCODEC_CS35L56_SPI is not set
+# CONFIG_SND_HDA_SCODEC_TAS2781_I2C is not set
+# CONFIG_SND_HDA_SCODEC_TAS2781_SPI is not set
+CONFIG_SND_HDA_CODEC_REALTEK=m
+CONFIG_SND_HDA_CODEC_ANALOG=m
+CONFIG_SND_HDA_CODEC_SIGMATEL=m
+CONFIG_SND_HDA_CODEC_VIA=m
+CONFIG_SND_HDA_CODEC_HDMI=m
+CONFIG_SND_HDA_CODEC_CIRRUS=m
+# CONFIG_SND_HDA_CODEC_CS8409 is not set
+CONFIG_SND_HDA_CODEC_CONEXANT=m
+# CONFIG_SND_HDA_CODEC_SENARYTECH is not set
+CONFIG_SND_HDA_CODEC_CA0110=m
+CONFIG_SND_HDA_CODEC_CA0132=m
+# CONFIG_SND_HDA_CODEC_CA0132_DSP is not set
+CONFIG_SND_HDA_CODEC_CMEDIA=m
+CONFIG_SND_HDA_CODEC_SI3054=m
+CONFIG_SND_HDA_GENERIC=m
+CONFIG_SND_HDA_POWER_SAVE_DEFAULT=60
+# CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM is not set
+# CONFIG_SND_HDA_CTL_DEV_ID is not set
+# end of HD-Audio
+
+CONFIG_SND_HDA_CORE=m
+CONFIG_SND_HDA_COMPONENT=y
+CONFIG_SND_HDA_I915=y
+CONFIG_SND_HDA_EXT_CORE=m
+CONFIG_SND_HDA_PREALLOC_SIZE=0
+CONFIG_SND_INTEL_NHLT=y
+CONFIG_SND_INTEL_DSP_CONFIG=m
+CONFIG_SND_INTEL_SOUNDWIRE_ACPI=m
+CONFIG_SND_SPI=y
+CONFIG_SND_USB=y
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_USB_AUDIO_MIDI_V2=y
+CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER=y
+# CONFIG_SND_USB_UA101 is not set
+# CONFIG_SND_USB_USX2Y is not set
+# CONFIG_SND_USB_CAIAQ is not set
+# CONFIG_SND_USB_US122L is not set
+# CONFIG_SND_USB_6FIRE is not set
+# CONFIG_SND_USB_HIFACE is not set
+# CONFIG_SND_BCD2000 is not set
+# CONFIG_SND_USB_POD is not set
+# CONFIG_SND_USB_PODHD is not set
+# CONFIG_SND_USB_TONEPORT is not set
+# CONFIG_SND_USB_VARIAX is not set
+CONFIG_SND_SOC=m
+CONFIG_SND_SOC_COMPRESS=y
+CONFIG_SND_SOC_TOPOLOGY=y
+CONFIG_SND_SOC_ACPI=m
+# CONFIG_SND_SOC_ADI is not set
+# CONFIG_SND_SOC_AMD_ACP is not set
+# CONFIG_SND_SOC_AMD_ACP3x is not set
+# CONFIG_SND_SOC_AMD_RENOIR is not set
+# CONFIG_SND_SOC_AMD_ACP5x is not set
+# CONFIG_SND_SOC_AMD_ACP6x is not set
+# CONFIG_SND_AMD_ACP_CONFIG is not set
+# CONFIG_SND_SOC_AMD_ACP_COMMON is not set
+# CONFIG_SND_SOC_AMD_RPL_ACP6x is not set
+# CONFIG_SND_ATMEL_SOC is not set
+# CONFIG_SND_BCM63XX_I2S_WHISTLER is not set
+# CONFIG_SND_DESIGNWARE_I2S is not set
+
+#
+# SoC Audio for Freescale CPUs
+#
+
+#
+# Common SoC Audio options for Freescale CPUs:
+#
+# CONFIG_SND_SOC_FSL_ASRC is not set
+# CONFIG_SND_SOC_FSL_SAI is not set
+# CONFIG_SND_SOC_FSL_AUDMIX is not set
+# CONFIG_SND_SOC_FSL_SSI is not set
+# CONFIG_SND_SOC_FSL_SPDIF is not set
+# CONFIG_SND_SOC_FSL_ESAI is not set
+# CONFIG_SND_SOC_FSL_MICFIL is not set
+# CONFIG_SND_SOC_FSL_XCVR is not set
+# CONFIG_SND_SOC_IMX_AUDMUX is not set
+# end of SoC Audio for Freescale CPUs
+
+# CONFIG_SND_SOC_CHV3_I2S is not set
+# CONFIG_SND_I2S_HI6210_I2S is not set
+
+#
+# SoC Audio for Loongson CPUs
+#
+# end of SoC Audio for Loongson CPUs
+
+# CONFIG_SND_SOC_IMG is not set
+# CONFIG_SND_SOC_INTEL_SST_TOPLEVEL is not set
+CONFIG_SND_SOC_ACPI_INTEL_MATCH=m
+CONFIG_SND_SOC_ACPI_INTEL_SDCA_QUIRKS=m
+# CONFIG_SND_SOC_INTEL_AVS is not set
+CONFIG_SND_SOC_INTEL_MACH=y
+# CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES is not set
+CONFIG_SND_SOC_INTEL_HDA_DSP_COMMON=m
+CONFIG_SND_SOC_INTEL_SOF_BOARD_HELPERS=m
+# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH is not set
+# CONFIG_SND_SOC_INTEL_GLK_DA7219_MAX98357A_MACH is not set
+# CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH is not set
+CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH=m
+# CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH is not set
+# CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH is not set
+# CONFIG_SND_SOC_INTEL_SOF_PCM512x_MACH is not set
+# CONFIG_SND_SOC_INTEL_SOF_NAU8825_MACH is not set
+# CONFIG_SND_SOC_INTEL_CML_LP_DA7219_MAX98357A_MACH is not set
+# CONFIG_SND_SOC_INTEL_SOF_CML_RT1011_RT5682_MACH is not set
+# CONFIG_SND_SOC_INTEL_SOF_DA7219_MACH is not set
+# CONFIG_SND_SOC_INTEL_SOF_SSP_AMP_MACH is not set
+# CONFIG_SND_SOC_INTEL_EHL_RT5660_MACH is not set
+# CONFIG_SND_SOC_MTK_BTCVSD is not set
+CONFIG_SND_SOC_SDCA=m
+CONFIG_SND_SOC_SDCA_OPTIONAL=m
+CONFIG_SND_SOC_SOF_TOPLEVEL=y
+CONFIG_SND_SOC_SOF_PCI_DEV=m
+CONFIG_SND_SOC_SOF_PCI=m
+CONFIG_SND_SOC_SOF_ACPI=m
+CONFIG_SND_SOC_SOF_ACPI_DEV=m
+CONFIG_SND_SOC_SOF_DEBUG_PROBES=m
+CONFIG_SND_SOC_SOF_CLIENT=m
+CONFIG_SND_SOC_SOF=m
+CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE=y
+CONFIG_SND_SOC_SOF_IPC3=y
+CONFIG_SND_SOC_SOF_IPC4=y
+CONFIG_SND_SOC_SOF_AMD_TOPLEVEL=m
+# CONFIG_SND_SOC_SOF_AMD_RENOIR is not set
+# CONFIG_SND_SOC_SOF_AMD_VANGOGH is not set
+# CONFIG_SND_SOC_SOF_AMD_REMBRANDT is not set
+# CONFIG_SND_SOC_SOF_AMD_ACP63 is not set
+# CONFIG_SND_SOC_SOF_AMD_ACP70 is not set
+CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL=y
+CONFIG_SND_SOC_SOF_INTEL_HIFI_EP_IPC=m
+CONFIG_SND_SOC_SOF_INTEL_ATOM_HIFI_EP=m
+CONFIG_SND_SOC_SOF_INTEL_COMMON=m
+CONFIG_SND_SOC_SOF_BAYTRAIL=m
+CONFIG_SND_SOC_SOF_BROADWELL=m
+CONFIG_SND_SOC_SOF_MERRIFIELD=m
+CONFIG_SND_SOC_SOF_INTEL_SKL=m
+CONFIG_SND_SOC_SOF_SKYLAKE=m
+CONFIG_SND_SOC_SOF_KABYLAKE=m
+CONFIG_SND_SOC_SOF_INTEL_APL=m
+CONFIG_SND_SOC_SOF_APOLLOLAKE=m
+CONFIG_SND_SOC_SOF_GEMINILAKE=m
+CONFIG_SND_SOC_SOF_INTEL_CNL=m
+CONFIG_SND_SOC_SOF_CANNONLAKE=m
+CONFIG_SND_SOC_SOF_COFFEELAKE=m
+CONFIG_SND_SOC_SOF_COMETLAKE=m
+CONFIG_SND_SOC_SOF_INTEL_ICL=m
+CONFIG_SND_SOC_SOF_ICELAKE=m
+CONFIG_SND_SOC_SOF_JASPERLAKE=m
+CONFIG_SND_SOC_SOF_INTEL_TGL=m
+CONFIG_SND_SOC_SOF_TIGERLAKE=m
+CONFIG_SND_SOC_SOF_ELKHARTLAKE=m
+CONFIG_SND_SOC_SOF_ALDERLAKE=m
+CONFIG_SND_SOC_SOF_INTEL_MTL=m
+CONFIG_SND_SOC_SOF_METEORLAKE=m
+CONFIG_SND_SOC_SOF_INTEL_LNL=m
+CONFIG_SND_SOC_SOF_LUNARLAKE=m
+# CONFIG_SND_SOC_SOF_PANTHERLAKE is not set
+CONFIG_SND_SOC_SOF_HDA_COMMON=m
+CONFIG_SND_SOC_SOF_HDA_GENERIC=m
+CONFIG_SND_SOC_SOF_HDA_MLINK=m
+CONFIG_SND_SOC_SOF_HDA_LINK=y
+CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC=y
+CONFIG_SND_SOC_SOF_HDA_LINK_BASELINE=m
+CONFIG_SND_SOC_SOF_HDA=m
+CONFIG_SND_SOC_SOF_HDA_PROBES=m
+CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE=m
+CONFIG_SND_SOC_SOF_XTENSA=m
+
+#
+# STMicroelectronics STM32 SOC audio support
+#
+# end of STMicroelectronics STM32 SOC audio support
+
+# CONFIG_SND_SOC_XILINX_I2S is not set
+# CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER is not set
+# CONFIG_SND_SOC_XILINX_SPDIF is not set
+# CONFIG_SND_SOC_XTFPGA_I2S is not set
+CONFIG_SND_SOC_I2C_AND_SPI=m
+
+#
+# CODEC drivers
+#
+# CONFIG_SND_SOC_AC97_CODEC is not set
+# CONFIG_SND_SOC_ADAU1372_I2C is not set
+# CONFIG_SND_SOC_ADAU1372_SPI is not set
+# CONFIG_SND_SOC_ADAU1373 is not set
+# CONFIG_SND_SOC_ADAU1701 is not set
+# CONFIG_SND_SOC_ADAU1761_I2C is not set
+# CONFIG_SND_SOC_ADAU1761_SPI is not set
+# CONFIG_SND_SOC_ADAU7002 is not set
+# CONFIG_SND_SOC_ADAU7118_HW is not set
+# CONFIG_SND_SOC_ADAU7118_I2C is not set
+# CONFIG_SND_SOC_AK4104 is not set
+# CONFIG_SND_SOC_AK4118 is not set
+# CONFIG_SND_SOC_AK4375 is not set
+# CONFIG_SND_SOC_AK4458 is not set
+# CONFIG_SND_SOC_AK4554 is not set
+# CONFIG_SND_SOC_AK4613 is not set
+# CONFIG_SND_SOC_AK4619 is not set
+# CONFIG_SND_SOC_AK4642 is not set
+# CONFIG_SND_SOC_AK5386 is not set
+# CONFIG_SND_SOC_AK5558 is not set
+# CONFIG_SND_SOC_ALC5623 is not set
+# CONFIG_SND_SOC_AW8738 is not set
+# CONFIG_SND_SOC_AW88395 is not set
+# CONFIG_SND_SOC_AW88261 is not set
+# CONFIG_SND_SOC_AW88081 is not set
+# CONFIG_SND_SOC_AW87390 is not set
+# CONFIG_SND_SOC_AW88399 is not set
+# CONFIG_SND_SOC_BD28623 is not set
+# CONFIG_SND_SOC_BT_SCO is not set
+# CONFIG_SND_SOC_CHV3_CODEC is not set
+# CONFIG_SND_SOC_CS35L32 is not set
+# CONFIG_SND_SOC_CS35L33 is not set
+# CONFIG_SND_SOC_CS35L34 is not set
+# CONFIG_SND_SOC_CS35L35 is not set
+# CONFIG_SND_SOC_CS35L36 is not set
+# CONFIG_SND_SOC_CS35L41_SPI is not set
+# CONFIG_SND_SOC_CS35L41_I2C is not set
+# CONFIG_SND_SOC_CS35L45_SPI is not set
+# CONFIG_SND_SOC_CS35L45_I2C is not set
+# CONFIG_SND_SOC_CS35L56_I2C is not set
+# CONFIG_SND_SOC_CS35L56_SPI is not set
+# CONFIG_SND_SOC_CS42L42 is not set
+# CONFIG_SND_SOC_CS42L51_I2C is not set
+# CONFIG_SND_SOC_CS42L52 is not set
+# CONFIG_SND_SOC_CS42L56 is not set
+# CONFIG_SND_SOC_CS42L73 is not set
+# CONFIG_SND_SOC_CS42L83 is not set
+# CONFIG_SND_SOC_CS42L84 is not set
+# CONFIG_SND_SOC_CS4234 is not set
+# CONFIG_SND_SOC_CS4265 is not set
+# CONFIG_SND_SOC_CS4270 is not set
+# CONFIG_SND_SOC_CS4271_I2C is not set
+# CONFIG_SND_SOC_CS4271_SPI is not set
+# CONFIG_SND_SOC_CS42XX8_I2C is not set
+# CONFIG_SND_SOC_CS43130 is not set
+# CONFIG_SND_SOC_CS4341 is not set
+# CONFIG_SND_SOC_CS4349 is not set
+# CONFIG_SND_SOC_CS53L30 is not set
+# CONFIG_SND_SOC_CS530X_I2C is not set
+# CONFIG_SND_SOC_CX2072X is not set
+# CONFIG_SND_SOC_DA7213 is not set
+CONFIG_SND_SOC_DMIC=m
+# CONFIG_SND_SOC_ES7134 is not set
+# CONFIG_SND_SOC_ES7241 is not set
+# CONFIG_SND_SOC_ES8311 is not set
+# CONFIG_SND_SOC_ES8316 is not set
+# CONFIG_SND_SOC_ES8323 is not set
+# CONFIG_SND_SOC_ES8326 is not set
+# CONFIG_SND_SOC_ES8328_I2C is not set
+# CONFIG_SND_SOC_ES8328_SPI is not set
+# CONFIG_SND_SOC_GTM601 is not set
+CONFIG_SND_SOC_HDAC_HDA=m
+# CONFIG_SND_SOC_HDA is not set
+# CONFIG_SND_SOC_ICS43432 is not set
+# CONFIG_SND_SOC_IDT821034 is not set
+# CONFIG_SND_SOC_MAX98088 is not set
+# CONFIG_SND_SOC_MAX98090 is not set
+# CONFIG_SND_SOC_MAX98357A is not set
+# CONFIG_SND_SOC_MAX98504 is not set
+# CONFIG_SND_SOC_MAX9867 is not set
+# CONFIG_SND_SOC_MAX98927 is not set
+# CONFIG_SND_SOC_MAX98520 is not set
+# CONFIG_SND_SOC_MAX98373_I2C is not set
+# CONFIG_SND_SOC_MAX98388 is not set
+# CONFIG_SND_SOC_MAX98390 is not set
+# CONFIG_SND_SOC_MAX98396 is not set
+# CONFIG_SND_SOC_MAX9860 is not set
+# CONFIG_SND_SOC_MSM8916_WCD_ANALOG is not set
+# CONFIG_SND_SOC_MSM8916_WCD_DIGITAL is not set
+# CONFIG_SND_SOC_PCM1681 is not set
+# CONFIG_SND_SOC_PCM1789_I2C is not set
+# CONFIG_SND_SOC_PCM179X_I2C is not set
+# CONFIG_SND_SOC_PCM179X_SPI is not set
+# CONFIG_SND_SOC_PCM186X_I2C is not set
+# CONFIG_SND_SOC_PCM186X_SPI is not set
+# CONFIG_SND_SOC_PCM3060_I2C is not set
+# CONFIG_SND_SOC_PCM3060_SPI is not set
+# CONFIG_SND_SOC_PCM3168A_I2C is not set
+# CONFIG_SND_SOC_PCM3168A_SPI is not set
+# CONFIG_SND_SOC_PCM5102A is not set
+# CONFIG_SND_SOC_PCM512x_I2C is not set
+# CONFIG_SND_SOC_PCM512x_SPI is not set
+# CONFIG_SND_SOC_PCM6240 is not set
+# CONFIG_SND_SOC_PEB2466 is not set
+# CONFIG_SND_SOC_RT5616 is not set
+# CONFIG_SND_SOC_RT5631 is not set
+# CONFIG_SND_SOC_RT5640 is not set
+# CONFIG_SND_SOC_RT5659 is not set
+# CONFIG_SND_SOC_RT9120 is not set
+# CONFIG_SND_SOC_RTQ9128 is not set
+# CONFIG_SND_SOC_SGTL5000 is not set
+# CONFIG_SND_SOC_SIMPLE_AMPLIFIER is not set
+# CONFIG_SND_SOC_SMA1303 is not set
+# CONFIG_SND_SOC_SMA1307 is not set
+# CONFIG_SND_SOC_SPDIF is not set
+# CONFIG_SND_SOC_SRC4XXX_I2C is not set
+# CONFIG_SND_SOC_SSM2305 is not set
+# CONFIG_SND_SOC_SSM2518 is not set
+# CONFIG_SND_SOC_SSM2602_SPI is not set
+# CONFIG_SND_SOC_SSM2602_I2C is not set
+# CONFIG_SND_SOC_SSM4567 is not set
+# CONFIG_SND_SOC_STA32X is not set
+# CONFIG_SND_SOC_STA350 is not set
+# CONFIG_SND_SOC_STI_SAS is not set
+# CONFIG_SND_SOC_TAS2552 is not set
+# CONFIG_SND_SOC_TAS2562 is not set
+# CONFIG_SND_SOC_TAS2764 is not set
+# CONFIG_SND_SOC_TAS2770 is not set
+# CONFIG_SND_SOC_TAS2780 is not set
+# CONFIG_SND_SOC_TAS2781_I2C is not set
+# CONFIG_SND_SOC_TAS5086 is not set
+# CONFIG_SND_SOC_TAS571X is not set
+# CONFIG_SND_SOC_TAS5720 is not set
+# CONFIG_SND_SOC_TAS5805M is not set
+# CONFIG_SND_SOC_TAS6424 is not set
+# CONFIG_SND_SOC_TDA7419 is not set
+# CONFIG_SND_SOC_TFA9879 is not set
+# CONFIG_SND_SOC_TFA989X is not set
+# CONFIG_SND_SOC_TLV320AIC23_I2C is not set
+# CONFIG_SND_SOC_TLV320AIC23_SPI is not set
+# CONFIG_SND_SOC_TLV320AIC31XX is not set
+# CONFIG_SND_SOC_TLV320AIC32X4_I2C is not set
+# CONFIG_SND_SOC_TLV320AIC32X4_SPI is not set
+# CONFIG_SND_SOC_TLV320AIC3X_I2C is not set
+# CONFIG_SND_SOC_TLV320AIC3X_SPI is not set
+# CONFIG_SND_SOC_TLV320ADCX140 is not set
+# CONFIG_SND_SOC_TS3A227E is not set
+# CONFIG_SND_SOC_TSCS42XX is not set
+# CONFIG_SND_SOC_TSCS454 is not set
+# CONFIG_SND_SOC_UDA1342 is not set
+# CONFIG_SND_SOC_WM8510 is not set
+# CONFIG_SND_SOC_WM8523 is not set
+# CONFIG_SND_SOC_WM8580 is not set
+# CONFIG_SND_SOC_WM8711 is not set
+# CONFIG_SND_SOC_WM8728 is not set
+# CONFIG_SND_SOC_WM8731_I2C is not set
+# CONFIG_SND_SOC_WM8731_SPI is not set
+# CONFIG_SND_SOC_WM8737 is not set
+# CONFIG_SND_SOC_WM8741 is not set
+# CONFIG_SND_SOC_WM8750 is not set
+# CONFIG_SND_SOC_WM8753 is not set
+# CONFIG_SND_SOC_WM8770 is not set
+# CONFIG_SND_SOC_WM8776 is not set
+# CONFIG_SND_SOC_WM8782 is not set
+# CONFIG_SND_SOC_WM8804_I2C is not set
+# CONFIG_SND_SOC_WM8804_SPI is not set
+# CONFIG_SND_SOC_WM8903 is not set
+# CONFIG_SND_SOC_WM8904 is not set
+# CONFIG_SND_SOC_WM8940 is not set
+# CONFIG_SND_SOC_WM8960 is not set
+# CONFIG_SND_SOC_WM8961 is not set
+# CONFIG_SND_SOC_WM8962 is not set
+# CONFIG_SND_SOC_WM8974 is not set
+# CONFIG_SND_SOC_WM8978 is not set
+# CONFIG_SND_SOC_WM8985 is not set
+# CONFIG_SND_SOC_MT6351 is not set
+# CONFIG_SND_SOC_MT6357 is not set
+# CONFIG_SND_SOC_MT6358 is not set
+# CONFIG_SND_SOC_MT6660 is not set
+# CONFIG_SND_SOC_NAU8315 is not set
+# CONFIG_SND_SOC_NAU8540 is not set
+# CONFIG_SND_SOC_NAU8810 is not set
+# CONFIG_SND_SOC_NAU8821 is not set
+# CONFIG_SND_SOC_NAU8822 is not set
+# CONFIG_SND_SOC_NAU8824 is not set
+# CONFIG_SND_SOC_NTP8918 is not set
+# CONFIG_SND_SOC_NTP8835 is not set
+# CONFIG_SND_SOC_TPA6130A2 is not set
+# CONFIG_SND_SOC_LPASS_WSA_MACRO is not set
+# CONFIG_SND_SOC_LPASS_VA_MACRO is not set
+# CONFIG_SND_SOC_LPASS_RX_MACRO is not set
+# CONFIG_SND_SOC_LPASS_TX_MACRO is not set
+# end of CODEC drivers
+
+# CONFIG_SND_SIMPLE_CARD is not set
+CONFIG_SND_X86=y
+# CONFIG_HDMI_LPE_AUDIO is not set
+CONFIG_SND_VIRTIO=m
+CONFIG_AC97_BUS=m
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=m
+CONFIG_HID_BATTERY_STRENGTH=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=m
+CONFIG_HID_GENERIC=m
+
+#
+# Special HID drivers
+#
+CONFIG_HID_A4TECH=m
+# CONFIG_HID_ACCUTOUCH is not set
+# CONFIG_HID_ACRUX is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_APPLEIR is not set
+# CONFIG_HID_ASUS is not set
+# CONFIG_HID_AUREAL is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_BETOP_FF is not set
+# CONFIG_HID_BIGBEN_FF is not set
+# CONFIG_HID_CHERRY is not set
+CONFIG_HID_CHICONY=m
+# CONFIG_HID_CORSAIR is not set
+# CONFIG_HID_COUGAR is not set
+# CONFIG_HID_MACALLY is not set
+# CONFIG_HID_PRODIKEYS is not set
+# CONFIG_HID_CMEDIA is not set
+# CONFIG_HID_CREATIVE_SB0540 is not set
+CONFIG_HID_CYPRESS=m
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_EMS_FF is not set
+# CONFIG_HID_ELAN is not set
+# CONFIG_HID_ELECOM is not set
+# CONFIG_HID_ELO is not set
+# CONFIG_HID_EVISION is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_FT260 is not set
+# CONFIG_HID_GEMBIRD is not set
+# CONFIG_HID_GFRM is not set
+# CONFIG_HID_GLORIOUS is not set
+# CONFIG_HID_HOLTEK is not set
+# CONFIG_HID_GOODIX_SPI is not set
+# CONFIG_HID_GOOGLE_STADIA_FF is not set
+# CONFIG_HID_VIVALDI is not set
+# CONFIG_HID_GT683R is not set
+# CONFIG_HID_KEYTOUCH is not set
+CONFIG_HID_KYE=m
+# CONFIG_HID_KYSONA is not set
+# CONFIG_HID_UCLOGIC is not set
+# CONFIG_HID_WALTOP is not set
+# CONFIG_HID_VIEWSONIC is not set
+# CONFIG_HID_VRC2 is not set
+CONFIG_HID_XIAOMI=m
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_ICADE is not set
+# CONFIG_HID_ITE is not set
+# CONFIG_HID_JABRA is not set
+# CONFIG_HID_TWINHAN is not set
+# CONFIG_HID_KENSINGTON is not set
+# CONFIG_HID_LCPOWER is not set
+# CONFIG_HID_LED is not set
+CONFIG_HID_LENOVO=m
+# CONFIG_HID_LETSKETCH is not set
+CONFIG_HID_LOGITECH=m
+CONFIG_HID_LOGITECH_DJ=m
+CONFIG_HID_LOGITECH_HIDPP=m
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+# CONFIG_LOGIG940_FF is not set
+# CONFIG_LOGIWHEELS_FF is not set
+# CONFIG_HID_MAGICMOUSE is not set
+# CONFIG_HID_MALTRON is not set
+# CONFIG_HID_MAYFLASH is not set
+# CONFIG_HID_MEGAWORLD_FF is not set
+# CONFIG_HID_REDRAGON is not set
+CONFIG_HID_MICROSOFT=m
+# CONFIG_HID_MONTEREY is not set
+CONFIG_HID_MULTITOUCH=m
+# CONFIG_HID_NINTENDO is not set
+# CONFIG_HID_NTI is not set
+# CONFIG_HID_NTRIG is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PENMOUNT is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_PLANTRONICS is not set
+# CONFIG_HID_PXRC is not set
+# CONFIG_HID_RAZER is not set
+# CONFIG_HID_PRIMAX is not set
+# CONFIG_HID_RETRODE is not set
+# CONFIG_HID_ROCCAT is not set
+# CONFIG_HID_SAITEK is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SEMITEK is not set
+# CONFIG_HID_SIGMAMICRO is not set
+# CONFIG_HID_SONY is not set
+# CONFIG_HID_SPEEDLINK is not set
+# CONFIG_HID_STEAM is not set
+# CONFIG_HID_STEELSERIES is not set
+# CONFIG_HID_SUNPLUS is not set
+CONFIG_HID_RMI=m
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_TIVO is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_TOPRE is not set
+# CONFIG_HID_THINGM is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_UDRAW_PS3 is not set
+CONFIG_HID_U2FZERO=m
+# CONFIG_HID_WACOM is not set
+# CONFIG_HID_WIIMOTE is not set
+# CONFIG_HID_WINWING is not set
+# CONFIG_HID_XINMO is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
+CONFIG_HID_SENSOR_HUB=m
+CONFIG_HID_SENSOR_CUSTOM_SENSOR=m
+# CONFIG_HID_ALPS is not set
+# CONFIG_HID_MCP2221 is not set
+# end of Special HID drivers
+
+#
+# HID-BPF support
+#
+CONFIG_HID_BPF=y
+# end of HID-BPF support
+
+CONFIG_I2C_HID=m
+CONFIG_I2C_HID_ACPI=m
+CONFIG_I2C_HID_OF=m
+CONFIG_I2C_HID_CORE=m
+
+#
+# Intel ISH HID support
+#
+CONFIG_INTEL_ISH_HID=m
+CONFIG_INTEL_ISH_FIRMWARE_DOWNLOADER=m
+# end of Intel ISH HID support
+
+#
+# AMD SFH HID Support
+#
+CONFIG_AMD_SFH_HID=m
+# end of AMD SFH HID Support
+
+#
+# Intel THC HID Support
+#
+# CONFIG_INTEL_THC_HID is not set
+# end of Intel THC HID Support
+
+#
+# USB HID support
+#
+CONFIG_USB_HID=m
+# CONFIG_HID_PID is not set
+CONFIG_USB_HIDDEV=y
+# end of USB HID support
+
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_COMMON=m
+# CONFIG_USB_LED_TRIG is not set
+# CONFIG_USB_ULPI_BUS is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB=m
+CONFIG_USB_PCI=y
+CONFIG_USB_PCI_AMD=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEFAULT_PERSIST=y
+# CONFIG_USB_FEW_INIT_RETRIES is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+CONFIG_USB_OTG=y
+# CONFIG_USB_OTG_PRODUCTLIST is not set
+# CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB is not set
+CONFIG_USB_OTG_FSM=m
+# CONFIG_USB_LEDS_TRIGGER_USBPORT is not set
+CONFIG_USB_AUTOSUSPEND_DELAY=2
+CONFIG_USB_DEFAULT_AUTHORIZATION_MODE=1
+# CONFIG_USB_MON is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+CONFIG_USB_XHCI_HCD=m
+# CONFIG_USB_XHCI_DBGCAP is not set
+CONFIG_USB_XHCI_PCI=m
+# CONFIG_USB_XHCI_PCI_RENESAS is not set
+CONFIG_USB_XHCI_PLATFORM=m
+CONFIG_USB_EHCI_HCD=m
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_TT_NEWSCHED=y
+CONFIG_USB_EHCI_PCI=m
+CONFIG_USB_EHCI_FSL=m
+CONFIG_USB_EHCI_HCD_PLATFORM=m
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_MAX3421_HCD is not set
+CONFIG_USB_OHCI_HCD=m
+CONFIG_USB_OHCI_HCD_PCI=m
+# CONFIG_USB_OHCI_HCD_SSB is not set
+# CONFIG_USB_OHCI_HCD_PLATFORM is not set
+CONFIG_USB_UHCI_HCD=m
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HCD_SSB is not set
+# CONFIG_USB_HCD_TEST_MODE is not set
+
+#
+# USB Device Class drivers
+#
+CONFIG_USB_ACM=m
+# CONFIG_USB_PRINTER is not set
+CONFIG_USB_WDM=m
+CONFIG_USB_TMC=m
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed; see USB_STORAGE Help for more info
+#
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_REALTEK is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_STORAGE_ENE_UB6250 is not set
+CONFIG_USB_UAS=m
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+# CONFIG_USBIP_CORE is not set
+
+#
+# USB dual-mode controller drivers
+#
+# CONFIG_USB_CDNS_SUPPORT is not set
+# CONFIG_USB_MUSB_HDRC is not set
+# CONFIG_USB_DWC3 is not set
+# CONFIG_USB_DWC2 is not set
+# CONFIG_USB_CHIPIDEA is not set
+# CONFIG_USB_ISP1760 is not set
+
+#
+# USB port drivers
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_SIMPLE is not set
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+CONFIG_USB_SERIAL_ARK3116=m
+# CONFIG_USB_SERIAL_BELKIN is not set
+CONFIG_USB_SERIAL_CH341=m
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
+CONFIG_USB_SERIAL_CP210X=m
+# CONFIG_USB_SERIAL_CYPRESS_M8 is not set
+# CONFIG_USB_SERIAL_EMPEG is not set
+# CONFIG_USB_SERIAL_FTDI_SIO is not set
+# CONFIG_USB_SERIAL_VISOR is not set
+# CONFIG_USB_SERIAL_IPAQ is not set
+# CONFIG_USB_SERIAL_IR is not set
+# CONFIG_USB_SERIAL_EDGEPORT is not set
+# CONFIG_USB_SERIAL_EDGEPORT_TI is not set
+# CONFIG_USB_SERIAL_F81232 is not set
+# CONFIG_USB_SERIAL_F8153X is not set
+# CONFIG_USB_SERIAL_GARMIN is not set
+# CONFIG_USB_SERIAL_IPW is not set
+# CONFIG_USB_SERIAL_IUU is not set
+# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
+# CONFIG_USB_SERIAL_KEYSPAN is not set
+# CONFIG_USB_SERIAL_KLSI is not set
+# CONFIG_USB_SERIAL_KOBIL_SCT is not set
+# CONFIG_USB_SERIAL_MCT_U232 is not set
+# CONFIG_USB_SERIAL_METRO is not set
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_MXUPORT is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+# CONFIG_USB_SERIAL_PL2303 is not set
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_QCAUX is not set
+# CONFIG_USB_SERIAL_QUALCOMM is not set
+# CONFIG_USB_SERIAL_SPCP8X5 is not set
+# CONFIG_USB_SERIAL_SAFE is not set
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+# CONFIG_USB_SERIAL_SYMBOL is not set
+# CONFIG_USB_SERIAL_TI is not set
+# CONFIG_USB_SERIAL_CYBERJACK is not set
+CONFIG_USB_SERIAL_WWAN=m
+CONFIG_USB_SERIAL_OPTION=m
+# CONFIG_USB_SERIAL_OMNINET is not set
+# CONFIG_USB_SERIAL_OPTICON is not set
+# CONFIG_USB_SERIAL_XSENS_MT is not set
+# CONFIG_USB_SERIAL_WISHBONE is not set
+# CONFIG_USB_SERIAL_SSU100 is not set
+# CONFIG_USB_SERIAL_QT2 is not set
+# CONFIG_USB_SERIAL_UPD78F0730 is not set
+# CONFIG_USB_SERIAL_XR is not set
+# CONFIG_USB_SERIAL_DEBUG is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_APPLE_MFI_FASTCHARGE is not set
+# CONFIG_USB_LJCA is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_EHSET_TEST_FIXTURE is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_YUREX is not set
+# CONFIG_USB_EZUSB_FX2 is not set
+# CONFIG_USB_HUB_USB251XB is not set
+# CONFIG_USB_HSIC_USB3503 is not set
+# CONFIG_USB_HSIC_USB4604 is not set
+# CONFIG_USB_LINK_LAYER_TEST is not set
+# CONFIG_USB_CHAOSKEY is not set
+# CONFIG_USB_ATM is not set
+
+#
+# USB Physical Layer drivers
+#
+CONFIG_USB_PHY=y
+# CONFIG_NOP_USB_XCEIV is not set
+# CONFIG_USB_ISP1301 is not set
+# end of USB Physical Layer drivers
+
+# CONFIG_USB_GADGET is not set
+CONFIG_TYPEC=m
+CONFIG_TYPEC_TCPM=m
+CONFIG_TYPEC_TCPCI=m
+CONFIG_TYPEC_RT1711H=m
+CONFIG_TYPEC_TCPCI_MAXIM=m
+# CONFIG_TYPEC_FUSB302 is not set
+CONFIG_TYPEC_UCSI=m
+CONFIG_UCSI_CCG=m
+CONFIG_UCSI_ACPI=m
+# CONFIG_UCSI_STM32G0 is not set
+# CONFIG_TYPEC_TPS6598X is not set
+# CONFIG_TYPEC_ANX7411 is not set
+# CONFIG_TYPEC_RT1719 is not set
+# CONFIG_TYPEC_HD3SS3220 is not set
+# CONFIG_TYPEC_STUSB160X is not set
+# CONFIG_TYPEC_WUSB3801 is not set
+
+#
+# USB Type-C Multiplexer/DeMultiplexer Switch support
+#
+# CONFIG_TYPEC_MUX_FSA4480 is not set
+# CONFIG_TYPEC_MUX_GPIO_SBU is not set
+# CONFIG_TYPEC_MUX_PI3USB30532 is not set
+# CONFIG_TYPEC_MUX_INTEL_PMC is not set
+# CONFIG_TYPEC_MUX_IT5205 is not set
+# CONFIG_TYPEC_MUX_NB7VPQ904M is not set
+# CONFIG_TYPEC_MUX_PTN36502 is not set
+# CONFIG_TYPEC_MUX_TUSB1046 is not set
+# CONFIG_TYPEC_MUX_WCD939X_USBSS is not set
+# end of USB Type-C Multiplexer/DeMultiplexer Switch support
+
+#
+# USB Type-C Alternate Mode drivers
+#
+CONFIG_TYPEC_DP_ALTMODE=m
+# CONFIG_TYPEC_NVIDIA_ALTMODE is not set
+# CONFIG_TYPEC_TBT_ALTMODE is not set
+# end of USB Type-C Alternate Mode drivers
+
+CONFIG_USB_ROLE_SWITCH=m
+# CONFIG_USB_ROLES_INTEL_XHCI is not set
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK=m
+CONFIG_MMC_BLOCK_MINORS=8
+# CONFIG_SDIO_UART is not set
+CONFIG_MMC_TEST=m
+# CONFIG_MMC_CRYPTO is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_SDHCI=m
+CONFIG_MMC_SDHCI_IO_ACCESSORS=y
+CONFIG_MMC_SDHCI_UHS2=m
+CONFIG_MMC_SDHCI_PCI=m
+CONFIG_MMC_RICOH_MMC=y
+CONFIG_MMC_SDHCI_ACPI=m
+CONFIG_MMC_SDHCI_PLTFM=m
+# CONFIG_MMC_SDHCI_F_SDH30 is not set
+# CONFIG_MMC_WBSD is not set
+# CONFIG_MMC_TIFM_SD is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MMC_CB710 is not set
+# CONFIG_MMC_VIA_SDMMC is not set
+# CONFIG_MMC_VUB300 is not set
+CONFIG_MMC_USHC=m
+# CONFIG_MMC_USDHI6ROL0 is not set
+CONFIG_MMC_CQHCI=m
+CONFIG_MMC_HSQ=m
+# CONFIG_MMC_TOSHIBA_PCI is not set
+# CONFIG_MMC_MTK is not set
+# CONFIG_MMC_SDHCI_XENON is not set
+# CONFIG_SCSI_UFSHCD is not set
+# CONFIG_MEMSTICK is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=m
+# CONFIG_LEDS_CLASS_FLASH is not set
+# CONFIG_LEDS_CLASS_MULTICOLOR is not set
+# CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set
+
+#
+# LED drivers
+#
+# CONFIG_LEDS_APU is not set
+# CONFIG_LEDS_AW200XX is not set
+# CONFIG_LEDS_LM3530 is not set
+# CONFIG_LEDS_LM3532 is not set
+# CONFIG_LEDS_LM3642 is not set
+# CONFIG_LEDS_PCA9532 is not set
+# CONFIG_LEDS_LP3944 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_PCA963X is not set
+# CONFIG_LEDS_PCA995X is not set
+# CONFIG_LEDS_DAC124S085 is not set
+# CONFIG_LEDS_PWM is not set
+# CONFIG_LEDS_BD2606MVV is not set
+# CONFIG_LEDS_BD2802 is not set
+# CONFIG_LEDS_INTEL_SS4200 is not set
+# CONFIG_LEDS_TCA6507 is not set
+# CONFIG_LEDS_TLC591XX is not set
+# CONFIG_LEDS_LM355x is not set
+# CONFIG_LEDS_IS31FL319X is not set
+
+#
+# LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM)
+#
+# CONFIG_LEDS_BLINKM is not set
+# CONFIG_LEDS_MLXCPLD is not set
+# CONFIG_LEDS_MLXREG is not set
+# CONFIG_LEDS_USER is not set
+# CONFIG_LEDS_NIC78BX is not set
+# CONFIG_LEDS_SPI_BYTE is not set
+
+#
+# Flash and Torch LED drivers
+#
+
+#
+# RGB LED drivers
+#
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=m
+# CONFIG_LEDS_TRIGGER_ONESHOT is not set
+# CONFIG_LEDS_TRIGGER_DISK is not set
+CONFIG_LEDS_TRIGGER_HEARTBEAT=m
+# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
+# CONFIG_LEDS_TRIGGER_CPU is not set
+# CONFIG_LEDS_TRIGGER_ACTIVITY is not set
+# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set
+
+#
+# iptables trigger is under Netfilter config (LED target)
+#
+# CONFIG_LEDS_TRIGGER_TRANSIENT is not set
+# CONFIG_LEDS_TRIGGER_CAMERA is not set
+# CONFIG_LEDS_TRIGGER_PANIC is not set
+# CONFIG_LEDS_TRIGGER_NETDEV is not set
+# CONFIG_LEDS_TRIGGER_PATTERN is not set
+# CONFIG_LEDS_TRIGGER_TTY is not set
+# CONFIG_LEDS_TRIGGER_INPUT_EVENTS is not set
+
+#
+# Simple LED drivers
+#
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_INFINIBAND is not set
+CONFIG_EDAC_ATOMIC_SCRUB=y
+CONFIG_EDAC_SUPPORT=y
+CONFIG_EDAC=y
+# CONFIG_EDAC_LEGACY_SYSFS is not set
+# CONFIG_EDAC_DEBUG is not set
+CONFIG_EDAC_DECODE_MCE=m
+# CONFIG_EDAC_GHES is not set
+CONFIG_EDAC_AMD64=m
+# CONFIG_EDAC_E752X is not set
+# CONFIG_EDAC_I82975X is not set
+# CONFIG_EDAC_I3000 is not set
+# CONFIG_EDAC_I3200 is not set
+CONFIG_EDAC_IE31200=m
+# CONFIG_EDAC_X38 is not set
+# CONFIG_EDAC_I5400 is not set
+CONFIG_EDAC_I7CORE=m
+# CONFIG_EDAC_I5100 is not set
+# CONFIG_EDAC_I7300 is not set
+CONFIG_EDAC_SBRIDGE=m
+# CONFIG_EDAC_SKX is not set
+# CONFIG_EDAC_I10NM is not set
+# CONFIG_EDAC_PND2 is not set
+# CONFIG_EDAC_IGEN6 is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_MC146818_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+CONFIG_RTC_SYSTOHC=y
+CONFIG_RTC_SYSTOHC_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+# CONFIG_RTC_NVMEM is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_ABB5ZES3 is not set
+# CONFIG_RTC_DRV_ABEOZ9 is not set
+# CONFIG_RTC_DRV_ABX80X is not set
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_MAX31335 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_ISL12022 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8523 is not set
+# CONFIG_RTC_DRV_PCF85063 is not set
+# CONFIG_RTC_DRV_PCF85363 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_BQ32K is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8010 is not set
+# CONFIG_RTC_DRV_RX8111 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+# CONFIG_RTC_DRV_EM3027 is not set
+# CONFIG_RTC_DRV_RV3028 is not set
+# CONFIG_RTC_DRV_RV3032 is not set
+# CONFIG_RTC_DRV_RV8803 is not set
+# CONFIG_RTC_DRV_SD2405AL is not set
+# CONFIG_RTC_DRV_SD3078 is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T93 is not set
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1302 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1343 is not set
+# CONFIG_RTC_DRV_DS1347 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6916 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RX4581 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+# CONFIG_RTC_DRV_MCP795 is not set
+CONFIG_RTC_I2C_AND_SPI=y
+
+#
+# SPI and I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS3232 is not set
+# CONFIG_RTC_DRV_PCF2127 is not set
+# CONFIG_RTC_DRV_RV3029C2 is not set
+# CONFIG_RTC_DRV_RX6110 is not set
+
+#
+# Platform RTC drivers
+#
+CONFIG_RTC_DRV_CMOS=y
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1685_FAMILY is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_DS2404 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_MSM6242 is not set
+# CONFIG_RTC_DRV_RP5C01 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_DRV_FTRTC010 is not set
+
+#
+# HID Sensor RTC drivers
+#
+# CONFIG_RTC_DRV_GOLDFISH is not set
+CONFIG_DMADEVICES=y
+# CONFIG_DMADEVICES_DEBUG is not set
+
+#
+# DMA Devices
+#
+CONFIG_DMA_ENGINE=y
+CONFIG_DMA_VIRTUAL_CHANNELS=y
+CONFIG_DMA_ACPI=y
+# CONFIG_ALTERA_MSGDMA is not set
+CONFIG_INTEL_IDMA64=m
+# CONFIG_INTEL_IDXD is not set
+# CONFIG_INTEL_IDXD_COMPAT is not set
+CONFIG_INTEL_IOATDMA=m
+# CONFIG_PLX_DMA is not set
+# CONFIG_XILINX_DMA is not set
+# CONFIG_XILINX_XDMA is not set
+# CONFIG_AMD_AE4DMA is not set
+CONFIG_AMD_PTDMA=m
+# CONFIG_AMD_QDMA is not set
+# CONFIG_QCOM_HIDMA_MGMT is not set
+# CONFIG_QCOM_HIDMA is not set
+CONFIG_DW_DMAC_CORE=y
+# CONFIG_DW_DMAC is not set
+CONFIG_DW_DMAC_PCI=y
+# CONFIG_DW_EDMA is not set
+CONFIG_HSU_DMA=y
+# CONFIG_SF_PDMA is not set
+# CONFIG_INTEL_LDMA is not set
+
+#
+# DMA Clients
+#
+CONFIG_ASYNC_TX_DMA=y
+# CONFIG_DMATEST is not set
+CONFIG_DMA_ENGINE_RAID=y
+
+#
+# DMABUF options
+#
+CONFIG_SYNC_FILE=y
+# CONFIG_SW_SYNC is not set
+CONFIG_UDMABUF=y
+CONFIG_DMABUF_MOVE_NOTIFY=y
+# CONFIG_DMABUF_DEBUG is not set
+# CONFIG_DMABUF_SELFTESTS is not set
+# CONFIG_DMABUF_HEAPS is not set
+# CONFIG_DMABUF_SYSFS_STATS is not set
+# end of DMABUF options
+
+CONFIG_DCA=m
+# CONFIG_UIO is not set
+CONFIG_VFIO=m
+CONFIG_VFIO_GROUP=y
+CONFIG_VFIO_CONTAINER=y
+CONFIG_VFIO_IOMMU_TYPE1=m
+# CONFIG_VFIO_NOIOMMU is not set
+CONFIG_VFIO_VIRQFD=y
+# CONFIG_VFIO_DEBUGFS is not set
+
+#
+# VFIO support for PCI devices
+#
+CONFIG_VFIO_PCI_CORE=m
+CONFIG_VFIO_PCI_MMAP=y
+CONFIG_VFIO_PCI_INTX=y
+CONFIG_VFIO_PCI=m
+CONFIG_VFIO_PCI_VGA=y
+CONFIG_VFIO_PCI_IGD=y
+CONFIG_VIRTIO_VFIO_PCI=m
+CONFIG_VIRTIO_VFIO_PCI_ADMIN_LEGACY=y
+CONFIG_QAT_VFIO_PCI=m
+# end of VFIO support for PCI devices
+
+CONFIG_VFIO_MDEV=m
+CONFIG_IRQ_BYPASS_MANAGER=y
+CONFIG_VIRT_DRIVERS=y
+# CONFIG_VMGENID is not set
+# CONFIG_VBOXGUEST is not set
+# CONFIG_NITRO_ENCLAVES is not set
+CONFIG_EFI_SECRET=m
+CONFIG_VIRTIO_ANCHOR=y
+CONFIG_VIRTIO=y
+CONFIG_VIRTIO_PCI_LIB=y
+CONFIG_VIRTIO_PCI_LIB_LEGACY=y
+CONFIG_VIRTIO_MENU=y
+CONFIG_VIRTIO_PCI=y
+CONFIG_VIRTIO_PCI_ADMIN_LEGACY=y
+CONFIG_VIRTIO_PCI_LEGACY=y
+CONFIG_VIRTIO_VDPA=m
+CONFIG_VIRTIO_PMEM=m
+CONFIG_VIRTIO_BALLOON=m
+CONFIG_VIRTIO_MEM=m
+CONFIG_VIRTIO_INPUT=m
+CONFIG_VIRTIO_MMIO=m
+# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set
+CONFIG_VIRTIO_DMA_SHARED_BUFFER=m
+# CONFIG_VIRTIO_DEBUG is not set
+CONFIG_VDPA=m
+# CONFIG_VDPA_SIM is not set
+CONFIG_IFCVF=m
+# CONFIG_MLX5_VDPA_STEERING_DEBUG is not set
+CONFIG_VP_VDPA=m
+# CONFIG_ALIBABA_ENI_VDPA is not set
+CONFIG_SNET_VDPA=m
+CONFIG_OCTEONEP_VDPA=m
+CONFIG_VHOST_IOTLB=m
+CONFIG_VHOST_TASK=y
+CONFIG_VHOST=m
+CONFIG_VHOST_MENU=y
+CONFIG_VHOST_NET=m
+CONFIG_VHOST_VDPA=m
+# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set
+
+#
+# Microsoft Hyper-V guest support
+#
+# CONFIG_HYPERV is not set
+# end of Microsoft Hyper-V guest support
+
+# CONFIG_GREYBUS is not set
+# CONFIG_COMEDI is not set
+# CONFIG_STAGING is not set
+# CONFIG_GOLDFISH is not set
+# CONFIG_CHROME_PLATFORMS is not set
+# CONFIG_MELLANOX_PLATFORM is not set
+# CONFIG_SURFACE_PLATFORMS is not set
+CONFIG_X86_PLATFORM_DEVICES=y
+CONFIG_ACPI_WMI=m
+CONFIG_WMI_BMOF=m
+# CONFIG_HUAWEI_WMI is not set
+CONFIG_MXM_WMI=m
+# CONFIG_NVIDIA_WMI_EC_BACKLIGHT is not set
+# CONFIG_XIAOMI_WMI is not set
+# CONFIG_GIGABYTE_WMI is not set
+CONFIG_YOGABOOK=m
+# CONFIG_ACERHDF is not set
+# CONFIG_ACER_WIRELESS is not set
+# CONFIG_ACER_WMI is not set
+
+#
+# AMD HSMP Driver
+#
+# CONFIG_AMD_HSMP_ACPI is not set
+# CONFIG_AMD_HSMP_PLAT is not set
+# end of AMD HSMP Driver
+
+# CONFIG_AMD_PMC is not set
+CONFIG_AMD_3D_VCACHE=m
+CONFIG_AMD_WBRF=y
+# CONFIG_ADV_SWBUTTON is not set
+# CONFIG_APPLE_GMUX is not set
+# CONFIG_ASUS_LAPTOP is not set
+# CONFIG_ASUS_WIRELESS is not set
+# CONFIG_ASUS_WMI is not set
+# CONFIG_EEEPC_LAPTOP is not set
+# CONFIG_X86_PLATFORM_DRIVERS_DELL is not set
+# CONFIG_AMILO_RFKILL is not set
+# CONFIG_FUJITSU_LAPTOP is not set
+# CONFIG_FUJITSU_TABLET is not set
+# CONFIG_GPD_POCKET_FAN is not set
+# CONFIG_X86_PLATFORM_DRIVERS_HP is not set
+CONFIG_WIRELESS_HOTKEY=m
+# CONFIG_IBM_RTL is not set
+CONFIG_IDEAPAD_LAPTOP=m
+CONFIG_LENOVO_YMC=m
+CONFIG_SENSORS_HDAPS=m
+CONFIG_THINKPAD_ACPI=m
+CONFIG_THINKPAD_ACPI_ALSA_SUPPORT=y
+# CONFIG_THINKPAD_ACPI_DEBUGFACILITIES is not set
+# CONFIG_THINKPAD_ACPI_DEBUG is not set
+# CONFIG_THINKPAD_ACPI_UNSAFE_LEDS is not set
+CONFIG_THINKPAD_ACPI_VIDEO=y
+CONFIG_THINKPAD_ACPI_HOTKEY_POLL=y
+CONFIG_THINKPAD_LMI=m
+# CONFIG_INTEL_ATOMISP2_PM is not set
+# CONFIG_INTEL_IFS is not set
+# CONFIG_INTEL_SAR_INT1092 is not set
+# CONFIG_INTEL_PMT_TELEMETRY is not set
+# CONFIG_INTEL_PMT_CRASHLOG is not set
+
+#
+# Intel Speed Select Technology interface support
+#
+CONFIG_INTEL_SPEED_SELECT_TPMI=m
+CONFIG_INTEL_SPEED_SELECT_INTERFACE=m
+# end of Intel Speed Select Technology interface support
+
+CONFIG_INTEL_WMI=y
+# CONFIG_INTEL_WMI_SBL_FW_UPDATE is not set
+CONFIG_INTEL_WMI_THUNDERBOLT=m
+
+#
+# Intel Uncore Frequency Control
+#
+CONFIG_INTEL_UNCORE_FREQ_CONTROL_TPMI=m
+CONFIG_INTEL_UNCORE_FREQ_CONTROL=m
+# end of Intel Uncore Frequency Control
+
+CONFIG_INTEL_HID_EVENT=m
+CONFIG_INTEL_VBTN=m
+CONFIG_INTEL_OAKTRAIL=m
+CONFIG_INTEL_ISHTP_ECLITE=m
+# CONFIG_INTEL_PUNIT_IPC is not set
+CONFIG_INTEL_RST=m
+# CONFIG_INTEL_SDSI is not set
+CONFIG_INTEL_SMARTCONNECT=m
+CONFIG_INTEL_TPMI_POWER_DOMAINS=m
+CONFIG_INTEL_TPMI=m
+# CONFIG_INTEL_PLR_TPMI is not set
+CONFIG_INTEL_TURBO_MAX_3=y
+CONFIG_INTEL_VSEC=m
+# CONFIG_ACPI_QUICKSTART is not set
+# CONFIG_MSI_EC is not set
+# CONFIG_MSI_LAPTOP is not set
+# CONFIG_MSI_WMI is not set
+# CONFIG_MSI_WMI_PLATFORM is not set
+# CONFIG_SAMSUNG_LAPTOP is not set
+# CONFIG_SAMSUNG_Q10 is not set
+# CONFIG_TOSHIBA_BT_RFKILL is not set
+# CONFIG_TOSHIBA_HAPS is not set
+# CONFIG_TOSHIBA_WMI is not set
+# CONFIG_ACPI_CMPC is not set
+# CONFIG_COMPAL_LAPTOP is not set
+# CONFIG_LG_LAPTOP is not set
+# CONFIG_PANASONIC_LAPTOP is not set
+# CONFIG_SONY_LAPTOP is not set
+# CONFIG_SYSTEM76_ACPI is not set
+# CONFIG_TOPSTAR_LAPTOP is not set
+# CONFIG_SERIAL_MULTI_INSTANTIATE is not set
+# CONFIG_MLX_PLATFORM is not set
+# CONFIG_INSPUR_PLATFORM_PROFILE is not set
+# CONFIG_LENOVO_WMI_CAMERA is not set
+CONFIG_FW_ATTR_CLASS=m
+CONFIG_INTEL_IPS=m
+CONFIG_INTEL_SCU_IPC=y
+CONFIG_INTEL_SCU=y
+CONFIG_INTEL_SCU_PCI=y
+CONFIG_INTEL_SCU_PLATFORM=m
+CONFIG_INTEL_SCU_IPC_UTIL=m
+# CONFIG_SIEMENS_SIMATIC_IPC is not set
+# CONFIG_WINMATE_FM07_KEYS is not set
+CONFIG_P2SB=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_COMMON_CLK=y
+# CONFIG_LMK04832 is not set
+# CONFIG_COMMON_CLK_MAX9485 is not set
+# CONFIG_COMMON_CLK_SI5341 is not set
+# CONFIG_COMMON_CLK_SI5351 is not set
+# CONFIG_COMMON_CLK_SI544 is not set
+# CONFIG_COMMON_CLK_CDCE706 is not set
+# CONFIG_COMMON_CLK_CS2000_CP is not set
+# CONFIG_COMMON_CLK_PWM is not set
+# CONFIG_XILINX_VCU is not set
+CONFIG_HWSPINLOCK=y
+
+#
+# Clock Source drivers
+#
+CONFIG_CLKEVT_I8253=y
+CONFIG_I8253_LOCK=y
+CONFIG_CLKBLD_I8253=y
+# end of Clock Source drivers
+
+CONFIG_MAILBOX=y
+CONFIG_PCC=y
+# CONFIG_ALTERA_MBOX is not set
+CONFIG_IOMMU_IOVA=y
+CONFIG_IOMMU_API=y
+CONFIG_IOMMU_SUPPORT=y
+
+#
+# Generic IOMMU Pagetable Support
+#
+CONFIG_IOMMU_IO_PGTABLE=y
+# end of Generic IOMMU Pagetable Support
+
+# CONFIG_IOMMU_DEBUGFS is not set
+CONFIG_IOMMU_DEFAULT_DMA_STRICT=y
+# CONFIG_IOMMU_DEFAULT_DMA_LAZY is not set
+# CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set
+CONFIG_IOMMU_DMA=y
+CONFIG_IOMMU_SVA=y
+CONFIG_IOMMU_IOPF=y
+CONFIG_AMD_IOMMU=y
+CONFIG_DMAR_TABLE=y
+CONFIG_INTEL_IOMMU=y
+CONFIG_INTEL_IOMMU_SVM=y
+CONFIG_INTEL_IOMMU_DEFAULT_ON=y
+CONFIG_INTEL_IOMMU_FLOPPY_WA=y
+CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON=y
+CONFIG_INTEL_IOMMU_PERF_EVENTS=y
+# CONFIG_IOMMUFD is not set
+CONFIG_IRQ_REMAP=y
+CONFIG_VIRTIO_IOMMU=m
+
+#
+# Remoteproc drivers
+#
+# CONFIG_REMOTEPROC is not set
+# end of Remoteproc drivers
+
+#
+# Rpmsg drivers
+#
+# CONFIG_RPMSG_QCOM_GLINK_RPM is not set
+# CONFIG_RPMSG_VIRTIO is not set
+# end of Rpmsg drivers
+
+# CONFIG_SOUNDWIRE is not set
+
+#
+# SOC (System On Chip) specific Drivers
+#
+
+#
+# Amlogic SoC drivers
+#
+# end of Amlogic SoC drivers
+
+#
+# Broadcom SoC drivers
+#
+# end of Broadcom SoC drivers
+
+#
+# NXP/Freescale QorIQ SoC drivers
+#
+# end of NXP/Freescale QorIQ SoC drivers
+
+#
+# fujitsu SoC drivers
+#
+# end of fujitsu SoC drivers
+
+#
+# i.MX SoC drivers
+#
+# end of i.MX SoC drivers
+
+#
+# Enable LiteX SoC Builder specific drivers
+#
+# end of Enable LiteX SoC Builder specific drivers
+
+# CONFIG_WPCM450_SOC is not set
+
+#
+# Qualcomm SoC drivers
+#
+# CONFIG_QCOM_PBS is not set
+# end of Qualcomm SoC drivers
+
+# CONFIG_SOC_TI is not set
+
+#
+# Xilinx SoC drivers
+#
+# end of Xilinx SoC drivers
+# end of SOC (System On Chip) specific Drivers
+
+#
+# PM Domains
+#
+
+#
+# Amlogic PM Domains
+#
+# end of Amlogic PM Domains
+
+#
+# Broadcom PM Domains
+#
+# end of Broadcom PM Domains
+
+#
+# i.MX PM Domains
+#
+# end of i.MX PM Domains
+
+#
+# Qualcomm PM Domains
+#
+# end of Qualcomm PM Domains
+# end of PM Domains
+
+CONFIG_PM_DEVFREQ=y
+
+#
+# DEVFREQ Governors
+#
+CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
+CONFIG_DEVFREQ_GOV_PERFORMANCE=m
+CONFIG_DEVFREQ_GOV_POWERSAVE=m
+CONFIG_DEVFREQ_GOV_USERSPACE=m
+CONFIG_DEVFREQ_GOV_PASSIVE=m
+
+#
+# DEVFREQ Drivers
+#
+# CONFIG_PM_DEVFREQ_EVENT is not set
+CONFIG_EXTCON=y
+
+#
+# Extcon Device Drivers
+#
+# CONFIG_EXTCON_FSA9480 is not set
+# CONFIG_EXTCON_LC824206XA is not set
+# CONFIG_EXTCON_RT8973A is not set
+# CONFIG_EXTCON_SM5502 is not set
+# CONFIG_EXTCON_USBC_TUSB320 is not set
+CONFIG_MEMORY=y
+# CONFIG_IIO is not set
+# CONFIG_NTB is not set
+CONFIG_PWM=y
+# CONFIG_PWM_DEBUG is not set
+CONFIG_PWM_CLK=m
+# CONFIG_PWM_DWC is not set
+CONFIG_PWM_LPSS=m
+CONFIG_PWM_LPSS_PCI=m
+CONFIG_PWM_LPSS_PLATFORM=m
+# CONFIG_PWM_PCA9685 is not set
+
+#
+# IRQ chip support
+#
+# end of IRQ chip support
+
+# CONFIG_IPACK_BUS is not set
+# CONFIG_RESET_CONTROLLER is not set
+
+#
+# PHY Subsystem
+#
+# CONFIG_GENERIC_PHY is not set
+# CONFIG_USB_LGM_PHY is not set
+# CONFIG_PHY_CAN_TRANSCEIVER is not set
+
+#
+# PHY drivers for Broadcom platforms
+#
+# CONFIG_BCM_KONA_USB2_PHY is not set
+# end of PHY drivers for Broadcom platforms
+
+# CONFIG_PHY_PXA_28NM_HSIC is not set
+# CONFIG_PHY_PXA_28NM_USB2 is not set
+# CONFIG_PHY_INTEL_LGM_EMMC is not set
+# end of PHY Subsystem
+
+CONFIG_POWERCAP=y
+CONFIG_INTEL_RAPL_CORE=m
+CONFIG_INTEL_RAPL=m
+CONFIG_INTEL_RAPL_TPMI=m
+# CONFIG_IDLE_INJECT is not set
+# CONFIG_MCB is not set
+
+#
+# Performance monitor support
+#
+# CONFIG_DWC_PCIE_PMU is not set
+# end of Performance monitor support
+
+CONFIG_RAS=y
+# CONFIG_RAS_CEC is not set
+CONFIG_AMD_ATL=m
+CONFIG_AMD_ATL_PRM=y
+CONFIG_RAS_FMPM=m
+CONFIG_USB4=m
+# CONFIG_USB4_DEBUGFS_WRITE is not set
+# CONFIG_USB4_DMA_TEST is not set
+
+#
+# Android
+#
+# CONFIG_ANDROID_BINDER_IPC is not set
+# end of Android
+
+CONFIG_LIBNVDIMM=y
+CONFIG_BLK_DEV_PMEM=y
+CONFIG_ND_CLAIM=y
+CONFIG_ND_BTT=y
+CONFIG_BTT=y
+CONFIG_ND_PFN=y
+CONFIG_NVDIMM_PFN=y
+CONFIG_NVDIMM_DAX=y
+CONFIG_NVDIMM_KEYS=y
+# CONFIG_NVDIMM_SECURITY_TEST is not set
+CONFIG_DAX=y
+CONFIG_DEV_DAX=m
+CONFIG_DEV_DAX_PMEM=m
+CONFIG_DEV_DAX_KMEM=m
+CONFIG_NVMEM=y
+CONFIG_NVMEM_SYSFS=y
+# CONFIG_NVMEM_LAYOUTS is not set
+# CONFIG_NVMEM_RMEM is not set
+CONFIG_NVMEM_SPMI_SDAM=m
+
+#
+# HW tracing support
+#
+# CONFIG_STM is not set
+# CONFIG_INTEL_TH is not set
+# end of HW tracing support
+
+# CONFIG_FPGA is not set
+# CONFIG_TEE is not set
+CONFIG_PM_OPP=y
+# CONFIG_SIOX is not set
+# CONFIG_SLIMBUS is not set
+# CONFIG_INTERCONNECT is not set
+CONFIG_COUNTER=m
+# CONFIG_INTEL_QEP is not set
+# CONFIG_MOST is not set
+# CONFIG_PECI is not set
+CONFIG_HTE=y
+# end of Device Drivers
+
+#
+# File systems
+#
+CONFIG_DCACHE_WORD_ACCESS=y
+CONFIG_VALIDATE_FS_PARSER=y
+CONFIG_FS_IOMAP=y
+CONFIG_FS_STACK=y
+CONFIG_BUFFER_HEAD=y
+CONFIG_LEGACY_DIRECT_IO=y
+CONFIG_EXT2_FS=m
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=m
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+# CONFIG_EXT4_DEBUG is not set
+CONFIG_JBD2=y
+# CONFIG_JBD2_DEBUG is not set
+CONFIG_FS_MBCACHE=y
+# CONFIG_JFS_FS is not set
+CONFIG_XFS_FS=m
+# CONFIG_XFS_SUPPORT_V4 is not set
+# CONFIG_XFS_SUPPORT_ASCII_CI is not set
+CONFIG_XFS_QUOTA=y
+CONFIG_XFS_POSIX_ACL=y
+CONFIG_XFS_RT=y
+# CONFIG_XFS_ONLINE_SCRUB is not set
+# CONFIG_XFS_WARN is not set
+# CONFIG_XFS_DEBUG is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set
+# CONFIG_BTRFS_DEBUG is not set
+# CONFIG_BTRFS_ASSERT is not set
+CONFIG_BTRFS_EXPERIMENTAL=y
+# CONFIG_BTRFS_FS_REF_VERIFY is not set
+CONFIG_NILFS2_FS=m
+CONFIG_F2FS_FS=m
+CONFIG_F2FS_STAT_FS=y
+CONFIG_F2FS_FS_XATTR=y
+CONFIG_F2FS_FS_POSIX_ACL=y
+CONFIG_F2FS_FS_SECURITY=y
+CONFIG_F2FS_CHECK_FS=y
+# CONFIG_F2FS_FAULT_INJECTION is not set
+CONFIG_F2FS_FS_COMPRESSION=y
+CONFIG_F2FS_FS_LZO=y
+CONFIG_F2FS_FS_LZORLE=y
+CONFIG_F2FS_FS_LZ4=y
+CONFIG_F2FS_FS_LZ4HC=y
+CONFIG_F2FS_FS_ZSTD=y
+CONFIG_F2FS_IOSTAT=y
+# CONFIG_F2FS_UNFAIR_RWSEM is not set
+CONFIG_BCACHEFS_FS=m
+CONFIG_BCACHEFS_QUOTA=y
+CONFIG_BCACHEFS_ERASURE_CODING=y
+CONFIG_BCACHEFS_POSIX_ACL=y
+# CONFIG_BCACHEFS_DEBUG is not set
+# CONFIG_BCACHEFS_TESTS is not set
+CONFIG_BCACHEFS_LOCK_TIME_STATS=y
+# CONFIG_BCACHEFS_NO_LATENCY_ACCT is not set
+CONFIG_BCACHEFS_SIX_OPTIMISTIC_SPIN=y
+# CONFIG_BCACHEFS_PATH_TRACEPOINTS is not set
+CONFIG_ZONEFS_FS=m
+CONFIG_FS_DAX=y
+CONFIG_FS_DAX_PMD=y
+CONFIG_FS_POSIX_ACL=y
+CONFIG_EXPORTFS=y
+CONFIG_EXPORTFS_BLOCK_OPS=y
+CONFIG_FILE_LOCKING=y
+CONFIG_FS_ENCRYPTION=y
+CONFIG_FS_ENCRYPTION_ALGS=y
+CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y
+CONFIG_FS_VERITY=y
+CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY_USER=y
+CONFIG_FANOTIFY=y
+# CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_QUOTA_DEBUG=y
+CONFIG_QUOTA_TREE=m
+# CONFIG_QFMT_V1 is not set
+CONFIG_QFMT_V2=m
+CONFIG_QUOTACTL=y
+CONFIG_AUTOFS_FS=y
+CONFIG_FUSE_FS=m
+# CONFIG_CUSE is not set
+CONFIG_VIRTIO_FS=m
+CONFIG_FUSE_DAX=y
+CONFIG_FUSE_PASSTHROUGH=y
+CONFIG_FUSE_IO_URING=y
+CONFIG_OVERLAY_FS=m
+CONFIG_OVERLAY_FS_REDIRECT_DIR=y
+# CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW is not set
+CONFIG_OVERLAY_FS_INDEX=y
+CONFIG_OVERLAY_FS_XINO_AUTO=y
+CONFIG_OVERLAY_FS_METACOPY=y
+# CONFIG_OVERLAY_FS_DEBUG is not set
+
+#
+# Caches
+#
+CONFIG_NETFS_SUPPORT=m
+CONFIG_NETFS_STATS=y
+# CONFIG_NETFS_DEBUG is not set
+CONFIG_FSCACHE=y
+CONFIG_FSCACHE_STATS=y
+CONFIG_CACHEFILES=m
+# CONFIG_CACHEFILES_DEBUG is not set
+# CONFIG_CACHEFILES_ERROR_INJECTION is not set
+CONFIG_CACHEFILES_ONDEMAND=y
+# end of Caches
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+# end of CD-ROM/DVD Filesystems
+
+#
+# DOS/FAT/EXFAT/NT Filesystems
+#
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+CONFIG_FAT_DEFAULT_UTF8=y
+CONFIG_EXFAT_FS=m
+CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8"
+CONFIG_NTFS3_FS=m
+# CONFIG_NTFS3_64BIT_CLUSTER is not set
+CONFIG_NTFS3_LZX_XPRESS=y
+CONFIG_NTFS3_FS_POSIX_ACL=y
+# CONFIG_NTFS_FS is not set
+# end of DOS/FAT/EXFAT/NT Filesystems
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+# CONFIG_PROC_KCORE is not set
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_PROC_CHILDREN=y
+CONFIG_PROC_PID_ARCH_STATUS=y
+CONFIG_PROC_CPU_RESCTRL=y
+CONFIG_KERNFS=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_TMPFS_XATTR=y
+CONFIG_TMPFS_INODE64=y
+CONFIG_TMPFS_QUOTA=y
+CONFIG_HUGETLBFS=y
+# CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON is not set
+CONFIG_HUGETLB_PAGE=y
+CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP=y
+CONFIG_HUGETLB_PMD_PAGE_TABLE_SHARING=y
+CONFIG_ARCH_HAS_GIGANTIC_PAGE=y
+CONFIG_CONFIGFS_FS=y
+CONFIG_EFIVAR_FS=m
+# end of Pseudo filesystems
+
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ORANGEFS_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+CONFIG_ECRYPT_FS=m
+# CONFIG_ECRYPT_FS_MESSAGING is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+CONFIG_SQUASHFS=m
+# CONFIG_SQUASHFS_FILE_CACHE is not set
+CONFIG_SQUASHFS_FILE_DIRECT=y
+CONFIG_SQUASHFS_DECOMP_SINGLE=y
+CONFIG_SQUASHFS_DECOMP_MULTI=y
+CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
+CONFIG_SQUASHFS_CHOICE_DECOMP_BY_MOUNT=y
+CONFIG_SQUASHFS_MOUNT_DECOMP_THREADS=y
+CONFIG_SQUASHFS_XATTR=y
+CONFIG_SQUASHFS_ZLIB=y
+CONFIG_SQUASHFS_LZ4=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_SQUASHFS_ZSTD=y
+CONFIG_SQUASHFS_4K_DEVBLK_SIZE=y
+CONFIG_SQUASHFS_EMBEDDED=y
+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX6FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_PSTORE=y
+CONFIG_PSTORE_DEFAULT_KMSG_BYTES=10240
+CONFIG_PSTORE_COMPRESS=y
+# CONFIG_PSTORE_CONSOLE is not set
+# CONFIG_PSTORE_PMSG is not set
+# CONFIG_PSTORE_FTRACE is not set
+CONFIG_PSTORE_RAM=m
+CONFIG_PSTORE_ZONE=y
+CONFIG_PSTORE_BLK=y
+CONFIG_PSTORE_BLK_BLKDEV=""
+CONFIG_PSTORE_BLK_KMSG_SIZE=64
+CONFIG_PSTORE_BLK_MAX_REASON=2
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_EROFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=m
+# CONFIG_NFS_V2 is not set
+# CONFIG_NFS_V3 is not set
+CONFIG_NFS_V4=m
+# CONFIG_NFS_SWAP is not set
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_PNFS_FILE_LAYOUT=m
+CONFIG_PNFS_BLOCK=m
+CONFIG_PNFS_FLEXFILE_LAYOUT=m
+CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="millerson.name"
+CONFIG_NFS_V4_1_MIGRATION=y
+CONFIG_NFS_V4_SECURITY_LABEL=y
+CONFIG_NFS_FSCACHE=y
+# CONFIG_NFS_USE_LEGACY_DNS is not set
+CONFIG_NFS_USE_KERNEL_DNS=y
+CONFIG_NFS_DISABLE_UDP_SUPPORT=y
+CONFIG_NFS_V4_2_READ_PLUS=y
+CONFIG_NFSD=m
+# CONFIG_NFSD_V2 is not set
+# CONFIG_NFSD_V3_ACL is not set
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_PNFS=y
+CONFIG_NFSD_BLOCKLAYOUT=y
+# CONFIG_NFSD_SCSILAYOUT is not set
+# CONFIG_NFSD_FLEXFILELAYOUT is not set
+CONFIG_NFSD_V4_2_INTER_SSC=y
+CONFIG_NFSD_V4_SECURITY_LABEL=y
+# CONFIG_NFSD_LEGACY_CLIENT_TRACKING is not set
+CONFIG_GRACE_PERIOD=m
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_NFS_COMMON_LOCALIO_SUPPORT=m
+CONFIG_NFS_LOCALIO=y
+CONFIG_NFS_V4_2_SSC_HELPER=y
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+CONFIG_SUNRPC_BACKCHANNEL=y
+CONFIG_RPCSEC_GSS_KRB5=m
+CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1=y
+# CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA is not set
+# CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2 is not set
+# CONFIG_SUNRPC_DEBUG is not set
+# CONFIG_CEPH_FS is not set
+CONFIG_CIFS=m
+CONFIG_CIFS_STATS2=y
+CONFIG_CIFS_ALLOW_INSECURE_LEGACY=y
+CONFIG_CIFS_UPCALL=y
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG is not set
+CONFIG_CIFS_DFS_UPCALL=y
+CONFIG_CIFS_SWN_UPCALL=y
+CONFIG_CIFS_FSCACHE=y
+# CONFIG_CIFS_COMPRESSION is not set
+# CONFIG_SMB_SERVER is not set
+CONFIG_SMBFS=m
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+CONFIG_NLS_CODEPAGE_866=y
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+CONFIG_NLS_KOI8_R=y
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_MAC_ROMAN is not set
+# CONFIG_NLS_MAC_CELTIC is not set
+# CONFIG_NLS_MAC_CENTEURO is not set
+# CONFIG_NLS_MAC_CROATIAN is not set
+# CONFIG_NLS_MAC_CYRILLIC is not set
+# CONFIG_NLS_MAC_GAELIC is not set
+# CONFIG_NLS_MAC_GREEK is not set
+# CONFIG_NLS_MAC_ICELAND is not set
+# CONFIG_NLS_MAC_INUIT is not set
+# CONFIG_NLS_MAC_ROMANIAN is not set
+# CONFIG_NLS_MAC_TURKISH is not set
+CONFIG_NLS_UTF8=y
+CONFIG_NLS_UCS2_UTILS=m
+# CONFIG_DLM is not set
+CONFIG_UNICODE=y
+# CONFIG_UNICODE_NORMALIZATION_SELFTEST is not set
+CONFIG_IO_WQ=y
+# end of File systems
+
+#
+# Security options
+#
+CONFIG_KEYS=y
+CONFIG_KEYS_REQUEST_CACHE=y
+CONFIG_PERSISTENT_KEYRINGS=y
+# CONFIG_TRUSTED_KEYS is not set
+CONFIG_ENCRYPTED_KEYS=y
+# CONFIG_USER_DECRYPTED_DATA is not set
+# CONFIG_KEY_DH_OPERATIONS is not set
+CONFIG_KEY_NOTIFICATIONS=y
+CONFIG_SECURITY_DMESG_RESTRICT=y
+# CONFIG_PROC_MEM_ALWAYS_FORCE is not set
+CONFIG_PROC_MEM_FORCE_PTRACE=y
+# CONFIG_PROC_MEM_NO_FORCE is not set
+CONFIG_SECURITY=y
+CONFIG_HAS_SECURITY_AUDIT=y
+CONFIG_SECURITYFS=y
+CONFIG_SECURITY_NETWORK=y
+# CONFIG_SECURITY_NETWORK_XFRM is not set
+CONFIG_SECURITY_PATH=y
+# CONFIG_INTEL_TXT is not set
+CONFIG_LSM_MMAP_MIN_ADDR=65536
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_FORTIFY_SOURCE=y
+# CONFIG_STATIC_USERMODEHELPER is not set
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SELINUX_BOOTPARAM=y
+# CONFIG_SECURITY_SELINUX_DEVELOP is not set
+CONFIG_SECURITY_SELINUX_AVC_STATS=y
+CONFIG_SECURITY_SELINUX_SIDTAB_HASH_BITS=9
+CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE=256
+# CONFIG_SECURITY_SELINUX_DEBUG is not set
+# CONFIG_SECURITY_SMACK is not set
+# CONFIG_SECURITY_TOMOYO is not set
+CONFIG_SECURITY_APPARMOR=y
+# CONFIG_SECURITY_APPARMOR_DEBUG is not set
+CONFIG_SECURITY_APPARMOR_INTROSPECT_POLICY=y
+CONFIG_SECURITY_APPARMOR_HASH=y
+CONFIG_SECURITY_APPARMOR_HASH_DEFAULT=y
+CONFIG_SECURITY_APPARMOR_EXPORT_BINARY=y
+CONFIG_SECURITY_APPARMOR_PARANOID_LOAD=y
+# CONFIG_SECURITY_LOADPIN is not set
+CONFIG_SECURITY_YAMA=y
+CONFIG_SECURITY_SAFESETID=y
+CONFIG_SECURITY_LOCKDOWN_LSM=y
+CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y
+CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE=y
+# CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY is not set
+# CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY is not set
+CONFIG_SECURITY_LANDLOCK=y
+# CONFIG_SECURITY_IPE is not set
+CONFIG_INTEGRITY=y
+CONFIG_INTEGRITY_SIGNATURE=y
+CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y
+CONFIG_INTEGRITY_TRUSTED_KEYRING=y
+CONFIG_INTEGRITY_AUDIT=y
+# CONFIG_IMA is not set
+# CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set
+# CONFIG_EVM is not set
+# CONFIG_DEFAULT_SECURITY_SELINUX is not set
+# CONFIG_DEFAULT_SECURITY_APPARMOR is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_LSM="lockdown,yama,integrity,selinux,bpf,landlock"
+
+#
+# Kernel hardening options
+#
+
+#
+# Memory initialization
+#
+CONFIG_CC_HAS_AUTO_VAR_INIT_PATTERN=y
+CONFIG_CC_HAS_AUTO_VAR_INIT_ZERO_BARE=y
+CONFIG_CC_HAS_AUTO_VAR_INIT_ZERO=y
+# CONFIG_INIT_STACK_NONE is not set
+# CONFIG_INIT_STACK_ALL_PATTERN is not set
+CONFIG_INIT_STACK_ALL_ZERO=y
+CONFIG_GCC_PLUGIN_STACKLEAK=y
+CONFIG_STACKLEAK_TRACK_MIN_SIZE=100
+# CONFIG_STACKLEAK_METRICS is not set
+# CONFIG_STACKLEAK_RUNTIME_DISABLE is not set
+CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y
+CONFIG_INIT_ON_FREE_DEFAULT_ON=y
+CONFIG_CC_HAS_ZERO_CALL_USED_REGS=y
+CONFIG_ZERO_CALL_USED_REGS=y
+# end of Memory initialization
+
+#
+# Hardening of kernel data structures
+#
+CONFIG_LIST_HARDENED=y
+CONFIG_BUG_ON_DATA_CORRUPTION=y
+# end of Hardening of kernel data structures
+
+# CONFIG_RANDSTRUCT_NONE is not set
+CONFIG_RANDSTRUCT_FULL=y
+# CONFIG_RANDSTRUCT_PERFORMANCE is not set
+CONFIG_RANDSTRUCT=y
+CONFIG_GCC_PLUGIN_RANDSTRUCT=y
+# end of Kernel hardening options
+# end of Security options
+
+CONFIG_XOR_BLOCKS=m
+CONFIG_ASYNC_CORE=m
+CONFIG_ASYNC_MEMCPY=m
+CONFIG_ASYNC_XOR=m
+CONFIG_ASYNC_PQ=m
+CONFIG_ASYNC_RAID6_RECOV=m
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_SIG=y
+CONFIG_CRYPTO_SIG2=y
+CONFIG_CRYPTO_SKCIPHER=y
+CONFIG_CRYPTO_SKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_RNG_DEFAULT=y
+CONFIG_CRYPTO_AKCIPHER2=y
+CONFIG_CRYPTO_AKCIPHER=y
+CONFIG_CRYPTO_KPP2=y
+CONFIG_CRYPTO_KPP=m
+CONFIG_CRYPTO_ACOMP2=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_USER is not set
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_NULL2=y
+CONFIG_CRYPTO_PCRYPT=y
+CONFIG_CRYPTO_CRYPTD=y
+CONFIG_CRYPTO_AUTHENC=y
+# CONFIG_CRYPTO_TEST is not set
+CONFIG_CRYPTO_SIMD=y
+CONFIG_CRYPTO_ENGINE=m
+# end of Crypto core or helper
+
+#
+# Public-key cryptography
+#
+CONFIG_CRYPTO_RSA=y
+CONFIG_CRYPTO_DH=m
+CONFIG_CRYPTO_DH_RFC7919_GROUPS=y
+CONFIG_CRYPTO_ECC=y
+CONFIG_CRYPTO_ECDH=m
+CONFIG_CRYPTO_ECDSA=y
+CONFIG_CRYPTO_ECRDSA=m
+CONFIG_CRYPTO_CURVE25519=m
+# end of Public-key cryptography
+
+#
+# Block ciphers
+#
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_AES_TI=m
+CONFIG_CRYPTO_ARIA=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_BLOWFISH_COMMON=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_CAST_COMMON=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_DES=m
+CONFIG_CRYPTO_FCRYPT=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_SM4=m
+CONFIG_CRYPTO_SM4_GENERIC=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+# end of Block ciphers
+
+#
+# Length-preserving ciphers and modes
+#
+CONFIG_CRYPTO_ADIANTUM=m
+CONFIG_CRYPTO_CHACHA20=m
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_CTR=y
+CONFIG_CRYPTO_CTS=y
+CONFIG_CRYPTO_ECB=y
+CONFIG_CRYPTO_HCTR2=m
+CONFIG_CRYPTO_LRW=m
+CONFIG_CRYPTO_PCBC=m
+CONFIG_CRYPTO_XCTR=m
+CONFIG_CRYPTO_XTS=y
+CONFIG_CRYPTO_NHPOLY1305=m
+# end of Length-preserving ciphers and modes
+
+#
+# AEAD (authenticated encryption with associated data) ciphers
+#
+CONFIG_CRYPTO_AEGIS128=m
+CONFIG_CRYPTO_CHACHA20POLY1305=m
+CONFIG_CRYPTO_CCM=m
+CONFIG_CRYPTO_GCM=m
+CONFIG_CRYPTO_GENIV=m
+CONFIG_CRYPTO_SEQIV=m
+CONFIG_CRYPTO_ECHAINIV=m
+CONFIG_CRYPTO_ESSIV=m
+# end of AEAD (authenticated encryption with associated data) ciphers
+
+#
+# Hashes, digests, and MACs
+#
+CONFIG_CRYPTO_BLAKE2B=y
+CONFIG_CRYPTO_CMAC=m
+CONFIG_CRYPTO_GHASH=m
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_POLYVAL=m
+CONFIG_CRYPTO_POLY1305=m
+# CONFIG_CRYPTO_RMD160 is not set
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_SHA512=y
+CONFIG_CRYPTO_SHA3=y
+CONFIG_CRYPTO_SM3=m
+# CONFIG_CRYPTO_SM3_GENERIC is not set
+CONFIG_CRYPTO_STREEBOG=m
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_XCBC=m
+CONFIG_CRYPTO_XXHASH=y
+# end of Hashes, digests, and MACs
+
+#
+# CRCs (cyclic redundancy checks)
+#
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_CRC32=m
+CONFIG_CRYPTO_CRCT10DIF=y
+CONFIG_CRYPTO_CRC64_ROCKSOFT=y
+# end of CRCs (cyclic redundancy checks)
+
+#
+# Compression
+#
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_LZO=y
+# CONFIG_CRYPTO_842 is not set
+CONFIG_CRYPTO_LZ4=m
+CONFIG_CRYPTO_LZ4HC=m
+CONFIG_CRYPTO_ZSTD=y
+# end of Compression
+
+#
+# Random number generation
+#
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_DRBG_MENU=y
+CONFIG_CRYPTO_DRBG_HMAC=y
+CONFIG_CRYPTO_DRBG_HASH=y
+CONFIG_CRYPTO_DRBG_CTR=y
+CONFIG_CRYPTO_DRBG=y
+CONFIG_CRYPTO_JITTERENTROPY=y
+CONFIG_CRYPTO_JITTERENTROPY_MEMORY_BLOCKS=64
+CONFIG_CRYPTO_JITTERENTROPY_MEMORY_BLOCKSIZE=32
+CONFIG_CRYPTO_JITTERENTROPY_OSR=1
+# end of Random number generation
+
+#
+# Userspace interface
+#
+CONFIG_CRYPTO_USER_API=y
+CONFIG_CRYPTO_USER_API_HASH=y
+CONFIG_CRYPTO_USER_API_SKCIPHER=y
+CONFIG_CRYPTO_USER_API_RNG=y
+# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set
+CONFIG_CRYPTO_USER_API_AEAD=m
+# CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE is not set
+# end of Userspace interface
+
+CONFIG_CRYPTO_HASH_INFO=y
+
+#
+# Accelerated Cryptographic Algorithms for CPU (x86)
+#
+CONFIG_CRYPTO_CURVE25519_X86=m
+CONFIG_CRYPTO_AES_NI_INTEL=y
+CONFIG_CRYPTO_BLOWFISH_X86_64=m
+CONFIG_CRYPTO_CAMELLIA_X86_64=m
+CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64=m
+CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64=m
+CONFIG_CRYPTO_CAST5_AVX_X86_64=m
+CONFIG_CRYPTO_CAST6_AVX_X86_64=m
+CONFIG_CRYPTO_DES3_EDE_X86_64=m
+CONFIG_CRYPTO_SERPENT_SSE2_X86_64=m
+CONFIG_CRYPTO_SERPENT_AVX_X86_64=m
+CONFIG_CRYPTO_SERPENT_AVX2_X86_64=m
+CONFIG_CRYPTO_SM4_AESNI_AVX_X86_64=m
+CONFIG_CRYPTO_SM4_AESNI_AVX2_X86_64=m
+CONFIG_CRYPTO_TWOFISH_X86_64=m
+CONFIG_CRYPTO_TWOFISH_X86_64_3WAY=m
+CONFIG_CRYPTO_TWOFISH_AVX_X86_64=m
+CONFIG_CRYPTO_ARIA_AESNI_AVX_X86_64=m
+CONFIG_CRYPTO_ARIA_AESNI_AVX2_X86_64=m
+CONFIG_CRYPTO_ARIA_GFNI_AVX512_X86_64=m
+CONFIG_CRYPTO_CHACHA20_X86_64=y
+CONFIG_CRYPTO_AEGIS128_AESNI_SSE2=m
+CONFIG_CRYPTO_NHPOLY1305_SSE2=m
+CONFIG_CRYPTO_NHPOLY1305_AVX2=m
+CONFIG_CRYPTO_BLAKE2S_X86=y
+CONFIG_CRYPTO_POLYVAL_CLMUL_NI=m
+CONFIG_CRYPTO_POLY1305_X86_64=y
+CONFIG_CRYPTO_SHA1_SSSE3=m
+CONFIG_CRYPTO_SHA256_SSSE3=m
+CONFIG_CRYPTO_SHA512_SSSE3=m
+CONFIG_CRYPTO_SM3_AVX_X86_64=m
+CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL=m
+# end of Accelerated Cryptographic Algorithms for CPU (x86)
+
+CONFIG_CRYPTO_HW=y
+# CONFIG_CRYPTO_DEV_PADLOCK is not set
+# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set
+# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set
+CONFIG_CRYPTO_DEV_CCP=y
+CONFIG_CRYPTO_DEV_CCP_DD=m
+CONFIG_CRYPTO_DEV_SP_CCP=y
+CONFIG_CRYPTO_DEV_CCP_CRYPTO=m
+CONFIG_CRYPTO_DEV_SP_PSP=y
+# CONFIG_CRYPTO_DEV_CCP_DEBUGFS is not set
+# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set
+CONFIG_CRYPTO_DEV_QAT=m
+CONFIG_CRYPTO_DEV_QAT_DH895xCC=m
+CONFIG_CRYPTO_DEV_QAT_C3XXX=m
+CONFIG_CRYPTO_DEV_QAT_C62X=m
+CONFIG_CRYPTO_DEV_QAT_4XXX=m
+# CONFIG_CRYPTO_DEV_QAT_420XX is not set
+CONFIG_CRYPTO_DEV_QAT_DH895xCCVF=m
+CONFIG_CRYPTO_DEV_QAT_C3XXXVF=m
+CONFIG_CRYPTO_DEV_QAT_C62XVF=m
+# CONFIG_CRYPTO_DEV_QAT_ERROR_INJECTION is not set
+CONFIG_CRYPTO_DEV_VIRTIO=m
+# CONFIG_CRYPTO_DEV_SAFEXCEL is not set
+# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set
+CONFIG_ASYMMETRIC_KEY_TYPE=y
+CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
+CONFIG_X509_CERTIFICATE_PARSER=y
+CONFIG_PKCS8_PRIVATE_KEY_PARSER=m
+CONFIG_PKCS7_MESSAGE_PARSER=y
+# CONFIG_PKCS7_TEST_KEY is not set
+# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set
+# CONFIG_FIPS_SIGNATURE_SELFTEST is not set
+
+#
+# Certificates for signature checking
+#
+CONFIG_MODULE_SIG_KEY="certs/signing_key.pem"
+# CONFIG_MODULE_SIG_KEY_TYPE_RSA is not set
+CONFIG_MODULE_SIG_KEY_TYPE_ECDSA=y
+CONFIG_SYSTEM_TRUSTED_KEYRING=y
+CONFIG_SYSTEM_TRUSTED_KEYS=""
+# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set
+# CONFIG_SECONDARY_TRUSTED_KEYRING is not set
+# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set
+# end of Certificates for signature checking
+
+CONFIG_BINARY_PRINTF=y
+
+#
+# Library routines
+#
+CONFIG_RAID6_PQ=m
+CONFIG_RAID6_PQ_BENCHMARK=y
+CONFIG_PACKING=y
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GENERIC_NET_UTILS=y
+# CONFIG_CORDIC is not set
+# CONFIG_PRIME_NUMBERS is not set
+CONFIG_RATIONAL=y
+CONFIG_GENERIC_IOMAP=y
+CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
+CONFIG_ARCH_HAS_FAST_MULTIPLIER=y
+CONFIG_ARCH_USE_SYM_ANNOTATIONS=y
+
+#
+# Crypto library routines
+#
+CONFIG_CRYPTO_LIB_UTILS=y
+CONFIG_CRYPTO_LIB_AES=y
+CONFIG_CRYPTO_LIB_AESCFB=m
+CONFIG_CRYPTO_LIB_ARC4=m
+CONFIG_CRYPTO_LIB_GF128MUL=y
+CONFIG_CRYPTO_ARCH_HAVE_LIB_BLAKE2S=y
+CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
+CONFIG_CRYPTO_ARCH_HAVE_LIB_CHACHA=y
+CONFIG_CRYPTO_LIB_CHACHA_GENERIC=y
+CONFIG_CRYPTO_LIB_CHACHA=m
+CONFIG_CRYPTO_ARCH_HAVE_LIB_CURVE25519=m
+CONFIG_CRYPTO_LIB_CURVE25519_GENERIC=m
+CONFIG_CRYPTO_LIB_CURVE25519=m
+CONFIG_CRYPTO_LIB_DES=m
+CONFIG_CRYPTO_LIB_POLY1305_RSIZE=11
+CONFIG_CRYPTO_ARCH_HAVE_LIB_POLY1305=y
+CONFIG_CRYPTO_LIB_POLY1305_GENERIC=y
+CONFIG_CRYPTO_LIB_POLY1305=m
+CONFIG_CRYPTO_LIB_CHACHA20POLY1305=m
+CONFIG_CRYPTO_LIB_SHA1=y
+CONFIG_CRYPTO_LIB_SHA256=y
+# end of Crypto library routines
+
+CONFIG_CRC_CCITT=y
+CONFIG_CRC16=y
+CONFIG_CRC_T10DIF=y
+CONFIG_ARCH_HAS_CRC_T10DIF=y
+CONFIG_CRC_T10DIF_ARCH=y
+CONFIG_CRC64_ROCKSOFT=y
+CONFIG_CRC_ITU_T=y
+CONFIG_CRC32=y
+CONFIG_ARCH_HAS_CRC32=y
+CONFIG_CRC32_ARCH=y
+CONFIG_CRC64=y
+# CONFIG_CRC4 is not set
+CONFIG_CRC7=m
+CONFIG_LIBCRC32C=y
+CONFIG_CRC8=m
+CONFIG_CRC_OPTIMIZATIONS=y
+CONFIG_XXHASH=y
+# CONFIG_RANDOM32_SELFTEST is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_LZ4_COMPRESS=m
+CONFIG_LZ4HC_COMPRESS=m
+CONFIG_LZ4_DECOMPRESS=m
+CONFIG_ZSTD_COMMON=y
+CONFIG_ZSTD_COMPRESS=y
+CONFIG_ZSTD_DECOMPRESS=y
+CONFIG_XZ_DEC=y
+CONFIG_XZ_DEC_X86=y
+CONFIG_XZ_DEC_POWERPC=y
+CONFIG_XZ_DEC_ARM=y
+CONFIG_XZ_DEC_ARMTHUMB=y
+CONFIG_XZ_DEC_ARM64=y
+CONFIG_XZ_DEC_SPARC=y
+CONFIG_XZ_DEC_RISCV=y
+CONFIG_XZ_DEC_MICROLZMA=y
+CONFIG_XZ_DEC_BCJ=y
+# CONFIG_XZ_DEC_TEST is not set
+CONFIG_DECOMPRESS_XZ=y
+CONFIG_DECOMPRESS_ZSTD=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_REED_SOLOMON=m
+CONFIG_REED_SOLOMON_ENC8=y
+CONFIG_REED_SOLOMON_DEC8=y
+CONFIG_TEXTSEARCH=y
+CONFIG_TEXTSEARCH_KMP=m
+CONFIG_TEXTSEARCH_BM=m
+CONFIG_TEXTSEARCH_FSM=m
+CONFIG_INTERVAL_TREE=y
+CONFIG_XARRAY_MULTI=y
+CONFIG_ASSOCIATIVE_ARRAY=y
+CONFIG_CLOSURES=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_IOPORT_MAP=y
+CONFIG_HAS_DMA=y
+CONFIG_DMA_OPS_HELPERS=y
+CONFIG_NEED_SG_DMA_FLAGS=y
+CONFIG_NEED_SG_DMA_LENGTH=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_ARCH_DMA_ADDR_T_64BIT=y
+CONFIG_SWIOTLB=y
+# CONFIG_SWIOTLB_DYNAMIC is not set
+CONFIG_DMA_NEED_SYNC=y
+# CONFIG_DMA_API_DEBUG is not set
+# CONFIG_DMA_MAP_BENCHMARK is not set
+CONFIG_SGL_ALLOC=y
+CONFIG_CHECK_SIGNATURE=y
+CONFIG_CPU_RMAP=y
+CONFIG_DQL=y
+CONFIG_GLOB=y
+# CONFIG_GLOB_SELFTEST is not set
+CONFIG_NLATTR=y
+CONFIG_LRU_CACHE=m
+CONFIG_CLZ_TAB=y
+# CONFIG_IRQ_POLL is not set
+CONFIG_MPILIB=y
+CONFIG_SIGNATURE=y
+CONFIG_DIMLIB=y
+CONFIG_OID_REGISTRY=y
+CONFIG_UCS2_STRING=y
+CONFIG_HAVE_GENERIC_VDSO=y
+CONFIG_GENERIC_GETTIMEOFDAY=y
+CONFIG_GENERIC_VDSO_TIME_NS=y
+CONFIG_GENERIC_VDSO_OVERFLOW_PROTECT=y
+CONFIG_VDSO_GETRANDOM=y
+CONFIG_FONT_SUPPORT=y
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_SG_POOL=y
+CONFIG_ARCH_HAS_PMEM_API=y
+CONFIG_MEMREGION=y
+CONFIG_ARCH_HAS_CPU_CACHE_INVALIDATE_MEMREGION=y
+CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE=y
+CONFIG_ARCH_HAS_COPY_MC=y
+CONFIG_ARCH_STACKWALK=y
+CONFIG_STACKDEPOT=y
+CONFIG_STACKDEPOT_MAX_FRAMES=64
+CONFIG_SBITMAP=y
+# CONFIG_LWQ_TEST is not set
+# end of Library routines
+
+CONFIG_FIRMWARE_TABLE=y
+CONFIG_UNION_FIND=y
+CONFIG_MIN_HEAP=y
+
+#
+# Kernel hacking
+#
+
+#
+# printk and dmesg options
+#
+CONFIG_PRINTK_TIME=y
+# CONFIG_PRINTK_CALLER is not set
+# CONFIG_STACKTRACE_BUILD_ID is not set
+CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
+CONFIG_CONSOLE_LOGLEVEL_QUIET=4
+CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+# CONFIG_DYNAMIC_DEBUG_CORE is not set
+CONFIG_SYMBOLIC_ERRNAME=y
+CONFIG_DEBUG_BUGVERBOSE=y
+# end of printk and dmesg options
+
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DEBUG_MISC=y
+
+#
+# Compile-time checks and compiler options
+#
+CONFIG_AS_HAS_NON_CONST_ULEB128=y
+CONFIG_DEBUG_INFO_NONE=y
+# CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set
+# CONFIG_DEBUG_INFO_DWARF4 is not set
+# CONFIG_DEBUG_INFO_DWARF5 is not set
+CONFIG_FRAME_WARN=2048
+CONFIG_STRIP_ASM_SYMS=y
+# CONFIG_READABLE_ASM is not set
+# CONFIG_HEADERS_INSTALL is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+CONFIG_SECTION_MISMATCH_WARN_ONLY=y
+CONFIG_OBJTOOL=y
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+# end of Compile-time checks and compiler options
+
+#
+# Generic Kernel Debugging Instruments
+#
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
+CONFIG_MAGIC_SYSRQ_SERIAL=y
+CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE=""
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_FS_ALLOW_ALL=y
+# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set
+# CONFIG_DEBUG_FS_ALLOW_NONE is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+CONFIG_ARCH_HAS_UBSAN=y
+# CONFIG_UBSAN is not set
+CONFIG_HAVE_ARCH_KCSAN=y
+CONFIG_HAVE_KCSAN_COMPILER=y
+# CONFIG_KCSAN is not set
+# end of Generic Kernel Debugging Instruments
+
+#
+# Networking Debugging
+#
+# CONFIG_NET_DEV_REFCNT_TRACKER is not set
+# CONFIG_NET_NS_REFCNT_TRACKER is not set
+# CONFIG_DEBUG_NET is not set
+# CONFIG_DEBUG_NET_SMALL_RTNL is not set
+# end of Networking Debugging
+
+#
+# Memory Debugging
+#
+CONFIG_PAGE_EXTENSION=y
+# CONFIG_DEBUG_PAGEALLOC is not set
+CONFIG_SLUB_DEBUG=y
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_PAGE_OWNER is not set
+# CONFIG_PAGE_TABLE_CHECK is not set
+CONFIG_PAGE_POISONING=y
+# CONFIG_DEBUG_PAGE_REF is not set
+CONFIG_DEBUG_RODATA_TEST=y
+CONFIG_ARCH_HAS_DEBUG_WX=y
+CONFIG_DEBUG_WX=y
+CONFIG_GENERIC_PTDUMP=y
+CONFIG_PTDUMP_CORE=y
+# CONFIG_PTDUMP_DEBUGFS is not set
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_PER_VMA_LOCK_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_SHRINKER_DEBUG is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+CONFIG_SCHED_STACK_END_CHECK=y
+CONFIG_ARCH_HAS_DEBUG_VM_PGTABLE=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_VM_PGTABLE is not set
+CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y
+CONFIG_DEBUG_VIRTUAL=y
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+CONFIG_ARCH_SUPPORTS_KMAP_LOCAL_FORCE_MAP=y
+# CONFIG_DEBUG_KMAP_LOCAL_FORCE_MAP is not set
+# CONFIG_MEM_ALLOC_PROFILING is not set
+CONFIG_HAVE_ARCH_KASAN=y
+CONFIG_HAVE_ARCH_KASAN_VMALLOC=y
+CONFIG_CC_HAS_KASAN_GENERIC=y
+CONFIG_CC_HAS_KASAN_SW_TAGS=y
+CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y
+# CONFIG_KASAN is not set
+CONFIG_HAVE_ARCH_KFENCE=y
+CONFIG_KFENCE=y
+CONFIG_KFENCE_SAMPLE_INTERVAL=100
+CONFIG_KFENCE_NUM_OBJECTS=255
+CONFIG_KFENCE_DEFERRABLE=y
+CONFIG_KFENCE_STRESS_TEST_FAULTS=0
+CONFIG_HAVE_ARCH_KMSAN=y
+# end of Memory Debugging
+
+# CONFIG_DEBUG_SHIRQ is not set
+
+#
+# Debug Oops, Lockups and Hangs
+#
+# CONFIG_PANIC_ON_OOPS is not set
+CONFIG_PANIC_ON_OOPS_VALUE=0
+CONFIG_PANIC_TIMEOUT=0
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_SOFTLOCKUP_DETECTOR=y
+CONFIG_SOFTLOCKUP_DETECTOR_INTR_STORM=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_HAVE_HARDLOCKUP_DETECTOR_BUDDY=y
+CONFIG_HARDLOCKUP_DETECTOR=y
+# CONFIG_HARDLOCKUP_DETECTOR_PREFER_BUDDY is not set
+CONFIG_HARDLOCKUP_DETECTOR_PERF=y
+# CONFIG_HARDLOCKUP_DETECTOR_BUDDY is not set
+# CONFIG_HARDLOCKUP_DETECTOR_ARCH is not set
+CONFIG_HARDLOCKUP_DETECTOR_COUNTS_HRTIMER=y
+CONFIG_HARDLOCKUP_CHECK_TIMESTAMP=y
+CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+CONFIG_WQ_WATCHDOG=y
+# CONFIG_WQ_CPU_INTENSIVE_REPORT is not set
+# CONFIG_TEST_LOCKUP is not set
+# end of Debug Oops, Lockups and Hangs
+
+#
+# Scheduler Debugging
+#
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHED_INFO=y
+# CONFIG_SCHEDSTATS is not set
+# end of Scheduler Debugging
+
+CONFIG_DEBUG_PREEMPT=y
+
+#
+# Lock Debugging (spinlocks, mutexes, etc...)
+#
+CONFIG_LOCK_DEBUGGING_SUPPORT=y
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+CONFIG_DEBUG_MUTEXES=y
+# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set
+# CONFIG_DEBUG_RWSEMS is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_LOCK_TORTURE_TEST is not set
+# CONFIG_WW_MUTEX_SELFTEST is not set
+# CONFIG_SCF_TORTURE_TEST is not set
+# CONFIG_CSD_LOCK_WAIT_DEBUG is not set
+# end of Lock Debugging (spinlocks, mutexes, etc...)
+
+# CONFIG_NMI_CHECK_CPU is not set
+# CONFIG_DEBUG_IRQFLAGS is not set
+CONFIG_STACKTRACE=y
+# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set
+# CONFIG_DEBUG_KOBJECT is not set
+
+#
+# Debug kernel data structures
+#
+CONFIG_DEBUG_LIST=y
+# CONFIG_DEBUG_PLIST is not set
+CONFIG_DEBUG_SG=y
+CONFIG_DEBUG_NOTIFIERS=y
+# CONFIG_DEBUG_CLOSURES is not set
+# CONFIG_DEBUG_MAPLE_TREE is not set
+# end of Debug kernel data structures
+
+#
+# RCU Debugging
+#
+# CONFIG_RCU_SCALE_TEST is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_REF_SCALE_TEST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+CONFIG_RCU_EXP_CPU_STALL_TIMEOUT=0
+# CONFIG_RCU_CPU_STALL_CPUTIME is not set
+# CONFIG_RCU_TRACE is not set
+# CONFIG_RCU_EQS_DEBUG is not set
+# end of RCU Debugging
+
+# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set
+# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_DEBUG_CGROUP_REF is not set
+CONFIG_USER_STACKTRACE_SUPPORT=y
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_RETHOOK=y
+CONFIG_RETHOOK=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_FREGS=y
+CONFIG_HAVE_FTRACE_GRAPH_FUNC=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y
+CONFIG_HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS=y
+CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS=y
+CONFIG_HAVE_FTRACE_REGS_HAVING_PT_REGS=y
+CONFIG_HAVE_DYNAMIC_FTRACE_NO_PATCHABLE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_FENTRY=y
+CONFIG_HAVE_OBJTOOL_MCOUNT=y
+CONFIG_HAVE_OBJTOOL_NOP_MCOUNT=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_HAVE_BUILDTIME_MCOUNT_SORT=y
+CONFIG_BUILDTIME_MCOUNT_SORT=y
+CONFIG_TRACER_MAX_TRACE=y
+CONFIG_TRACE_CLOCK=y
+CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_TRACING=y
+CONFIG_GENERIC_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_FTRACE=y
+# CONFIG_BOOTTIME_TRACING is not set
+CONFIG_FUNCTION_TRACER=y
+CONFIG_FUNCTION_GRAPH_TRACER=y
+CONFIG_FUNCTION_GRAPH_RETVAL=y
+CONFIG_FUNCTION_GRAPH_RETADDR=y
+CONFIG_DYNAMIC_FTRACE=y
+CONFIG_DYNAMIC_FTRACE_WITH_REGS=y
+CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS=y
+CONFIG_DYNAMIC_FTRACE_WITH_ARGS=y
+CONFIG_FPROBE=y
+# CONFIG_FUNCTION_PROFILER is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_PREEMPT_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_HWLAT_TRACER is not set
+CONFIG_OSNOISE_TRACER=y
+CONFIG_TIMERLAT_TRACER=y
+# CONFIG_MMIOTRACE is not set
+# CONFIG_FTRACE_SYSCALLS is not set
+# CONFIG_TRACER_SNAPSHOT is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_FPROBE_EVENTS=y
+CONFIG_KPROBE_EVENTS=y
+# CONFIG_KPROBE_EVENTS_ON_NOTRACE is not set
+CONFIG_UPROBE_EVENTS=y
+CONFIG_BPF_EVENTS=y
+CONFIG_DYNAMIC_EVENTS=y
+CONFIG_PROBE_EVENTS=y
+# CONFIG_BPF_KPROBE_OVERRIDE is not set
+CONFIG_FTRACE_MCOUNT_RECORD=y
+CONFIG_FTRACE_MCOUNT_USE_CC=y
+# CONFIG_SYNTH_EVENTS is not set
+# CONFIG_USER_EVENTS is not set
+# CONFIG_HIST_TRIGGERS is not set
+# CONFIG_TRACE_EVENT_INJECT is not set
+# CONFIG_TRACEPOINT_BENCHMARK is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_TRACE_EVAL_MAP_FILE is not set
+# CONFIG_FTRACE_RECORD_RECURSION is not set
+# CONFIG_FTRACE_VALIDATE_RCU_IS_WATCHING is not set
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_FTRACE_SORT_STARTUP_TEST is not set
+# CONFIG_RING_BUFFER_STARTUP_TEST is not set
+# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set
+# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
+# CONFIG_KPROBE_EVENT_GEN_TEST is not set
+# CONFIG_RV is not set
+# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_SAMPLE_FTRACE_DIRECT=y
+CONFIG_HAVE_SAMPLE_FTRACE_DIRECT_MULTI=y
+CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y
+# CONFIG_STRICT_DEVMEM is not set
+
+#
+# x86 Debugging
+#
+CONFIG_X86_VERBOSE_BOOTUP=y
+CONFIG_EARLY_PRINTK=y
+# CONFIG_EARLY_PRINTK_DBGP is not set
+# CONFIG_EARLY_PRINTK_USB_XDBC is not set
+# CONFIG_EFI_PGT_DUMP is not set
+# CONFIG_DEBUG_TLBFLUSH is not set
+CONFIG_HAVE_MMIOTRACE_SUPPORT=y
+# CONFIG_X86_DECODER_SELFTEST is not set
+# CONFIG_IO_DELAY_0X80 is not set
+# CONFIG_IO_DELAY_0XED is not set
+# CONFIG_IO_DELAY_UDELAY is not set
+CONFIG_IO_DELAY_NONE=y
+# CONFIG_DEBUG_BOOT_PARAMS is not set
+# CONFIG_CPA_DEBUG is not set
+# CONFIG_DEBUG_ENTRY is not set
+# CONFIG_DEBUG_NMI_SELFTEST is not set
+# CONFIG_X86_DEBUG_FPU is not set
+# CONFIG_PUNIT_ATOM_DEBUG is not set
+CONFIG_UNWINDER_ORC=y
+# CONFIG_UNWINDER_FRAME_POINTER is not set
+# end of x86 Debugging
+
+#
+# Kernel Testing and Coverage
+#
+# CONFIG_KUNIT is not set
+# CONFIG_NOTIFIER_ERROR_INJECTION is not set
+CONFIG_FUNCTION_ERROR_INJECTION=y
+# CONFIG_FAULT_INJECTION is not set
+CONFIG_ARCH_HAS_KCOV=y
+CONFIG_CC_HAS_SANCOV_TRACE_PC=y
+# CONFIG_KCOV is not set
+CONFIG_RUNTIME_TESTING_MENU=y
+# CONFIG_TEST_DHRY is not set
+# CONFIG_LKDTM is not set
+# CONFIG_TEST_MIN_HEAP is not set
+# CONFIG_TEST_DIV64 is not set
+# CONFIG_TEST_MULDIV64 is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_TEST_REF_TRACKER is not set
+# CONFIG_RBTREE_TEST is not set
+# CONFIG_REED_SOLOMON_TEST is not set
+# CONFIG_INTERVAL_TREE_TEST is not set
+# CONFIG_PERCPU_TEST is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_ASYNC_RAID6_TEST is not set
+# CONFIG_TEST_HEXDUMP is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_TEST_PRINTF is not set
+# CONFIG_TEST_SCANF is not set
+# CONFIG_TEST_BITMAP is not set
+# CONFIG_TEST_UUID is not set
+# CONFIG_TEST_XARRAY is not set
+# CONFIG_TEST_MAPLE_TREE is not set
+# CONFIG_TEST_RHASHTABLE is not set
+# CONFIG_TEST_IDA is not set
+# CONFIG_TEST_LKM is not set
+# CONFIG_TEST_BITOPS is not set
+# CONFIG_TEST_VMALLOC is not set
+CONFIG_TEST_BPF=m
+# CONFIG_TEST_BLACKHOLE_DEV is not set
+# CONFIG_FIND_BIT_BENCHMARK is not set
+# CONFIG_TEST_FIRMWARE is not set
+# CONFIG_TEST_SYSCTL is not set
+# CONFIG_TEST_UDELAY is not set
+# CONFIG_TEST_STATIC_KEYS is not set
+# CONFIG_TEST_KMOD is not set
+# CONFIG_TEST_KALLSYMS is not set
+# CONFIG_TEST_DEBUG_VIRTUAL is not set
+# CONFIG_TEST_MEMCAT_P is not set
+# CONFIG_TEST_MEMINIT is not set
+# CONFIG_TEST_HMM is not set
+# CONFIG_TEST_FREE_PAGES is not set
+# CONFIG_TEST_FPU is not set
+# CONFIG_TEST_CLOCKSOURCE_WATCHDOG is not set
+# CONFIG_TEST_OBJPOOL is not set
+CONFIG_ARCH_USE_MEMTEST=y
+# CONFIG_MEMTEST is not set
+# end of Kernel Testing and Coverage
+
+#
+# Rust hacking
+#
+# end of Rust hacking
+# end of Kernel hacking
diff --git a/sys-kernel/vanilla-kernel/files/linux-6.14/1191-bcachefs-cherry-pick-updates-from-master-4594600.patch b/sys-kernel/vanilla-kernel/files/linux-6.14/1191-bcachefs-cherry-pick-updates-from-master-4594600.patch
new file mode 100644
index 0000000..d33f5f9
--- /dev/null
+++ b/sys-kernel/vanilla-kernel/files/linux-6.14/1191-bcachefs-cherry-pick-updates-from-master-4594600.patch
@@ -0,0 +1,15091 @@
+From b78adf1d9502a4e206b13f7348370eb483a5edcc Mon Sep 17 00:00:00 2001
+From: Alexander Miroshnichenko <alex@millerson.name>
+Date: Thu, 27 Mar 2025 14:52:55 +0300
+Subject: [PATCH] bcachefs: cherry-pick updates from master 4594600
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Alexander Miroshnichenko <alex@millerson.name>
+---
+ .../bcachefs/SubmittingPatches.rst            |  43 +-
+ .../filesystems/bcachefs/casefolding.rst      |  90 ++
+ Documentation/filesystems/bcachefs/index.rst  |  20 +-
+ block/blk-core.c                              |  19 +-
+ fs/bcachefs/Kconfig                           |   2 +-
+ fs/bcachefs/Makefile                          |   3 +-
+ fs/bcachefs/alloc_background.c                | 190 ++--
+ fs/bcachefs/alloc_background.h                |   2 +-
+ fs/bcachefs/alloc_foreground.c                |  31 +-
+ fs/bcachefs/alloc_foreground.h                |  19 +-
+ fs/bcachefs/alloc_types.h                     |   2 +
+ fs/bcachefs/backpointers.c                    | 151 ++-
+ fs/bcachefs/backpointers.h                    |  26 +-
+ fs/bcachefs/bcachefs.h                        |  20 +-
+ fs/bcachefs/bcachefs_format.h                 |  16 +-
+ fs/bcachefs/bcachefs_ioctl.h                  |  29 +-
+ fs/bcachefs/bkey.h                            |   1 +
+ fs/bcachefs/btree_cache.c                     |   1 +
+ fs/bcachefs/btree_gc.c                        |  18 +-
+ fs/bcachefs/btree_io.c                        | 259 +++++-
+ fs/bcachefs/btree_io.h                        |   4 +
+ fs/bcachefs/btree_iter.c                      |  14 -
+ fs/bcachefs/btree_iter.h                      |   9 +-
+ fs/bcachefs/btree_locking.c                   |   8 +-
+ fs/bcachefs/btree_node_scan.c                 |  29 +-
+ fs/bcachefs/btree_trans_commit.c              | 120 +--
+ fs/bcachefs/btree_types.h                     |  13 +
+ fs/bcachefs/btree_update.c                    |   5 +-
+ fs/bcachefs/btree_update.h                    |   2 +
+ fs/bcachefs/btree_update_interior.c           | 150 +--
+ fs/bcachefs/btree_update_interior.h           |   7 +
+ fs/bcachefs/buckets.c                         |  80 +-
+ fs/bcachefs/buckets.h                         |  31 +-
+ fs/bcachefs/buckets_types.h                   |  27 +
+ fs/bcachefs/chardev.c                         |  38 +-
+ fs/bcachefs/checksum.c                        |  25 +-
+ fs/bcachefs/checksum.h                        |   2 +
+ fs/bcachefs/compress.c                        |  65 +-
+ fs/bcachefs/data_update.c                     | 237 +++--
+ fs/bcachefs/data_update.h                     |  17 +-
+ fs/bcachefs/debug.c                           |  34 +-
+ fs/bcachefs/dirent.c                          | 274 +++++-
+ fs/bcachefs/dirent.h                          |  17 +-
+ fs/bcachefs/dirent_format.h                   |  20 +-
+ fs/bcachefs/disk_accounting.c                 |  11 +
+ fs/bcachefs/disk_accounting.h                 |  26 +-
+ fs/bcachefs/disk_accounting_format.h          |  90 +-
+ fs/bcachefs/ec.c                              | 482 ++++------
+ fs/bcachefs/ec.h                              |  46 +-
+ fs/bcachefs/ec_types.h                        |  12 +-
+ fs/bcachefs/errcode.h                         |  65 +-
+ fs/bcachefs/error.c                           |  88 +-
+ fs/bcachefs/error.h                           |  57 +-
+ fs/bcachefs/extents.c                         | 245 +++--
+ fs/bcachefs/extents.h                         |  24 +-
+ fs/bcachefs/extents_format.h                  |  24 +-
+ fs/bcachefs/extents_types.h                   |  11 +-
+ fs/bcachefs/eytzinger.c                       |  76 +-
+ fs/bcachefs/eytzinger.h                       |  95 +-
+ fs/bcachefs/fs-io-buffered.c                  |  40 +-
+ fs/bcachefs/fs-io-direct.c                    |  20 +-
+ fs/bcachefs/fs-ioctl.c                        |  30 +-
+ fs/bcachefs/fs-ioctl.h                        |  20 +-
+ fs/bcachefs/fs.c                              | 139 +--
+ fs/bcachefs/fsck.c                            | 231 +----
+ fs/bcachefs/inode.c                           |  24 +-
+ fs/bcachefs/inode.h                           |   1 +
+ fs/bcachefs/inode_format.h                    |   3 +-
+ fs/bcachefs/io_misc.c                         |   3 +-
+ fs/bcachefs/io_read.c                         | 856 +++++++++++-------
+ fs/bcachefs/io_read.h                         |  97 +-
+ fs/bcachefs/io_write.c                        | 438 ++++-----
+ fs/bcachefs/io_write.h                        |  38 +-
+ fs/bcachefs/io_write_types.h                  |   2 +-
+ fs/bcachefs/journal.c                         | 202 +++--
+ fs/bcachefs/journal.h                         |  42 +-
+ fs/bcachefs/journal_io.c                      |  99 +-
+ fs/bcachefs/journal_reclaim.c                 |  10 +-
+ fs/bcachefs/journal_seq_blacklist.c           |   7 +-
+ fs/bcachefs/journal_types.h                   |  37 +-
+ fs/bcachefs/lru.c                             | 100 +-
+ fs/bcachefs/lru.h                             |  22 +-
+ fs/bcachefs/lru_format.h                      |   6 +-
+ fs/bcachefs/migrate.c                         |  26 +-
+ fs/bcachefs/move.c                            | 505 +++++++----
+ fs/bcachefs/move_types.h                      |  20 +-
+ fs/bcachefs/movinggc.c                        |  15 +-
+ fs/bcachefs/{fs-common.c => namei.c}          | 210 ++++-
+ fs/bcachefs/{fs-common.h => namei.h}          |  31 +-
+ fs/bcachefs/opts.c                            | 117 ++-
+ fs/bcachefs/opts.h                            |  70 +-
+ fs/bcachefs/progress.c                        |  63 ++
+ fs/bcachefs/progress.h                        |  29 +
+ fs/bcachefs/rebalance.c                       |  52 +-
+ fs/bcachefs/recovery.c                        |   4 +-
+ fs/bcachefs/recovery_passes_types.h           |   2 +-
+ fs/bcachefs/reflink.c                         |  23 +-
+ fs/bcachefs/sb-counters.c                     |  90 +-
+ fs/bcachefs/sb-counters.h                     |   4 +
+ fs/bcachefs/sb-counters_format.h              |  32 +-
+ fs/bcachefs/sb-downgrade.c                    |   8 +-
+ fs/bcachefs/sb-errors_format.h                |   6 +-
+ fs/bcachefs/sb-members.h                      |  16 +-
+ fs/bcachefs/sb-members_format.h               |   1 +
+ fs/bcachefs/snapshot.c                        |   7 +-
+ fs/bcachefs/snapshot.h                        |   1 +
+ fs/bcachefs/str_hash.c                        |   2 +-
+ fs/bcachefs/str_hash.h                        |  12 +-
+ fs/bcachefs/super-io.c                        |  92 +-
+ fs/bcachefs/super-io.h                        |  10 +-
+ fs/bcachefs/super.c                           | 141 ++-
+ fs/bcachefs/super.h                           |   2 +
+ fs/bcachefs/super_types.h                     |   8 +-
+ fs/bcachefs/sysfs.c                           | 257 ++++--
+ fs/bcachefs/sysfs.h                           |   5 +-
+ fs/bcachefs/time_stats.c                      |  22 +-
+ fs/bcachefs/time_stats.h                      |   1 +
+ fs/bcachefs/trace.h                           | 106 +--
+ fs/bcachefs/util.c                            | 231 ++++-
+ fs/bcachefs/util.h                            |  16 +-
+ fs/bcachefs/xattr.c                           |   2 +-
+ 121 files changed, 5238 insertions(+), 2990 deletions(-)
+ create mode 100644 Documentation/filesystems/bcachefs/casefolding.rst
+ rename fs/bcachefs/{fs-common.c => namei.c} (73%)
+ rename fs/bcachefs/{fs-common.h => namei.h} (61%)
+ create mode 100644 fs/bcachefs/progress.c
+ create mode 100644 fs/bcachefs/progress.h
+
+diff --git a/Documentation/filesystems/bcachefs/SubmittingPatches.rst b/Documentation/filesystems/bcachefs/SubmittingPatches.rst
+index 026b12ae0d6a..a455f9cfd15c 100644
+--- a/Documentation/filesystems/bcachefs/SubmittingPatches.rst
++++ b/Documentation/filesystems/bcachefs/SubmittingPatches.rst
+@@ -1,8 +1,13 @@
+-Submitting patches to bcachefs:
+-===============================
++Submitting patches to bcachefs
++==============================
++
++Here are suggestions for submitting patches to bcachefs subsystem.
++
++Submission checklist
++--------------------
+ 
+ Patches must be tested before being submitted, either with the xfstests suite
+-[0], or the full bcachefs test suite in ktest [1], depending on what's being
++[0]_, or the full bcachefs test suite in ktest [1]_, depending on what's being
+ touched. Note that ktest wraps xfstests and will be an easier method to running
+ it for most users; it includes single-command wrappers for all the mainstream
+ in-kernel local filesystems.
+@@ -26,21 +31,21 @@ considered out of date), but try not to deviate too much without reason.
+ Focus on writing code that reads well and is organized well; code should be
+ aesthetically pleasing.
+ 
+-CI:
+-===
++CI
++--
+ 
+ Instead of running your tests locally, when running the full test suite it's
+ prefereable to let a server farm do it in parallel, and then have the results
+ in a nice test dashboard (which can tell you which failures are new, and
+ presents results in a git log view, avoiding the need for most bisecting).
+ 
+-That exists [2], and community members may request an account. If you work for
++That exists [2]_, and community members may request an account. If you work for
+ a big tech company, you'll need to help out with server costs to get access -
+ but the CI is not restricted to running bcachefs tests: it runs any ktest test
+ (which generally makes it easy to wrap other tests that can run in qemu).
+ 
+-Other things to think about:
+-============================
++Other things to think about
++---------------------------
+ 
+ - How will we debug this code? Is there sufficient introspection to diagnose
+   when something starts acting wonky on a user machine?
+@@ -79,20 +84,22 @@ Other things to think about:
+   tested? (Automated tests exists but aren't in the CI, due to the hassle of
+   disk image management; coordinate to have them run.)
+ 
+-Mailing list, IRC:
+-==================
++Mailing list, IRC
++-----------------
+ 
+-Patches should hit the list [3], but much discussion and code review happens on
+-IRC as well [4]; many people appreciate the more conversational approach and
+-quicker feedback.
++Patches should hit the list [3]_, but much discussion and code review happens
++on IRC as well [4]_; many people appreciate the more conversational approach
++and quicker feedback.
+ 
+ Additionally, we have a lively user community doing excellent QA work, which
+ exists primarily on IRC. Please make use of that resource; user feedback is
+ important for any nontrivial feature, and documenting it in commit messages
+ would be a good idea.
+ 
+-[0]: git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git
+-[1]: https://evilpiepirate.org/git/ktest.git/
+-[2]: https://evilpiepirate.org/~testdashboard/ci/
+-[3]: linux-bcachefs@vger.kernel.org
+-[4]: irc.oftc.net#bcache, #bcachefs-dev
++.. rubric:: References
++
++.. [0] git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git
++.. [1] https://evilpiepirate.org/git/ktest.git/
++.. [2] https://evilpiepirate.org/~testdashboard/ci/
++.. [3] linux-bcachefs@vger.kernel.org
++.. [4] irc.oftc.net#bcache, #bcachefs-dev
+diff --git a/Documentation/filesystems/bcachefs/casefolding.rst b/Documentation/filesystems/bcachefs/casefolding.rst
+new file mode 100644
+index 000000000000..ba5de97d155f
+--- /dev/null
++++ b/Documentation/filesystems/bcachefs/casefolding.rst
+@@ -0,0 +1,90 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++Casefolding
++===========
++
++bcachefs has support for case-insensitive file and directory
++lookups using the regular `chattr +F` (`S_CASEFOLD`, `FS_CASEFOLD_FL`)
++casefolding attributes.
++
++The main usecase for casefolding is compatibility with software written
++against other filesystems that rely on casefolded lookups
++(eg. NTFS and Wine/Proton).
++Taking advantage of file-system level casefolding can lead to great
++loading time gains in many applications and games.
++
++Casefolding support requires a kernel with the `CONFIG_UNICODE` enabled.
++Once a directory has been flagged for casefolding, a feature bit
++is enabled on the superblock which marks the filesystem as using
++casefolding.
++When the feature bit for casefolding is enabled, it is no longer possible
++to mount that filesystem on kernels without `CONFIG_UNICODE` enabled.
++
++On the lookup/query side: casefolding is implemented by allocating a new
++string of `BCH_NAME_MAX` length using the `utf8_casefold` function to
++casefold the query string.
++
++On the dirent side: casefolding is implemented by ensuring the `bkey`'s
++hash is made from the casefolded string and storing the cached casefolded
++name with the regular name in the dirent.
++
++The structure looks like this:
++
++* Regular:    [dirent data][regular name][nul][nul]...
++* Casefolded: [dirent data][reg len][cf len][regular name][casefolded name][nul][nul]...
++
++(Do note, the number of NULs here is merely for illustration; their count can
++vary per-key, and they may not even be present if the key is aligned to
++`sizeof(u64)`.)
++
++This is efficient as it means that for all file lookups that require casefolding,
++it has identical performance to a regular lookup:
++a hash comparison and a `memcmp` of the name.
++
++Rationale
++---------
++
++Several designs were considered for this system:
++One was to introduce a dirent_v2, however that would be painful especially as
++the hash system only has support for a single key type. This would also need
++`BCH_NAME_MAX` to change between versions, and a new feature bit.
++
++Another option was to store without the two lengths, and just take the length of
++the regular name and casefolded name contiguously / 2 as the length. This would
++assume that the regular length == casefolded length, but that could potentially
++not be true, if the uppercase unicode glyph had a different UTF-8 encoding than
++the lowercase unicode glyph.
++It would be possible to disregard the casefold cache for those cases, but it was
++decided to simply encode the two string lengths in the key to avoid random
++performance issues if this edgecase was ever hit.
++
++The option settled on was to use a free-bit in d_type to mark a dirent as having
++a casefold cache, and then treat the first 4 bytes the name block as lengths.
++You can see this in the `d_cf_name_block` member of union in `bch_dirent`.
++
++The feature bit was used to allow casefolding support to be enabled for the majority
++of users, but some allow users who have no need for the feature to still use bcachefs as
++`CONFIG_UNICODE` can increase the kernel side a significant amount due to the tables used,
++which may be decider between using bcachefs for eg. embedded platforms.
++
++Other filesystems like ext4 and f2fs have a super-block level option for casefolding
++encoding, but bcachefs currently does not provide this. ext4 and f2fs do not expose
++any encodings than a single UTF-8 version. When future encodings are desirable,
++they will be added trivially using the opts mechanism.
++
++dentry/dcache considerations
++----------------------------
++
++Currently, in casefolded directories, bcachefs (like other filesystems) will not cache
++negative dentry's.
++
++This is because currently doing so presents a problem in the following scenario:
++
++ - Lookup file "blAH" in a casefolded directory
++ - Creation of file "BLAH" in a casefolded directory
++ - Lookup file "blAH" in a casefolded directory
++
++This would fail if negative dentry's were cached.
++
++This is slightly suboptimal, but could be fixed in future with some vfs work.
++
+diff --git a/Documentation/filesystems/bcachefs/index.rst b/Documentation/filesystems/bcachefs/index.rst
+index 7db4d7ceab58..3864d0ae89c1 100644
+--- a/Documentation/filesystems/bcachefs/index.rst
++++ b/Documentation/filesystems/bcachefs/index.rst
+@@ -4,10 +4,28 @@
+ bcachefs Documentation
+ ======================
+ 
++Subsystem-specific development process notes
++--------------------------------------------
++
++Development notes specific to bcachefs. These are intended to supplement
++:doc:`general kernel development handbook </process/index>`.
++
+ .. toctree::
+-   :maxdepth: 2
++   :maxdepth: 1
+    :numbered:
+ 
+    CodingStyle
+    SubmittingPatches
++
++Filesystem implementation
++-------------------------
++
++Documentation for filesystem features and their implementation details.
++At this moment, only a few of these are described here.
++
++.. toctree::
++   :maxdepth: 1
++   :numbered:
++
++   casefolding
+    errorcodes
+diff --git a/block/blk-core.c b/block/blk-core.c
+index d6c4fa3943b5..7b1103eb877d 100644
+--- a/block/blk-core.c
++++ b/block/blk-core.c
+@@ -793,20 +793,21 @@ void submit_bio_noacct(struct bio *bio)
+ 			goto end_io;
+ 	}
+ 
++	if (WARN_ON_ONCE((bio->bi_opf & REQ_PREFLUSH) &&
++			 bio_op(bio) != REQ_OP_WRITE &&
++			 bio_op(bio) != REQ_OP_ZONE_APPEND))
++		goto end_io;
++
+ 	/*
+ 	 * Filter flush bio's early so that bio based drivers without flush
+ 	 * support don't have to worry about them.
+ 	 */
+-	if (op_is_flush(bio->bi_opf)) {
+-		if (WARN_ON_ONCE(bio_op(bio) != REQ_OP_WRITE &&
+-				 bio_op(bio) != REQ_OP_ZONE_APPEND))
++	if (op_is_flush(bio->bi_opf) &&
++	    !bdev_write_cache(bdev)) {
++		bio->bi_opf &= ~(REQ_PREFLUSH | REQ_FUA);
++		if (!bio_sectors(bio)) {
++			status = BLK_STS_OK;
+ 			goto end_io;
+-		if (!bdev_write_cache(bdev)) {
+-			bio->bi_opf &= ~(REQ_PREFLUSH | REQ_FUA);
+-			if (!bio_sectors(bio)) {
+-				status = BLK_STS_OK;
+-				goto end_io;
+-			}
+ 		}
+ 	}
+ 
+diff --git a/fs/bcachefs/Kconfig b/fs/bcachefs/Kconfig
+index fc7efd0a7525..c9798750202d 100644
+--- a/fs/bcachefs/Kconfig
++++ b/fs/bcachefs/Kconfig
+@@ -16,7 +16,7 @@ config BCACHEFS_FS
+ 	select ZSTD_COMPRESS
+ 	select ZSTD_DECOMPRESS
+ 	select CRYPTO
+-	select CRYPTO_SHA256
++	select CRYPTO_LIB_SHA256
+ 	select CRYPTO_CHACHA20
+ 	select CRYPTO_POLY1305
+ 	select KEYS
+diff --git a/fs/bcachefs/Makefile b/fs/bcachefs/Makefile
+index d2689388d5e8..9af65079374f 100644
+--- a/fs/bcachefs/Makefile
++++ b/fs/bcachefs/Makefile
+@@ -41,7 +41,6 @@ bcachefs-y		:=	\
+ 	extent_update.o		\
+ 	eytzinger.o		\
+ 	fs.o			\
+-	fs-common.o		\
+ 	fs-ioctl.o		\
+ 	fs-io.o			\
+ 	fs-io-buffered.o	\
+@@ -64,9 +63,11 @@ bcachefs-y		:=	\
+ 	migrate.o		\
+ 	move.o			\
+ 	movinggc.o		\
++	namei.o			\
+ 	nocow_locking.o		\
+ 	opts.o			\
+ 	printbuf.o		\
++	progress.o		\
+ 	quota.o			\
+ 	rebalance.o		\
+ 	rcu_pending.o		\
+diff --git a/fs/bcachefs/alloc_background.c b/fs/bcachefs/alloc_background.c
+index 3ea809990ef1..5fb396be9127 100644
+--- a/fs/bcachefs/alloc_background.c
++++ b/fs/bcachefs/alloc_background.c
+@@ -232,7 +232,7 @@ int bch2_alloc_v3_validate(struct bch_fs *c, struct bkey_s_c k,
+ 	int ret = 0;
+ 
+ 	bkey_fsck_err_on(bch2_alloc_unpack_v3(&u, k),
+-			 c, alloc_v2_unpack_error,
++			 c, alloc_v3_unpack_error,
+ 			 "unpack error");
+ fsck_err:
+ 	return ret;
+@@ -777,14 +777,12 @@ static inline int bch2_dev_data_type_accounting_mod(struct btree_trans *trans, s
+ 						    s64 delta_sectors,
+ 						    s64 delta_fragmented, unsigned flags)
+ {
+-	struct disk_accounting_pos acc = {
+-		.type = BCH_DISK_ACCOUNTING_dev_data_type,
+-		.dev_data_type.dev		= ca->dev_idx,
+-		.dev_data_type.data_type	= data_type,
+-	};
+ 	s64 d[3] = { delta_buckets, delta_sectors, delta_fragmented };
+ 
+-	return bch2_disk_accounting_mod(trans, &acc, d, 3, flags & BTREE_TRIGGER_gc);
++	return bch2_disk_accounting_mod2(trans, flags & BTREE_TRIGGER_gc,
++					 d, dev_data_type,
++					 .dev		= ca->dev_idx,
++					 .data_type	= data_type);
+ }
+ 
+ int bch2_alloc_key_to_dev_counters(struct btree_trans *trans, struct bch_dev *ca,
+@@ -837,7 +835,7 @@ int bch2_trigger_alloc(struct btree_trans *trans,
+ 
+ 	struct bch_dev *ca = bch2_dev_bucket_tryget(c, new.k->p);
+ 	if (!ca)
+-		return -EIO;
++		return -BCH_ERR_trigger_alloc;
+ 
+ 	struct bch_alloc_v4 old_a_convert;
+ 	const struct bch_alloc_v4 *old_a = bch2_alloc_to_v4(old, &old_a_convert);
+@@ -871,6 +869,9 @@ int bch2_trigger_alloc(struct btree_trans *trans,
+ 		if (data_type_is_empty(new_a->data_type) &&
+ 		    BCH_ALLOC_V4_NEED_INC_GEN(new_a) &&
+ 		    !bch2_bucket_is_open_safe(c, new.k->p.inode, new.k->p.offset)) {
++			if (new_a->oldest_gen == new_a->gen &&
++			    !bch2_bucket_sectors_total(*new_a))
++				new_a->oldest_gen++;
+ 			new_a->gen++;
+ 			SET_BCH_ALLOC_V4_NEED_INC_GEN(new_a, false);
+ 			alloc_data_type_set(new_a, new_a->data_type);
+@@ -889,26 +890,20 @@ int bch2_trigger_alloc(struct btree_trans *trans,
+ 		    !new_a->io_time[READ])
+ 			new_a->io_time[READ] = bch2_current_io_time(c, READ);
+ 
+-		u64 old_lru = alloc_lru_idx_read(*old_a);
+-		u64 new_lru = alloc_lru_idx_read(*new_a);
+-		if (old_lru != new_lru) {
+-			ret = bch2_lru_change(trans, new.k->p.inode,
+-					      bucket_to_u64(new.k->p),
+-					      old_lru, new_lru);
+-			if (ret)
+-				goto err;
+-		}
++		ret = bch2_lru_change(trans, new.k->p.inode,
++				      bucket_to_u64(new.k->p),
++				      alloc_lru_idx_read(*old_a),
++				      alloc_lru_idx_read(*new_a));
++		if (ret)
++			goto err;
+ 
+-		old_lru = alloc_lru_idx_fragmentation(*old_a, ca);
+-		new_lru = alloc_lru_idx_fragmentation(*new_a, ca);
+-		if (old_lru != new_lru) {
+-			ret = bch2_lru_change(trans,
+-					BCH_LRU_FRAGMENTATION_START,
+-					bucket_to_u64(new.k->p),
+-					old_lru, new_lru);
+-			if (ret)
+-				goto err;
+-		}
++		ret = bch2_lru_change(trans,
++				      BCH_LRU_BUCKET_FRAGMENTATION,
++				      bucket_to_u64(new.k->p),
++				      alloc_lru_idx_fragmentation(*old_a, ca),
++				      alloc_lru_idx_fragmentation(*new_a, ca));
++		if (ret)
++			goto err;
+ 
+ 		if (old_a->gen != new_a->gen) {
+ 			ret = bch2_bucket_gen_update(trans, new.k->p, new_a->gen);
+@@ -1034,7 +1029,7 @@ int bch2_trigger_alloc(struct btree_trans *trans,
+ invalid_bucket:
+ 	bch2_fs_inconsistent(c, "reference to invalid bucket\n  %s",
+ 			     (bch2_bkey_val_to_text(&buf, c, new.s_c), buf.buf));
+-	ret = -EIO;
++	ret = -BCH_ERR_trigger_alloc;
+ 	goto err;
+ }
+ 
+@@ -1705,7 +1700,8 @@ static int bch2_check_alloc_to_lru_ref(struct btree_trans *trans,
+ 
+ 	u64 lru_idx = alloc_lru_idx_fragmentation(*a, ca);
+ 	if (lru_idx) {
+-		ret = bch2_lru_check_set(trans, BCH_LRU_FRAGMENTATION_START,
++		ret = bch2_lru_check_set(trans, BCH_LRU_BUCKET_FRAGMENTATION,
++					 bucket_to_u64(alloc_k.k->p),
+ 					 lru_idx, alloc_k, last_flushed);
+ 		if (ret)
+ 			goto err;
+@@ -1735,7 +1731,9 @@ static int bch2_check_alloc_to_lru_ref(struct btree_trans *trans,
+ 		a = &a_mut->v;
+ 	}
+ 
+-	ret = bch2_lru_check_set(trans, alloc_k.k->p.inode, a->io_time[READ],
++	ret = bch2_lru_check_set(trans, alloc_k.k->p.inode,
++				 bucket_to_u64(alloc_k.k->p),
++				 a->io_time[READ],
+ 				 alloc_k, last_flushed);
+ 	if (ret)
+ 		goto err;
+@@ -1757,7 +1755,8 @@ int bch2_check_alloc_to_lru_refs(struct bch_fs *c)
+ 		for_each_btree_key_commit(trans, iter, BTREE_ID_alloc,
+ 				POS_MIN, BTREE_ITER_prefetch, k,
+ 				NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
+-			bch2_check_alloc_to_lru_ref(trans, &iter, &last_flushed)));
++			bch2_check_alloc_to_lru_ref(trans, &iter, &last_flushed))) ?:
++		bch2_check_stripe_to_lru_refs(c);
+ 
+ 	bch2_bkey_buf_exit(&last_flushed, c);
+ 	bch_err_fn(c, ret);
+@@ -1805,6 +1804,19 @@ struct discard_buckets_state {
+ 	u64		discarded;
+ };
+ 
++/*
++ * This is needed because discard is both a filesystem option and a device
++ * option, and mount options are supposed to apply to that mount and not be
++ * persisted, i.e. if it's set as a mount option we can't propagate it to the
++ * device.
++ */
++static inline bool discard_opt_enabled(struct bch_fs *c, struct bch_dev *ca)
++{
++	return test_bit(BCH_FS_discard_mount_opt_set, &c->flags)
++		? c->opts.discard
++		: ca->mi.discard;
++}
++
+ static int bch2_discard_one_bucket(struct btree_trans *trans,
+ 				   struct bch_dev *ca,
+ 				   struct btree_iter *need_discard_iter,
+@@ -1868,7 +1880,7 @@ static int bch2_discard_one_bucket(struct btree_trans *trans,
+ 		s->discarded++;
+ 		*discard_pos_done = iter.pos;
+ 
+-		if (ca->mi.discard && !c->opts.nochanges) {
++		if (discard_opt_enabled(c, ca) && !c->opts.nochanges) {
+ 			/*
+ 			 * This works without any other locks because this is the only
+ 			 * thread that removes items from the need_discard tree
+@@ -1897,7 +1909,10 @@ static int bch2_discard_one_bucket(struct btree_trans *trans,
+ 	if (ret)
+ 		goto out;
+ 
+-	count_event(c, bucket_discard);
++	if (!fastpath)
++		count_event(c, bucket_discard);
++	else
++		count_event(c, bucket_discard_fast);
+ out:
+ fsck_err:
+ 	if (discard_locked)
+@@ -2055,16 +2070,71 @@ static void bch2_discard_one_bucket_fast(struct bch_dev *ca, u64 bucket)
+ 	bch2_write_ref_put(c, BCH_WRITE_REF_discard_fast);
+ }
+ 
++static int invalidate_one_bp(struct btree_trans *trans,
++			     struct bch_dev *ca,
++			     struct bkey_s_c_backpointer bp,
++			     struct bkey_buf *last_flushed)
++{
++	struct btree_iter extent_iter;
++	struct bkey_s_c extent_k =
++		bch2_backpointer_get_key(trans, bp, &extent_iter, 0, last_flushed);
++	int ret = bkey_err(extent_k);
++	if (ret)
++		return ret;
++
++	struct bkey_i *n =
++		bch2_bkey_make_mut(trans, &extent_iter, &extent_k,
++				   BTREE_UPDATE_internal_snapshot_node);
++	ret = PTR_ERR_OR_ZERO(n);
++	if (ret)
++		goto err;
++
++	bch2_bkey_drop_device(bkey_i_to_s(n), ca->dev_idx);
++err:
++	bch2_trans_iter_exit(trans, &extent_iter);
++	return ret;
++}
++
++static int invalidate_one_bucket_by_bps(struct btree_trans *trans,
++					struct bch_dev *ca,
++					struct bpos bucket,
++					u8 gen,
++					struct bkey_buf *last_flushed)
++{
++	struct bpos bp_start	= bucket_pos_to_bp_start(ca,	bucket);
++	struct bpos bp_end	= bucket_pos_to_bp_end(ca,	bucket);
++
++	return for_each_btree_key_max_commit(trans, iter, BTREE_ID_backpointers,
++				      bp_start, bp_end, 0, k,
++				      NULL, NULL,
++				      BCH_WATERMARK_btree|
++				      BCH_TRANS_COMMIT_no_enospc, ({
++		if (k.k->type != KEY_TYPE_backpointer)
++			continue;
++
++		struct bkey_s_c_backpointer bp = bkey_s_c_to_backpointer(k);
++
++		if (bp.v->bucket_gen != gen)
++			continue;
++
++		/* filter out bps with gens that don't match */
++
++		invalidate_one_bp(trans, ca, bp, last_flushed);
++	}));
++}
++
++noinline_for_stack
+ static int invalidate_one_bucket(struct btree_trans *trans,
++				 struct bch_dev *ca,
+ 				 struct btree_iter *lru_iter,
+ 				 struct bkey_s_c lru_k,
++				 struct bkey_buf *last_flushed,
+ 				 s64 *nr_to_invalidate)
+ {
+ 	struct bch_fs *c = trans->c;
+-	struct bkey_i_alloc_v4 *a = NULL;
+ 	struct printbuf buf = PRINTBUF;
+ 	struct bpos bucket = u64_to_bucket(lru_k.k->p.offset);
+-	unsigned cached_sectors;
++	struct btree_iter alloc_iter = {};
+ 	int ret = 0;
+ 
+ 	if (*nr_to_invalidate <= 0)
+@@ -2081,35 +2151,37 @@ static int invalidate_one_bucket(struct btree_trans *trans,
+ 	if (bch2_bucket_is_open_safe(c, bucket.inode, bucket.offset))
+ 		return 0;
+ 
+-	a = bch2_trans_start_alloc_update(trans, bucket, BTREE_TRIGGER_bucket_invalidate);
+-	ret = PTR_ERR_OR_ZERO(a);
++	struct bkey_s_c alloc_k = bch2_bkey_get_iter(trans, &alloc_iter,
++						     BTREE_ID_alloc, bucket,
++						     BTREE_ITER_cached);
++	ret = bkey_err(alloc_k);
+ 	if (ret)
+-		goto out;
++		return ret;
++
++	struct bch_alloc_v4 a_convert;
++	const struct bch_alloc_v4 *a = bch2_alloc_to_v4(alloc_k, &a_convert);
+ 
+ 	/* We expect harmless races here due to the btree write buffer: */
+-	if (lru_pos_time(lru_iter->pos) != alloc_lru_idx_read(a->v))
++	if (lru_pos_time(lru_iter->pos) != alloc_lru_idx_read(*a))
+ 		goto out;
+ 
+-	BUG_ON(a->v.data_type != BCH_DATA_cached);
+-	BUG_ON(a->v.dirty_sectors);
++	/*
++	 * Impossible since alloc_lru_idx_read() only returns nonzero if the
++	 * bucket is supposed to be on the cached bucket LRU (i.e.
++	 * BCH_DATA_cached)
++	 *
++	 * bch2_lru_validate() also disallows lru keys with lru_pos_time() == 0
++	 */
++	BUG_ON(a->data_type != BCH_DATA_cached);
++	BUG_ON(a->dirty_sectors);
+ 
+-	if (!a->v.cached_sectors)
++	if (!a->cached_sectors)
+ 		bch_err(c, "invalidating empty bucket, confused");
+ 
+-	cached_sectors = a->v.cached_sectors;
++	unsigned cached_sectors = a->cached_sectors;
++	u8 gen = a->gen;
+ 
+-	SET_BCH_ALLOC_V4_NEED_INC_GEN(&a->v, false);
+-	a->v.gen++;
+-	a->v.data_type		= 0;
+-	a->v.dirty_sectors	= 0;
+-	a->v.stripe_sectors	= 0;
+-	a->v.cached_sectors	= 0;
+-	a->v.io_time[READ]	= bch2_current_io_time(c, READ);
+-	a->v.io_time[WRITE]	= bch2_current_io_time(c, WRITE);
+-
+-	ret = bch2_trans_commit(trans, NULL, NULL,
+-				BCH_WATERMARK_btree|
+-				BCH_TRANS_COMMIT_no_enospc);
++	ret = invalidate_one_bucket_by_bps(trans, ca, bucket, gen, last_flushed);
+ 	if (ret)
+ 		goto out;
+ 
+@@ -2117,6 +2189,7 @@ static int invalidate_one_bucket(struct btree_trans *trans,
+ 	--*nr_to_invalidate;
+ out:
+ fsck_err:
++	bch2_trans_iter_exit(trans, &alloc_iter);
+ 	printbuf_exit(&buf);
+ 	return ret;
+ }
+@@ -2143,6 +2216,10 @@ static void bch2_do_invalidates_work(struct work_struct *work)
+ 	struct btree_trans *trans = bch2_trans_get(c);
+ 	int ret = 0;
+ 
++	struct bkey_buf last_flushed;
++	bch2_bkey_buf_init(&last_flushed);
++	bkey_init(&last_flushed.k->k);
++
+ 	ret = bch2_btree_write_buffer_tryflush(trans);
+ 	if (ret)
+ 		goto err;
+@@ -2167,7 +2244,7 @@ static void bch2_do_invalidates_work(struct work_struct *work)
+ 		if (!k.k)
+ 			break;
+ 
+-		ret = invalidate_one_bucket(trans, &iter, k, &nr_to_invalidate);
++		ret = invalidate_one_bucket(trans, ca, &iter, k, &last_flushed, &nr_to_invalidate);
+ restart_err:
+ 		if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
+ 			continue;
+@@ -2180,6 +2257,7 @@ static void bch2_do_invalidates_work(struct work_struct *work)
+ err:
+ 	bch2_trans_put(trans);
+ 	percpu_ref_put(&ca->io_ref);
++	bch2_bkey_buf_exit(&last_flushed, c);
+ 	bch2_write_ref_put(c, BCH_WRITE_REF_invalidate);
+ }
+ 
+diff --git a/fs/bcachefs/alloc_background.h b/fs/bcachefs/alloc_background.h
+index de25ba4ee94b..c556ccaffe89 100644
+--- a/fs/bcachefs/alloc_background.h
++++ b/fs/bcachefs/alloc_background.h
+@@ -131,7 +131,7 @@ static inline enum bch_data_type alloc_data_type(struct bch_alloc_v4 a,
+ 	if (a.stripe)
+ 		return data_type == BCH_DATA_parity ? data_type : BCH_DATA_stripe;
+ 	if (bch2_bucket_sectors_dirty(a))
+-		return data_type;
++		return bucket_data_type(data_type);
+ 	if (a.cached_sectors)
+ 		return BCH_DATA_cached;
+ 	if (BCH_ALLOC_V4_NEED_DISCARD(&a))
+diff --git a/fs/bcachefs/alloc_foreground.c b/fs/bcachefs/alloc_foreground.c
+index 5a781fb4c794..0cac65347a5d 100644
+--- a/fs/bcachefs/alloc_foreground.c
++++ b/fs/bcachefs/alloc_foreground.c
+@@ -127,14 +127,14 @@ void __bch2_open_bucket_put(struct bch_fs *c, struct open_bucket *ob)
+ 
+ void bch2_open_bucket_write_error(struct bch_fs *c,
+ 				  struct open_buckets *obs,
+-				  unsigned dev)
++				  unsigned dev, int err)
+ {
+ 	struct open_bucket *ob;
+ 	unsigned i;
+ 
+ 	open_bucket_for_each(c, obs, ob, i)
+ 		if (ob->dev == dev && ob->ec)
+-			bch2_ec_bucket_cancel(c, ob);
++			bch2_ec_bucket_cancel(c, ob, err);
+ }
+ 
+ static struct open_bucket *bch2_open_bucket_alloc(struct bch_fs *c)
+@@ -179,23 +179,6 @@ static void open_bucket_free_unused(struct bch_fs *c, struct open_bucket *ob)
+ 	closure_wake_up(&c->freelist_wait);
+ }
+ 
+-static inline unsigned open_buckets_reserved(enum bch_watermark watermark)
+-{
+-	switch (watermark) {
+-	case BCH_WATERMARK_interior_updates:
+-		return 0;
+-	case BCH_WATERMARK_reclaim:
+-		return OPEN_BUCKETS_COUNT / 6;
+-	case BCH_WATERMARK_btree:
+-	case BCH_WATERMARK_btree_copygc:
+-		return OPEN_BUCKETS_COUNT / 4;
+-	case BCH_WATERMARK_copygc:
+-		return OPEN_BUCKETS_COUNT / 3;
+-	default:
+-		return OPEN_BUCKETS_COUNT / 2;
+-	}
+-}
+-
+ static inline bool may_alloc_bucket(struct bch_fs *c,
+ 				    struct bpos bucket,
+ 				    struct bucket_alloc_state *s)
+@@ -239,7 +222,7 @@ static struct open_bucket *__try_alloc_bucket(struct bch_fs *c, struct bch_dev *
+ 
+ 	spin_lock(&c->freelist_lock);
+ 
+-	if (unlikely(c->open_buckets_nr_free <= open_buckets_reserved(watermark))) {
++	if (unlikely(c->open_buckets_nr_free <= bch2_open_buckets_reserved(watermark))) {
+ 		if (cl)
+ 			closure_wait(&c->open_buckets_wait, cl);
+ 
+@@ -648,7 +631,7 @@ static inline void bch2_dev_stripe_increment_inlined(struct bch_dev *ca,
+ 			       struct bch_dev_usage *usage)
+ {
+ 	u64 *v = stripe->next_alloc + ca->dev_idx;
+-	u64 free_space = dev_buckets_available(ca, BCH_WATERMARK_normal);
++	u64 free_space = __dev_buckets_available(ca, *usage, BCH_WATERMARK_normal);
+ 	u64 free_space_inv = free_space
+ 		? div64_u64(1ULL << 48, free_space)
+ 		: 1ULL << 48;
+@@ -728,7 +711,7 @@ int bch2_bucket_alloc_set_trans(struct btree_trans *trans,
+ 
+ 		struct bch_dev_usage usage;
+ 		struct open_bucket *ob = bch2_bucket_alloc_trans(trans, ca, watermark, data_type,
+-						     cl, flags & BCH_WRITE_ALLOC_NOWAIT, &usage);
++						     cl, flags & BCH_WRITE_alloc_nowait, &usage);
+ 		if (!IS_ERR(ob))
+ 			bch2_dev_stripe_increment_inlined(ca, stripe, &usage);
+ 		bch2_dev_put(ca);
+@@ -1336,7 +1319,7 @@ int bch2_alloc_sectors_start_trans(struct btree_trans *trans,
+ 	if (wp->data_type != BCH_DATA_user)
+ 		have_cache = true;
+ 
+-	if (target && !(flags & BCH_WRITE_ONLY_SPECIFIED_DEVS)) {
++	if (target && !(flags & BCH_WRITE_only_specified_devs)) {
+ 		ret = open_bucket_add_buckets(trans, &ptrs, wp, devs_have,
+ 					      target, erasure_code,
+ 					      nr_replicas, &nr_effective,
+@@ -1426,7 +1409,7 @@ int bch2_alloc_sectors_start_trans(struct btree_trans *trans,
+ 	if (cl && bch2_err_matches(ret, BCH_ERR_open_buckets_empty))
+ 		ret = -BCH_ERR_bucket_alloc_blocked;
+ 
+-	if (cl && !(flags & BCH_WRITE_ALLOC_NOWAIT) &&
++	if (cl && !(flags & BCH_WRITE_alloc_nowait) &&
+ 	    bch2_err_matches(ret, BCH_ERR_freelist_empty))
+ 		ret = -BCH_ERR_bucket_alloc_blocked;
+ 
+diff --git a/fs/bcachefs/alloc_foreground.h b/fs/bcachefs/alloc_foreground.h
+index f25481a0d1a0..69ec6a012898 100644
+--- a/fs/bcachefs/alloc_foreground.h
++++ b/fs/bcachefs/alloc_foreground.h
+@@ -33,6 +33,23 @@ static inline struct bch_dev *ob_dev(struct bch_fs *c, struct open_bucket *ob)
+ 	return bch2_dev_have_ref(c, ob->dev);
+ }
+ 
++static inline unsigned bch2_open_buckets_reserved(enum bch_watermark watermark)
++{
++	switch (watermark) {
++	case BCH_WATERMARK_interior_updates:
++		return 0;
++	case BCH_WATERMARK_reclaim:
++		return OPEN_BUCKETS_COUNT / 6;
++	case BCH_WATERMARK_btree:
++	case BCH_WATERMARK_btree_copygc:
++		return OPEN_BUCKETS_COUNT / 4;
++	case BCH_WATERMARK_copygc:
++		return OPEN_BUCKETS_COUNT / 3;
++	default:
++		return OPEN_BUCKETS_COUNT / 2;
++	}
++}
++
+ struct open_bucket *bch2_bucket_alloc(struct bch_fs *, struct bch_dev *,
+ 				      enum bch_watermark, enum bch_data_type,
+ 				      struct closure *);
+@@ -65,7 +82,7 @@ static inline struct open_bucket *ec_open_bucket(struct bch_fs *c,
+ }
+ 
+ void bch2_open_bucket_write_error(struct bch_fs *,
+-			struct open_buckets *, unsigned);
++			struct open_buckets *, unsigned, int);
+ 
+ void __bch2_open_bucket_put(struct bch_fs *, struct open_bucket *);
+ 
+diff --git a/fs/bcachefs/alloc_types.h b/fs/bcachefs/alloc_types.h
+index 4aa8ee026cb8..8f79f46c2a78 100644
+--- a/fs/bcachefs/alloc_types.h
++++ b/fs/bcachefs/alloc_types.h
+@@ -90,6 +90,7 @@ struct dev_stripe_state {
+ 	x(stopped)			\
+ 	x(waiting_io)			\
+ 	x(waiting_work)			\
++	x(runnable)			\
+ 	x(running)
+ 
+ enum write_point_state {
+@@ -125,6 +126,7 @@ struct write_point {
+ 		enum write_point_state	state;
+ 		u64			last_state_change;
+ 		u64			time[WRITE_POINT_STATE_NR];
++		u64			last_runtime;
+ 	} __aligned(SMP_CACHE_BYTES);
+ };
+ 
+diff --git a/fs/bcachefs/backpointers.c b/fs/bcachefs/backpointers.c
+index ebeb6a5ff9d2..20c497f0c2cb 100644
+--- a/fs/bcachefs/backpointers.c
++++ b/fs/bcachefs/backpointers.c
+@@ -11,6 +11,7 @@
+ #include "checksum.h"
+ #include "disk_accounting.h"
+ #include "error.h"
++#include "progress.h"
+ 
+ #include <linux/mm.h>
+ 
+@@ -49,6 +50,8 @@ void bch2_backpointer_to_text(struct printbuf *out, struct bch_fs *c, struct bke
+ 	}
+ 
+ 	bch2_btree_id_level_to_text(out, bp.v->btree_id, bp.v->level);
++	prt_str(out, " data_type=");
++	bch2_prt_data_type(out, bp.v->data_type);
+ 	prt_printf(out, " suboffset=%u len=%u gen=%u pos=",
+ 		   (u32) bp.k->p.offset & ~(~0U << MAX_EXTENT_COMPRESS_RATIO_SHIFT),
+ 		   bp.v->bucket_len,
+@@ -244,27 +247,31 @@ struct bkey_s_c bch2_backpointer_get_key(struct btree_trans *trans,
+ 	if (unlikely(bp.v->btree_id >= btree_id_nr_alive(c)))
+ 		return bkey_s_c_null;
+ 
+-	if (likely(!bp.v->level)) {
+-		bch2_trans_node_iter_init(trans, iter,
+-					  bp.v->btree_id,
+-					  bp.v->pos,
+-					  0, 0,
+-					  iter_flags);
+-		struct bkey_s_c k = bch2_btree_iter_peek_slot(iter);
+-		if (bkey_err(k)) {
+-			bch2_trans_iter_exit(trans, iter);
+-			return k;
+-		}
++	bch2_trans_node_iter_init(trans, iter,
++				  bp.v->btree_id,
++				  bp.v->pos,
++				  0,
++				  bp.v->level,
++				  iter_flags);
++	struct bkey_s_c k = bch2_btree_iter_peek_slot(iter);
++	if (bkey_err(k)) {
++		bch2_trans_iter_exit(trans, iter);
++		return k;
++	}
+ 
+-		if (k.k &&
+-		    extent_matches_bp(c, bp.v->btree_id, bp.v->level, k, bp))
+-			return k;
++	if (k.k &&
++	    extent_matches_bp(c, bp.v->btree_id, bp.v->level, k, bp))
++		return k;
+ 
+-		bch2_trans_iter_exit(trans, iter);
++	bch2_trans_iter_exit(trans, iter);
++
++	if (!bp.v->level) {
+ 		int ret = backpointer_target_not_found(trans, bp, k, last_flushed);
+ 		return ret ? bkey_s_c_err(ret) : bkey_s_c_null;
+ 	} else {
+ 		struct btree *b = bch2_backpointer_get_node(trans, bp, iter, last_flushed);
++		if (b == ERR_PTR(-BCH_ERR_backpointer_to_overwritten_btree_node))
++			return bkey_s_c_null;
+ 		if (IS_ERR_OR_NULL(b))
+ 			return ((struct bkey_s_c) { .k = ERR_CAST(b) });
+ 
+@@ -514,6 +521,22 @@ static int check_bp_exists(struct btree_trans *trans,
+ 	if (!other_extent.k)
+ 		goto missing;
+ 
++	rcu_read_lock();
++	struct bch_dev *ca = bch2_dev_rcu_noerror(c, bp->k.p.inode);
++	if (ca) {
++		struct bkey_ptrs_c other_extent_ptrs = bch2_bkey_ptrs_c(other_extent);
++		bkey_for_each_ptr(other_extent_ptrs, ptr)
++			if (ptr->dev == bp->k.p.inode &&
++			    dev_ptr_stale_rcu(ca, ptr)) {
++				ret = drop_dev_and_update(trans, other_bp.v->btree_id,
++							  other_extent, bp->k.p.inode);
++				if (ret)
++					goto err;
++				goto out;
++			}
++	}
++	rcu_read_unlock();
++
+ 	if (bch2_extents_match(orig_k, other_extent)) {
+ 		printbuf_reset(&buf);
+ 		prt_printf(&buf, "duplicate versions of same extent, deleting smaller\n  ");
+@@ -590,9 +613,6 @@ static int check_extent_to_backpointers(struct btree_trans *trans,
+ 	struct extent_ptr_decoded p;
+ 
+ 	bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
+-		if (p.ptr.cached)
+-			continue;
+-
+ 		if (p.ptr.dev == BCH_SB_MEMBER_INVALID)
+ 			continue;
+ 
+@@ -600,9 +620,11 @@ static int check_extent_to_backpointers(struct btree_trans *trans,
+ 		struct bch_dev *ca = bch2_dev_rcu_noerror(c, p.ptr.dev);
+ 		bool check = ca && test_bit(PTR_BUCKET_NR(ca, &p.ptr), ca->bucket_backpointer_mismatches);
+ 		bool empty = ca && test_bit(PTR_BUCKET_NR(ca, &p.ptr), ca->bucket_backpointer_empty);
++
++		bool stale = p.ptr.cached && (!ca || dev_ptr_stale_rcu(ca, &p.ptr));
+ 		rcu_read_unlock();
+ 
+-		if (check || empty) {
++		if ((check || empty) && !stale) {
+ 			struct bkey_i_backpointer bp;
+ 			bch2_extent_ptr_to_bp(c, btree, level, k, p, entry, &bp);
+ 
+@@ -715,71 +737,6 @@ static int bch2_get_btree_in_memory_pos(struct btree_trans *trans,
+ 	return ret;
+ }
+ 
+-struct progress_indicator_state {
+-	unsigned long		next_print;
+-	u64			nodes_seen;
+-	u64			nodes_total;
+-	struct btree		*last_node;
+-};
+-
+-static inline void progress_init(struct progress_indicator_state *s,
+-				 struct bch_fs *c,
+-				 u64 btree_id_mask)
+-{
+-	memset(s, 0, sizeof(*s));
+-
+-	s->next_print = jiffies + HZ * 10;
+-
+-	for (unsigned i = 0; i < BTREE_ID_NR; i++) {
+-		if (!(btree_id_mask & BIT_ULL(i)))
+-			continue;
+-
+-		struct disk_accounting_pos acc = {
+-			.type		= BCH_DISK_ACCOUNTING_btree,
+-			.btree.id	= i,
+-		};
+-
+-		u64 v;
+-		bch2_accounting_mem_read(c, disk_accounting_pos_to_bpos(&acc), &v, 1);
+-		s->nodes_total += div64_ul(v, btree_sectors(c));
+-	}
+-}
+-
+-static inline bool progress_update_p(struct progress_indicator_state *s)
+-{
+-	bool ret = time_after_eq(jiffies, s->next_print);
+-
+-	if (ret)
+-		s->next_print = jiffies + HZ * 10;
+-	return ret;
+-}
+-
+-static void progress_update_iter(struct btree_trans *trans,
+-				 struct progress_indicator_state *s,
+-				 struct btree_iter *iter,
+-				 const char *msg)
+-{
+-	struct bch_fs *c = trans->c;
+-	struct btree *b = path_l(btree_iter_path(trans, iter))->b;
+-
+-	s->nodes_seen += b != s->last_node;
+-	s->last_node = b;
+-
+-	if (progress_update_p(s)) {
+-		struct printbuf buf = PRINTBUF;
+-		unsigned percent = s->nodes_total
+-			? div64_u64(s->nodes_seen * 100, s->nodes_total)
+-			: 0;
+-
+-		prt_printf(&buf, "%s: %d%%, done %llu/%llu nodes, at ",
+-			   msg, percent, s->nodes_seen, s->nodes_total);
+-		bch2_bbpos_to_text(&buf, BBPOS(iter->btree_id, iter->pos));
+-
+-		bch_info(c, "%s", buf.buf);
+-		printbuf_exit(&buf);
+-	}
+-}
+-
+ static int bch2_check_extents_to_backpointers_pass(struct btree_trans *trans,
+ 						   struct extents_to_bp_state *s)
+ {
+@@ -787,7 +744,7 @@ static int bch2_check_extents_to_backpointers_pass(struct btree_trans *trans,
+ 	struct progress_indicator_state progress;
+ 	int ret = 0;
+ 
+-	progress_init(&progress, trans->c, BIT_ULL(BTREE_ID_extents)|BIT_ULL(BTREE_ID_reflink));
++	bch2_progress_init(&progress, trans->c, BIT_ULL(BTREE_ID_extents)|BIT_ULL(BTREE_ID_reflink));
+ 
+ 	for (enum btree_id btree_id = 0;
+ 	     btree_id < btree_id_nr_alive(c);
+@@ -806,7 +763,7 @@ static int bch2_check_extents_to_backpointers_pass(struct btree_trans *trans,
+ 						  BTREE_ITER_prefetch);
+ 
+ 			ret = for_each_btree_key_continue(trans, iter, 0, k, ({
+-				progress_update_iter(trans, &progress, &iter, "extents_to_backpointers");
++				bch2_progress_update_iter(trans, &progress, &iter, "extents_to_backpointers");
+ 				check_extent_to_backpointers(trans, s, btree_id, level, k) ?:
+ 				bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc);
+ 			}));
+@@ -827,7 +784,7 @@ enum alloc_sector_counter {
+ 	ALLOC_SECTORS_NR
+ };
+ 
+-static enum alloc_sector_counter data_type_to_alloc_counter(enum bch_data_type t)
++static int data_type_to_alloc_counter(enum bch_data_type t)
+ {
+ 	switch (t) {
+ 	case BCH_DATA_btree:
+@@ -836,9 +793,10 @@ static enum alloc_sector_counter data_type_to_alloc_counter(enum bch_data_type t
+ 	case BCH_DATA_cached:
+ 		return ALLOC_cached;
+ 	case BCH_DATA_stripe:
++	case BCH_DATA_parity:
+ 		return ALLOC_stripe;
+ 	default:
+-		BUG();
++		return -1;
+ 	}
+ }
+ 
+@@ -889,7 +847,11 @@ static int check_bucket_backpointer_mismatch(struct btree_trans *trans, struct b
+ 		if (bp.v->bucket_gen != a->gen)
+ 			continue;
+ 
+-		sectors[data_type_to_alloc_counter(bp.v->data_type)] += bp.v->bucket_len;
++		int alloc_counter = data_type_to_alloc_counter(bp.v->data_type);
++		if (alloc_counter < 0)
++			continue;
++
++		sectors[alloc_counter] += bp.v->bucket_len;
+ 	};
+ 	bch2_trans_iter_exit(trans, &iter);
+ 	if (ret)
+@@ -901,9 +863,8 @@ static int check_bucket_backpointer_mismatch(struct btree_trans *trans, struct b
+ 			goto err;
+ 	}
+ 
+-	/* Cached pointers don't have backpointers: */
+-
+ 	if (sectors[ALLOC_dirty]  != a->dirty_sectors ||
++	    sectors[ALLOC_cached] != a->cached_sectors ||
+ 	    sectors[ALLOC_stripe] != a->stripe_sectors) {
+ 		if (c->sb.version_upgrade_complete >= bcachefs_metadata_version_backpointer_bucket_gen) {
+ 			ret = bch2_backpointers_maybe_flush(trans, alloc_k, last_flushed);
+@@ -912,6 +873,7 @@ static int check_bucket_backpointer_mismatch(struct btree_trans *trans, struct b
+ 		}
+ 
+ 		if (sectors[ALLOC_dirty]  > a->dirty_sectors ||
++		    sectors[ALLOC_cached] > a->cached_sectors ||
+ 		    sectors[ALLOC_stripe] > a->stripe_sectors) {
+ 			ret = check_bucket_backpointers_to_extents(trans, ca, alloc_k.k->p) ?:
+ 				-BCH_ERR_transaction_restart_nested;
+@@ -919,7 +881,8 @@ static int check_bucket_backpointer_mismatch(struct btree_trans *trans, struct b
+ 		}
+ 
+ 		if (!sectors[ALLOC_dirty] &&
+-		    !sectors[ALLOC_stripe])
++		    !sectors[ALLOC_stripe] &&
++		    !sectors[ALLOC_cached])
+ 			__set_bit(alloc_k.k->p.offset, ca->bucket_backpointer_empty);
+ 		else
+ 			__set_bit(alloc_k.k->p.offset, ca->bucket_backpointer_mismatches);
+@@ -1206,11 +1169,11 @@ static int bch2_check_backpointers_to_extents_pass(struct btree_trans *trans,
+ 
+ 	bch2_bkey_buf_init(&last_flushed);
+ 	bkey_init(&last_flushed.k->k);
+-	progress_init(&progress, trans->c, BIT_ULL(BTREE_ID_backpointers));
++	bch2_progress_init(&progress, trans->c, BIT_ULL(BTREE_ID_backpointers));
+ 
+ 	int ret = for_each_btree_key(trans, iter, BTREE_ID_backpointers,
+ 				     POS_MIN, BTREE_ITER_prefetch, k, ({
+-			progress_update_iter(trans, &progress, &iter, "backpointers_to_extents");
++			bch2_progress_update_iter(trans, &progress, &iter, "backpointers_to_extents");
+ 			check_one_backpointer(trans, start, end, k, &last_flushed);
+ 	}));
+ 
+diff --git a/fs/bcachefs/backpointers.h b/fs/bcachefs/backpointers.h
+index 060dad1521ee..16575dbc5736 100644
+--- a/fs/bcachefs/backpointers.h
++++ b/fs/bcachefs/backpointers.h
+@@ -1,6 +1,6 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-#ifndef _BCACHEFS_BACKPOINTERS_BACKGROUND_H
+-#define _BCACHEFS_BACKPOINTERS_BACKGROUND_H
++#ifndef _BCACHEFS_BACKPOINTERS_H
++#define _BCACHEFS_BACKPOINTERS_H
+ 
+ #include "btree_cache.h"
+ #include "btree_iter.h"
+@@ -123,7 +123,12 @@ static inline enum bch_data_type bch2_bkey_ptr_data_type(struct bkey_s_c k,
+ 		return BCH_DATA_btree;
+ 	case KEY_TYPE_extent:
+ 	case KEY_TYPE_reflink_v:
+-		return p.has_ec ? BCH_DATA_stripe : BCH_DATA_user;
++		if (p.has_ec)
++			return BCH_DATA_stripe;
++		if (p.ptr.cached)
++			return BCH_DATA_cached;
++		else
++			return BCH_DATA_user;
+ 	case KEY_TYPE_stripe: {
+ 		const struct bch_extent_ptr *ptr = &entry->ptr;
+ 		struct bkey_s_c_stripe s = bkey_s_c_to_stripe(k);
+@@ -147,7 +152,20 @@ static inline void bch2_extent_ptr_to_bp(struct bch_fs *c,
+ 			   struct bkey_i_backpointer *bp)
+ {
+ 	bkey_backpointer_init(&bp->k_i);
+-	bp->k.p = POS(p.ptr.dev, ((u64) p.ptr.offset << MAX_EXTENT_COMPRESS_RATIO_SHIFT) + p.crc.offset);
++	bp->k.p.inode = p.ptr.dev;
++
++	if (k.k->type != KEY_TYPE_stripe)
++		bp->k.p.offset = ((u64) p.ptr.offset << MAX_EXTENT_COMPRESS_RATIO_SHIFT) + p.crc.offset;
++	else {
++		/*
++		 * Put stripe backpointers where they won't collide with the
++		 * extent backpointers within the stripe:
++		 */
++		struct bkey_s_c_stripe s = bkey_s_c_to_stripe(k);
++		bp->k.p.offset = ((u64) (p.ptr.offset + le16_to_cpu(s.v->sectors)) <<
++				  MAX_EXTENT_COMPRESS_RATIO_SHIFT) - 1;
++	}
++
+ 	bp->v	= (struct bch_backpointer) {
+ 		.btree_id	= btree_id,
+ 		.level		= level,
+diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h
+index 161cf2f05d2a..f52311017aee 100644
+--- a/fs/bcachefs/bcachefs.h
++++ b/fs/bcachefs/bcachefs.h
+@@ -203,6 +203,7 @@
+ #include <linux/types.h>
+ #include <linux/workqueue.h>
+ #include <linux/zstd.h>
++#include <linux/unicode.h>
+ 
+ #include "bcachefs_format.h"
+ #include "btree_journal_iter_types.h"
+@@ -444,6 +445,7 @@ BCH_DEBUG_PARAMS_DEBUG()
+ 	x(btree_node_sort)			\
+ 	x(btree_node_read)			\
+ 	x(btree_node_read_done)			\
++	x(btree_node_write)			\
+ 	x(btree_interior_update_foreground)	\
+ 	x(btree_interior_update_total)		\
+ 	x(btree_gc)				\
+@@ -456,6 +458,7 @@ BCH_DEBUG_PARAMS_DEBUG()
+ 	x(blocked_journal_low_on_space)		\
+ 	x(blocked_journal_low_on_pin)		\
+ 	x(blocked_journal_max_in_flight)	\
++	x(blocked_journal_max_open)		\
+ 	x(blocked_key_cache_flush)		\
+ 	x(blocked_allocate)			\
+ 	x(blocked_allocate_open_bucket)		\
+@@ -533,6 +536,7 @@ struct bch_dev {
+ 	 */
+ 	struct bch_member_cpu	mi;
+ 	atomic64_t		errors[BCH_MEMBER_ERROR_NR];
++	unsigned long		write_errors_start;
+ 
+ 	__uuid_t		uuid;
+ 	char			name[BDEVNAME_SIZE];
+@@ -623,7 +627,8 @@ struct bch_dev {
+ 	x(topology_error)		\
+ 	x(errors_fixed)			\
+ 	x(errors_not_fixed)		\
+-	x(no_invalid_checks)
++	x(no_invalid_checks)		\
++	x(discard_mount_opt_set)	\
+ 
+ enum bch_fs_flags {
+ #define x(n)		BCH_FS_##n,
+@@ -687,7 +692,8 @@ struct btree_trans_buf {
+ 	x(gc_gens)							\
+ 	x(snapshot_delete_pagecache)					\
+ 	x(sysfs)							\
+-	x(btree_write_buffer)
++	x(btree_write_buffer)						\
++	x(btree_node_scrub)
+ 
+ enum bch_write_ref {
+ #define x(n) BCH_WRITE_REF_##n,
+@@ -696,6 +702,8 @@ enum bch_write_ref {
+ 	BCH_WRITE_REF_NR,
+ };
+ 
++#define BCH_FS_DEFAULT_UTF8_ENCODING UNICODE_AGE(12, 1, 0)
++
+ struct bch_fs {
+ 	struct closure		cl;
+ 
+@@ -780,6 +788,9 @@ struct bch_fs {
+ 		u64		btrees_lost_data;
+ 	}			sb;
+ 
++#ifdef CONFIG_UNICODE
++	struct unicode_map	*cf_encoding;
++#endif
+ 
+ 	struct bch_sb_handle	disk_sb;
+ 
+@@ -969,7 +980,6 @@ struct bch_fs {
+ 	mempool_t		compress_workspace[BCH_COMPRESSION_OPT_NR];
+ 	size_t			zstd_workspace_size;
+ 
+-	struct crypto_shash	*sha256;
+ 	struct crypto_sync_skcipher *chacha20;
+ 	struct crypto_shash	*poly1305;
+ 
+@@ -993,15 +1003,11 @@ struct bch_fs {
+ 	wait_queue_head_t	copygc_running_wq;
+ 
+ 	/* STRIPES: */
+-	GENRADIX(struct stripe) stripes;
+ 	GENRADIX(struct gc_stripe) gc_stripes;
+ 
+ 	struct hlist_head	ec_stripes_new[32];
+ 	spinlock_t		ec_stripes_new_lock;
+ 
+-	ec_stripes_heap		ec_stripes_heap;
+-	struct mutex		ec_stripes_heap_lock;
+-
+ 	/* ERASURE CODING */
+ 	struct list_head	ec_stripe_head_list;
+ 	struct mutex		ec_stripe_head_lock;
+diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h
+index f70f0108401f..e96d87767020 100644
+--- a/fs/bcachefs/bcachefs_format.h
++++ b/fs/bcachefs/bcachefs_format.h
+@@ -686,7 +686,12 @@ struct bch_sb_field_ext {
+ 	x(inode_depth,			BCH_VERSION(1, 17))		\
+ 	x(persistent_inode_cursors,	BCH_VERSION(1, 18))		\
+ 	x(autofix_errors,		BCH_VERSION(1, 19))		\
+-	x(directory_size,		BCH_VERSION(1, 20))
++	x(directory_size,		BCH_VERSION(1, 20))		\
++	x(cached_backpointers,		BCH_VERSION(1, 21))		\
++	x(stripe_backpointers,		BCH_VERSION(1, 22))		\
++	x(stripe_lru,			BCH_VERSION(1, 23))		\
++	x(casefolding,			BCH_VERSION(1, 24))		\
++	x(extent_flags,			BCH_VERSION(1, 25))
+ 
+ enum bcachefs_metadata_version {
+ 	bcachefs_metadata_version_min = 9,
+@@ -837,6 +842,7 @@ LE64_BITMASK(BCH_SB_SHARD_INUMS,	struct bch_sb, flags[3], 28, 29);
+ LE64_BITMASK(BCH_SB_INODES_USE_KEY_CACHE,struct bch_sb, flags[3], 29, 30);
+ LE64_BITMASK(BCH_SB_JOURNAL_FLUSH_DELAY,struct bch_sb, flags[3], 30, 62);
+ LE64_BITMASK(BCH_SB_JOURNAL_FLUSH_DISABLED,struct bch_sb, flags[3], 62, 63);
++/* one free bit */
+ LE64_BITMASK(BCH_SB_JOURNAL_RECLAIM_DELAY,struct bch_sb, flags[4], 0, 32);
+ LE64_BITMASK(BCH_SB_JOURNAL_TRANSACTION_NAMES,struct bch_sb, flags[4], 32, 33);
+ LE64_BITMASK(BCH_SB_NOCOW,		struct bch_sb, flags[4], 33, 34);
+@@ -855,6 +861,8 @@ LE64_BITMASK(BCH_SB_VERSION_INCOMPAT,	struct bch_sb, flags[5], 32, 48);
+ LE64_BITMASK(BCH_SB_VERSION_INCOMPAT_ALLOWED,
+ 					struct bch_sb, flags[5], 48, 64);
+ LE64_BITMASK(BCH_SB_SHARD_INUMS_NBITS,	struct bch_sb, flags[6],  0,  4);
++LE64_BITMASK(BCH_SB_WRITE_ERROR_TIMEOUT,struct bch_sb, flags[6],  4, 14);
++LE64_BITMASK(BCH_SB_CSUM_ERR_RETRY_NR,	struct bch_sb, flags[6], 14, 20);
+ 
+ static inline __u64 BCH_SB_COMPRESSION_TYPE(const struct bch_sb *sb)
+ {
+@@ -908,7 +916,8 @@ static inline void SET_BCH_SB_BACKGROUND_COMPRESSION_TYPE(struct bch_sb *sb, __u
+ 	x(journal_no_flush,		16)	\
+ 	x(alloc_v2,			17)	\
+ 	x(extents_across_btree_nodes,	18)	\
+-	x(incompat_version_field,	19)
++	x(incompat_version_field,	19)	\
++	x(casefolding,			20)
+ 
+ #define BCH_SB_FEATURES_ALWAYS				\
+ 	(BIT_ULL(BCH_FEATURE_new_extent_overwrite)|	\
+@@ -922,7 +931,8 @@ static inline void SET_BCH_SB_BACKGROUND_COMPRESSION_TYPE(struct bch_sb *sb, __u
+ 	 BIT_ULL(BCH_FEATURE_new_siphash)|		\
+ 	 BIT_ULL(BCH_FEATURE_btree_ptr_v2)|		\
+ 	 BIT_ULL(BCH_FEATURE_new_varint)|		\
+-	 BIT_ULL(BCH_FEATURE_journal_no_flush))
++	 BIT_ULL(BCH_FEATURE_journal_no_flush)|		\
++	 BIT_ULL(BCH_FEATURE_incompat_version_field))
+ 
+ enum bch_sb_feature {
+ #define x(f, n) BCH_FEATURE_##f,
+diff --git a/fs/bcachefs/bcachefs_ioctl.h b/fs/bcachefs/bcachefs_ioctl.h
+index 3c23bdf788ce..52594e925eb7 100644
+--- a/fs/bcachefs/bcachefs_ioctl.h
++++ b/fs/bcachefs/bcachefs_ioctl.h
+@@ -87,6 +87,7 @@ struct bch_ioctl_incremental {
+ #define BCH_IOCTL_FSCK_OFFLINE	_IOW(0xbc,	19,  struct bch_ioctl_fsck_offline)
+ #define BCH_IOCTL_FSCK_ONLINE	_IOW(0xbc,	20,  struct bch_ioctl_fsck_online)
+ #define BCH_IOCTL_QUERY_ACCOUNTING _IOW(0xbc,	21,  struct bch_ioctl_query_accounting)
++#define BCH_IOCTL_QUERY_COUNTERS _IOW(0xbc,	21,  struct bch_ioctl_query_counters)
+ 
+ /* ioctl below act on a particular file, not the filesystem as a whole: */
+ 
+@@ -213,6 +214,10 @@ struct bch_ioctl_data {
+ 	struct bpos		end_pos;
+ 
+ 	union {
++	struct {
++		__u32		dev;
++		__u32		data_types;
++	}			scrub;
+ 	struct {
+ 		__u32		dev;
+ 		__u32		pad;
+@@ -229,6 +234,11 @@ enum bch_data_event {
+ 	BCH_DATA_EVENT_NR	= 1,
+ };
+ 
++enum data_progress_data_type_special {
++	DATA_PROGRESS_DATA_TYPE_phys	= 254,
++	DATA_PROGRESS_DATA_TYPE_done	= 255,
++};
++
+ struct bch_ioctl_data_progress {
+ 	__u8			data_type;
+ 	__u8			btree_id;
+@@ -237,11 +247,19 @@ struct bch_ioctl_data_progress {
+ 
+ 	__u64			sectors_done;
+ 	__u64			sectors_total;
++	__u64			sectors_error_corrected;
++	__u64			sectors_error_uncorrected;
+ } __packed __aligned(8);
+ 
++enum bch_ioctl_data_event_ret {
++	BCH_IOCTL_DATA_EVENT_RET_done		= 1,
++	BCH_IOCTL_DATA_EVENT_RET_device_offline	= 2,
++};
++
+ struct bch_ioctl_data_event {
+ 	__u8			type;
+-	__u8			pad[7];
++	__u8			ret;
++	__u8			pad[6];
+ 	union {
+ 	struct bch_ioctl_data_progress p;
+ 	__u64			pad2[15];
+@@ -443,4 +461,13 @@ struct bch_ioctl_query_accounting {
+ 	struct bkey_i_accounting accounting[];
+ };
+ 
++#define BCH_IOCTL_QUERY_COUNTERS_MOUNT	(1 << 0)
++
++struct bch_ioctl_query_counters {
++	__u16			nr;
++	__u16			flags;
++	__u32			pad;
++	__u64			d[];
++};
++
+ #endif /* _BCACHEFS_IOCTL_H */
+diff --git a/fs/bcachefs/bkey.h b/fs/bcachefs/bkey.h
+index 054e2d5e8448..082632905649 100644
+--- a/fs/bcachefs/bkey.h
++++ b/fs/bcachefs/bkey.h
+@@ -191,6 +191,7 @@ static inline struct bpos bkey_max(struct bpos l, struct bpos r)
+ static inline bool bkey_and_val_eq(struct bkey_s_c l, struct bkey_s_c r)
+ {
+ 	return bpos_eq(l.k->p, r.k->p) &&
++		l.k->size == r.k->size &&
+ 		bkey_bytes(l.k) == bkey_bytes(r.k) &&
+ 		!memcmp(l.v, r.v, bkey_val_bytes(l.k));
+ }
+diff --git a/fs/bcachefs/btree_cache.c b/fs/bcachefs/btree_cache.c
+index 1ec1f90e0eb3..54666027aa85 100644
+--- a/fs/bcachefs/btree_cache.c
++++ b/fs/bcachefs/btree_cache.c
+@@ -610,6 +610,7 @@ void bch2_fs_btree_cache_exit(struct bch_fs *c)
+ 		       btree_node_write_in_flight(b));
+ 
+ 		btree_node_data_free(bc, b);
++		cond_resched();
+ 	}
+ 
+ 	BUG_ON(!bch2_journal_error(&c->journal) &&
+diff --git a/fs/bcachefs/btree_gc.c b/fs/bcachefs/btree_gc.c
+index dd1d9b74076e..ff681e733598 100644
+--- a/fs/bcachefs/btree_gc.c
++++ b/fs/bcachefs/btree_gc.c
+@@ -27,6 +27,7 @@
+ #include "journal.h"
+ #include "keylist.h"
+ #include "move.h"
++#include "progress.h"
+ #include "recovery_passes.h"
+ #include "reflink.h"
+ #include "recovery.h"
+@@ -656,7 +657,9 @@ static int bch2_gc_mark_key(struct btree_trans *trans, enum btree_id btree_id,
+ 	return ret;
+ }
+ 
+-static int bch2_gc_btree(struct btree_trans *trans, enum btree_id btree, bool initial)
++static int bch2_gc_btree(struct btree_trans *trans,
++			 struct progress_indicator_state *progress,
++			 enum btree_id btree, bool initial)
+ {
+ 	struct bch_fs *c = trans->c;
+ 	unsigned target_depth = btree_node_type_has_triggers(__btree_node_type(0, btree)) ? 0 : 1;
+@@ -673,6 +676,7 @@ static int bch2_gc_btree(struct btree_trans *trans, enum btree_id btree, bool in
+ 					  BTREE_ITER_prefetch);
+ 
+ 		ret = for_each_btree_key_continue(trans, iter, 0, k, ({
++			bch2_progress_update_iter(trans, progress, &iter, "check_allocations");
+ 			gc_pos_set(c, gc_pos_btree(btree, level, k.k->p));
+ 			bch2_gc_mark_key(trans, btree, level, &prev, &iter, k, initial);
+ 		}));
+@@ -717,22 +721,24 @@ static inline int btree_id_gc_phase_cmp(enum btree_id l, enum btree_id r)
+ static int bch2_gc_btrees(struct bch_fs *c)
+ {
+ 	struct btree_trans *trans = bch2_trans_get(c);
+-	enum btree_id ids[BTREE_ID_NR];
+ 	struct printbuf buf = PRINTBUF;
+-	unsigned i;
+ 	int ret = 0;
+ 
+-	for (i = 0; i < BTREE_ID_NR; i++)
++	struct progress_indicator_state progress;
++	bch2_progress_init(&progress, c, ~0ULL);
++
++	enum btree_id ids[BTREE_ID_NR];
++	for (unsigned i = 0; i < BTREE_ID_NR; i++)
+ 		ids[i] = i;
+ 	bubble_sort(ids, BTREE_ID_NR, btree_id_gc_phase_cmp);
+ 
+-	for (i = 0; i < btree_id_nr_alive(c) && !ret; i++) {
++	for (unsigned i = 0; i < btree_id_nr_alive(c) && !ret; i++) {
+ 		unsigned btree = i < BTREE_ID_NR ? ids[i] : i;
+ 
+ 		if (IS_ERR_OR_NULL(bch2_btree_id_root(c, btree)->b))
+ 			continue;
+ 
+-		ret = bch2_gc_btree(trans, btree, true);
++		ret = bch2_gc_btree(trans, &progress, btree, true);
+ 	}
+ 
+ 	printbuf_exit(&buf);
+diff --git a/fs/bcachefs/btree_io.c b/fs/bcachefs/btree_io.c
+index 756736f9243d..2ba33ffc9795 100644
+--- a/fs/bcachefs/btree_io.c
++++ b/fs/bcachefs/btree_io.c
+@@ -1,6 +1,7 @@
+ // SPDX-License-Identifier: GPL-2.0
+ 
+ #include "bcachefs.h"
++#include "bkey_buf.h"
+ #include "bkey_methods.h"
+ #include "bkey_sort.h"
+ #include "btree_cache.h"
+@@ -1328,6 +1329,7 @@ static void btree_node_read_work(struct work_struct *work)
+ 		bch_info(c, "retrying read");
+ 		ca = bch2_dev_get_ioref(c, rb->pick.ptr.dev, READ);
+ 		rb->have_ioref		= ca != NULL;
++		rb->start_time		= local_clock();
+ 		bio_reset(bio, NULL, REQ_OP_READ|REQ_SYNC|REQ_META);
+ 		bio->bi_iter.bi_sector	= rb->pick.ptr.offset;
+ 		bio->bi_iter.bi_size	= btree_buf_bytes(b);
+@@ -1338,21 +1340,26 @@ static void btree_node_read_work(struct work_struct *work)
+ 		} else {
+ 			bio->bi_status = BLK_STS_REMOVED;
+ 		}
++
++		bch2_account_io_completion(ca, BCH_MEMBER_ERROR_read,
++					   rb->start_time, !bio->bi_status);
+ start:
+ 		printbuf_reset(&buf);
+ 		bch2_btree_pos_to_text(&buf, c, b);
+-		bch2_dev_io_err_on(ca && bio->bi_status, ca, BCH_MEMBER_ERROR_read,
+-				   "btree read error %s for %s",
+-				   bch2_blk_status_to_str(bio->bi_status), buf.buf);
++
++		if (ca && bio->bi_status)
++			bch_err_dev_ratelimited(ca,
++					"btree read error %s for %s",
++					bch2_blk_status_to_str(bio->bi_status), buf.buf);
+ 		if (rb->have_ioref)
+ 			percpu_ref_put(&ca->io_ref);
+ 		rb->have_ioref = false;
+ 
+-		bch2_mark_io_failure(&failed, &rb->pick);
++		bch2_mark_io_failure(&failed, &rb->pick, false);
+ 
+ 		can_retry = bch2_bkey_pick_read_device(c,
+ 				bkey_i_to_s_c(&b->key),
+-				&failed, &rb->pick) > 0;
++				&failed, &rb->pick, -1) > 0;
+ 
+ 		if (!bio->bi_status &&
+ 		    !bch2_btree_node_read_done(c, ca, b, can_retry, &saw_error)) {
+@@ -1400,12 +1407,11 @@ static void btree_node_read_endio(struct bio *bio)
+ 	struct btree_read_bio *rb =
+ 		container_of(bio, struct btree_read_bio, bio);
+ 	struct bch_fs *c	= rb->c;
++	struct bch_dev *ca	= rb->have_ioref
++		? bch2_dev_have_ref(c, rb->pick.ptr.dev) : NULL;
+ 
+-	if (rb->have_ioref) {
+-		struct bch_dev *ca = bch2_dev_have_ref(c, rb->pick.ptr.dev);
+-
+-		bch2_latency_acct(ca, rb->start_time, READ);
+-	}
++	bch2_account_io_completion(ca, BCH_MEMBER_ERROR_read,
++				   rb->start_time, !bio->bi_status);
+ 
+ 	queue_work(c->btree_read_complete_wq, &rb->work);
+ }
+@@ -1697,7 +1703,7 @@ void bch2_btree_node_read(struct btree_trans *trans, struct btree *b,
+ 		return;
+ 
+ 	ret = bch2_bkey_pick_read_device(c, bkey_i_to_s_c(&b->key),
+-					 NULL, &pick);
++					 NULL, &pick, -1);
+ 
+ 	if (ret <= 0) {
+ 		struct printbuf buf = PRINTBUF;
+@@ -1811,6 +1817,190 @@ int bch2_btree_root_read(struct bch_fs *c, enum btree_id id,
+ 	return bch2_trans_run(c, __bch2_btree_root_read(trans, id, k, level));
+ }
+ 
++struct btree_node_scrub {
++	struct bch_fs		*c;
++	struct bch_dev		*ca;
++	void			*buf;
++	bool			used_mempool;
++	unsigned		written;
++
++	enum btree_id		btree;
++	unsigned		level;
++	struct bkey_buf		key;
++	__le64			seq;
++
++	struct work_struct	work;
++	struct bio		bio;
++};
++
++static bool btree_node_scrub_check(struct bch_fs *c, struct btree_node *data, unsigned ptr_written,
++				   struct printbuf *err)
++{
++	unsigned written = 0;
++
++	if (le64_to_cpu(data->magic) != bset_magic(c)) {
++		prt_printf(err, "bad magic: want %llx, got %llx",
++			   bset_magic(c), le64_to_cpu(data->magic));
++		return false;
++	}
++
++	while (written < (ptr_written ?: btree_sectors(c))) {
++		struct btree_node_entry *bne;
++		struct bset *i;
++		bool first = !written;
++
++		if (first) {
++			bne = NULL;
++			i = &data->keys;
++		} else {
++			bne = (void *) data + (written << 9);
++			i = &bne->keys;
++
++			if (!ptr_written && i->seq != data->keys.seq)
++				break;
++		}
++
++		struct nonce nonce = btree_nonce(i, written << 9);
++		bool good_csum_type = bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i));
++
++		if (first) {
++			if (good_csum_type) {
++				struct bch_csum csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, data);
++				if (bch2_crc_cmp(data->csum, csum)) {
++					bch2_csum_err_msg(err, BSET_CSUM_TYPE(i), data->csum, csum);
++					return false;
++				}
++			}
++
++			written += vstruct_sectors(data, c->block_bits);
++		} else {
++			if (good_csum_type) {
++				struct bch_csum csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, bne);
++				if (bch2_crc_cmp(bne->csum, csum)) {
++					bch2_csum_err_msg(err, BSET_CSUM_TYPE(i), bne->csum, csum);
++					return false;
++				}
++			}
++
++			written += vstruct_sectors(bne, c->block_bits);
++		}
++	}
++
++	return true;
++}
++
++static void btree_node_scrub_work(struct work_struct *work)
++{
++	struct btree_node_scrub *scrub = container_of(work, struct btree_node_scrub, work);
++	struct bch_fs *c = scrub->c;
++	struct printbuf err = PRINTBUF;
++
++	__bch2_btree_pos_to_text(&err, c, scrub->btree, scrub->level,
++				 bkey_i_to_s_c(scrub->key.k));
++	prt_newline(&err);
++
++	if (!btree_node_scrub_check(c, scrub->buf, scrub->written, &err)) {
++		struct btree_trans *trans = bch2_trans_get(c);
++
++		struct btree_iter iter;
++		bch2_trans_node_iter_init(trans, &iter, scrub->btree,
++					  scrub->key.k->k.p, 0, scrub->level - 1, 0);
++
++		struct btree *b;
++		int ret = lockrestart_do(trans, PTR_ERR_OR_ZERO(b = bch2_btree_iter_peek_node(&iter)));
++		if (ret)
++			goto err;
++
++		if (bkey_i_to_btree_ptr_v2(&b->key)->v.seq == scrub->seq) {
++			bch_err(c, "error validating btree node during scrub on %s at btree %s",
++				scrub->ca->name, err.buf);
++
++			ret = bch2_btree_node_rewrite(trans, &iter, b, 0);
++		}
++err:
++		bch2_trans_iter_exit(trans, &iter);
++		bch2_trans_begin(trans);
++		bch2_trans_put(trans);
++	}
++
++	printbuf_exit(&err);
++	bch2_bkey_buf_exit(&scrub->key, c);;
++	btree_bounce_free(c, c->opts.btree_node_size, scrub->used_mempool, scrub->buf);
++	percpu_ref_put(&scrub->ca->io_ref);
++	kfree(scrub);
++	bch2_write_ref_put(c, BCH_WRITE_REF_btree_node_scrub);
++}
++
++static void btree_node_scrub_endio(struct bio *bio)
++{
++	struct btree_node_scrub *scrub = container_of(bio, struct btree_node_scrub, bio);
++
++	queue_work(scrub->c->btree_read_complete_wq, &scrub->work);
++}
++
++int bch2_btree_node_scrub(struct btree_trans *trans,
++			  enum btree_id btree, unsigned level,
++			  struct bkey_s_c k, unsigned dev)
++{
++	if (k.k->type != KEY_TYPE_btree_ptr_v2)
++		return 0;
++
++	struct bch_fs *c = trans->c;
++
++	if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_btree_node_scrub))
++		return -BCH_ERR_erofs_no_writes;
++
++	struct extent_ptr_decoded pick;
++	int ret = bch2_bkey_pick_read_device(c, k, NULL, &pick, dev);
++	if (ret <= 0)
++		goto err;
++
++	struct bch_dev *ca = bch2_dev_get_ioref(c, pick.ptr.dev, READ);
++	if (!ca) {
++		ret = -BCH_ERR_device_offline;
++		goto err;
++	}
++
++	bool used_mempool = false;
++	void *buf = btree_bounce_alloc(c, c->opts.btree_node_size, &used_mempool);
++
++	unsigned vecs = buf_pages(buf, c->opts.btree_node_size);
++
++	struct btree_node_scrub *scrub =
++		kzalloc(sizeof(*scrub) + sizeof(struct bio_vec) * vecs, GFP_KERNEL);
++	if (!scrub) {
++		ret = -ENOMEM;
++		goto err_free;
++	}
++
++	scrub->c		= c;
++	scrub->ca		= ca;
++	scrub->buf		= buf;
++	scrub->used_mempool	= used_mempool;
++	scrub->written		= btree_ptr_sectors_written(k);
++
++	scrub->btree		= btree;
++	scrub->level		= level;
++	bch2_bkey_buf_init(&scrub->key);
++	bch2_bkey_buf_reassemble(&scrub->key, c, k);
++	scrub->seq		= bkey_s_c_to_btree_ptr_v2(k).v->seq;
++
++	INIT_WORK(&scrub->work, btree_node_scrub_work);
++
++	bio_init(&scrub->bio, ca->disk_sb.bdev, scrub->bio.bi_inline_vecs, vecs, REQ_OP_READ);
++	bch2_bio_map(&scrub->bio, scrub->buf, c->opts.btree_node_size);
++	scrub->bio.bi_iter.bi_sector	= pick.ptr.offset;
++	scrub->bio.bi_end_io		= btree_node_scrub_endio;
++	submit_bio(&scrub->bio);
++	return 0;
++err_free:
++	btree_bounce_free(c, c->opts.btree_node_size, used_mempool, buf);
++	percpu_ref_put(&ca->io_ref);
++err:
++	bch2_write_ref_put(c, BCH_WRITE_REF_btree_node_scrub);
++	return ret;
++}
++
+ static void bch2_btree_complete_write(struct bch_fs *c, struct btree *b,
+ 				      struct btree_write *w)
+ {
+@@ -1831,7 +2021,7 @@ static void bch2_btree_complete_write(struct bch_fs *c, struct btree *b,
+ 	bch2_journal_pin_drop(&c->journal, &w->journal);
+ }
+ 
+-static void __btree_node_write_done(struct bch_fs *c, struct btree *b)
++static void __btree_node_write_done(struct bch_fs *c, struct btree *b, u64 start_time)
+ {
+ 	struct btree_write *w = btree_prev_write(b);
+ 	unsigned long old, new;
+@@ -1839,6 +2029,9 @@ static void __btree_node_write_done(struct bch_fs *c, struct btree *b)
+ 
+ 	bch2_btree_complete_write(c, b, w);
+ 
++	if (start_time)
++		bch2_time_stats_update(&c->times[BCH_TIME_btree_node_write], start_time);
++
+ 	old = READ_ONCE(b->flags);
+ 	do {
+ 		new = old;
+@@ -1869,7 +2062,7 @@ static void __btree_node_write_done(struct bch_fs *c, struct btree *b)
+ 		wake_up_bit(&b->flags, BTREE_NODE_write_in_flight);
+ }
+ 
+-static void btree_node_write_done(struct bch_fs *c, struct btree *b)
++static void btree_node_write_done(struct bch_fs *c, struct btree *b, u64 start_time)
+ {
+ 	struct btree_trans *trans = bch2_trans_get(c);
+ 
+@@ -1877,7 +2070,7 @@ static void btree_node_write_done(struct bch_fs *c, struct btree *b)
+ 
+ 	/* we don't need transaction context anymore after we got the lock. */
+ 	bch2_trans_put(trans);
+-	__btree_node_write_done(c, b);
++	__btree_node_write_done(c, b, start_time);
+ 	six_unlock_read(&b->c.lock);
+ }
+ 
+@@ -1887,6 +2080,7 @@ static void btree_node_write_work(struct work_struct *work)
+ 		container_of(work, struct btree_write_bio, work);
+ 	struct bch_fs *c	= wbio->wbio.c;
+ 	struct btree *b		= wbio->wbio.bio.bi_private;
++	u64 start_time		= wbio->start_time;
+ 	int ret = 0;
+ 
+ 	btree_bounce_free(c,
+@@ -1919,12 +2113,18 @@ static void btree_node_write_work(struct work_struct *work)
+ 	}
+ out:
+ 	bio_put(&wbio->wbio.bio);
+-	btree_node_write_done(c, b);
++	btree_node_write_done(c, b, start_time);
+ 	return;
+ err:
+ 	set_btree_node_noevict(b);
+-	bch2_fs_fatal_err_on(!bch2_err_matches(ret, EROFS), c,
+-			     "writing btree node: %s", bch2_err_str(ret));
++
++	if (!bch2_err_matches(ret, EROFS)) {
++		struct printbuf buf = PRINTBUF;
++		prt_printf(&buf, "writing btree node: %s\n  ", bch2_err_str(ret));
++		bch2_btree_pos_to_text(&buf, c, b);
++		bch2_fs_fatal_error(c, "%s", buf.buf);
++		printbuf_exit(&buf);
++	}
+ 	goto out;
+ }
+ 
+@@ -1937,16 +2137,21 @@ static void btree_node_write_endio(struct bio *bio)
+ 	struct bch_fs *c		= wbio->c;
+ 	struct btree *b			= wbio->bio.bi_private;
+ 	struct bch_dev *ca		= wbio->have_ioref ? bch2_dev_have_ref(c, wbio->dev) : NULL;
+-	unsigned long flags;
+ 
+-	if (wbio->have_ioref)
+-		bch2_latency_acct(ca, wbio->submit_time, WRITE);
++	bch2_account_io_completion(ca, BCH_MEMBER_ERROR_write,
++				   wbio->submit_time, !bio->bi_status);
+ 
+-	if (!ca ||
+-	    bch2_dev_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_write,
+-			       "btree write error: %s",
+-			       bch2_blk_status_to_str(bio->bi_status)) ||
+-	    bch2_meta_write_fault("btree")) {
++	if (ca && bio->bi_status) {
++		struct printbuf buf = PRINTBUF;
++		prt_printf(&buf, "btree write error: %s\n  ",
++			   bch2_blk_status_to_str(bio->bi_status));
++		bch2_btree_pos_to_text(&buf, c, b);
++		bch_err_dev_ratelimited(ca, "%s", buf.buf);
++		printbuf_exit(&buf);
++	}
++
++	if (bio->bi_status) {
++		unsigned long flags;
+ 		spin_lock_irqsave(&c->btree_write_error_lock, flags);
+ 		bch2_dev_list_add_dev(&orig->failed, wbio->dev);
+ 		spin_unlock_irqrestore(&c->btree_write_error_lock, flags);
+@@ -2023,6 +2228,7 @@ void __bch2_btree_node_write(struct bch_fs *c, struct btree *b, unsigned flags)
+ 	bool validate_before_checksum = false;
+ 	enum btree_write_type type = flags & BTREE_WRITE_TYPE_MASK;
+ 	void *data;
++	u64 start_time = local_clock();
+ 	int ret;
+ 
+ 	if (flags & BTREE_WRITE_ALREADY_STARTED)
+@@ -2231,6 +2437,7 @@ void __bch2_btree_node_write(struct bch_fs *c, struct btree *b, unsigned flags)
+ 	wbio->data			= data;
+ 	wbio->data_bytes		= bytes;
+ 	wbio->sector_offset		= b->written;
++	wbio->start_time		= start_time;
+ 	wbio->wbio.c			= c;
+ 	wbio->wbio.used_mempool		= used_mempool;
+ 	wbio->wbio.first_btree_write	= !b->written;
+@@ -2258,7 +2465,7 @@ void __bch2_btree_node_write(struct bch_fs *c, struct btree *b, unsigned flags)
+ 	b->written += sectors_to_write;
+ nowrite:
+ 	btree_bounce_free(c, bytes, used_mempool, data);
+-	__btree_node_write_done(c, b);
++	__btree_node_write_done(c, b, 0);
+ }
+ 
+ /*
+diff --git a/fs/bcachefs/btree_io.h b/fs/bcachefs/btree_io.h
+index 6f9e4a6dacf7..dbf76d22c660 100644
+--- a/fs/bcachefs/btree_io.h
++++ b/fs/bcachefs/btree_io.h
+@@ -52,6 +52,7 @@ struct btree_write_bio {
+ 	void			*data;
+ 	unsigned		data_bytes;
+ 	unsigned		sector_offset;
++	u64			start_time;
+ 	struct bch_write_bio	wbio;
+ };
+ 
+@@ -132,6 +133,9 @@ void bch2_btree_node_read(struct btree_trans *, struct btree *, bool);
+ int bch2_btree_root_read(struct bch_fs *, enum btree_id,
+ 			 const struct bkey_i *, unsigned);
+ 
++int bch2_btree_node_scrub(struct btree_trans *, enum btree_id, unsigned,
++			  struct bkey_s_c, unsigned);
++
+ bool bch2_btree_post_write_cleanup(struct bch_fs *, struct btree *);
+ 
+ enum btree_write_flags {
+diff --git a/fs/bcachefs/btree_iter.c b/fs/bcachefs/btree_iter.c
+index e32fce4fd258..7542c6f9c88e 100644
+--- a/fs/bcachefs/btree_iter.c
++++ b/fs/bcachefs/btree_iter.c
+@@ -562,20 +562,6 @@ static inline struct bkey_s_c btree_path_level_peek_all(struct bch_fs *c,
+ 			bch2_btree_node_iter_peek_all(&l->iter, l->b));
+ }
+ 
+-static inline struct bkey_s_c btree_path_level_peek(struct btree_trans *trans,
+-						    struct btree_path *path,
+-						    struct btree_path_level *l,
+-						    struct bkey *u)
+-{
+-	struct bkey_s_c k = __btree_iter_unpack(trans->c, l, u,
+-			bch2_btree_node_iter_peek(&l->iter, l->b));
+-
+-	path->pos = k.k ? k.k->p : l->b->key.k.p;
+-	trans->paths_sorted = false;
+-	bch2_btree_path_verify_level(trans, path, l - path->l);
+-	return k;
+-}
+-
+ static inline struct bkey_s_c btree_path_level_prev(struct btree_trans *trans,
+ 						    struct btree_path *path,
+ 						    struct btree_path_level *l,
+diff --git a/fs/bcachefs/btree_iter.h b/fs/bcachefs/btree_iter.h
+index b96157f3dc9c..8823eec6b284 100644
+--- a/fs/bcachefs/btree_iter.h
++++ b/fs/bcachefs/btree_iter.h
+@@ -335,13 +335,20 @@ static inline void bch2_trans_verify_not_unlocked_or_in_restart(struct btree_tra
+ }
+ 
+ __always_inline
+-static int btree_trans_restart_ip(struct btree_trans *trans, int err, unsigned long ip)
++static int btree_trans_restart_foreign_task(struct btree_trans *trans, int err, unsigned long ip)
+ {
+ 	BUG_ON(err <= 0);
+ 	BUG_ON(!bch2_err_matches(-err, BCH_ERR_transaction_restart));
+ 
+ 	trans->restarted = err;
+ 	trans->last_restarted_ip = ip;
++	return -err;
++}
++
++__always_inline
++static int btree_trans_restart_ip(struct btree_trans *trans, int err, unsigned long ip)
++{
++	btree_trans_restart_foreign_task(trans, err, ip);
+ #ifdef CONFIG_BCACHEFS_DEBUG
+ 	darray_exit(&trans->last_restarted_trace);
+ 	bch2_save_backtrace(&trans->last_restarted_trace, current, 0, GFP_NOWAIT);
+diff --git a/fs/bcachefs/btree_locking.c b/fs/bcachefs/btree_locking.c
+index caef65adeae4..94eb2b73a843 100644
+--- a/fs/bcachefs/btree_locking.c
++++ b/fs/bcachefs/btree_locking.c
+@@ -91,10 +91,10 @@ static noinline void print_chain(struct printbuf *out, struct lock_graph *g)
+ 	struct trans_waiting_for_lock *i;
+ 
+ 	for (i = g->g; i != g->g + g->nr; i++) {
+-		struct task_struct *task = i->trans->locking_wait.task;
++		struct task_struct *task = READ_ONCE(i->trans->locking_wait.task);
+ 		if (i != g->g)
+ 			prt_str(out, "<- ");
+-		prt_printf(out, "%u ", task ?task->pid : 0);
++		prt_printf(out, "%u ", task ? task->pid : 0);
+ 	}
+ 	prt_newline(out);
+ }
+@@ -172,7 +172,9 @@ static int abort_lock(struct lock_graph *g, struct trans_waiting_for_lock *i)
+ {
+ 	if (i == g->g) {
+ 		trace_would_deadlock(g, i->trans);
+-		return btree_trans_restart(i->trans, BCH_ERR_transaction_restart_would_deadlock);
++		return btree_trans_restart_foreign_task(i->trans,
++					BCH_ERR_transaction_restart_would_deadlock,
++					_THIS_IP_);
+ 	} else {
+ 		i->trans->lock_must_abort = true;
+ 		wake_up_process(i->trans->locking_wait.task);
+diff --git a/fs/bcachefs/btree_node_scan.c b/fs/bcachefs/btree_node_scan.c
+index a7f06deee13c..678161321e42 100644
+--- a/fs/bcachefs/btree_node_scan.c
++++ b/fs/bcachefs/btree_node_scan.c
+@@ -166,11 +166,17 @@ static void try_read_btree_node(struct find_btree_nodes *f, struct bch_dev *ca,
+ 	bio->bi_iter.bi_sector	= offset;
+ 	bch2_bio_map(bio, bn, PAGE_SIZE);
+ 
++	u64 submit_time = local_clock();
+ 	submit_bio_wait(bio);
+-	if (bch2_dev_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_read,
+-			       "IO error in try_read_btree_node() at %llu: %s",
+-			       offset, bch2_blk_status_to_str(bio->bi_status)))
++
++	bch2_account_io_completion(ca, BCH_MEMBER_ERROR_read, submit_time, !bio->bi_status);
++
++	if (bio->bi_status) {
++		bch_err_dev_ratelimited(ca,
++				"IO error in try_read_btree_node() at %llu: %s",
++				offset, bch2_blk_status_to_str(bio->bi_status));
+ 		return;
++	}
+ 
+ 	if (le64_to_cpu(bn->magic) != bset_magic(c))
+ 		return;
+@@ -264,7 +270,7 @@ static int read_btree_nodes_worker(void *p)
+ err:
+ 	bio_put(bio);
+ 	free_page((unsigned long) buf);
+-	percpu_ref_get(&ca->io_ref);
++	percpu_ref_put(&ca->io_ref);
+ 	closure_put(w->cl);
+ 	kfree(w);
+ 	return 0;
+@@ -283,29 +289,28 @@ static int read_btree_nodes(struct find_btree_nodes *f)
+ 			continue;
+ 
+ 		struct find_btree_nodes_worker *w = kmalloc(sizeof(*w), GFP_KERNEL);
+-		struct task_struct *t;
+-
+ 		if (!w) {
+ 			percpu_ref_put(&ca->io_ref);
+ 			ret = -ENOMEM;
+ 			goto err;
+ 		}
+ 
+-		percpu_ref_get(&ca->io_ref);
+-		closure_get(&cl);
+ 		w->cl		= &cl;
+ 		w->f		= f;
+ 		w->ca		= ca;
+ 
+-		t = kthread_run(read_btree_nodes_worker, w, "read_btree_nodes/%s", ca->name);
++		struct task_struct *t = kthread_create(read_btree_nodes_worker, w, "read_btree_nodes/%s", ca->name);
+ 		ret = PTR_ERR_OR_ZERO(t);
+ 		if (ret) {
+ 			percpu_ref_put(&ca->io_ref);
+-			closure_put(&cl);
+-			f->ret = ret;
+-			bch_err(c, "error starting kthread: %i", ret);
++			kfree(w);
++			bch_err_msg(c, ret, "starting kthread");
+ 			break;
+ 		}
++
++		closure_get(&cl);
++		percpu_ref_get(&ca->io_ref);
++		wake_up_process(t);
+ 	}
+ err:
+ 	closure_sync(&cl);
+diff --git a/fs/bcachefs/btree_trans_commit.c b/fs/bcachefs/btree_trans_commit.c
+index c4f524b2ca9a..7d7e52ddde02 100644
+--- a/fs/bcachefs/btree_trans_commit.c
++++ b/fs/bcachefs/btree_trans_commit.c
+@@ -164,6 +164,7 @@ bool bch2_btree_bset_insert_key(struct btree_trans *trans,
+ 	EBUG_ON(bpos_gt(insert->k.p, b->data->max_key));
+ 	EBUG_ON(insert->k.u64s > bch2_btree_keys_u64s_remaining(b));
+ 	EBUG_ON(!b->c.level && !bpos_eq(insert->k.p, path->pos));
++	kmsan_check_memory(insert, bkey_bytes(&insert->k));
+ 
+ 	k = bch2_btree_node_iter_peek_all(node_iter, b);
+ 	if (k && bkey_cmp_left_packed(b, k, &insert->k.p))
+@@ -336,6 +337,7 @@ static inline void btree_insert_entry_checks(struct btree_trans *trans,
+ 	BUG_ON(i->cached	!= path->cached);
+ 	BUG_ON(i->level		!= path->level);
+ 	BUG_ON(i->btree_id	!= path->btree_id);
++	BUG_ON(i->bkey_type	!= __btree_node_type(path->level, path->btree_id));
+ 	EBUG_ON(!i->level &&
+ 		btree_type_has_snapshots(i->btree_id) &&
+ 		!(i->flags & BTREE_UPDATE_internal_snapshot_node) &&
+@@ -517,69 +519,45 @@ static int run_one_trans_trigger(struct btree_trans *trans, struct btree_insert_
+ 	}
+ }
+ 
+-static int run_btree_triggers(struct btree_trans *trans, enum btree_id btree_id,
+-			      unsigned *btree_id_updates_start)
++static int bch2_trans_commit_run_triggers(struct btree_trans *trans)
+ {
+-	bool trans_trigger_run;
++	unsigned sort_id_start = 0;
+ 
+-	/*
+-	 * Running triggers will append more updates to the list of updates as
+-	 * we're walking it:
+-	 */
+-	do {
+-		trans_trigger_run = false;
++	while (sort_id_start < trans->nr_updates) {
++		unsigned i, sort_id = trans->updates[sort_id_start].sort_order;
++		bool trans_trigger_run;
+ 
+-		for (unsigned i = *btree_id_updates_start;
+-		     i < trans->nr_updates && trans->updates[i].btree_id <= btree_id;
+-		     i++) {
+-			if (trans->updates[i].btree_id < btree_id) {
+-				*btree_id_updates_start = i;
+-				continue;
++		/*
++		 * For a given btree, this algorithm runs insert triggers before
++		 * overwrite triggers: this is so that when extents are being
++		 * moved (e.g. by FALLOCATE_FL_INSERT_RANGE), we don't drop
++		 * references before they are re-added.
++		 *
++		 * Running triggers will append more updates to the list of
++		 * updates as we're walking it:
++		 */
++		do {
++			trans_trigger_run = false;
++
++			for (i = sort_id_start;
++			     i < trans->nr_updates && trans->updates[i].sort_order <= sort_id;
++			     i++) {
++				if (trans->updates[i].sort_order < sort_id) {
++					sort_id_start = i;
++					continue;
++				}
++
++				int ret = run_one_trans_trigger(trans, trans->updates + i);
++				if (ret < 0)
++					return ret;
++				if (ret)
++					trans_trigger_run = true;
+ 			}
++		} while (trans_trigger_run);
+ 
+-			int ret = run_one_trans_trigger(trans, trans->updates + i);
+-			if (ret < 0)
+-				return ret;
+-			if (ret)
+-				trans_trigger_run = true;
+-		}
+-	} while (trans_trigger_run);
+-
+-	trans_for_each_update(trans, i)
+-		BUG_ON(!(i->flags & BTREE_TRIGGER_norun) &&
+-		       i->btree_id == btree_id &&
+-		       btree_node_type_has_trans_triggers(i->bkey_type) &&
+-		       (!i->insert_trigger_run || !i->overwrite_trigger_run));
+-
+-	return 0;
+-}
+-
+-static int bch2_trans_commit_run_triggers(struct btree_trans *trans)
+-{
+-	unsigned btree_id = 0, btree_id_updates_start = 0;
+-	int ret = 0;
+-
+-	/*
+-	 *
+-	 * For a given btree, this algorithm runs insert triggers before
+-	 * overwrite triggers: this is so that when extents are being moved
+-	 * (e.g. by FALLOCATE_FL_INSERT_RANGE), we don't drop references before
+-	 * they are re-added.
+-	 */
+-	for (btree_id = 0; btree_id < BTREE_ID_NR; btree_id++) {
+-		if (btree_id == BTREE_ID_alloc)
+-			continue;
+-
+-		ret = run_btree_triggers(trans, btree_id, &btree_id_updates_start);
+-		if (ret)
+-			return ret;
++		sort_id_start = i;
+ 	}
+ 
+-	btree_id_updates_start = 0;
+-	ret = run_btree_triggers(trans, BTREE_ID_alloc, &btree_id_updates_start);
+-	if (ret)
+-		return ret;
+-
+ #ifdef CONFIG_BCACHEFS_DEBUG
+ 	trans_for_each_update(trans, i)
+ 		BUG_ON(!(i->flags & BTREE_TRIGGER_norun) &&
+@@ -903,18 +881,7 @@ int bch2_trans_commit_error(struct btree_trans *trans, unsigned flags,
+ 	struct bch_fs *c = trans->c;
+ 	enum bch_watermark watermark = flags & BCH_WATERMARK_MASK;
+ 
+-	switch (ret) {
+-	case -BCH_ERR_btree_insert_btree_node_full:
+-		ret = bch2_btree_split_leaf(trans, i->path, flags);
+-		if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
+-			trace_and_count(c, trans_restart_btree_node_split, trans,
+-					trace_ip, trans->paths + i->path);
+-		break;
+-	case -BCH_ERR_btree_insert_need_mark_replicas:
+-		ret = drop_locks_do(trans,
+-			bch2_accounting_update_sb(trans));
+-		break;
+-	case -BCH_ERR_journal_res_get_blocked:
++	if (bch2_err_matches(ret, BCH_ERR_journal_res_blocked)) {
+ 		/*
+ 		 * XXX: this should probably be a separate BTREE_INSERT_NONBLOCK
+ 		 * flag
+@@ -922,13 +889,26 @@ int bch2_trans_commit_error(struct btree_trans *trans, unsigned flags,
+ 		if ((flags & BCH_TRANS_COMMIT_journal_reclaim) &&
+ 		    watermark < BCH_WATERMARK_reclaim) {
+ 			ret = -BCH_ERR_journal_reclaim_would_deadlock;
+-			break;
++			goto out;
+ 		}
+ 
+ 		ret = drop_locks_do(trans,
+ 			bch2_trans_journal_res_get(trans,
+ 					(flags & BCH_WATERMARK_MASK)|
+ 					JOURNAL_RES_GET_CHECK));
++		goto out;
++	}
++
++	switch (ret) {
++	case -BCH_ERR_btree_insert_btree_node_full:
++		ret = bch2_btree_split_leaf(trans, i->path, flags);
++		if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
++			trace_and_count(c, trans_restart_btree_node_split, trans,
++					trace_ip, trans->paths + i->path);
++		break;
++	case -BCH_ERR_btree_insert_need_mark_replicas:
++		ret = drop_locks_do(trans,
++			bch2_accounting_update_sb(trans));
+ 		break;
+ 	case -BCH_ERR_btree_insert_need_journal_reclaim:
+ 		bch2_trans_unlock(trans);
+@@ -950,7 +930,7 @@ int bch2_trans_commit_error(struct btree_trans *trans, unsigned flags,
+ 		BUG_ON(ret >= 0);
+ 		break;
+ 	}
+-
++out:
+ 	BUG_ON(bch2_err_matches(ret, BCH_ERR_transaction_restart) != !!trans->restarted);
+ 
+ 	bch2_fs_inconsistent_on(bch2_err_matches(ret, ENOSPC) &&
+diff --git a/fs/bcachefs/btree_types.h b/fs/bcachefs/btree_types.h
+index a09cbe9cd94f..77578da2d23f 100644
+--- a/fs/bcachefs/btree_types.h
++++ b/fs/bcachefs/btree_types.h
+@@ -423,6 +423,7 @@ static inline struct bpos btree_node_pos(struct btree_bkey_cached_common *b)
+ 
+ struct btree_insert_entry {
+ 	unsigned		flags;
++	u8			sort_order;
+ 	u8			bkey_type;
+ 	enum btree_id		btree_id:8;
+ 	u8			level:4;
+@@ -853,6 +854,18 @@ static inline bool btree_type_uses_write_buffer(enum btree_id btree)
+ 	return BIT_ULL(btree) & mask;
+ }
+ 
++static inline u8 btree_trigger_order(enum btree_id btree)
++{
++	switch (btree) {
++	case BTREE_ID_alloc:
++		return U8_MAX;
++	case BTREE_ID_stripes:
++		return U8_MAX - 1;
++	default:
++		return btree;
++	}
++}
++
+ struct btree_root {
+ 	struct btree		*b;
+ 
+diff --git a/fs/bcachefs/btree_update.c b/fs/bcachefs/btree_update.c
+index 13d794f201a5..bd2eb42edb24 100644
+--- a/fs/bcachefs/btree_update.c
++++ b/fs/bcachefs/btree_update.c
+@@ -17,7 +17,7 @@
+ static inline int btree_insert_entry_cmp(const struct btree_insert_entry *l,
+ 					 const struct btree_insert_entry *r)
+ {
+-	return   cmp_int(l->btree_id,	r->btree_id) ?:
++	return   cmp_int(l->sort_order,	r->sort_order) ?:
+ 		 cmp_int(l->cached,	r->cached) ?:
+ 		 -cmp_int(l->level,	r->level) ?:
+ 		 bpos_cmp(l->k->k.p,	r->k->k.p);
+@@ -397,6 +397,7 @@ bch2_trans_update_by_path(struct btree_trans *trans, btree_path_idx_t path_idx,
+ 
+ 	n = (struct btree_insert_entry) {
+ 		.flags		= flags,
++		.sort_order	= btree_trigger_order(path->btree_id),
+ 		.bkey_type	= __btree_node_type(path->level, path->btree_id),
+ 		.btree_id	= path->btree_id,
+ 		.level		= path->level,
+@@ -511,6 +512,8 @@ static noinline int bch2_trans_update_get_key_cache(struct btree_trans *trans,
+ int __must_check bch2_trans_update(struct btree_trans *trans, struct btree_iter *iter,
+ 				   struct bkey_i *k, enum btree_iter_update_trigger_flags flags)
+ {
++	kmsan_check_memory(k, bkey_bytes(&k->k));
++
+ 	btree_path_idx_t path_idx = iter->update_path ?: iter->path;
+ 	int ret;
+ 
+diff --git a/fs/bcachefs/btree_update.h b/fs/bcachefs/btree_update.h
+index 47d8690f01bf..d2e1c04353f6 100644
+--- a/fs/bcachefs/btree_update.h
++++ b/fs/bcachefs/btree_update.h
+@@ -133,6 +133,8 @@ static inline int __must_check bch2_trans_update_buffered(struct btree_trans *tr
+ 					    enum btree_id btree,
+ 					    struct bkey_i *k)
+ {
++	kmsan_check_memory(k, bkey_bytes(&k->k));
++
+ 	if (unlikely(!btree_type_uses_write_buffer(btree))) {
+ 		int ret = bch2_btree_write_buffer_insert_err(trans, btree, k);
+ 		dump_stack();
+diff --git a/fs/bcachefs/btree_update_interior.c b/fs/bcachefs/btree_update_interior.c
+index e4e7c804625e..67f1e3202835 100644
+--- a/fs/bcachefs/btree_update_interior.c
++++ b/fs/bcachefs/btree_update_interior.c
+@@ -649,6 +649,14 @@ static int btree_update_nodes_written_trans(struct btree_trans *trans,
+ 	return 0;
+ }
+ 
++/* If the node has been reused, we might be reading uninitialized memory - that's fine: */
++static noinline __no_kmsan_checks bool btree_node_seq_matches(struct btree *b, __le64 seq)
++{
++	struct btree_node *b_data = READ_ONCE(b->data);
++
++	return (b_data ? b_data->keys.seq : 0) == seq;
++}
++
+ static void btree_update_nodes_written(struct btree_update *as)
+ {
+ 	struct bch_fs *c = as->c;
+@@ -677,17 +685,9 @@ static void btree_update_nodes_written(struct btree_update *as)
+ 	 * on disk:
+ 	 */
+ 	for (i = 0; i < as->nr_old_nodes; i++) {
+-		__le64 seq;
+-
+ 		b = as->old_nodes[i];
+ 
+-		bch2_trans_begin(trans);
+-		btree_node_lock_nopath_nofail(trans, &b->c, SIX_LOCK_read);
+-		seq = b->data ? b->data->keys.seq : 0;
+-		six_unlock_read(&b->c.lock);
+-		bch2_trans_unlock_long(trans);
+-
+-		if (seq == as->old_nodes_seq[i])
++		if (btree_node_seq_matches(b, as->old_nodes_seq[i]))
+ 			wait_on_bit_io(&b->flags, BTREE_NODE_write_in_flight_inner,
+ 				       TASK_UNINTERRUPTIBLE);
+ 	}
+@@ -2126,6 +2126,31 @@ int __bch2_foreground_maybe_merge(struct btree_trans *trans,
+ 	goto out;
+ }
+ 
++static int get_iter_to_node(struct btree_trans *trans, struct btree_iter *iter,
++			    struct btree *b)
++{
++	bch2_trans_node_iter_init(trans, iter, b->c.btree_id, b->key.k.p,
++				  BTREE_MAX_DEPTH, b->c.level,
++				  BTREE_ITER_intent);
++	int ret = bch2_btree_iter_traverse(iter);
++	if (ret)
++		goto err;
++
++	/* has node been freed? */
++	if (btree_iter_path(trans, iter)->l[b->c.level].b != b) {
++		/* node has been freed: */
++		BUG_ON(!btree_node_dying(b));
++		ret = -BCH_ERR_btree_node_dying;
++		goto err;
++	}
++
++	BUG_ON(!btree_node_hashed(b));
++	return 0;
++err:
++	bch2_trans_iter_exit(trans, iter);
++	return ret;
++}
++
+ int bch2_btree_node_rewrite(struct btree_trans *trans,
+ 			    struct btree_iter *iter,
+ 			    struct btree *b,
+@@ -2191,66 +2216,78 @@ int bch2_btree_node_rewrite(struct btree_trans *trans,
+ 	goto out;
+ }
+ 
+-struct async_btree_rewrite {
+-	struct bch_fs		*c;
+-	struct work_struct	work;
+-	struct list_head	list;
+-	enum btree_id		btree_id;
+-	unsigned		level;
+-	struct bkey_buf		key;
+-};
+-
+-static int async_btree_node_rewrite_trans(struct btree_trans *trans,
+-					  struct async_btree_rewrite *a)
++static int bch2_btree_node_rewrite_key(struct btree_trans *trans,
++				       enum btree_id btree, unsigned level,
++				       struct bkey_i *k, unsigned flags)
+ {
+ 	struct btree_iter iter;
+ 	bch2_trans_node_iter_init(trans, &iter,
+-				  a->btree_id, a->key.k->k.p,
+-				  BTREE_MAX_DEPTH, a->level, 0);
++				  btree, k->k.p,
++				  BTREE_MAX_DEPTH, level, 0);
+ 	struct btree *b = bch2_btree_iter_peek_node(&iter);
+ 	int ret = PTR_ERR_OR_ZERO(b);
+ 	if (ret)
+ 		goto out;
+ 
+-	bool found = b && btree_ptr_hash_val(&b->key) == btree_ptr_hash_val(a->key.k);
++	bool found = b && btree_ptr_hash_val(&b->key) == btree_ptr_hash_val(k);
+ 	ret = found
+-		? bch2_btree_node_rewrite(trans, &iter, b, 0)
++		? bch2_btree_node_rewrite(trans, &iter, b, flags)
+ 		: -ENOENT;
++out:
++	bch2_trans_iter_exit(trans, &iter);
++	return ret;
++}
+ 
+-#if 0
+-	/* Tracepoint... */
+-	if (!ret || ret == -ENOENT) {
+-		struct bch_fs *c = trans->c;
+-		struct printbuf buf = PRINTBUF;
++int bch2_btree_node_rewrite_pos(struct btree_trans *trans,
++				enum btree_id btree, unsigned level,
++				struct bpos pos, unsigned flags)
++{
++	BUG_ON(!level);
+ 
+-		if (!ret) {
+-			prt_printf(&buf, "rewrite node:\n  ");
+-			bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(a->key.k));
+-		} else {
+-			prt_printf(&buf, "node to rewrite not found:\n  want: ");
+-			bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(a->key.k));
+-			prt_printf(&buf, "\n  got:  ");
+-			if (b)
+-				bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
+-			else
+-				prt_str(&buf, "(null)");
+-		}
+-		bch_info(c, "%s", buf.buf);
+-		printbuf_exit(&buf);
+-	}
+-#endif
+-out:
++	/* Traverse one depth lower to get a pointer to the node itself: */
++	struct btree_iter iter;
++	bch2_trans_node_iter_init(trans, &iter, btree, pos, 0, level - 1, 0);
++	struct btree *b = bch2_btree_iter_peek_node(&iter);
++	int ret = PTR_ERR_OR_ZERO(b);
++	if (ret)
++		goto err;
++
++	ret = bch2_btree_node_rewrite(trans, &iter, b, flags);
++err:
+ 	bch2_trans_iter_exit(trans, &iter);
+ 	return ret;
+ }
+ 
++int bch2_btree_node_rewrite_key_get_iter(struct btree_trans *trans,
++					 struct btree *b, unsigned flags)
++{
++	struct btree_iter iter;
++	int ret = get_iter_to_node(trans, &iter, b);
++	if (ret)
++		return ret == -BCH_ERR_btree_node_dying ? 0 : ret;
++
++	ret = bch2_btree_node_rewrite(trans, &iter, b, flags);
++	bch2_trans_iter_exit(trans, &iter);
++	return ret;
++}
++
++struct async_btree_rewrite {
++	struct bch_fs		*c;
++	struct work_struct	work;
++	struct list_head	list;
++	enum btree_id		btree_id;
++	unsigned		level;
++	struct bkey_buf		key;
++};
++
+ static void async_btree_node_rewrite_work(struct work_struct *work)
+ {
+ 	struct async_btree_rewrite *a =
+ 		container_of(work, struct async_btree_rewrite, work);
+ 	struct bch_fs *c = a->c;
+ 
+-	int ret = bch2_trans_do(c, async_btree_node_rewrite_trans(trans, a));
++	int ret = bch2_trans_do(c, bch2_btree_node_rewrite_key(trans,
++						a->btree_id, a->level, a->key.k, 0));
+ 	if (ret != -ENOENT)
+ 		bch_err_fn_ratelimited(c, ret);
+ 
+@@ -2494,30 +2531,15 @@ int bch2_btree_node_update_key_get_iter(struct btree_trans *trans,
+ 					unsigned commit_flags, bool skip_triggers)
+ {
+ 	struct btree_iter iter;
+-	int ret;
+-
+-	bch2_trans_node_iter_init(trans, &iter, b->c.btree_id, b->key.k.p,
+-				  BTREE_MAX_DEPTH, b->c.level,
+-				  BTREE_ITER_intent);
+-	ret = bch2_btree_iter_traverse(&iter);
++	int ret = get_iter_to_node(trans, &iter, b);
+ 	if (ret)
+-		goto out;
+-
+-	/* has node been freed? */
+-	if (btree_iter_path(trans, &iter)->l[b->c.level].b != b) {
+-		/* node has been freed: */
+-		BUG_ON(!btree_node_dying(b));
+-		goto out;
+-	}
+-
+-	BUG_ON(!btree_node_hashed(b));
++		return ret == -BCH_ERR_btree_node_dying ? 0 : ret;
+ 
+ 	bch2_bkey_drop_ptrs(bkey_i_to_s(new_key), ptr,
+ 			    !bch2_bkey_has_device(bkey_i_to_s(&b->key), ptr->dev));
+ 
+ 	ret = bch2_btree_node_update_key(trans, &iter, b, new_key,
+ 					 commit_flags, skip_triggers);
+-out:
+ 	bch2_trans_iter_exit(trans, &iter);
+ 	return ret;
+ }
+diff --git a/fs/bcachefs/btree_update_interior.h b/fs/bcachefs/btree_update_interior.h
+index 26d646e1275c..be71cd73b864 100644
+--- a/fs/bcachefs/btree_update_interior.h
++++ b/fs/bcachefs/btree_update_interior.h
+@@ -169,7 +169,14 @@ static inline int bch2_foreground_maybe_merge(struct btree_trans *trans,
+ 
+ int bch2_btree_node_rewrite(struct btree_trans *, struct btree_iter *,
+ 			    struct btree *, unsigned);
++int bch2_btree_node_rewrite_pos(struct btree_trans *,
++				enum btree_id, unsigned,
++				struct bpos, unsigned);
++int bch2_btree_node_rewrite_key_get_iter(struct btree_trans *,
++					 struct btree *, unsigned);
++
+ void bch2_btree_node_rewrite_async(struct bch_fs *, struct btree *);
++
+ int bch2_btree_node_update_key(struct btree_trans *, struct btree_iter *,
+ 			       struct btree *, struct bkey_i *,
+ 			       unsigned, bool);
+diff --git a/fs/bcachefs/buckets.c b/fs/bcachefs/buckets.c
+index 345b117a4a4a..e56ef623ebc1 100644
+--- a/fs/bcachefs/buckets.c
++++ b/fs/bcachefs/buckets.c
+@@ -590,11 +590,9 @@ static int bch2_trigger_pointer(struct btree_trans *trans,
+ 		if (ret)
+ 			goto err;
+ 
+-		if (!p.ptr.cached) {
+-			ret = bch2_bucket_backpointer_mod(trans, k, &bp, insert);
+-			if (ret)
+-				goto err;
+-		}
++		ret = bch2_bucket_backpointer_mod(trans, k, &bp, insert);
++		if (ret)
++			goto err;
+ 	}
+ 
+ 	if (flags & BTREE_TRIGGER_gc) {
+@@ -674,10 +672,10 @@ static int bch2_trigger_stripe_ptr(struct btree_trans *trans,
+ 			return -BCH_ERR_ENOMEM_mark_stripe_ptr;
+ 		}
+ 
+-		mutex_lock(&c->ec_stripes_heap_lock);
++		gc_stripe_lock(m);
+ 
+ 		if (!m || !m->alive) {
+-			mutex_unlock(&c->ec_stripes_heap_lock);
++			gc_stripe_unlock(m);
+ 			struct printbuf buf = PRINTBUF;
+ 			bch2_bkey_val_to_text(&buf, c, k);
+ 			bch_err_ratelimited(c, "pointer to nonexistent stripe %llu\n  while marking %s",
+@@ -693,7 +691,7 @@ static int bch2_trigger_stripe_ptr(struct btree_trans *trans,
+ 			.type = BCH_DISK_ACCOUNTING_replicas,
+ 		};
+ 		memcpy(&acc.replicas, &m->r.e, replicas_entry_bytes(&m->r.e));
+-		mutex_unlock(&c->ec_stripes_heap_lock);
++		gc_stripe_unlock(m);
+ 
+ 		acc.replicas.data_type = data_type;
+ 		int ret = bch2_disk_accounting_mod(trans, &acc, &sectors, 1, true);
+@@ -726,9 +724,7 @@ static int __trigger_extent(struct btree_trans *trans,
+ 		.replicas.nr_required	= 1,
+ 	};
+ 
+-	struct disk_accounting_pos acct_compression_key = {
+-		.type			= BCH_DISK_ACCOUNTING_compression,
+-	};
++	unsigned cur_compression_type = 0;
+ 	u64 compression_acct[3] = { 1, 0, 0 };
+ 
+ 	bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
+@@ -762,13 +758,13 @@ static int __trigger_extent(struct btree_trans *trans,
+ 			acc_replicas_key.replicas.nr_required = 0;
+ 		}
+ 
+-		if (acct_compression_key.compression.type &&
+-		    acct_compression_key.compression.type != p.crc.compression_type) {
++		if (cur_compression_type &&
++		    cur_compression_type != p.crc.compression_type) {
+ 			if (flags & BTREE_TRIGGER_overwrite)
+ 				bch2_u64s_neg(compression_acct, ARRAY_SIZE(compression_acct));
+ 
+-			ret = bch2_disk_accounting_mod(trans, &acct_compression_key, compression_acct,
+-						       ARRAY_SIZE(compression_acct), gc);
++			ret = bch2_disk_accounting_mod2(trans, gc, compression_acct,
++							compression, cur_compression_type);
+ 			if (ret)
+ 				return ret;
+ 
+@@ -777,7 +773,7 @@ static int __trigger_extent(struct btree_trans *trans,
+ 			compression_acct[2] = 0;
+ 		}
+ 
+-		acct_compression_key.compression.type = p.crc.compression_type;
++		cur_compression_type = p.crc.compression_type;
+ 		if (p.crc.compression_type) {
+ 			compression_acct[1] += p.crc.uncompressed_size;
+ 			compression_acct[2] += p.crc.compressed_size;
+@@ -791,45 +787,34 @@ static int __trigger_extent(struct btree_trans *trans,
+ 	}
+ 
+ 	if (acc_replicas_key.replicas.nr_devs && !level && k.k->p.snapshot) {
+-		struct disk_accounting_pos acc_snapshot_key = {
+-			.type			= BCH_DISK_ACCOUNTING_snapshot,
+-			.snapshot.id		= k.k->p.snapshot,
+-		};
+-		ret = bch2_disk_accounting_mod(trans, &acc_snapshot_key, replicas_sectors, 1, gc);
++		ret = bch2_disk_accounting_mod2_nr(trans, gc, replicas_sectors, 1, snapshot, k.k->p.snapshot);
+ 		if (ret)
+ 			return ret;
+ 	}
+ 
+-	if (acct_compression_key.compression.type) {
++	if (cur_compression_type) {
+ 		if (flags & BTREE_TRIGGER_overwrite)
+ 			bch2_u64s_neg(compression_acct, ARRAY_SIZE(compression_acct));
+ 
+-		ret = bch2_disk_accounting_mod(trans, &acct_compression_key, compression_acct,
+-					       ARRAY_SIZE(compression_acct), gc);
++		ret = bch2_disk_accounting_mod2(trans, gc, compression_acct,
++						compression, cur_compression_type);
+ 		if (ret)
+ 			return ret;
+ 	}
+ 
+ 	if (level) {
+-		struct disk_accounting_pos acc_btree_key = {
+-			.type		= BCH_DISK_ACCOUNTING_btree,
+-			.btree.id	= btree_id,
+-		};
+-		ret = bch2_disk_accounting_mod(trans, &acc_btree_key, replicas_sectors, 1, gc);
++		ret = bch2_disk_accounting_mod2_nr(trans, gc, replicas_sectors, 1, btree, btree_id);
+ 		if (ret)
+ 			return ret;
+ 	} else {
+ 		bool insert = !(flags & BTREE_TRIGGER_overwrite);
+-		struct disk_accounting_pos acc_inum_key = {
+-			.type		= BCH_DISK_ACCOUNTING_inum,
+-			.inum.inum	= k.k->p.inode,
+-		};
++
+ 		s64 v[3] = {
+ 			insert ? 1 : -1,
+ 			insert ? k.k->size : -((s64) k.k->size),
+ 			*replicas_sectors,
+ 		};
+-		ret = bch2_disk_accounting_mod(trans, &acc_inum_key, v, ARRAY_SIZE(v), gc);
++		ret = bch2_disk_accounting_mod2(trans, gc, v, inum, k.k->p.inode);
+ 		if (ret)
+ 			return ret;
+ 	}
+@@ -878,15 +863,15 @@ int bch2_trigger_extent(struct btree_trans *trans,
+ 		}
+ 
+ 		int need_rebalance_delta = 0;
+-		s64 need_rebalance_sectors_delta = 0;
++		s64 need_rebalance_sectors_delta[1] = { 0 };
+ 
+ 		s64 s = bch2_bkey_sectors_need_rebalance(c, old);
+ 		need_rebalance_delta -= s != 0;
+-		need_rebalance_sectors_delta -= s;
++		need_rebalance_sectors_delta[0] -= s;
+ 
+ 		s = bch2_bkey_sectors_need_rebalance(c, new.s_c);
+ 		need_rebalance_delta += s != 0;
+-		need_rebalance_sectors_delta += s;
++		need_rebalance_sectors_delta[0] += s;
+ 
+ 		if ((flags & BTREE_TRIGGER_transactional) && need_rebalance_delta) {
+ 			int ret = bch2_btree_bit_mod_buffered(trans, BTREE_ID_rebalance_work,
+@@ -895,12 +880,9 @@ int bch2_trigger_extent(struct btree_trans *trans,
+ 				return ret;
+ 		}
+ 
+-		if (need_rebalance_sectors_delta) {
+-			struct disk_accounting_pos acc = {
+-				.type		= BCH_DISK_ACCOUNTING_rebalance_work,
+-			};
+-			int ret = bch2_disk_accounting_mod(trans, &acc, &need_rebalance_sectors_delta, 1,
+-							   flags & BTREE_TRIGGER_gc);
++		if (need_rebalance_sectors_delta[0]) {
++			int ret = bch2_disk_accounting_mod2(trans, flags & BTREE_TRIGGER_gc,
++							    need_rebalance_sectors_delta, rebalance_work);
+ 			if (ret)
+ 				return ret;
+ 		}
+@@ -916,17 +898,13 @@ static int __trigger_reservation(struct btree_trans *trans,
+ 			enum btree_iter_update_trigger_flags flags)
+ {
+ 	if (flags & (BTREE_TRIGGER_transactional|BTREE_TRIGGER_gc)) {
+-		s64 sectors = k.k->size;
++		s64 sectors[1] = { k.k->size };
+ 
+ 		if (flags & BTREE_TRIGGER_overwrite)
+-			sectors = -sectors;
+-
+-		struct disk_accounting_pos acc = {
+-			.type = BCH_DISK_ACCOUNTING_persistent_reserved,
+-			.persistent_reserved.nr_replicas = bkey_s_c_to_reservation(k).v->nr_replicas,
+-		};
++			sectors[0] = -sectors[0];
+ 
+-		return bch2_disk_accounting_mod(trans, &acc, &sectors, 1, flags & BTREE_TRIGGER_gc);
++		return bch2_disk_accounting_mod2(trans, flags & BTREE_TRIGGER_gc, sectors,
++				persistent_reserved, bkey_s_c_to_reservation(k).v->nr_replicas);
+ 	}
+ 
+ 	return 0;
+diff --git a/fs/bcachefs/buckets.h b/fs/bcachefs/buckets.h
+index a9acdd6c0c86..c5363256e363 100644
+--- a/fs/bcachefs/buckets.h
++++ b/fs/bcachefs/buckets.h
+@@ -39,33 +39,6 @@ static inline u64 sector_to_bucket_and_offset(const struct bch_dev *ca, sector_t
+ 	for (_b = (_buckets)->b + (_buckets)->first_bucket;	\
+ 	     _b < (_buckets)->b + (_buckets)->nbuckets; _b++)
+ 
+-/*
+- * Ugly hack alert:
+- *
+- * We need to cram a spinlock in a single byte, because that's what we have left
+- * in struct bucket, and we care about the size of these - during fsck, we need
+- * in memory state for every single bucket on every device.
+- *
+- * We used to do
+- *   while (xchg(&b->lock, 1) cpu_relax();
+- * but, it turns out not all architectures support xchg on a single byte.
+- *
+- * So now we use bit_spin_lock(), with fun games since we can't burn a whole
+- * ulong for this - we just need to make sure the lock bit always ends up in the
+- * first byte.
+- */
+-
+-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+-#define BUCKET_LOCK_BITNR	0
+-#else
+-#define BUCKET_LOCK_BITNR	(BITS_PER_LONG - 1)
+-#endif
+-
+-union ulong_byte_assert {
+-	ulong	ulong;
+-	u8	byte;
+-};
+-
+ static inline void bucket_unlock(struct bucket *b)
+ {
+ 	BUILD_BUG_ON(!((union ulong_byte_assert) { .ulong = 1UL << BUCKET_LOCK_BITNR }).byte);
+@@ -167,9 +140,7 @@ static inline int gen_cmp(u8 a, u8 b)
+ 
+ static inline int gen_after(u8 a, u8 b)
+ {
+-	int r = gen_cmp(a, b);
+-
+-	return r > 0 ? r : 0;
++	return max(0, gen_cmp(a, b));
+ }
+ 
+ static inline int dev_ptr_stale_rcu(struct bch_dev *ca, const struct bch_extent_ptr *ptr)
+diff --git a/fs/bcachefs/buckets_types.h b/fs/bcachefs/buckets_types.h
+index 7174047b8e92..900b8680c8b5 100644
+--- a/fs/bcachefs/buckets_types.h
++++ b/fs/bcachefs/buckets_types.h
+@@ -7,6 +7,33 @@
+ 
+ #define BUCKET_JOURNAL_SEQ_BITS		16
+ 
++/*
++ * Ugly hack alert:
++ *
++ * We need to cram a spinlock in a single byte, because that's what we have left
++ * in struct bucket, and we care about the size of these - during fsck, we need
++ * in memory state for every single bucket on every device.
++ *
++ * We used to do
++ *   while (xchg(&b->lock, 1) cpu_relax();
++ * but, it turns out not all architectures support xchg on a single byte.
++ *
++ * So now we use bit_spin_lock(), with fun games since we can't burn a whole
++ * ulong for this - we just need to make sure the lock bit always ends up in the
++ * first byte.
++ */
++
++#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
++#define BUCKET_LOCK_BITNR	0
++#else
++#define BUCKET_LOCK_BITNR	(BITS_PER_LONG - 1)
++#endif
++
++union ulong_byte_assert {
++	ulong	ulong;
++	u8	byte;
++};
++
+ struct bucket {
+ 	u8			lock;
+ 	u8			gen_valid:1;
+diff --git a/fs/bcachefs/chardev.c b/fs/bcachefs/chardev.c
+index 46e9e32105a9..57d55b3ddc71 100644
+--- a/fs/bcachefs/chardev.c
++++ b/fs/bcachefs/chardev.c
+@@ -11,6 +11,7 @@
+ #include "move.h"
+ #include "recovery_passes.h"
+ #include "replicas.h"
++#include "sb-counters.h"
+ #include "super-io.h"
+ #include "thread_with_file.h"
+ 
+@@ -312,7 +313,12 @@ static int bch2_data_thread(void *arg)
+ 	struct bch_data_ctx *ctx = container_of(arg, struct bch_data_ctx, thr);
+ 
+ 	ctx->thr.ret = bch2_data_job(ctx->c, &ctx->stats, ctx->arg);
+-	ctx->stats.data_type = U8_MAX;
++	if (ctx->thr.ret == -BCH_ERR_device_offline)
++		ctx->stats.ret = BCH_IOCTL_DATA_EVENT_RET_device_offline;
++	else {
++		ctx->stats.ret = BCH_IOCTL_DATA_EVENT_RET_done;
++		ctx->stats.data_type = (int) DATA_PROGRESS_DATA_TYPE_done;
++	}
+ 	return 0;
+ }
+ 
+@@ -331,14 +337,30 @@ static ssize_t bch2_data_job_read(struct file *file, char __user *buf,
+ 	struct bch_data_ctx *ctx = container_of(file->private_data, struct bch_data_ctx, thr);
+ 	struct bch_fs *c = ctx->c;
+ 	struct bch_ioctl_data_event e = {
+-		.type			= BCH_DATA_EVENT_PROGRESS,
+-		.p.data_type		= ctx->stats.data_type,
+-		.p.btree_id		= ctx->stats.pos.btree,
+-		.p.pos			= ctx->stats.pos.pos,
+-		.p.sectors_done		= atomic64_read(&ctx->stats.sectors_seen),
+-		.p.sectors_total	= bch2_fs_usage_read_short(c).used,
++		.type				= BCH_DATA_EVENT_PROGRESS,
++		.ret				= ctx->stats.ret,
++		.p.data_type			= ctx->stats.data_type,
++		.p.btree_id			= ctx->stats.pos.btree,
++		.p.pos				= ctx->stats.pos.pos,
++		.p.sectors_done			= atomic64_read(&ctx->stats.sectors_seen),
++		.p.sectors_error_corrected	= atomic64_read(&ctx->stats.sectors_error_corrected),
++		.p.sectors_error_uncorrected	= atomic64_read(&ctx->stats.sectors_error_uncorrected),
+ 	};
+ 
++	if (ctx->arg.op == BCH_DATA_OP_scrub) {
++		struct bch_dev *ca = bch2_dev_tryget(c, ctx->arg.scrub.dev);
++		if (ca) {
++			struct bch_dev_usage u;
++			bch2_dev_usage_read_fast(ca, &u);
++			for (unsigned i = BCH_DATA_btree; i < ARRAY_SIZE(u.d); i++)
++				if (ctx->arg.scrub.data_types & BIT(i))
++					e.p.sectors_total += u.d[i].sectors;
++			bch2_dev_put(ca);
++		}
++	} else {
++		e.p.sectors_total	= bch2_fs_usage_read_short(c).used;
++	}
++
+ 	if (len < sizeof(e))
+ 		return -EINVAL;
+ 
+@@ -710,6 +732,8 @@ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
+ 		BCH_IOCTL(fsck_online, struct bch_ioctl_fsck_online);
+ 	case BCH_IOCTL_QUERY_ACCOUNTING:
+ 		return bch2_ioctl_query_accounting(c, arg);
++	case BCH_IOCTL_QUERY_COUNTERS:
++		return bch2_ioctl_query_counters(c, arg);
+ 	default:
+ 		return -ENOTTY;
+ 	}
+diff --git a/fs/bcachefs/checksum.c b/fs/bcachefs/checksum.c
+index 23a383577d4c..3726689093e3 100644
+--- a/fs/bcachefs/checksum.c
++++ b/fs/bcachefs/checksum.c
+@@ -466,7 +466,7 @@ int bch2_rechecksum_bio(struct bch_fs *c, struct bio *bio,
+ 		prt_str(&buf, ")");
+ 		WARN_RATELIMIT(1, "%s", buf.buf);
+ 		printbuf_exit(&buf);
+-		return -EIO;
++		return -BCH_ERR_recompute_checksum;
+ 	}
+ 
+ 	for (i = splits; i < splits + ARRAY_SIZE(splits); i++) {
+@@ -693,6 +693,14 @@ static int bch2_alloc_ciphers(struct bch_fs *c)
+ 	return 0;
+ }
+ 
++#if 0
++
++/*
++ * This seems to be duplicating code in cmd_remove_passphrase() in
++ * bcachefs-tools, but we might want to switch userspace to use this - and
++ * perhaps add an ioctl for calling this at runtime, so we can take the
++ * passphrase off of a mounted filesystem (which has come up).
++ */
+ int bch2_disable_encryption(struct bch_fs *c)
+ {
+ 	struct bch_sb_field_crypt *crypt;
+@@ -725,6 +733,10 @@ int bch2_disable_encryption(struct bch_fs *c)
+ 	return ret;
+ }
+ 
++/*
++ * For enabling encryption on an existing filesystem: not hooked up yet, but it
++ * should be
++ */
+ int bch2_enable_encryption(struct bch_fs *c, bool keyed)
+ {
+ 	struct bch_encrypted_key key;
+@@ -781,6 +793,7 @@ int bch2_enable_encryption(struct bch_fs *c, bool keyed)
+ 	memzero_explicit(&key, sizeof(key));
+ 	return ret;
+ }
++#endif
+ 
+ void bch2_fs_encryption_exit(struct bch_fs *c)
+ {
+@@ -788,8 +801,6 @@ void bch2_fs_encryption_exit(struct bch_fs *c)
+ 		crypto_free_shash(c->poly1305);
+ 	if (c->chacha20)
+ 		crypto_free_sync_skcipher(c->chacha20);
+-	if (c->sha256)
+-		crypto_free_shash(c->sha256);
+ }
+ 
+ int bch2_fs_encryption_init(struct bch_fs *c)
+@@ -798,14 +809,6 @@ int bch2_fs_encryption_init(struct bch_fs *c)
+ 	struct bch_key key;
+ 	int ret = 0;
+ 
+-	c->sha256 = crypto_alloc_shash("sha256", 0, 0);
+-	ret = PTR_ERR_OR_ZERO(c->sha256);
+-	if (ret) {
+-		c->sha256 = NULL;
+-		bch_err(c, "error requesting sha256 module: %s", bch2_err_str(ret));
+-		goto out;
+-	}
+-
+ 	crypt = bch2_sb_field_get(c->disk_sb.sb, crypt);
+ 	if (!crypt)
+ 		goto out;
+diff --git a/fs/bcachefs/checksum.h b/fs/bcachefs/checksum.h
+index 43b9d71f2f2b..4ac251c8fcd8 100644
+--- a/fs/bcachefs/checksum.h
++++ b/fs/bcachefs/checksum.h
+@@ -103,8 +103,10 @@ extern const struct bch_sb_field_ops bch_sb_field_ops_crypt;
+ int bch2_decrypt_sb_key(struct bch_fs *, struct bch_sb_field_crypt *,
+ 			struct bch_key *);
+ 
++#if 0
+ int bch2_disable_encryption(struct bch_fs *);
+ int bch2_enable_encryption(struct bch_fs *, bool);
++#endif
+ 
+ void bch2_fs_encryption_exit(struct bch_fs *);
+ int bch2_fs_encryption_init(struct bch_fs *);
+diff --git a/fs/bcachefs/compress.c b/fs/bcachefs/compress.c
+index 114bf2f3879f..85fc90342492 100644
+--- a/fs/bcachefs/compress.c
++++ b/fs/bcachefs/compress.c
+@@ -177,7 +177,7 @@ static int __bio_uncompress(struct bch_fs *c, struct bio *src,
+ 	size_t src_len = src->bi_iter.bi_size;
+ 	size_t dst_len = crc.uncompressed_size << 9;
+ 	void *workspace;
+-	int ret;
++	int ret = 0, ret2;
+ 
+ 	enum bch_compression_opts opt = bch2_compression_type_to_opt(crc.compression_type);
+ 	mempool_t *workspace_pool = &c->compress_workspace[opt];
+@@ -189,7 +189,7 @@ static int __bio_uncompress(struct bch_fs *c, struct bio *src,
+ 		else
+ 			ret = -BCH_ERR_compression_workspace_not_initialized;
+ 		if (ret)
+-			goto out;
++			goto err;
+ 	}
+ 
+ 	src_data = bio_map_or_bounce(c, src, READ);
+@@ -197,10 +197,10 @@ static int __bio_uncompress(struct bch_fs *c, struct bio *src,
+ 	switch (crc.compression_type) {
+ 	case BCH_COMPRESSION_TYPE_lz4_old:
+ 	case BCH_COMPRESSION_TYPE_lz4:
+-		ret = LZ4_decompress_safe_partial(src_data.b, dst_data,
+-						  src_len, dst_len, dst_len);
+-		if (ret != dst_len)
+-			goto err;
++		ret2 = LZ4_decompress_safe_partial(src_data.b, dst_data,
++						   src_len, dst_len, dst_len);
++		if (ret2 != dst_len)
++			ret = -BCH_ERR_decompress_lz4;
+ 		break;
+ 	case BCH_COMPRESSION_TYPE_gzip: {
+ 		z_stream strm = {
+@@ -214,45 +214,43 @@ static int __bio_uncompress(struct bch_fs *c, struct bio *src,
+ 
+ 		zlib_set_workspace(&strm, workspace);
+ 		zlib_inflateInit2(&strm, -MAX_WBITS);
+-		ret = zlib_inflate(&strm, Z_FINISH);
++		ret2 = zlib_inflate(&strm, Z_FINISH);
+ 
+ 		mempool_free(workspace, workspace_pool);
+ 
+-		if (ret != Z_STREAM_END)
+-			goto err;
++		if (ret2 != Z_STREAM_END)
++			ret = -BCH_ERR_decompress_gzip;
+ 		break;
+ 	}
+ 	case BCH_COMPRESSION_TYPE_zstd: {
+ 		ZSTD_DCtx *ctx;
+ 		size_t real_src_len = le32_to_cpup(src_data.b);
+ 
+-		if (real_src_len > src_len - 4)
++		if (real_src_len > src_len - 4) {
++			ret = -BCH_ERR_decompress_zstd_src_len_bad;
+ 			goto err;
++		}
+ 
+ 		workspace = mempool_alloc(workspace_pool, GFP_NOFS);
+ 		ctx = zstd_init_dctx(workspace, zstd_dctx_workspace_bound());
+ 
+-		ret = zstd_decompress_dctx(ctx,
++		ret2 = zstd_decompress_dctx(ctx,
+ 				dst_data,	dst_len,
+ 				src_data.b + 4, real_src_len);
+ 
+ 		mempool_free(workspace, workspace_pool);
+ 
+-		if (ret != dst_len)
+-			goto err;
++		if (ret2 != dst_len)
++			ret = -BCH_ERR_decompress_zstd;
+ 		break;
+ 	}
+ 	default:
+ 		BUG();
+ 	}
+-	ret = 0;
++err:
+ fsck_err:
+-out:
+ 	bio_unmap_or_unbounce(c, src_data);
+ 	return ret;
+-err:
+-	ret = -EIO;
+-	goto out;
+ }
+ 
+ int bch2_bio_uncompress_inplace(struct bch_write_op *op,
+@@ -268,27 +266,22 @@ int bch2_bio_uncompress_inplace(struct bch_write_op *op,
+ 	BUG_ON(!bio->bi_vcnt);
+ 	BUG_ON(DIV_ROUND_UP(crc->live_size, PAGE_SECTORS) > bio->bi_max_vecs);
+ 
+-	if (crc->uncompressed_size << 9	> c->opts.encoded_extent_max ||
+-	    crc->compressed_size << 9	> c->opts.encoded_extent_max) {
+-		struct printbuf buf = PRINTBUF;
+-		bch2_write_op_error(&buf, op);
+-		prt_printf(&buf, "error rewriting existing data: extent too big");
+-		bch_err_ratelimited(c, "%s", buf.buf);
+-		printbuf_exit(&buf);
+-		return -EIO;
++	if (crc->uncompressed_size << 9	> c->opts.encoded_extent_max) {
++		bch2_write_op_error(op, op->pos.offset,
++				    "extent too big to decompress (%u > %u)",
++				    crc->uncompressed_size << 9, c->opts.encoded_extent_max);
++		return -BCH_ERR_decompress_exceeded_max_encoded_extent;
+ 	}
+ 
+ 	data = __bounce_alloc(c, dst_len, WRITE);
+ 
+-	if (__bio_uncompress(c, bio, data.b, *crc)) {
+-		if (!c->opts.no_data_io) {
+-			struct printbuf buf = PRINTBUF;
+-			bch2_write_op_error(&buf, op);
+-			prt_printf(&buf, "error rewriting existing data: decompression error");
+-			bch_err_ratelimited(c, "%s", buf.buf);
+-			printbuf_exit(&buf);
+-		}
+-		ret = -EIO;
++	ret = __bio_uncompress(c, bio, data.b, *crc);
++
++	if (c->opts.no_data_io)
++		ret = 0;
++
++	if (ret) {
++		bch2_write_op_error(op, op->pos.offset, "%s", bch2_err_str(ret));
+ 		goto err;
+ 	}
+ 
+@@ -321,7 +314,7 @@ int bch2_bio_uncompress(struct bch_fs *c, struct bio *src,
+ 
+ 	if (crc.uncompressed_size << 9	> c->opts.encoded_extent_max ||
+ 	    crc.compressed_size << 9	> c->opts.encoded_extent_max)
+-		return -EIO;
++		return -BCH_ERR_decompress_exceeded_max_encoded_extent;
+ 
+ 	dst_data = dst_len == dst_iter.bi_size
+ 		? __bio_map_or_bounce(c, dst, dst_iter, WRITE)
+diff --git a/fs/bcachefs/data_update.c b/fs/bcachefs/data_update.c
+index 642fbc60ecab..0ec273daccb7 100644
+--- a/fs/bcachefs/data_update.c
++++ b/fs/bcachefs/data_update.c
+@@ -20,6 +20,8 @@
+ #include "subvolume.h"
+ #include "trace.h"
+ 
++#include <linux/ioprio.h>
++
+ static void bkey_put_dev_refs(struct bch_fs *c, struct bkey_s_c k)
+ {
+ 	struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
+@@ -33,7 +35,7 @@ static bool bkey_get_dev_refs(struct bch_fs *c, struct bkey_s_c k)
+ 	struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
+ 
+ 	bkey_for_each_ptr(ptrs, ptr) {
+-		if (!bch2_dev_tryget(c, ptr->dev)) {
++		if (unlikely(!bch2_dev_tryget(c, ptr->dev))) {
+ 			bkey_for_each_ptr(ptrs, ptr2) {
+ 				if (ptr2 == ptr)
+ 					break;
+@@ -91,7 +93,7 @@ static bool bkey_nocow_lock(struct bch_fs *c, struct moving_context *ctxt, struc
+ 	return true;
+ }
+ 
+-static noinline void trace_move_extent_finish2(struct data_update *u,
++static noinline void trace_io_move_finish2(struct data_update *u,
+ 					       struct bkey_i *new,
+ 					       struct bkey_i *insert)
+ {
+@@ -111,11 +113,11 @@ static noinline void trace_move_extent_finish2(struct data_update *u,
+ 	bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(insert));
+ 	prt_newline(&buf);
+ 
+-	trace_move_extent_finish(c, buf.buf);
++	trace_io_move_finish(c, buf.buf);
+ 	printbuf_exit(&buf);
+ }
+ 
+-static void trace_move_extent_fail2(struct data_update *m,
++static void trace_io_move_fail2(struct data_update *m,
+ 			 struct bkey_s_c new,
+ 			 struct bkey_s_c wrote,
+ 			 struct bkey_i *insert,
+@@ -126,7 +128,7 @@ static void trace_move_extent_fail2(struct data_update *m,
+ 	struct printbuf buf = PRINTBUF;
+ 	unsigned rewrites_found = 0;
+ 
+-	if (!trace_move_extent_fail_enabled())
++	if (!trace_io_move_fail_enabled())
+ 		return;
+ 
+ 	prt_str(&buf, msg);
+@@ -166,7 +168,7 @@ static void trace_move_extent_fail2(struct data_update *m,
+ 		bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(insert));
+ 	}
+ 
+-	trace_move_extent_fail(c, buf.buf);
++	trace_io_move_fail(c, buf.buf);
+ 	printbuf_exit(&buf);
+ }
+ 
+@@ -214,7 +216,7 @@ static int __bch2_data_update_index_update(struct btree_trans *trans,
+ 		new = bkey_i_to_extent(bch2_keylist_front(keys));
+ 
+ 		if (!bch2_extents_match(k, old)) {
+-			trace_move_extent_fail2(m, k, bkey_i_to_s_c(&new->k_i),
++			trace_io_move_fail2(m, k, bkey_i_to_s_c(&new->k_i),
+ 						NULL, "no match:");
+ 			goto nowork;
+ 		}
+@@ -254,7 +256,7 @@ static int __bch2_data_update_index_update(struct btree_trans *trans,
+ 		if (m->data_opts.rewrite_ptrs &&
+ 		    !rewrites_found &&
+ 		    bch2_bkey_durability(c, k) >= m->op.opts.data_replicas) {
+-			trace_move_extent_fail2(m, k, bkey_i_to_s_c(&new->k_i), insert, "no rewrites found:");
++			trace_io_move_fail2(m, k, bkey_i_to_s_c(&new->k_i), insert, "no rewrites found:");
+ 			goto nowork;
+ 		}
+ 
+@@ -271,7 +273,7 @@ static int __bch2_data_update_index_update(struct btree_trans *trans,
+ 			}
+ 
+ 		if (!bkey_val_u64s(&new->k)) {
+-			trace_move_extent_fail2(m, k, bkey_i_to_s_c(&new->k_i), insert, "new replicas conflicted:");
++			trace_io_move_fail2(m, k, bkey_i_to_s_c(&new->k_i), insert, "new replicas conflicted:");
+ 			goto nowork;
+ 		}
+ 
+@@ -352,7 +354,7 @@ static int __bch2_data_update_index_update(struct btree_trans *trans,
+ 			printbuf_exit(&buf);
+ 
+ 			bch2_fatal_error(c);
+-			ret = -EIO;
++			ret = -BCH_ERR_invalid_bkey;
+ 			goto out;
+ 		}
+ 
+@@ -385,9 +387,9 @@ static int __bch2_data_update_index_update(struct btree_trans *trans,
+ 		if (!ret) {
+ 			bch2_btree_iter_set_pos(&iter, next_pos);
+ 
+-			this_cpu_add(c->counters[BCH_COUNTER_move_extent_finish], new->k.size);
+-			if (trace_move_extent_finish_enabled())
+-				trace_move_extent_finish2(m, &new->k_i, insert);
++			this_cpu_add(c->counters[BCH_COUNTER_io_move_finish], new->k.size);
++			if (trace_io_move_finish_enabled())
++				trace_io_move_finish2(m, &new->k_i, insert);
+ 		}
+ err:
+ 		if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
+@@ -409,7 +411,7 @@ static int __bch2_data_update_index_update(struct btree_trans *trans,
+ 				     &m->stats->sectors_raced);
+ 		}
+ 
+-		count_event(c, move_extent_fail);
++		count_event(c, io_move_fail);
+ 
+ 		bch2_btree_iter_advance(&iter);
+ 		goto next;
+@@ -427,14 +429,17 @@ int bch2_data_update_index_update(struct bch_write_op *op)
+ 	return bch2_trans_run(op->c, __bch2_data_update_index_update(trans, op));
+ }
+ 
+-void bch2_data_update_read_done(struct data_update *m,
+-				struct bch_extent_crc_unpacked crc)
++void bch2_data_update_read_done(struct data_update *m)
+ {
++	m->read_done = true;
++
+ 	/* write bio must own pages: */
+ 	BUG_ON(!m->op.wbio.bio.bi_vcnt);
+ 
+-	m->op.crc = crc;
+-	m->op.wbio.bio.bi_iter.bi_size = crc.compressed_size << 9;
++	m->op.crc = m->rbio.pick.crc;
++	m->op.wbio.bio.bi_iter.bi_size = m->op.crc.compressed_size << 9;
++
++	this_cpu_add(m->op.c->counters[BCH_COUNTER_io_move_write], m->k.k->k.size);
+ 
+ 	closure_call(&m->op.cl, bch2_write, NULL, NULL);
+ }
+@@ -444,31 +449,34 @@ void bch2_data_update_exit(struct data_update *update)
+ 	struct bch_fs *c = update->op.c;
+ 	struct bkey_s_c k = bkey_i_to_s_c(update->k.k);
+ 
++	bch2_bio_free_pages_pool(c, &update->op.wbio.bio);
++	kfree(update->bvecs);
++	update->bvecs = NULL;
++
+ 	if (c->opts.nocow_enabled)
+ 		bkey_nocow_unlock(c, k);
+ 	bkey_put_dev_refs(c, k);
+-	bch2_bkey_buf_exit(&update->k, c);
+ 	bch2_disk_reservation_put(c, &update->op.res);
+-	bch2_bio_free_pages_pool(c, &update->op.wbio.bio);
++	bch2_bkey_buf_exit(&update->k, c);
+ }
+ 
+-static void bch2_update_unwritten_extent(struct btree_trans *trans,
+-				  struct data_update *update)
++static int bch2_update_unwritten_extent(struct btree_trans *trans,
++					struct data_update *update)
+ {
+ 	struct bch_fs *c = update->op.c;
+-	struct bio *bio = &update->op.wbio.bio;
+ 	struct bkey_i_extent *e;
+ 	struct write_point *wp;
+ 	struct closure cl;
+ 	struct btree_iter iter;
+ 	struct bkey_s_c k;
+-	int ret;
++	int ret = 0;
+ 
+ 	closure_init_stack(&cl);
+ 	bch2_keylist_init(&update->op.insert_keys, update->op.inline_keys);
+ 
+-	while (bio_sectors(bio)) {
+-		unsigned sectors = bio_sectors(bio);
++	while (bpos_lt(update->op.pos, update->k.k->k.p)) {
++		unsigned sectors = update->k.k->k.p.offset -
++			update->op.pos.offset;
+ 
+ 		bch2_trans_begin(trans);
+ 
+@@ -504,7 +512,7 @@ static void bch2_update_unwritten_extent(struct btree_trans *trans,
+ 		bch_err_fn_ratelimited(c, ret);
+ 
+ 		if (ret)
+-			return;
++			break;
+ 
+ 		sectors = min(sectors, wp->sectors_free);
+ 
+@@ -514,7 +522,6 @@ static void bch2_update_unwritten_extent(struct btree_trans *trans,
+ 		bch2_alloc_sectors_append_ptrs(c, wp, &e->k_i, sectors, false);
+ 		bch2_alloc_sectors_done(c, wp);
+ 
+-		bio_advance(bio, sectors << 9);
+ 		update->op.pos.offset += sectors;
+ 
+ 		extent_for_each_ptr(extent_i_to_s(e), ptr)
+@@ -533,13 +540,16 @@ static void bch2_update_unwritten_extent(struct btree_trans *trans,
+ 		bch2_trans_unlock(trans);
+ 		closure_sync(&cl);
+ 	}
++
++	return ret;
+ }
+ 
+ void bch2_data_update_opts_to_text(struct printbuf *out, struct bch_fs *c,
+ 				   struct bch_io_opts *io_opts,
+ 				   struct data_update_opts *data_opts)
+ {
+-	printbuf_tabstop_push(out, 20);
++	if (!out->nr_tabstops)
++		printbuf_tabstop_push(out, 20);
+ 
+ 	prt_str_indented(out, "rewrite ptrs:\t");
+ 	bch2_prt_u64_base2(out, data_opts->rewrite_ptrs);
+@@ -574,6 +584,17 @@ void bch2_data_update_to_text(struct printbuf *out, struct data_update *m)
+ 	bch2_bkey_val_to_text(out, m->op.c, bkey_i_to_s_c(m->k.k));
+ }
+ 
++void bch2_data_update_inflight_to_text(struct printbuf *out, struct data_update *m)
++{
++	bch2_bkey_val_to_text(out, m->op.c, bkey_i_to_s_c(m->k.k));
++	prt_newline(out);
++	printbuf_indent_add(out, 2);
++	bch2_data_update_opts_to_text(out, m->op.c, &m->op.opts, &m->data_opts);
++	prt_printf(out, "read_done:\t\%u\n", m->read_done);
++	bch2_write_op_to_text(out, &m->op);
++	printbuf_indent_sub(out, 2);
++}
++
+ int bch2_extent_drop_ptrs(struct btree_trans *trans,
+ 			  struct btree_iter *iter,
+ 			  struct bkey_s_c k,
+@@ -617,12 +638,85 @@ int bch2_extent_drop_ptrs(struct btree_trans *trans,
+ 		bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc);
+ }
+ 
++int bch2_data_update_bios_init(struct data_update *m, struct bch_fs *c,
++			       struct bch_io_opts *io_opts)
++{
++	struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(bkey_i_to_s_c(m->k.k));
++	const union bch_extent_entry *entry;
++	struct extent_ptr_decoded p;
++
++	/* write path might have to decompress data: */
++	unsigned buf_bytes = 0;
++	bkey_for_each_ptr_decode(&m->k.k->k, ptrs, p, entry)
++		buf_bytes = max_t(unsigned, buf_bytes, p.crc.uncompressed_size << 9);
++
++	unsigned nr_vecs = DIV_ROUND_UP(buf_bytes, PAGE_SIZE);
++
++	m->bvecs = kmalloc_array(nr_vecs, sizeof*(m->bvecs), GFP_KERNEL);
++	if (!m->bvecs)
++		return -ENOMEM;
++
++	bio_init(&m->rbio.bio,		NULL, m->bvecs, nr_vecs, REQ_OP_READ);
++	bio_init(&m->op.wbio.bio,	NULL, m->bvecs, nr_vecs, 0);
++
++	if (bch2_bio_alloc_pages(&m->op.wbio.bio, buf_bytes, GFP_KERNEL)) {
++		kfree(m->bvecs);
++		m->bvecs = NULL;
++		return -ENOMEM;
++	}
++
++	rbio_init(&m->rbio.bio, c, *io_opts, NULL);
++	m->rbio.data_update		= true;
++	m->rbio.bio.bi_iter.bi_size	= buf_bytes;
++	m->rbio.bio.bi_iter.bi_sector	= bkey_start_offset(&m->k.k->k);
++	m->op.wbio.bio.bi_ioprio	= IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
++	return 0;
++}
++
++static int can_write_extent(struct bch_fs *c, struct data_update *m)
++{
++	if ((m->op.flags & BCH_WRITE_alloc_nowait) &&
++	    unlikely(c->open_buckets_nr_free <= bch2_open_buckets_reserved(m->op.watermark)))
++		return -BCH_ERR_data_update_done_would_block;
++
++	unsigned target = m->op.flags & BCH_WRITE_only_specified_devs
++		? m->op.target
++		: 0;
++	struct bch_devs_mask devs = target_rw_devs(c, BCH_DATA_user, target);
++
++	darray_for_each(m->op.devs_have, i)
++		__clear_bit(*i, devs.d);
++
++	rcu_read_lock();
++	unsigned nr_replicas = 0, i;
++	for_each_set_bit(i, devs.d, BCH_SB_MEMBERS_MAX) {
++		struct bch_dev *ca = bch2_dev_rcu(c, i);
++
++		struct bch_dev_usage usage;
++		bch2_dev_usage_read_fast(ca, &usage);
++
++		if (!dev_buckets_free(ca, usage, m->op.watermark))
++			continue;
++
++		nr_replicas += ca->mi.durability;
++		if (nr_replicas >= m->op.nr_replicas)
++			break;
++	}
++	rcu_read_unlock();
++
++	if (!nr_replicas)
++		return -BCH_ERR_data_update_done_no_rw_devs;
++	if (nr_replicas < m->op.nr_replicas)
++		return -BCH_ERR_insufficient_devices;
++	return 0;
++}
++
+ int bch2_data_update_init(struct btree_trans *trans,
+ 			  struct btree_iter *iter,
+ 			  struct moving_context *ctxt,
+ 			  struct data_update *m,
+ 			  struct write_point_specifier wp,
+-			  struct bch_io_opts io_opts,
++			  struct bch_io_opts *io_opts,
+ 			  struct data_update_opts data_opts,
+ 			  enum btree_id btree_id,
+ 			  struct bkey_s_c k)
+@@ -640,16 +734,7 @@ int bch2_data_update_init(struct btree_trans *trans,
+ 	 * snapshots table - just skip it, we can move it later.
+ 	 */
+ 	if (unlikely(k.k->p.snapshot && !bch2_snapshot_exists(c, k.k->p.snapshot)))
+-		return -BCH_ERR_data_update_done;
+-
+-	if (!bkey_get_dev_refs(c, k))
+-		return -BCH_ERR_data_update_done;
+-
+-	if (c->opts.nocow_enabled &&
+-	    !bkey_nocow_lock(c, ctxt, k)) {
+-		bkey_put_dev_refs(c, k);
+-		return -BCH_ERR_nocow_lock_blocked;
+-	}
++		return -BCH_ERR_data_update_done_no_snapshot;
+ 
+ 	bch2_bkey_buf_init(&m->k);
+ 	bch2_bkey_buf_reassemble(&m->k, c, k);
+@@ -658,18 +743,18 @@ int bch2_data_update_init(struct btree_trans *trans,
+ 	m->ctxt		= ctxt;
+ 	m->stats	= ctxt ? ctxt->stats : NULL;
+ 
+-	bch2_write_op_init(&m->op, c, io_opts);
++	bch2_write_op_init(&m->op, c, *io_opts);
+ 	m->op.pos	= bkey_start_pos(k.k);
+ 	m->op.version	= k.k->bversion;
+ 	m->op.target	= data_opts.target;
+ 	m->op.write_point = wp;
+ 	m->op.nr_replicas = 0;
+-	m->op.flags	|= BCH_WRITE_PAGES_STABLE|
+-		BCH_WRITE_PAGES_OWNED|
+-		BCH_WRITE_DATA_ENCODED|
+-		BCH_WRITE_MOVE|
++	m->op.flags	|= BCH_WRITE_pages_stable|
++		BCH_WRITE_pages_owned|
++		BCH_WRITE_data_encoded|
++		BCH_WRITE_move|
+ 		m->data_opts.write_flags;
+-	m->op.compression_opt	= io_opts.background_compression;
++	m->op.compression_opt	= io_opts->background_compression;
+ 	m->op.watermark		= m->data_opts.btree_insert_flags & BCH_WATERMARK_MASK;
+ 
+ 	unsigned durability_have = 0, durability_removing = 0;
+@@ -707,7 +792,7 @@ int bch2_data_update_init(struct btree_trans *trans,
+ 		ptr_bit <<= 1;
+ 	}
+ 
+-	unsigned durability_required = max(0, (int) (io_opts.data_replicas - durability_have));
++	unsigned durability_required = max(0, (int) (io_opts->data_replicas - durability_have));
+ 
+ 	/*
+ 	 * If current extent durability is less than io_opts.data_replicas,
+@@ -740,28 +825,70 @@ int bch2_data_update_init(struct btree_trans *trans,
+ 		m->data_opts.rewrite_ptrs = 0;
+ 		/* if iter == NULL, it's just a promote */
+ 		if (iter)
+-			ret = bch2_extent_drop_ptrs(trans, iter, k, &io_opts, &m->data_opts);
+-		goto out;
++			ret = bch2_extent_drop_ptrs(trans, iter, k, io_opts, &m->data_opts);
++		if (!ret)
++			ret = -BCH_ERR_data_update_done_no_writes_needed;
++		goto out_bkey_buf_exit;
+ 	}
+ 
++	/*
++	 * Check if the allocation will succeed, to avoid getting an error later
++	 * in bch2_write() -> bch2_alloc_sectors_start() and doing a useless
++	 * read:
++	 *
++	 * This guards against
++	 * - BCH_WRITE_alloc_nowait allocations failing (promotes)
++	 * - Destination target full
++	 * - Device(s) in destination target offline
++	 * - Insufficient durability available in destination target
++	 *   (i.e. trying to move a durability=2 replica to a target with a
++	 *   single durability=2 device)
++	 */
++	ret = can_write_extent(c, m);
++	if (ret)
++		goto out_bkey_buf_exit;
++
+ 	if (reserve_sectors) {
+ 		ret = bch2_disk_reservation_add(c, &m->op.res, reserve_sectors,
+ 				m->data_opts.extra_replicas
+ 				? 0
+ 				: BCH_DISK_RESERVATION_NOFAIL);
+ 		if (ret)
+-			goto out;
++			goto out_bkey_buf_exit;
++	}
++
++	if (!bkey_get_dev_refs(c, k)) {
++		ret = -BCH_ERR_data_update_done_no_dev_refs;
++		goto out_put_disk_res;
++	}
++
++	if (c->opts.nocow_enabled &&
++	    !bkey_nocow_lock(c, ctxt, k)) {
++		ret = -BCH_ERR_nocow_lock_blocked;
++		goto out_put_dev_refs;
+ 	}
+ 
+ 	if (bkey_extent_is_unwritten(k)) {
+-		bch2_update_unwritten_extent(trans, m);
+-		goto out;
++		ret = bch2_update_unwritten_extent(trans, m) ?:
++			-BCH_ERR_data_update_done_unwritten;
++		goto out_nocow_unlock;
+ 	}
+ 
++	ret = bch2_data_update_bios_init(m, c, io_opts);
++	if (ret)
++		goto out_nocow_unlock;
++
+ 	return 0;
+-out:
+-	bch2_data_update_exit(m);
+-	return ret ?: -BCH_ERR_data_update_done;
++out_nocow_unlock:
++	if (c->opts.nocow_enabled)
++		bkey_nocow_unlock(c, k);
++out_put_dev_refs:
++	bkey_put_dev_refs(c, k);
++out_put_disk_res:
++	bch2_disk_reservation_put(c, &m->op.res);
++out_bkey_buf_exit:
++	bch2_bkey_buf_exit(&m->k, c);
++	return ret;
+ }
+ 
+ void bch2_data_update_opts_normalize(struct bkey_s_c k, struct data_update_opts *opts)
+diff --git a/fs/bcachefs/data_update.h b/fs/bcachefs/data_update.h
+index e4b50723428e..c194cbbf5b51 100644
+--- a/fs/bcachefs/data_update.h
++++ b/fs/bcachefs/data_update.h
+@@ -4,6 +4,7 @@
+ #define _BCACHEFS_DATA_UPDATE_H
+ 
+ #include "bkey_buf.h"
++#include "io_read.h"
+ #include "io_write_types.h"
+ 
+ struct moving_context;
+@@ -15,6 +16,9 @@ struct data_update_opts {
+ 	u8		extra_replicas;
+ 	unsigned	btree_insert_flags;
+ 	unsigned	write_flags;
++
++	int		read_dev;
++	bool		scrub;
+ };
+ 
+ void bch2_data_update_opts_to_text(struct printbuf *, struct bch_fs *,
+@@ -22,20 +26,24 @@ void bch2_data_update_opts_to_text(struct printbuf *, struct bch_fs *,
+ 
+ struct data_update {
+ 	/* extent being updated: */
++	bool			read_done;
+ 	enum btree_id		btree_id;
+ 	struct bkey_buf		k;
+ 	struct data_update_opts	data_opts;
+ 	struct moving_context	*ctxt;
+ 	struct bch_move_stats	*stats;
++
++	struct bch_read_bio	rbio;
+ 	struct bch_write_op	op;
++	struct bio_vec		*bvecs;
+ };
+ 
+ void bch2_data_update_to_text(struct printbuf *, struct data_update *);
++void bch2_data_update_inflight_to_text(struct printbuf *, struct data_update *);
+ 
+ int bch2_data_update_index_update(struct bch_write_op *);
+ 
+-void bch2_data_update_read_done(struct data_update *,
+-				struct bch_extent_crc_unpacked);
++void bch2_data_update_read_done(struct data_update *);
+ 
+ int bch2_extent_drop_ptrs(struct btree_trans *,
+ 			  struct btree_iter *,
+@@ -43,12 +51,15 @@ int bch2_extent_drop_ptrs(struct btree_trans *,
+ 			  struct bch_io_opts *,
+ 			  struct data_update_opts *);
+ 
++int bch2_data_update_bios_init(struct data_update *, struct bch_fs *,
++			       struct bch_io_opts *);
++
+ void bch2_data_update_exit(struct data_update *);
+ int bch2_data_update_init(struct btree_trans *, struct btree_iter *,
+ 			  struct moving_context *,
+ 			  struct data_update *,
+ 			  struct write_point_specifier,
+-			  struct bch_io_opts, struct data_update_opts,
++			  struct bch_io_opts *, struct data_update_opts,
+ 			  enum btree_id, struct bkey_s_c);
+ void bch2_data_update_opts_normalize(struct bkey_s_c, struct data_update_opts *);
+ 
+diff --git a/fs/bcachefs/debug.c b/fs/bcachefs/debug.c
+index 55333e82d1fe..788af88f6979 100644
+--- a/fs/bcachefs/debug.c
++++ b/fs/bcachefs/debug.c
+@@ -7,6 +7,7 @@
+  */
+ 
+ #include "bcachefs.h"
++#include "alloc_foreground.h"
+ #include "bkey_methods.h"
+ #include "btree_cache.h"
+ #include "btree_io.h"
+@@ -190,7 +191,7 @@ void bch2_btree_node_ondisk_to_text(struct printbuf *out, struct bch_fs *c,
+ 	unsigned offset = 0;
+ 	int ret;
+ 
+-	if (bch2_bkey_pick_read_device(c, bkey_i_to_s_c(&b->key), NULL, &pick) <= 0) {
++	if (bch2_bkey_pick_read_device(c, bkey_i_to_s_c(&b->key), NULL, &pick, -1) <= 0) {
+ 		prt_printf(out, "error getting device to read from: invalid device\n");
+ 		return;
+ 	}
+@@ -844,8 +845,11 @@ static void btree_deadlock_to_text(struct printbuf *out, struct bch_fs *c)
+ 	seqmutex_unlock(&c->btree_trans_lock);
+ }
+ 
+-static ssize_t bch2_btree_deadlock_read(struct file *file, char __user *buf,
+-					    size_t size, loff_t *ppos)
++typedef void (*fs_to_text_fn)(struct printbuf *, struct bch_fs *);
++
++static ssize_t bch2_simple_print(struct file *file, char __user *buf,
++				 size_t size, loff_t *ppos,
++				 fs_to_text_fn fn)
+ {
+ 	struct dump_iter *i = file->private_data;
+ 	struct bch_fs *c = i->c;
+@@ -856,7 +860,7 @@ static ssize_t bch2_btree_deadlock_read(struct file *file, char __user *buf,
+ 	i->ret	= 0;
+ 
+ 	if (!i->iter) {
+-		btree_deadlock_to_text(&i->buf, c);
++		fn(&i->buf, c);
+ 		i->iter++;
+ 	}
+ 
+@@ -869,6 +873,12 @@ static ssize_t bch2_btree_deadlock_read(struct file *file, char __user *buf,
+ 	return ret ?: i->ret;
+ }
+ 
++static ssize_t bch2_btree_deadlock_read(struct file *file, char __user *buf,
++					size_t size, loff_t *ppos)
++{
++	return bch2_simple_print(file, buf, size, ppos, btree_deadlock_to_text);
++}
++
+ static const struct file_operations btree_deadlock_ops = {
+ 	.owner		= THIS_MODULE,
+ 	.open		= bch2_dump_open,
+@@ -876,6 +886,19 @@ static const struct file_operations btree_deadlock_ops = {
+ 	.read		= bch2_btree_deadlock_read,
+ };
+ 
++static ssize_t bch2_write_points_read(struct file *file, char __user *buf,
++				     size_t size, loff_t *ppos)
++{
++	return bch2_simple_print(file, buf, size, ppos, bch2_write_points_to_text);
++}
++
++static const struct file_operations write_points_ops = {
++	.owner		= THIS_MODULE,
++	.open		= bch2_dump_open,
++	.release	= bch2_dump_release,
++	.read		= bch2_write_points_read,
++};
++
+ void bch2_fs_debug_exit(struct bch_fs *c)
+ {
+ 	if (!IS_ERR_OR_NULL(c->fs_debug_dir))
+@@ -927,6 +950,9 @@ void bch2_fs_debug_init(struct bch_fs *c)
+ 	debugfs_create_file("btree_deadlock", 0400, c->fs_debug_dir,
+ 			    c->btree_debug, &btree_deadlock_ops);
+ 
++	debugfs_create_file("write_points", 0400, c->fs_debug_dir,
++			    c->btree_debug, &write_points_ops);
++
+ 	c->btree_debug_dir = debugfs_create_dir("btrees", c->fs_debug_dir);
+ 	if (IS_ERR_OR_NULL(c->btree_debug_dir))
+ 		return;
+diff --git a/fs/bcachefs/dirent.c b/fs/bcachefs/dirent.c
+index 600eee936f13..d7f9f79318a2 100644
+--- a/fs/bcachefs/dirent.c
++++ b/fs/bcachefs/dirent.c
+@@ -13,6 +13,40 @@
+ 
+ #include <linux/dcache.h>
+ 
++static int bch2_casefold(struct btree_trans *trans, const struct bch_hash_info *info,
++				const struct qstr *str, struct qstr *out_cf)
++{
++	*out_cf = (struct qstr) QSTR_INIT(NULL, 0);
++
++#ifdef CONFIG_UNICODE
++	unsigned char *buf = bch2_trans_kmalloc(trans, BCH_NAME_MAX + 1);
++	int ret = PTR_ERR_OR_ZERO(buf);
++	if (ret)
++		return ret;
++
++	ret = utf8_casefold(info->cf_encoding, str, buf, BCH_NAME_MAX + 1);
++	if (ret <= 0)
++		return ret;
++
++	*out_cf = (struct qstr) QSTR_INIT(buf, ret);
++	return 0;
++#else
++	return -EOPNOTSUPP;
++#endif
++}
++
++static inline int bch2_maybe_casefold(struct btree_trans *trans,
++				      const struct bch_hash_info *info,
++				      const struct qstr *str, struct qstr *out_cf)
++{
++	if (likely(!info->cf_encoding)) {
++		*out_cf = *str;
++		return 0;
++	} else {
++		return bch2_casefold(trans, info, str, out_cf);
++	}
++}
++
+ static unsigned bch2_dirent_name_bytes(struct bkey_s_c_dirent d)
+ {
+ 	if (bkey_val_bytes(d.k) < offsetof(struct bch_dirent, d_name))
+@@ -28,13 +62,38 @@ static unsigned bch2_dirent_name_bytes(struct bkey_s_c_dirent d)
+ #endif
+ 
+ 	return bkey_bytes -
+-		offsetof(struct bch_dirent, d_name) -
++		(d.v->d_casefold
++		? offsetof(struct bch_dirent, d_cf_name_block.d_names)
++		: offsetof(struct bch_dirent, d_name)) -
+ 		trailing_nuls;
+ }
+ 
+ struct qstr bch2_dirent_get_name(struct bkey_s_c_dirent d)
+ {
+-	return (struct qstr) QSTR_INIT(d.v->d_name, bch2_dirent_name_bytes(d));
++	if (d.v->d_casefold) {
++		unsigned name_len = le16_to_cpu(d.v->d_cf_name_block.d_name_len);
++		return (struct qstr) QSTR_INIT(&d.v->d_cf_name_block.d_names[0], name_len);
++	} else {
++		return (struct qstr) QSTR_INIT(d.v->d_name, bch2_dirent_name_bytes(d));
++	}
++}
++
++static struct qstr bch2_dirent_get_casefold_name(struct bkey_s_c_dirent d)
++{
++	if (d.v->d_casefold) {
++		unsigned name_len = le16_to_cpu(d.v->d_cf_name_block.d_name_len);
++		unsigned cf_name_len = le16_to_cpu(d.v->d_cf_name_block.d_cf_name_len);
++		return (struct qstr) QSTR_INIT(&d.v->d_cf_name_block.d_names[name_len], cf_name_len);
++	} else {
++		return (struct qstr) QSTR_INIT(NULL, 0);
++	}
++}
++
++static inline struct qstr bch2_dirent_get_lookup_name(struct bkey_s_c_dirent d)
++{
++	return d.v->d_casefold
++		? bch2_dirent_get_casefold_name(d)
++		: bch2_dirent_get_name(d);
+ }
+ 
+ static u64 bch2_dirent_hash(const struct bch_hash_info *info,
+@@ -57,7 +116,7 @@ static u64 dirent_hash_key(const struct bch_hash_info *info, const void *key)
+ static u64 dirent_hash_bkey(const struct bch_hash_info *info, struct bkey_s_c k)
+ {
+ 	struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
+-	struct qstr name = bch2_dirent_get_name(d);
++	struct qstr name = bch2_dirent_get_lookup_name(d);
+ 
+ 	return bch2_dirent_hash(info, &name);
+ }
+@@ -65,7 +124,7 @@ static u64 dirent_hash_bkey(const struct bch_hash_info *info, struct bkey_s_c k)
+ static bool dirent_cmp_key(struct bkey_s_c _l, const void *_r)
+ {
+ 	struct bkey_s_c_dirent l = bkey_s_c_to_dirent(_l);
+-	const struct qstr l_name = bch2_dirent_get_name(l);
++	const struct qstr l_name = bch2_dirent_get_lookup_name(l);
+ 	const struct qstr *r_name = _r;
+ 
+ 	return !qstr_eq(l_name, *r_name);
+@@ -75,8 +134,8 @@ static bool dirent_cmp_bkey(struct bkey_s_c _l, struct bkey_s_c _r)
+ {
+ 	struct bkey_s_c_dirent l = bkey_s_c_to_dirent(_l);
+ 	struct bkey_s_c_dirent r = bkey_s_c_to_dirent(_r);
+-	const struct qstr l_name = bch2_dirent_get_name(l);
+-	const struct qstr r_name = bch2_dirent_get_name(r);
++	const struct qstr l_name = bch2_dirent_get_lookup_name(l);
++	const struct qstr r_name = bch2_dirent_get_lookup_name(r);
+ 
+ 	return !qstr_eq(l_name, r_name);
+ }
+@@ -104,17 +163,19 @@ int bch2_dirent_validate(struct bch_fs *c, struct bkey_s_c k,
+ 			 struct bkey_validate_context from)
+ {
+ 	struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
++	unsigned name_block_len = bch2_dirent_name_bytes(d);
+ 	struct qstr d_name = bch2_dirent_get_name(d);
++	struct qstr d_cf_name = bch2_dirent_get_casefold_name(d);
+ 	int ret = 0;
+ 
+ 	bkey_fsck_err_on(!d_name.len,
+ 			 c, dirent_empty_name,
+ 			 "empty name");
+ 
+-	bkey_fsck_err_on(bkey_val_u64s(k.k) > dirent_val_u64s(d_name.len),
++	bkey_fsck_err_on(d_name.len + d_cf_name.len > name_block_len,
+ 			 c, dirent_val_too_big,
+-			 "value too big (%zu > %u)",
+-			 bkey_val_u64s(k.k), dirent_val_u64s(d_name.len));
++			 "dirent names exceed bkey size (%d + %d > %d)",
++			 d_name.len, d_cf_name.len, name_block_len);
+ 
+ 	/*
+ 	 * Check new keys don't exceed the max length
+@@ -142,6 +203,18 @@ int bch2_dirent_validate(struct bch_fs *c, struct bkey_s_c k,
+ 			 le64_to_cpu(d.v->d_inum) == d.k->p.inode,
+ 			 c, dirent_to_itself,
+ 			 "dirent points to own directory");
++
++	if (d.v->d_casefold) {
++		bkey_fsck_err_on(from.from == BKEY_VALIDATE_commit &&
++				 d_cf_name.len > BCH_NAME_MAX,
++				 c, dirent_cf_name_too_big,
++				 "dirent w/ cf name too big (%u > %u)",
++				 d_cf_name.len, BCH_NAME_MAX);
++
++		bkey_fsck_err_on(d_cf_name.len != strnlen(d_cf_name.name, d_cf_name.len),
++				 c, dirent_stray_data_after_cf_name,
++				 "dirent has stray data after cf name's NUL");
++	}
+ fsck_err:
+ 	return ret;
+ }
+@@ -163,15 +236,14 @@ void bch2_dirent_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c
+ 	prt_printf(out, " type %s", bch2_d_type_str(d.v->d_type));
+ }
+ 
+-static struct bkey_i_dirent *dirent_create_key(struct btree_trans *trans,
+-				subvol_inum dir, u8 type,
+-				const struct qstr *name, u64 dst)
++static struct bkey_i_dirent *dirent_alloc_key(struct btree_trans *trans,
++				subvol_inum dir,
++				u8 type,
++				int name_len, int cf_name_len,
++				u64 dst)
+ {
+ 	struct bkey_i_dirent *dirent;
+-	unsigned u64s = BKEY_U64s + dirent_val_u64s(name->len);
+-
+-	if (name->len > BCH_NAME_MAX)
+-		return ERR_PTR(-ENAMETOOLONG);
++	unsigned u64s = BKEY_U64s + dirent_val_u64s(name_len, cf_name_len);
+ 
+ 	BUG_ON(u64s > U8_MAX);
+ 
+@@ -190,14 +262,65 @@ static struct bkey_i_dirent *dirent_create_key(struct btree_trans *trans,
+ 	}
+ 
+ 	dirent->v.d_type = type;
++	dirent->v.d_unused = 0;
++	dirent->v.d_casefold = cf_name_len ? 1 : 0;
+ 
+-	memcpy(dirent->v.d_name, name->name, name->len);
+-	memset(dirent->v.d_name + name->len, 0,
+-	       bkey_val_bytes(&dirent->k) -
+-	       offsetof(struct bch_dirent, d_name) -
+-	       name->len);
++	return dirent;
++}
+ 
+-	EBUG_ON(bch2_dirent_name_bytes(dirent_i_to_s_c(dirent)) != name->len);
++static void dirent_init_regular_name(struct bkey_i_dirent *dirent,
++				     const struct qstr *name)
++{
++	EBUG_ON(dirent->v.d_casefold);
++
++	memcpy(&dirent->v.d_name[0], name->name, name->len);
++	memset(&dirent->v.d_name[name->len], 0,
++		bkey_val_bytes(&dirent->k) -
++		offsetof(struct bch_dirent, d_name) -
++		name->len);
++}
++
++static void dirent_init_casefolded_name(struct bkey_i_dirent *dirent,
++					const struct qstr *name,
++					const struct qstr *cf_name)
++{
++	EBUG_ON(!dirent->v.d_casefold);
++	EBUG_ON(!cf_name->len);
++
++	dirent->v.d_cf_name_block.d_name_len = name->len;
++	dirent->v.d_cf_name_block.d_cf_name_len = cf_name->len;
++	memcpy(&dirent->v.d_cf_name_block.d_names[0], name->name, name->len);
++	memcpy(&dirent->v.d_cf_name_block.d_names[name->len], cf_name->name, cf_name->len);
++	memset(&dirent->v.d_cf_name_block.d_names[name->len + cf_name->len], 0,
++		bkey_val_bytes(&dirent->k) -
++		offsetof(struct bch_dirent, d_cf_name_block.d_names) -
++		name->len + cf_name->len);
++
++	EBUG_ON(bch2_dirent_get_casefold_name(dirent_i_to_s_c(dirent)).len != cf_name->len);
++}
++
++static struct bkey_i_dirent *dirent_create_key(struct btree_trans *trans,
++				subvol_inum dir,
++				u8 type,
++				const struct qstr *name,
++				const struct qstr *cf_name,
++				u64 dst)
++{
++	struct bkey_i_dirent *dirent;
++
++	if (name->len > BCH_NAME_MAX)
++		return ERR_PTR(-ENAMETOOLONG);
++
++	dirent = dirent_alloc_key(trans, dir, type, name->len, cf_name ? cf_name->len : 0, dst);
++	if (IS_ERR(dirent))
++		return dirent;
++
++	if (cf_name)
++		dirent_init_casefolded_name(dirent, name, cf_name);
++	else
++		dirent_init_regular_name(dirent, name);
++
++	EBUG_ON(bch2_dirent_get_name(dirent_i_to_s_c(dirent)).len != name->len);
+ 
+ 	return dirent;
+ }
+@@ -213,7 +336,7 @@ int bch2_dirent_create_snapshot(struct btree_trans *trans,
+ 	struct bkey_i_dirent *dirent;
+ 	int ret;
+ 
+-	dirent = dirent_create_key(trans, dir_inum, type, name, dst_inum);
++	dirent = dirent_create_key(trans, dir_inum, type, name, NULL, dst_inum);
+ 	ret = PTR_ERR_OR_ZERO(dirent);
+ 	if (ret)
+ 		return ret;
+@@ -233,16 +356,28 @@ int bch2_dirent_create(struct btree_trans *trans, subvol_inum dir,
+ 		       const struct bch_hash_info *hash_info,
+ 		       u8 type, const struct qstr *name, u64 dst_inum,
+ 		       u64 *dir_offset,
++		       u64 *i_size,
+ 		       enum btree_iter_update_trigger_flags flags)
+ {
+ 	struct bkey_i_dirent *dirent;
+ 	int ret;
+ 
+-	dirent = dirent_create_key(trans, dir, type, name, dst_inum);
++	if (hash_info->cf_encoding) {
++		struct qstr cf_name;
++		ret = bch2_casefold(trans, hash_info, name, &cf_name);
++		if (ret)
++			return ret;
++		dirent = dirent_create_key(trans, dir, type, name, &cf_name, dst_inum);
++	} else {
++		dirent = dirent_create_key(trans, dir, type, name, NULL, dst_inum);
++	}
++
+ 	ret = PTR_ERR_OR_ZERO(dirent);
+ 	if (ret)
+ 		return ret;
+ 
++	*i_size += bkey_bytes(&dirent->k);
++
+ 	ret = bch2_hash_set(trans, bch2_dirent_hash_desc, hash_info,
+ 			    dir, &dirent->k_i, flags);
+ 	*dir_offset = dirent->k.p.offset;
+@@ -275,12 +410,13 @@ int bch2_dirent_read_target(struct btree_trans *trans, subvol_inum dir,
+ }
+ 
+ int bch2_dirent_rename(struct btree_trans *trans,
+-		subvol_inum src_dir, struct bch_hash_info *src_hash,
+-		subvol_inum dst_dir, struct bch_hash_info *dst_hash,
++		subvol_inum src_dir, struct bch_hash_info *src_hash, u64 *src_dir_i_size,
++		subvol_inum dst_dir, struct bch_hash_info *dst_hash, u64 *dst_dir_i_size,
+ 		const struct qstr *src_name, subvol_inum *src_inum, u64 *src_offset,
+ 		const struct qstr *dst_name, subvol_inum *dst_inum, u64 *dst_offset,
+ 		enum bch_rename_mode mode)
+ {
++	struct qstr src_name_lookup, dst_name_lookup;
+ 	struct btree_iter src_iter = { NULL };
+ 	struct btree_iter dst_iter = { NULL };
+ 	struct bkey_s_c old_src, old_dst = bkey_s_c_null;
+@@ -295,8 +431,11 @@ int bch2_dirent_rename(struct btree_trans *trans,
+ 	memset(dst_inum, 0, sizeof(*dst_inum));
+ 
+ 	/* Lookup src: */
++	ret = bch2_maybe_casefold(trans, src_hash, src_name, &src_name_lookup);
++	if (ret)
++		goto out;
+ 	old_src = bch2_hash_lookup(trans, &src_iter, bch2_dirent_hash_desc,
+-				   src_hash, src_dir, src_name,
++				   src_hash, src_dir, &src_name_lookup,
+ 				   BTREE_ITER_intent);
+ 	ret = bkey_err(old_src);
+ 	if (ret)
+@@ -308,6 +447,9 @@ int bch2_dirent_rename(struct btree_trans *trans,
+ 		goto out;
+ 
+ 	/* Lookup dst: */
++	ret = bch2_maybe_casefold(trans, dst_hash, dst_name, &dst_name_lookup);
++	if (ret)
++		goto out;
+ 	if (mode == BCH_RENAME) {
+ 		/*
+ 		 * Note that we're _not_ checking if the target already exists -
+@@ -315,12 +457,12 @@ int bch2_dirent_rename(struct btree_trans *trans,
+ 		 * correctness:
+ 		 */
+ 		ret = bch2_hash_hole(trans, &dst_iter, bch2_dirent_hash_desc,
+-				     dst_hash, dst_dir, dst_name);
++				     dst_hash, dst_dir, &dst_name_lookup);
+ 		if (ret)
+ 			goto out;
+ 	} else {
+ 		old_dst = bch2_hash_lookup(trans, &dst_iter, bch2_dirent_hash_desc,
+-					    dst_hash, dst_dir, dst_name,
++					    dst_hash, dst_dir, &dst_name_lookup,
+ 					    BTREE_ITER_intent);
+ 		ret = bkey_err(old_dst);
+ 		if (ret)
+@@ -336,7 +478,8 @@ int bch2_dirent_rename(struct btree_trans *trans,
+ 		*src_offset = dst_iter.pos.offset;
+ 
+ 	/* Create new dst key: */
+-	new_dst = dirent_create_key(trans, dst_dir, 0, dst_name, 0);
++	new_dst = dirent_create_key(trans, dst_dir, 0, dst_name,
++				    dst_hash->cf_encoding ? &dst_name_lookup : NULL, 0);
+ 	ret = PTR_ERR_OR_ZERO(new_dst);
+ 	if (ret)
+ 		goto out;
+@@ -346,7 +489,8 @@ int bch2_dirent_rename(struct btree_trans *trans,
+ 
+ 	/* Create new src key: */
+ 	if (mode == BCH_RENAME_EXCHANGE) {
+-		new_src = dirent_create_key(trans, src_dir, 0, src_name, 0);
++		new_src = dirent_create_key(trans, src_dir, 0, src_name,
++					    src_hash->cf_encoding ? &src_name_lookup : NULL, 0);
+ 		ret = PTR_ERR_OR_ZERO(new_src);
+ 		if (ret)
+ 			goto out;
+@@ -406,6 +550,14 @@ int bch2_dirent_rename(struct btree_trans *trans,
+ 	    new_src->v.d_type == DT_SUBVOL)
+ 		new_src->v.d_parent_subvol = cpu_to_le32(src_dir.subvol);
+ 
++	if (old_dst.k)
++		*dst_dir_i_size -= bkey_bytes(old_dst.k);
++	*src_dir_i_size -= bkey_bytes(old_src.k);
++
++	if (mode == BCH_RENAME_EXCHANGE)
++		*src_dir_i_size += bkey_bytes(&new_src->k);
++	*dst_dir_i_size += bkey_bytes(&new_dst->k);
++
+ 	ret = bch2_trans_update(trans, &dst_iter, &new_dst->k_i, 0);
+ 	if (ret)
+ 		goto out;
+@@ -465,9 +617,14 @@ int bch2_dirent_lookup_trans(struct btree_trans *trans,
+ 			     const struct qstr *name, subvol_inum *inum,
+ 			     unsigned flags)
+ {
++	struct qstr lookup_name;
++	int ret = bch2_maybe_casefold(trans, hash_info, name, &lookup_name);
++	if (ret)
++		return ret;
++
+ 	struct bkey_s_c k = bch2_hash_lookup(trans, iter, bch2_dirent_hash_desc,
+-					     hash_info, dir, name, flags);
+-	int ret = bkey_err(k);
++					     hash_info, dir, &lookup_name, flags);
++	ret = bkey_err(k);
+ 	if (ret)
+ 		goto err;
+ 
+@@ -572,3 +729,54 @@ int bch2_readdir(struct bch_fs *c, subvol_inum inum, struct dir_context *ctx)
+ 
+ 	return ret < 0 ? ret : 0;
+ }
++
++/* fsck */
++
++static int lookup_first_inode(struct btree_trans *trans, u64 inode_nr,
++			      struct bch_inode_unpacked *inode)
++{
++	struct btree_iter iter;
++	struct bkey_s_c k;
++	int ret;
++
++	for_each_btree_key_norestart(trans, iter, BTREE_ID_inodes, POS(0, inode_nr),
++				     BTREE_ITER_all_snapshots, k, ret) {
++		if (k.k->p.offset != inode_nr)
++			break;
++		if (!bkey_is_inode(k.k))
++			continue;
++		ret = bch2_inode_unpack(k, inode);
++		goto found;
++	}
++	ret = -BCH_ERR_ENOENT_inode;
++found:
++	bch_err_msg(trans->c, ret, "fetching inode %llu", inode_nr);
++	bch2_trans_iter_exit(trans, &iter);
++	return ret;
++}
++
++int bch2_fsck_remove_dirent(struct btree_trans *trans, struct bpos pos)
++{
++	struct bch_fs *c = trans->c;
++	struct btree_iter iter;
++	struct bch_inode_unpacked dir_inode;
++	struct bch_hash_info dir_hash_info;
++	int ret;
++
++	ret = lookup_first_inode(trans, pos.inode, &dir_inode);
++	if (ret)
++		goto err;
++
++	dir_hash_info = bch2_hash_info_init(c, &dir_inode);
++
++	bch2_trans_iter_init(trans, &iter, BTREE_ID_dirents, pos, BTREE_ITER_intent);
++
++	ret =   bch2_btree_iter_traverse(&iter) ?:
++		bch2_hash_delete_at(trans, bch2_dirent_hash_desc,
++				    &dir_hash_info, &iter,
++				    BTREE_UPDATE_internal_snapshot_node);
++	bch2_trans_iter_exit(trans, &iter);
++err:
++	bch_err_fn(c, ret);
++	return ret;
++}
+diff --git a/fs/bcachefs/dirent.h b/fs/bcachefs/dirent.h
+index 362b3b2f2f2e..0880772b80a9 100644
+--- a/fs/bcachefs/dirent.h
++++ b/fs/bcachefs/dirent.h
+@@ -25,10 +25,13 @@ struct bch_inode_info;
+ 
+ struct qstr bch2_dirent_get_name(struct bkey_s_c_dirent d);
+ 
+-static inline unsigned dirent_val_u64s(unsigned len)
++static inline unsigned dirent_val_u64s(unsigned len, unsigned cf_len)
+ {
+-	return DIV_ROUND_UP(offsetof(struct bch_dirent, d_name) + len,
+-			    sizeof(u64));
++	unsigned bytes = cf_len
++		? offsetof(struct bch_dirent, d_cf_name_block.d_names) + len + cf_len
++		: offsetof(struct bch_dirent, d_name) + len;
++
++	return DIV_ROUND_UP(bytes, sizeof(u64));
+ }
+ 
+ int bch2_dirent_read_target(struct btree_trans *, subvol_inum,
+@@ -47,7 +50,7 @@ int bch2_dirent_create_snapshot(struct btree_trans *, u32, u64, u32,
+ 			enum btree_iter_update_trigger_flags);
+ int bch2_dirent_create(struct btree_trans *, subvol_inum,
+ 		       const struct bch_hash_info *, u8,
+-		       const struct qstr *, u64, u64 *,
++		       const struct qstr *, u64, u64 *, u64 *,
+ 		       enum btree_iter_update_trigger_flags);
+ 
+ static inline unsigned vfs_d_type(unsigned type)
+@@ -62,8 +65,8 @@ enum bch_rename_mode {
+ };
+ 
+ int bch2_dirent_rename(struct btree_trans *,
+-		       subvol_inum, struct bch_hash_info *,
+-		       subvol_inum, struct bch_hash_info *,
++		       subvol_inum, struct bch_hash_info *, u64 *,
++		       subvol_inum, struct bch_hash_info *, u64 *,
+ 		       const struct qstr *, subvol_inum *, u64 *,
+ 		       const struct qstr *, subvol_inum *, u64 *,
+ 		       enum bch_rename_mode);
+@@ -79,4 +82,6 @@ int bch2_empty_dir_snapshot(struct btree_trans *, u64, u32, u32);
+ int bch2_empty_dir_trans(struct btree_trans *, subvol_inum);
+ int bch2_readdir(struct bch_fs *, subvol_inum, struct dir_context *);
+ 
++int bch2_fsck_remove_dirent(struct btree_trans *, struct bpos);
++
+ #endif /* _BCACHEFS_DIRENT_H */
+diff --git a/fs/bcachefs/dirent_format.h b/fs/bcachefs/dirent_format.h
+index 5e116b88e814..a46dbddd21aa 100644
+--- a/fs/bcachefs/dirent_format.h
++++ b/fs/bcachefs/dirent_format.h
+@@ -29,9 +29,25 @@ struct bch_dirent {
+ 	 * Copy of mode bits 12-15 from the target inode - so userspace can get
+ 	 * the filetype without having to do a stat()
+ 	 */
+-	__u8			d_type;
++#if defined(__LITTLE_ENDIAN_BITFIELD)
++	__u8			d_type:5,
++				d_unused:2,
++				d_casefold:1;
++#elif defined(__BIG_ENDIAN_BITFIELD)
++	__u8			d_casefold:1,
++				d_unused:2,
++				d_type:5;
++#endif
+ 
+-	__u8			d_name[];
++	union {
++	struct {
++		__u8		d_pad;
++		__le16		d_name_len;
++		__le16		d_cf_name_len;
++		__u8		d_names[];
++	} d_cf_name_block __packed;
++	__DECLARE_FLEX_ARRAY(__u8, d_name);
++	} __packed;
+ } __packed __aligned(8);
+ 
+ #define DT_SUBVOL	16
+diff --git a/fs/bcachefs/disk_accounting.c b/fs/bcachefs/disk_accounting.c
+index b32e91ba8be8..8a8de61429d8 100644
+--- a/fs/bcachefs/disk_accounting.c
++++ b/fs/bcachefs/disk_accounting.c
+@@ -135,6 +135,12 @@ static inline bool is_zero(char *start, char *end)
+ 
+ #define field_end(p, member)	(((void *) (&p.member)) + sizeof(p.member))
+ 
++static const unsigned bch2_accounting_type_nr_counters[] = {
++#define x(f, id, nr)	[BCH_DISK_ACCOUNTING_##f]	= nr,
++	BCH_DISK_ACCOUNTING_TYPES()
++#undef x
++};
++
+ int bch2_accounting_validate(struct bch_fs *c, struct bkey_s_c k,
+ 			     struct bkey_validate_context from)
+ {
+@@ -193,6 +199,11 @@ int bch2_accounting_validate(struct bch_fs *c, struct bkey_s_c k,
+ 	bkey_fsck_err_on(!is_zero(end, (void *) (&acc_k + 1)),
+ 			 c, accounting_key_junk_at_end,
+ 			 "junk at end of accounting key");
++
++	bkey_fsck_err_on(bch2_accounting_counters(k.k) != bch2_accounting_type_nr_counters[acc_k.type],
++			 c, accounting_key_nr_counters_wrong,
++			 "accounting key with %u counters, should be %u",
++			 bch2_accounting_counters(k.k), bch2_accounting_type_nr_counters[acc_k.type]);
+ fsck_err:
+ 	return ret;
+ }
+diff --git a/fs/bcachefs/disk_accounting.h b/fs/bcachefs/disk_accounting.h
+index f4372cafea2e..abb1f6206fe9 100644
+--- a/fs/bcachefs/disk_accounting.h
++++ b/fs/bcachefs/disk_accounting.h
+@@ -33,10 +33,12 @@ static inline bool bch2_accounting_key_is_zero(struct bkey_s_c_accounting a)
+ static inline void bch2_accounting_accumulate(struct bkey_i_accounting *dst,
+ 					      struct bkey_s_c_accounting src)
+ {
+-	EBUG_ON(dst->k.u64s != src.k->u64s);
+-
+-	for (unsigned i = 0; i < bch2_accounting_counters(&dst->k); i++)
++	for (unsigned i = 0;
++	     i < min(bch2_accounting_counters(&dst->k),
++		     bch2_accounting_counters(src.k));
++	     i++)
+ 		dst->v.d[i] += src.v->d[i];
++
+ 	if (bversion_cmp(dst->k.bversion, src.k->bversion) < 0)
+ 		dst->k.bversion = src.k->bversion;
+ }
+@@ -85,6 +87,24 @@ static inline struct bpos disk_accounting_pos_to_bpos(struct disk_accounting_pos
+ 
+ int bch2_disk_accounting_mod(struct btree_trans *, struct disk_accounting_pos *,
+ 			     s64 *, unsigned, bool);
++
++#define disk_accounting_key_init(_k, _type, ...)			\
++do {									\
++	memset(&(_k), 0, sizeof(_k));					\
++	(_k).type	= BCH_DISK_ACCOUNTING_##_type;			\
++	(_k)._type	= (struct bch_acct_##_type) { __VA_ARGS__ };	\
++} while (0)
++
++#define bch2_disk_accounting_mod2_nr(_trans, _gc, _v, _nr, ...)		\
++({									\
++	struct disk_accounting_pos pos;					\
++	disk_accounting_key_init(pos, __VA_ARGS__);			\
++	bch2_disk_accounting_mod(trans, &pos, _v, _nr, _gc);		\
++})
++
++#define bch2_disk_accounting_mod2(_trans, _gc, _v, ...)			\
++	bch2_disk_accounting_mod2_nr(_trans, _gc, _v, ARRAY_SIZE(_v), __VA_ARGS__)
++
+ int bch2_mod_dev_cached_sectors(struct btree_trans *, unsigned, s64, bool);
+ 
+ int bch2_accounting_validate(struct bch_fs *, struct bkey_s_c,
+diff --git a/fs/bcachefs/disk_accounting_format.h b/fs/bcachefs/disk_accounting_format.h
+index 7b6e6c97e6aa..8269af1dbe2a 100644
+--- a/fs/bcachefs/disk_accounting_format.h
++++ b/fs/bcachefs/disk_accounting_format.h
+@@ -95,40 +95,81 @@ static inline bool data_type_is_hidden(enum bch_data_type type)
+ 	}
+ }
+ 
++/*
++ * field 1: name
++ * field 2: id
++ * field 3: number of counters (max 3)
++ */
++
+ #define BCH_DISK_ACCOUNTING_TYPES()		\
+-	x(nr_inodes,		0)		\
+-	x(persistent_reserved,	1)		\
+-	x(replicas,		2)		\
+-	x(dev_data_type,	3)		\
+-	x(compression,		4)		\
+-	x(snapshot,		5)		\
+-	x(btree,		6)		\
+-	x(rebalance_work,	7)		\
+-	x(inum,			8)
++	x(nr_inodes,		0,	1)	\
++	x(persistent_reserved,	1,	1)	\
++	x(replicas,		2,	1)	\
++	x(dev_data_type,	3,	3)	\
++	x(compression,		4,	3)	\
++	x(snapshot,		5,	1)	\
++	x(btree,		6,	1)	\
++	x(rebalance_work,	7,	1)	\
++	x(inum,			8,	3)
+ 
+ enum disk_accounting_type {
+-#define x(f, nr)	BCH_DISK_ACCOUNTING_##f	= nr,
++#define x(f, nr, ...)	BCH_DISK_ACCOUNTING_##f	= nr,
+ 	BCH_DISK_ACCOUNTING_TYPES()
+ #undef x
+ 	BCH_DISK_ACCOUNTING_TYPE_NR,
+ };
+ 
+-struct bch_nr_inodes {
++/*
++ * No subtypes - number of inodes in the entire filesystem
++ *
++ * XXX: perhaps we could add a per-subvolume counter?
++ */
++struct bch_acct_nr_inodes {
+ };
+ 
+-struct bch_persistent_reserved {
++/*
++ * Tracks KEY_TYPE_reservation sectors, broken out by number of replicas for the
++ * reservation:
++ */
++struct bch_acct_persistent_reserved {
+ 	__u8			nr_replicas;
+ };
+ 
+-struct bch_dev_data_type {
++/*
++ * device, data type counter fields:
++ * [
++ *   nr_buckets
++ *   live sectors (in buckets of that data type)
++ *   sectors of internal fragmentation
++ * ]
++ *
++ * XXX: live sectors should've been done differently, you can have multiple data
++ * types in the same bucket (user, stripe, cached) and this collapses them to
++ * the bucket data type, and makes the internal fragmentation counter redundant
++ */
++struct bch_acct_dev_data_type {
+ 	__u8			dev;
+ 	__u8			data_type;
+ };
+ 
++/*
++ * Compression type fields:
++ * [
++ *   number of extents
++ *   uncompressed size
++ *   compressed size
++ * ]
++ *
++ * Compression ratio, average extent size (fragmentation).
++ */
+ struct bch_acct_compression {
+ 	__u8			type;
+ };
+ 
++/*
++ * On disk usage by snapshot id; counts same values as replicas counter, but
++ * aggregated differently
++ */
+ struct bch_acct_snapshot {
+ 	__u32			id;
+ } __packed;
+@@ -137,10 +178,27 @@ struct bch_acct_btree {
+ 	__u32			id;
+ } __packed;
+ 
++/*
++ * inum counter fields:
++ * [
++ *   number of extents
++ *   sum of extent sizes - bkey size
++ *     this field is similar to inode.bi_sectors, except here extents in
++ *     different snapshots but the same inode number are all collapsed to the
++ *     same counter
++ *   sum of on disk size - same values tracked by replicas counters
++ * ]
++ *
++ * This tracks on disk fragmentation.
++ */
+ struct bch_acct_inum {
+ 	__u64			inum;
+ } __packed;
+ 
++/*
++ * Simple counter of the amount of data (on disk sectors) rebalance needs to
++ * move, extents counted here are also in the rebalance_work btree.
++ */
+ struct bch_acct_rebalance_work {
+ };
+ 
+@@ -149,10 +207,10 @@ struct disk_accounting_pos {
+ 	struct {
+ 		__u8				type;
+ 		union {
+-		struct bch_nr_inodes		nr_inodes;
+-		struct bch_persistent_reserved	persistent_reserved;
++		struct bch_acct_nr_inodes	nr_inodes;
++		struct bch_acct_persistent_reserved	persistent_reserved;
+ 		struct bch_replicas_entry_v1	replicas;
+-		struct bch_dev_data_type	dev_data_type;
++		struct bch_acct_dev_data_type	dev_data_type;
+ 		struct bch_acct_compression	compression;
+ 		struct bch_acct_snapshot	snapshot;
+ 		struct bch_acct_btree		btree;
+diff --git a/fs/bcachefs/ec.c b/fs/bcachefs/ec.c
+index d2a5e76e6479..f2b9225fe0bc 100644
+--- a/fs/bcachefs/ec.c
++++ b/fs/bcachefs/ec.c
+@@ -20,6 +20,7 @@
+ #include "io_read.h"
+ #include "io_write.h"
+ #include "keylist.h"
++#include "lru.h"
+ #include "recovery.h"
+ #include "replicas.h"
+ #include "super-io.h"
+@@ -104,6 +105,7 @@ struct ec_bio {
+ 	struct bch_dev		*ca;
+ 	struct ec_stripe_buf	*buf;
+ 	size_t			idx;
++	u64			submit_time;
+ 	struct bio		bio;
+ };
+ 
+@@ -298,10 +300,22 @@ static int mark_stripe_bucket(struct btree_trans *trans,
+ 	struct bpos bucket = PTR_BUCKET_POS(ca, ptr);
+ 
+ 	if (flags & BTREE_TRIGGER_transactional) {
++		struct extent_ptr_decoded p = {
++			.ptr = *ptr,
++			.crc = bch2_extent_crc_unpack(s.k, NULL),
++		};
++		struct bkey_i_backpointer bp;
++		bch2_extent_ptr_to_bp(c, BTREE_ID_stripes, 0, s.s_c, p,
++				      (const union bch_extent_entry *) ptr, &bp);
++
+ 		struct bkey_i_alloc_v4 *a =
+ 			bch2_trans_start_alloc_update(trans, bucket, 0);
+-		ret = PTR_ERR_OR_ZERO(a) ?:
+-			__mark_stripe_bucket(trans, ca, s, ptr_idx, deleting, bucket, &a->v, flags);
++		ret   = PTR_ERR_OR_ZERO(a) ?:
++			__mark_stripe_bucket(trans, ca, s, ptr_idx, deleting, bucket, &a->v, flags) ?:
++			bch2_bucket_backpointer_mod(trans, s.s_c, &bp,
++						    !(flags & BTREE_TRIGGER_overwrite));
++		if (ret)
++			goto err;
+ 	}
+ 
+ 	if (flags & BTREE_TRIGGER_gc) {
+@@ -366,19 +380,6 @@ static int mark_stripe_buckets(struct btree_trans *trans,
+ 	return 0;
+ }
+ 
+-static inline void stripe_to_mem(struct stripe *m, const struct bch_stripe *s)
+-{
+-	m->sectors	= le16_to_cpu(s->sectors);
+-	m->algorithm	= s->algorithm;
+-	m->nr_blocks	= s->nr_blocks;
+-	m->nr_redundant	= s->nr_redundant;
+-	m->disk_label	= s->disk_label;
+-	m->blocks_nonempty = 0;
+-
+-	for (unsigned i = 0; i < s->nr_blocks; i++)
+-		m->blocks_nonempty += !!stripe_blockcount_get(s, i);
+-}
+-
+ int bch2_trigger_stripe(struct btree_trans *trans,
+ 			enum btree_id btree, unsigned level,
+ 			struct bkey_s_c old, struct bkey_s _new,
+@@ -399,6 +400,15 @@ int bch2_trigger_stripe(struct btree_trans *trans,
+ 	       (new_s->nr_blocks	!= old_s->nr_blocks ||
+ 		new_s->nr_redundant	!= old_s->nr_redundant));
+ 
++	if (flags & BTREE_TRIGGER_transactional) {
++		int ret = bch2_lru_change(trans,
++					  BCH_LRU_STRIPE_FRAGMENTATION,
++					  idx,
++					  stripe_lru_pos(old_s),
++					  stripe_lru_pos(new_s));
++		if (ret)
++			return ret;
++	}
+ 
+ 	if (flags & (BTREE_TRIGGER_transactional|BTREE_TRIGGER_gc)) {
+ 		/*
+@@ -472,38 +482,6 @@ int bch2_trigger_stripe(struct btree_trans *trans,
+ 			return ret;
+ 	}
+ 
+-	if (flags & BTREE_TRIGGER_atomic) {
+-		struct stripe *m = genradix_ptr(&c->stripes, idx);
+-
+-		if (!m) {
+-			struct printbuf buf1 = PRINTBUF;
+-			struct printbuf buf2 = PRINTBUF;
+-
+-			bch2_bkey_val_to_text(&buf1, c, old);
+-			bch2_bkey_val_to_text(&buf2, c, new);
+-			bch_err_ratelimited(c, "error marking nonexistent stripe %llu while marking\n"
+-					    "old %s\n"
+-					    "new %s", idx, buf1.buf, buf2.buf);
+-			printbuf_exit(&buf2);
+-			printbuf_exit(&buf1);
+-			bch2_inconsistent_error(c);
+-			return -1;
+-		}
+-
+-		if (!new_s) {
+-			bch2_stripes_heap_del(c, m, idx);
+-
+-			memset(m, 0, sizeof(*m));
+-		} else {
+-			stripe_to_mem(m, new_s);
+-
+-			if (!old_s)
+-				bch2_stripes_heap_insert(c, m, idx);
+-			else
+-				bch2_stripes_heap_update(c, m, idx);
+-		}
+-	}
+-
+ 	return 0;
+ }
+ 
+@@ -726,14 +704,15 @@ static void ec_block_endio(struct bio *bio)
+ 	struct bch_dev *ca = ec_bio->ca;
+ 	struct closure *cl = bio->bi_private;
+ 
+-	if (bch2_dev_io_err_on(bio->bi_status, ca,
+-			       bio_data_dir(bio)
+-			       ? BCH_MEMBER_ERROR_write
+-			       : BCH_MEMBER_ERROR_read,
+-			       "erasure coding %s error: %s",
++	bch2_account_io_completion(ca, bio_data_dir(bio),
++				   ec_bio->submit_time, !bio->bi_status);
++
++	if (bio->bi_status) {
++		bch_err_dev_ratelimited(ca, "erasure coding %s error: %s",
+ 			       str_write_read(bio_data_dir(bio)),
+-			       bch2_blk_status_to_str(bio->bi_status)))
++			       bch2_blk_status_to_str(bio->bi_status));
+ 		clear_bit(ec_bio->idx, ec_bio->buf->valid);
++	}
+ 
+ 	int stale = dev_ptr_stale(ca, ptr);
+ 	if (stale) {
+@@ -796,6 +775,7 @@ static void ec_block_io(struct bch_fs *c, struct ec_stripe_buf *buf,
+ 		ec_bio->ca			= ca;
+ 		ec_bio->buf			= buf;
+ 		ec_bio->idx			= idx;
++		ec_bio->submit_time		= local_clock();
+ 
+ 		ec_bio->bio.bi_iter.bi_sector	= ptr->offset + buf->offset + (offset >> 9);
+ 		ec_bio->bio.bi_end_io		= ec_block_endio;
+@@ -917,26 +897,6 @@ int bch2_ec_read_extent(struct btree_trans *trans, struct bch_read_bio *rbio,
+ 
+ static int __ec_stripe_mem_alloc(struct bch_fs *c, size_t idx, gfp_t gfp)
+ {
+-	ec_stripes_heap n, *h = &c->ec_stripes_heap;
+-
+-	if (idx >= h->size) {
+-		if (!init_heap(&n, max(1024UL, roundup_pow_of_two(idx + 1)), gfp))
+-			return -BCH_ERR_ENOMEM_ec_stripe_mem_alloc;
+-
+-		mutex_lock(&c->ec_stripes_heap_lock);
+-		if (n.size > h->size) {
+-			memcpy(n.data, h->data, h->nr * sizeof(h->data[0]));
+-			n.nr = h->nr;
+-			swap(*h, n);
+-		}
+-		mutex_unlock(&c->ec_stripes_heap_lock);
+-
+-		free_heap(&n);
+-	}
+-
+-	if (!genradix_ptr_alloc(&c->stripes, idx, gfp))
+-		return -BCH_ERR_ENOMEM_ec_stripe_mem_alloc;
+-
+ 	if (c->gc_pos.phase != GC_PHASE_not_running &&
+ 	    !genradix_ptr_alloc(&c->gc_stripes, idx, gfp))
+ 		return -BCH_ERR_ENOMEM_ec_stripe_mem_alloc;
+@@ -1009,180 +969,50 @@ static void bch2_stripe_close(struct bch_fs *c, struct ec_stripe_new *s)
+ 	s->idx = 0;
+ }
+ 
+-/* Heap of all existing stripes, ordered by blocks_nonempty */
+-
+-static u64 stripe_idx_to_delete(struct bch_fs *c)
+-{
+-	ec_stripes_heap *h = &c->ec_stripes_heap;
+-
+-	lockdep_assert_held(&c->ec_stripes_heap_lock);
+-
+-	if (h->nr &&
+-	    h->data[0].blocks_nonempty == 0 &&
+-	    !bch2_stripe_is_open(c, h->data[0].idx))
+-		return h->data[0].idx;
+-
+-	return 0;
+-}
+-
+-static inline void ec_stripes_heap_set_backpointer(ec_stripes_heap *h,
+-						   size_t i)
+-{
+-	struct bch_fs *c = container_of(h, struct bch_fs, ec_stripes_heap);
+-
+-	genradix_ptr(&c->stripes, h->data[i].idx)->heap_idx = i;
+-}
+-
+-static inline bool ec_stripes_heap_cmp(const void *l, const void *r, void __always_unused *args)
+-{
+-	struct ec_stripe_heap_entry *_l = (struct ec_stripe_heap_entry *)l;
+-	struct ec_stripe_heap_entry *_r = (struct ec_stripe_heap_entry *)r;
+-
+-	return ((_l->blocks_nonempty > _r->blocks_nonempty) <
+-		(_l->blocks_nonempty < _r->blocks_nonempty));
+-}
+-
+-static inline void ec_stripes_heap_swap(void *l, void *r, void *h)
+-{
+-	struct ec_stripe_heap_entry *_l = (struct ec_stripe_heap_entry *)l;
+-	struct ec_stripe_heap_entry *_r = (struct ec_stripe_heap_entry *)r;
+-	ec_stripes_heap *_h = (ec_stripes_heap *)h;
+-	size_t i = _l - _h->data;
+-	size_t j = _r - _h->data;
+-
+-	swap(*_l, *_r);
+-
+-	ec_stripes_heap_set_backpointer(_h, i);
+-	ec_stripes_heap_set_backpointer(_h, j);
+-}
+-
+-static const struct min_heap_callbacks callbacks = {
+-	.less = ec_stripes_heap_cmp,
+-	.swp = ec_stripes_heap_swap,
+-};
+-
+-static void heap_verify_backpointer(struct bch_fs *c, size_t idx)
+-{
+-	ec_stripes_heap *h = &c->ec_stripes_heap;
+-	struct stripe *m = genradix_ptr(&c->stripes, idx);
+-
+-	BUG_ON(m->heap_idx >= h->nr);
+-	BUG_ON(h->data[m->heap_idx].idx != idx);
+-}
+-
+-void bch2_stripes_heap_del(struct bch_fs *c,
+-			   struct stripe *m, size_t idx)
+-{
+-	mutex_lock(&c->ec_stripes_heap_lock);
+-	heap_verify_backpointer(c, idx);
+-
+-	min_heap_del(&c->ec_stripes_heap, m->heap_idx, &callbacks, &c->ec_stripes_heap);
+-	mutex_unlock(&c->ec_stripes_heap_lock);
+-}
+-
+-void bch2_stripes_heap_insert(struct bch_fs *c,
+-			      struct stripe *m, size_t idx)
+-{
+-	mutex_lock(&c->ec_stripes_heap_lock);
+-	BUG_ON(min_heap_full(&c->ec_stripes_heap));
+-
+-	genradix_ptr(&c->stripes, idx)->heap_idx = c->ec_stripes_heap.nr;
+-	min_heap_push(&c->ec_stripes_heap, &((struct ec_stripe_heap_entry) {
+-			.idx = idx,
+-			.blocks_nonempty = m->blocks_nonempty,
+-		}),
+-		&callbacks,
+-		&c->ec_stripes_heap);
+-
+-	heap_verify_backpointer(c, idx);
+-	mutex_unlock(&c->ec_stripes_heap_lock);
+-}
+-
+-void bch2_stripes_heap_update(struct bch_fs *c,
+-			      struct stripe *m, size_t idx)
+-{
+-	ec_stripes_heap *h = &c->ec_stripes_heap;
+-	bool do_deletes;
+-	size_t i;
+-
+-	mutex_lock(&c->ec_stripes_heap_lock);
+-	heap_verify_backpointer(c, idx);
+-
+-	h->data[m->heap_idx].blocks_nonempty = m->blocks_nonempty;
+-
+-	i = m->heap_idx;
+-	min_heap_sift_up(h,	i, &callbacks, &c->ec_stripes_heap);
+-	min_heap_sift_down(h, i, &callbacks, &c->ec_stripes_heap);
+-
+-	heap_verify_backpointer(c, idx);
+-
+-	do_deletes = stripe_idx_to_delete(c) != 0;
+-	mutex_unlock(&c->ec_stripes_heap_lock);
+-
+-	if (do_deletes)
+-		bch2_do_stripe_deletes(c);
+-}
+-
+ /* stripe deletion */
+ 
+ static int ec_stripe_delete(struct btree_trans *trans, u64 idx)
+ {
+-	struct bch_fs *c = trans->c;
+ 	struct btree_iter iter;
+-	struct bkey_s_c k;
+-	struct bkey_s_c_stripe s;
+-	int ret;
+-
+-	k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_stripes, POS(0, idx),
+-			       BTREE_ITER_intent);
+-	ret = bkey_err(k);
++	struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter,
++					       BTREE_ID_stripes, POS(0, idx),
++					       BTREE_ITER_intent);
++	int ret = bkey_err(k);
+ 	if (ret)
+ 		goto err;
+ 
+-	if (k.k->type != KEY_TYPE_stripe) {
+-		bch2_fs_inconsistent(c, "attempting to delete nonexistent stripe %llu", idx);
+-		ret = -EINVAL;
+-		goto err;
+-	}
+-
+-	s = bkey_s_c_to_stripe(k);
+-	for (unsigned i = 0; i < s.v->nr_blocks; i++)
+-		if (stripe_blockcount_get(s.v, i)) {
+-			struct printbuf buf = PRINTBUF;
+-
+-			bch2_bkey_val_to_text(&buf, c, k);
+-			bch2_fs_inconsistent(c, "attempting to delete nonempty stripe %s", buf.buf);
+-			printbuf_exit(&buf);
+-			ret = -EINVAL;
+-			goto err;
+-		}
+-
+-	ret = bch2_btree_delete_at(trans, &iter, 0);
++	/*
++	 * We expect write buffer races here
++	 * Important: check stripe_is_open with stripe key locked:
++	 */
++	if (k.k->type == KEY_TYPE_stripe &&
++	    !bch2_stripe_is_open(trans->c, idx) &&
++	    stripe_lru_pos(bkey_s_c_to_stripe(k).v) == 1)
++		ret = bch2_btree_delete_at(trans, &iter, 0);
+ err:
+ 	bch2_trans_iter_exit(trans, &iter);
+ 	return ret;
+ }
+ 
++/*
++ * XXX
++ * can we kill this and delete stripes from the trigger?
++ */
+ static void ec_stripe_delete_work(struct work_struct *work)
+ {
+ 	struct bch_fs *c =
+ 		container_of(work, struct bch_fs, ec_stripe_delete_work);
+ 
+-	while (1) {
+-		mutex_lock(&c->ec_stripes_heap_lock);
+-		u64 idx = stripe_idx_to_delete(c);
+-		mutex_unlock(&c->ec_stripes_heap_lock);
+-
+-		if (!idx)
+-			break;
+-
+-		int ret = bch2_trans_commit_do(c, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
+-					ec_stripe_delete(trans, idx));
+-		bch_err_fn(c, ret);
+-		if (ret)
+-			break;
+-	}
+-
++	bch2_trans_run(c,
++		bch2_btree_write_buffer_tryflush(trans) ?:
++		for_each_btree_key_max_commit(trans, lru_iter, BTREE_ID_lru,
++				lru_pos(BCH_LRU_STRIPE_FRAGMENTATION, 1, 0),
++				lru_pos(BCH_LRU_STRIPE_FRAGMENTATION, 1, LRU_TIME_MAX),
++				0, lru_k,
++				NULL, NULL,
++				BCH_TRANS_COMMIT_no_enospc, ({
++			ec_stripe_delete(trans, lru_k.k->p.offset);
++		})));
+ 	bch2_write_ref_put(c, BCH_WRITE_REF_stripe_delete);
+ }
+ 
+@@ -1294,7 +1124,7 @@ static int ec_stripe_update_extent(struct btree_trans *trans,
+ 
+ 		bch2_fs_inconsistent(c, "%s", buf.buf);
+ 		printbuf_exit(&buf);
+-		return -EIO;
++		return -BCH_ERR_erasure_coding_found_btree_node;
+ 	}
+ 
+ 	k = bch2_backpointer_get_key(trans, bp, &iter, BTREE_ITER_intent, last_flushed);
+@@ -1360,7 +1190,7 @@ static int ec_stripe_update_bucket(struct btree_trans *trans, struct ec_stripe_b
+ 
+ 	struct bch_dev *ca = bch2_dev_tryget(c, ptr.dev);
+ 	if (!ca)
+-		return -EIO;
++		return -BCH_ERR_ENOENT_dev_not_found;
+ 
+ 	struct bpos bucket_pos = PTR_BUCKET_POS(ca, &ptr);
+ 
+@@ -1380,8 +1210,12 @@ static int ec_stripe_update_bucket(struct btree_trans *trans, struct ec_stripe_b
+ 		if (bp_k.k->type != KEY_TYPE_backpointer)
+ 			continue;
+ 
++		struct bkey_s_c_backpointer bp = bkey_s_c_to_backpointer(bp_k);
++		if (bp.v->btree_id == BTREE_ID_stripes)
++			continue;
++
+ 		ec_stripe_update_extent(trans, ca, bucket_pos, ptr.gen, s,
+-					bkey_s_c_to_backpointer(bp_k), &last_flushed);
++					bp, &last_flushed);
+ 	}));
+ 
+ 	bch2_bkey_buf_exit(&last_flushed, c);
+@@ -1393,21 +1227,19 @@ static int ec_stripe_update_extents(struct bch_fs *c, struct ec_stripe_buf *s)
+ {
+ 	struct btree_trans *trans = bch2_trans_get(c);
+ 	struct bch_stripe *v = &bkey_i_to_stripe(&s->key)->v;
+-	unsigned i, nr_data = v->nr_blocks - v->nr_redundant;
+-	int ret = 0;
++	unsigned nr_data = v->nr_blocks - v->nr_redundant;
+ 
+-	ret = bch2_btree_write_buffer_flush_sync(trans);
++	int ret = bch2_btree_write_buffer_flush_sync(trans);
+ 	if (ret)
+ 		goto err;
+ 
+-	for (i = 0; i < nr_data; i++) {
++	for (unsigned i = 0; i < nr_data; i++) {
+ 		ret = ec_stripe_update_bucket(trans, s, i);
+ 		if (ret)
+ 			break;
+ 	}
+ err:
+ 	bch2_trans_put(trans);
+-
+ 	return ret;
+ }
+ 
+@@ -1473,6 +1305,7 @@ static void ec_stripe_create(struct ec_stripe_new *s)
+ 	if (s->err) {
+ 		if (!bch2_err_matches(s->err, EROFS))
+ 			bch_err(c, "error creating stripe: error writing data buckets");
++		ret = s->err;
+ 		goto err;
+ 	}
+ 
+@@ -1481,6 +1314,7 @@ static void ec_stripe_create(struct ec_stripe_new *s)
+ 
+ 		if (ec_do_recov(c, &s->existing_stripe)) {
+ 			bch_err(c, "error creating stripe: error reading existing stripe");
++			ret = -BCH_ERR_ec_block_read;
+ 			goto err;
+ 		}
+ 
+@@ -1506,6 +1340,7 @@ static void ec_stripe_create(struct ec_stripe_new *s)
+ 
+ 	if (ec_nr_failed(&s->new_stripe)) {
+ 		bch_err(c, "error creating stripe: error writing redundancy buckets");
++		ret = -BCH_ERR_ec_block_write;
+ 		goto err;
+ 	}
+ 
+@@ -1527,6 +1362,8 @@ static void ec_stripe_create(struct ec_stripe_new *s)
+ 	if (ret)
+ 		goto err;
+ err:
++	trace_stripe_create(c, s->idx, ret);
++
+ 	bch2_disk_reservation_put(c, &s->res);
+ 
+ 	for (i = 0; i < v->nr_blocks; i++)
+@@ -1612,11 +1449,11 @@ static void ec_stripe_new_cancel(struct bch_fs *c, struct ec_stripe_head *h, int
+ 	ec_stripe_new_set_pending(c, h);
+ }
+ 
+-void bch2_ec_bucket_cancel(struct bch_fs *c, struct open_bucket *ob)
++void bch2_ec_bucket_cancel(struct bch_fs *c, struct open_bucket *ob, int err)
+ {
+ 	struct ec_stripe_new *s = ob->ec;
+ 
+-	s->err = -EIO;
++	s->err = err;
+ }
+ 
+ void *bch2_writepoint_ec_buf(struct bch_fs *c, struct write_point *wp)
+@@ -1968,39 +1805,40 @@ static int new_stripe_alloc_buckets(struct btree_trans *trans,
+ 	return 0;
+ }
+ 
+-static s64 get_existing_stripe(struct bch_fs *c,
+-			       struct ec_stripe_head *head)
++static int __get_existing_stripe(struct btree_trans *trans,
++				 struct ec_stripe_head *head,
++				 struct ec_stripe_buf *stripe,
++				 u64 idx)
+ {
+-	ec_stripes_heap *h = &c->ec_stripes_heap;
+-	struct stripe *m;
+-	size_t heap_idx;
+-	u64 stripe_idx;
+-	s64 ret = -1;
+-
+-	if (may_create_new_stripe(c))
+-		return -1;
++	struct bch_fs *c = trans->c;
+ 
+-	mutex_lock(&c->ec_stripes_heap_lock);
+-	for (heap_idx = 0; heap_idx < h->nr; heap_idx++) {
+-		/* No blocks worth reusing, stripe will just be deleted: */
+-		if (!h->data[heap_idx].blocks_nonempty)
+-			continue;
++	struct btree_iter iter;
++	struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter,
++					  BTREE_ID_stripes, POS(0, idx), 0);
++	int ret = bkey_err(k);
++	if (ret)
++		goto err;
+ 
+-		stripe_idx = h->data[heap_idx].idx;
++	/* We expect write buffer races here */
++	if (k.k->type != KEY_TYPE_stripe)
++		goto out;
+ 
+-		m = genradix_ptr(&c->stripes, stripe_idx);
++	struct bkey_s_c_stripe s = bkey_s_c_to_stripe(k);
++	if (stripe_lru_pos(s.v) <= 1)
++		goto out;
+ 
+-		if (m->disk_label	== head->disk_label &&
+-		    m->algorithm	== head->algo &&
+-		    m->nr_redundant	== head->redundancy &&
+-		    m->sectors		== head->blocksize &&
+-		    m->blocks_nonempty	< m->nr_blocks - m->nr_redundant &&
+-		    bch2_try_open_stripe(c, head->s, stripe_idx)) {
+-			ret = stripe_idx;
+-			break;
+-		}
++	if (s.v->disk_label		== head->disk_label &&
++	    s.v->algorithm		== head->algo &&
++	    s.v->nr_redundant		== head->redundancy &&
++	    le16_to_cpu(s.v->sectors)	== head->blocksize &&
++	    bch2_try_open_stripe(c, head->s, idx)) {
++		bkey_reassemble(&stripe->key, k);
++		ret = 1;
+ 	}
+-	mutex_unlock(&c->ec_stripes_heap_lock);
++out:
++	bch2_set_btree_iter_dontneed(&iter);
++err:
++	bch2_trans_iter_exit(trans, &iter);
+ 	return ret;
+ }
+ 
+@@ -2052,24 +1890,33 @@ static int __bch2_ec_stripe_head_reuse(struct btree_trans *trans, struct ec_stri
+ 				       struct ec_stripe_new *s)
+ {
+ 	struct bch_fs *c = trans->c;
+-	s64 idx;
+-	int ret;
+ 
+ 	/*
+ 	 * If we can't allocate a new stripe, and there's no stripes with empty
+ 	 * blocks for us to reuse, that means we have to wait on copygc:
+ 	 */
+-	idx = get_existing_stripe(c, h);
+-	if (idx < 0)
+-		return -BCH_ERR_stripe_alloc_blocked;
++	if (may_create_new_stripe(c))
++		return -1;
+ 
+-	ret = get_stripe_key_trans(trans, idx, &s->existing_stripe);
+-	bch2_fs_fatal_err_on(ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart), c,
+-			     "reading stripe key: %s", bch2_err_str(ret));
+-	if (ret) {
+-		bch2_stripe_close(c, s);
+-		return ret;
++	struct btree_iter lru_iter;
++	struct bkey_s_c lru_k;
++	int ret = 0;
++
++	for_each_btree_key_max_norestart(trans, lru_iter, BTREE_ID_lru,
++			lru_pos(BCH_LRU_STRIPE_FRAGMENTATION, 2, 0),
++			lru_pos(BCH_LRU_STRIPE_FRAGMENTATION, 2, LRU_TIME_MAX),
++			0, lru_k, ret) {
++		ret = __get_existing_stripe(trans, h, &s->existing_stripe, lru_k.k->p.offset);
++		if (ret)
++			break;
+ 	}
++	bch2_trans_iter_exit(trans, &lru_iter);
++	if (!ret)
++		ret = -BCH_ERR_stripe_alloc_blocked;
++	if (ret == 1)
++		ret = 0;
++	if (ret)
++		return ret;
+ 
+ 	return init_new_stripe_from_existing(c, s);
+ }
+@@ -2367,46 +2214,7 @@ void bch2_fs_ec_flush(struct bch_fs *c)
+ 
+ int bch2_stripes_read(struct bch_fs *c)
+ {
+-	int ret = bch2_trans_run(c,
+-		for_each_btree_key(trans, iter, BTREE_ID_stripes, POS_MIN,
+-				   BTREE_ITER_prefetch, k, ({
+-			if (k.k->type != KEY_TYPE_stripe)
+-				continue;
+-
+-			ret = __ec_stripe_mem_alloc(c, k.k->p.offset, GFP_KERNEL);
+-			if (ret)
+-				break;
+-
+-			struct stripe *m = genradix_ptr(&c->stripes, k.k->p.offset);
+-
+-			stripe_to_mem(m, bkey_s_c_to_stripe(k).v);
+-
+-			bch2_stripes_heap_insert(c, m, k.k->p.offset);
+-			0;
+-		})));
+-	bch_err_fn(c, ret);
+-	return ret;
+-}
+-
+-void bch2_stripes_heap_to_text(struct printbuf *out, struct bch_fs *c)
+-{
+-	ec_stripes_heap *h = &c->ec_stripes_heap;
+-	struct stripe *m;
+-	size_t i;
+-
+-	mutex_lock(&c->ec_stripes_heap_lock);
+-	for (i = 0; i < min_t(size_t, h->nr, 50); i++) {
+-		m = genradix_ptr(&c->stripes, h->data[i].idx);
+-
+-		prt_printf(out, "%zu %u/%u+%u", h->data[i].idx,
+-		       h->data[i].blocks_nonempty,
+-		       m->nr_blocks - m->nr_redundant,
+-		       m->nr_redundant);
+-		if (bch2_stripe_is_open(c, h->data[i].idx))
+-			prt_str(out, " open");
+-		prt_newline(out);
+-	}
+-	mutex_unlock(&c->ec_stripes_heap_lock);
++	return 0;
+ }
+ 
+ static void bch2_new_stripe_to_text(struct printbuf *out, struct bch_fs *c,
+@@ -2477,15 +2285,12 @@ void bch2_fs_ec_exit(struct bch_fs *c)
+ 
+ 	BUG_ON(!list_empty(&c->ec_stripe_new_list));
+ 
+-	free_heap(&c->ec_stripes_heap);
+-	genradix_free(&c->stripes);
+ 	bioset_exit(&c->ec_bioset);
+ }
+ 
+ void bch2_fs_ec_init_early(struct bch_fs *c)
+ {
+ 	spin_lock_init(&c->ec_stripes_new_lock);
+-	mutex_init(&c->ec_stripes_heap_lock);
+ 
+ 	INIT_LIST_HEAD(&c->ec_stripe_head_list);
+ 	mutex_init(&c->ec_stripe_head_lock);
+@@ -2503,3 +2308,40 @@ int bch2_fs_ec_init(struct bch_fs *c)
+ 	return bioset_init(&c->ec_bioset, 1, offsetof(struct ec_bio, bio),
+ 			   BIOSET_NEED_BVECS);
+ }
++
++static int bch2_check_stripe_to_lru_ref(struct btree_trans *trans,
++					struct bkey_s_c k,
++					struct bkey_buf *last_flushed)
++{
++	if (k.k->type != KEY_TYPE_stripe)
++		return 0;
++
++	struct bkey_s_c_stripe s = bkey_s_c_to_stripe(k);
++
++	u64 lru_idx = stripe_lru_pos(s.v);
++	if (lru_idx) {
++		int ret = bch2_lru_check_set(trans, BCH_LRU_STRIPE_FRAGMENTATION,
++					     k.k->p.offset, lru_idx, k, last_flushed);
++		if (ret)
++			return ret;
++	}
++	return 0;
++}
++
++int bch2_check_stripe_to_lru_refs(struct bch_fs *c)
++{
++	struct bkey_buf last_flushed;
++
++	bch2_bkey_buf_init(&last_flushed);
++	bkey_init(&last_flushed.k->k);
++
++	int ret = bch2_trans_run(c,
++		for_each_btree_key_commit(trans, iter, BTREE_ID_stripes,
++				POS_MIN, BTREE_ITER_prefetch, k,
++				NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
++			bch2_check_stripe_to_lru_ref(trans, k, &last_flushed)));
++
++	bch2_bkey_buf_exit(&last_flushed, c);
++	bch_err_fn(c, ret);
++	return ret;
++}
+diff --git a/fs/bcachefs/ec.h b/fs/bcachefs/ec.h
+index 583ca6a226da..62d27e04d763 100644
+--- a/fs/bcachefs/ec.h
++++ b/fs/bcachefs/ec.h
+@@ -92,6 +92,29 @@ static inline void stripe_csum_set(struct bch_stripe *s,
+ 	memcpy(stripe_csum(s, block, csum_idx), &csum, bch_crc_bytes[s->csum_type]);
+ }
+ 
++#define STRIPE_LRU_POS_EMPTY	1
++
++static inline u64 stripe_lru_pos(const struct bch_stripe *s)
++{
++	if (!s)
++		return 0;
++
++	unsigned nr_data = s->nr_blocks - s->nr_redundant, blocks_empty = 0;
++
++	for (unsigned i = 0; i < nr_data; i++)
++		blocks_empty += !stripe_blockcount_get(s, i);
++
++	/* Will be picked up by the stripe_delete worker */
++	if (blocks_empty == nr_data)
++		return STRIPE_LRU_POS_EMPTY;
++
++	if (!blocks_empty)
++		return 0;
++
++	/* invert: more blocks empty = reuse first */
++	return LRU_TIME_MAX - blocks_empty;
++}
++
+ static inline bool __bch2_ptr_matches_stripe(const struct bch_extent_ptr *stripe_ptr,
+ 					     const struct bch_extent_ptr *data_ptr,
+ 					     unsigned sectors)
+@@ -132,6 +155,20 @@ static inline bool bch2_ptr_matches_stripe_m(const struct gc_stripe *m,
+ 					 m->sectors);
+ }
+ 
++static inline void gc_stripe_unlock(struct gc_stripe *s)
++{
++	BUILD_BUG_ON(!((union ulong_byte_assert) { .ulong = 1UL << BUCKET_LOCK_BITNR }).byte);
++
++	clear_bit_unlock(BUCKET_LOCK_BITNR, (void *) &s->lock);
++	wake_up_bit((void *) &s->lock, BUCKET_LOCK_BITNR);
++}
++
++static inline void gc_stripe_lock(struct gc_stripe *s)
++{
++	wait_on_bit_lock((void *) &s->lock, BUCKET_LOCK_BITNR,
++			 TASK_UNINTERRUPTIBLE);
++}
++
+ struct bch_read_bio;
+ 
+ struct ec_stripe_buf {
+@@ -212,7 +249,7 @@ int bch2_ec_read_extent(struct btree_trans *, struct bch_read_bio *, struct bkey
+ 
+ void *bch2_writepoint_ec_buf(struct bch_fs *, struct write_point *);
+ 
+-void bch2_ec_bucket_cancel(struct bch_fs *, struct open_bucket *);
++void bch2_ec_bucket_cancel(struct bch_fs *, struct open_bucket *, int);
+ 
+ int bch2_ec_stripe_new_alloc(struct bch_fs *, struct ec_stripe_head *);
+ 
+@@ -221,10 +258,6 @@ struct ec_stripe_head *bch2_ec_stripe_head_get(struct btree_trans *,
+ 			unsigned, unsigned, unsigned,
+ 			enum bch_watermark, struct closure *);
+ 
+-void bch2_stripes_heap_update(struct bch_fs *, struct stripe *, size_t);
+-void bch2_stripes_heap_del(struct bch_fs *, struct stripe *, size_t);
+-void bch2_stripes_heap_insert(struct bch_fs *, struct stripe *, size_t);
+-
+ void bch2_do_stripe_deletes(struct bch_fs *);
+ void bch2_ec_do_stripe_creates(struct bch_fs *);
+ void bch2_ec_stripe_new_free(struct bch_fs *, struct ec_stripe_new *);
+@@ -261,11 +294,12 @@ void bch2_fs_ec_flush(struct bch_fs *);
+ 
+ int bch2_stripes_read(struct bch_fs *);
+ 
+-void bch2_stripes_heap_to_text(struct printbuf *, struct bch_fs *);
+ void bch2_new_stripes_to_text(struct printbuf *, struct bch_fs *);
+ 
+ void bch2_fs_ec_exit(struct bch_fs *);
+ void bch2_fs_ec_init_early(struct bch_fs *);
+ int bch2_fs_ec_init(struct bch_fs *);
+ 
++int bch2_check_stripe_to_lru_refs(struct bch_fs *);
++
+ #endif /* _BCACHEFS_EC_H */
+diff --git a/fs/bcachefs/ec_types.h b/fs/bcachefs/ec_types.h
+index 8d1e70e830ac..06144bfd9c19 100644
+--- a/fs/bcachefs/ec_types.h
++++ b/fs/bcachefs/ec_types.h
+@@ -20,23 +20,15 @@ struct stripe {
+ };
+ 
+ struct gc_stripe {
++	u8			lock;
++	unsigned		alive:1; /* does a corresponding key exist in stripes btree? */
+ 	u16			sectors;
+-
+ 	u8			nr_blocks;
+ 	u8			nr_redundant;
+-
+-	unsigned		alive:1; /* does a corresponding key exist in stripes btree? */
+ 	u16			block_sectors[BCH_BKEY_PTRS_MAX];
+ 	struct bch_extent_ptr	ptrs[BCH_BKEY_PTRS_MAX];
+ 
+ 	struct bch_replicas_padded r;
+ };
+ 
+-struct ec_stripe_heap_entry {
+-	size_t			idx;
+-	unsigned		blocks_nonempty;
+-};
+-
+-typedef DEFINE_MIN_HEAP(struct ec_stripe_heap_entry, ec_stripes_heap) ec_stripes_heap;
+-
+ #endif /* _BCACHEFS_EC_TYPES_H */
+diff --git a/fs/bcachefs/errcode.h b/fs/bcachefs/errcode.h
+index 4590cd0c7c90..101806d7ebe1 100644
+--- a/fs/bcachefs/errcode.h
++++ b/fs/bcachefs/errcode.h
+@@ -116,9 +116,11 @@
+ 	x(ENOENT,			ENOENT_snapshot_tree)			\
+ 	x(ENOENT,			ENOENT_dirent_doesnt_match_inode)	\
+ 	x(ENOENT,			ENOENT_dev_not_found)			\
++	x(ENOENT,			ENOENT_dev_bucket_not_found)		\
+ 	x(ENOENT,			ENOENT_dev_idx_not_found)		\
+ 	x(ENOENT,			ENOENT_inode_no_backpointer)		\
+ 	x(ENOENT,			ENOENT_no_snapshot_tree_subvol)		\
++	x(ENOENT,			btree_node_dying)			\
+ 	x(ENOTEMPTY,			ENOTEMPTY_dir_not_empty)		\
+ 	x(ENOTEMPTY,			ENOTEMPTY_subvol_not_empty)		\
+ 	x(EEXIST,			EEXIST_str_hash_set)			\
+@@ -180,6 +182,12 @@
+ 	x(EINVAL,			not_in_recovery)			\
+ 	x(EINVAL,			cannot_rewind_recovery)			\
+ 	x(0,				data_update_done)			\
++	x(BCH_ERR_data_update_done,	data_update_done_would_block)		\
++	x(BCH_ERR_data_update_done,	data_update_done_unwritten)		\
++	x(BCH_ERR_data_update_done,	data_update_done_no_writes_needed)	\
++	x(BCH_ERR_data_update_done,	data_update_done_no_snapshot)		\
++	x(BCH_ERR_data_update_done,	data_update_done_no_dev_refs)		\
++	x(BCH_ERR_data_update_done,	data_update_done_no_rw_devs)		\
+ 	x(EINVAL,			device_state_not_allowed)		\
+ 	x(EINVAL,			member_info_missing)			\
+ 	x(EINVAL,			mismatched_block_size)			\
+@@ -200,6 +208,8 @@
+ 	x(EINVAL,			no_resize_with_buckets_nouse)		\
+ 	x(EINVAL,			inode_unpack_error)			\
+ 	x(EINVAL,			varint_decode_error)			\
++	x(EINVAL,			erasure_coding_found_btree_node)	\
++	x(EOPNOTSUPP,			may_not_use_incompat_feature)		\
+ 	x(EROFS,			erofs_trans_commit)			\
+ 	x(EROFS,			erofs_no_writes)			\
+ 	x(EROFS,			erofs_journal_err)			\
+@@ -210,10 +220,18 @@
+ 	x(EROFS,			insufficient_devices)			\
+ 	x(0,				operation_blocked)			\
+ 	x(BCH_ERR_operation_blocked,	btree_cache_cannibalize_lock_blocked)	\
+-	x(BCH_ERR_operation_blocked,	journal_res_get_blocked)		\
+-	x(BCH_ERR_operation_blocked,	journal_preres_get_blocked)		\
+-	x(BCH_ERR_operation_blocked,	bucket_alloc_blocked)			\
+-	x(BCH_ERR_operation_blocked,	stripe_alloc_blocked)			\
++	x(BCH_ERR_operation_blocked,	journal_res_blocked)			\
++	x(BCH_ERR_journal_res_blocked,	journal_blocked)			\
++	x(BCH_ERR_journal_res_blocked,	journal_max_in_flight)			\
++	x(BCH_ERR_journal_res_blocked,	journal_max_open)			\
++	x(BCH_ERR_journal_res_blocked,	journal_full)				\
++	x(BCH_ERR_journal_res_blocked,	journal_pin_full)			\
++	x(BCH_ERR_journal_res_blocked,	journal_buf_enomem)			\
++	x(BCH_ERR_journal_res_blocked,	journal_stuck)				\
++	x(BCH_ERR_journal_res_blocked,	journal_retry_open)			\
++	x(BCH_ERR_journal_res_blocked,	journal_preres_get_blocked)		\
++	x(BCH_ERR_journal_res_blocked,	bucket_alloc_blocked)			\
++	x(BCH_ERR_journal_res_blocked,	stripe_alloc_blocked)			\
+ 	x(BCH_ERR_invalid,		invalid_sb)				\
+ 	x(BCH_ERR_invalid_sb,		invalid_sb_magic)			\
+ 	x(BCH_ERR_invalid_sb,		invalid_sb_version)			\
+@@ -223,6 +241,7 @@
+ 	x(BCH_ERR_invalid_sb,		invalid_sb_csum)			\
+ 	x(BCH_ERR_invalid_sb,		invalid_sb_block_size)			\
+ 	x(BCH_ERR_invalid_sb,		invalid_sb_uuid)			\
++	x(BCH_ERR_invalid_sb,		invalid_sb_offset)			\
+ 	x(BCH_ERR_invalid_sb,		invalid_sb_too_many_members)		\
+ 	x(BCH_ERR_invalid_sb,		invalid_sb_dev_idx)			\
+ 	x(BCH_ERR_invalid_sb,		invalid_sb_time_precision)		\
+@@ -250,6 +269,7 @@
+ 	x(BCH_ERR_operation_blocked,    nocow_lock_blocked)			\
+ 	x(EIO,				journal_shutdown)			\
+ 	x(EIO,				journal_flush_err)			\
++	x(EIO,				journal_write_err)			\
+ 	x(EIO,				btree_node_read_err)			\
+ 	x(BCH_ERR_btree_node_read_err,	btree_node_read_err_cached)		\
+ 	x(EIO,				sb_not_downgraded)			\
+@@ -258,17 +278,52 @@
+ 	x(EIO,				btree_node_read_validate_error)		\
+ 	x(EIO,				btree_need_topology_repair)		\
+ 	x(EIO,				bucket_ref_update)			\
++	x(EIO,				trigger_alloc)				\
+ 	x(EIO,				trigger_pointer)			\
+ 	x(EIO,				trigger_stripe_pointer)			\
+ 	x(EIO,				metadata_bucket_inconsistency)		\
+ 	x(EIO,				mark_stripe)				\
+ 	x(EIO,				stripe_reconstruct)			\
+ 	x(EIO,				key_type_error)				\
+-	x(EIO,				no_device_to_read_from)			\
++	x(EIO,				extent_poisened)			\
+ 	x(EIO,				missing_indirect_extent)		\
+ 	x(EIO,				invalidate_stripe_to_dev)		\
+ 	x(EIO,				no_encryption_key)			\
+ 	x(EIO,				insufficient_journal_devices)		\
++	x(EIO,				device_offline)				\
++	x(EIO,				EIO_fault_injected)			\
++	x(EIO,				ec_block_read)				\
++	x(EIO,				ec_block_write)				\
++	x(EIO,				recompute_checksum)			\
++	x(EIO,				decompress)				\
++	x(BCH_ERR_decompress,		decompress_exceeded_max_encoded_extent)	\
++	x(BCH_ERR_decompress,		decompress_lz4)				\
++	x(BCH_ERR_decompress,		decompress_gzip)			\
++	x(BCH_ERR_decompress,		decompress_zstd_src_len_bad)		\
++	x(BCH_ERR_decompress,		decompress_zstd)			\
++	x(EIO,				data_write)				\
++	x(BCH_ERR_data_write,		data_write_io)				\
++	x(BCH_ERR_data_write,		data_write_csum)			\
++	x(BCH_ERR_data_write,		data_write_invalid_ptr)			\
++	x(BCH_ERR_data_write,		data_write_misaligned)			\
++	x(BCH_ERR_decompress,		data_read)				\
++	x(BCH_ERR_data_read,		no_device_to_read_from)			\
++	x(BCH_ERR_data_read,		data_read_io_err)			\
++	x(BCH_ERR_data_read,		data_read_csum_err)			\
++	x(BCH_ERR_data_read,		data_read_retry)			\
++	x(BCH_ERR_data_read_retry,	data_read_retry_avoid)			\
++	x(BCH_ERR_data_read_retry_avoid,data_read_retry_device_offline)		\
++	x(BCH_ERR_data_read_retry_avoid,data_read_retry_io_err)			\
++	x(BCH_ERR_data_read_retry_avoid,data_read_retry_ec_reconstruct_err)	\
++	x(BCH_ERR_data_read_retry_avoid,data_read_retry_csum_err)		\
++	x(BCH_ERR_data_read_retry,	data_read_retry_csum_err_maybe_userspace)\
++	x(BCH_ERR_data_read,		data_read_decompress_err)		\
++	x(BCH_ERR_data_read,		data_read_decrypt_err)			\
++	x(BCH_ERR_data_read,		data_read_ptr_stale_race)		\
++	x(BCH_ERR_data_read_retry,	data_read_ptr_stale_retry)		\
++	x(BCH_ERR_data_read,		data_read_no_encryption_key)		\
++	x(BCH_ERR_data_read,		data_read_buffer_too_small)		\
++	x(BCH_ERR_data_read,		data_read_key_overwritten)		\
+ 	x(BCH_ERR_btree_node_read_err,	btree_node_read_err_fixable)		\
+ 	x(BCH_ERR_btree_node_read_err,	btree_node_read_err_want_retry)		\
+ 	x(BCH_ERR_btree_node_read_err,	btree_node_read_err_must_retry)		\
+diff --git a/fs/bcachefs/error.c b/fs/bcachefs/error.c
+index 038da6a61f6b..207f35d3cce2 100644
+--- a/fs/bcachefs/error.c
++++ b/fs/bcachefs/error.c
+@@ -3,8 +3,8 @@
+ #include "btree_cache.h"
+ #include "btree_iter.h"
+ #include "error.h"
+-#include "fs-common.h"
+ #include "journal.h"
++#include "namei.h"
+ #include "recovery_passes.h"
+ #include "super.h"
+ #include "thread_with_file.h"
+@@ -54,25 +54,41 @@ void bch2_io_error_work(struct work_struct *work)
+ {
+ 	struct bch_dev *ca = container_of(work, struct bch_dev, io_error_work);
+ 	struct bch_fs *c = ca->fs;
+-	bool dev;
++
++	/* XXX: if it's reads or checksums that are failing, set it to failed */
+ 
+ 	down_write(&c->state_lock);
+-	dev = bch2_dev_state_allowed(c, ca, BCH_MEMBER_STATE_ro,
+-				    BCH_FORCE_IF_DEGRADED);
+-	if (dev
+-	    ? __bch2_dev_set_state(c, ca, BCH_MEMBER_STATE_ro,
+-				  BCH_FORCE_IF_DEGRADED)
+-	    : bch2_fs_emergency_read_only(c))
++	unsigned long write_errors_start = READ_ONCE(ca->write_errors_start);
++
++	if (write_errors_start &&
++	    time_after(jiffies,
++		       write_errors_start + c->opts.write_error_timeout * HZ)) {
++		if (ca->mi.state >= BCH_MEMBER_STATE_ro)
++			goto out;
++
++		bool dev = !__bch2_dev_set_state(c, ca, BCH_MEMBER_STATE_ro,
++						 BCH_FORCE_IF_DEGRADED);
++
+ 		bch_err(ca,
+-			"too many IO errors, setting %s RO",
++			"writes erroring for %u seconds, setting %s ro",
++			c->opts.write_error_timeout,
+ 			dev ? "device" : "filesystem");
++		if (!dev)
++			bch2_fs_emergency_read_only(c);
++
++	}
++out:
+ 	up_write(&c->state_lock);
+ }
+ 
+ void bch2_io_error(struct bch_dev *ca, enum bch_member_error_type type)
+ {
+ 	atomic64_inc(&ca->errors[type]);
+-	//queue_work(system_long_wq, &ca->io_error_work);
++
++	if (type == BCH_MEMBER_ERROR_write && !ca->write_errors_start)
++		ca->write_errors_start = jiffies;
++
++	queue_work(system_long_wq, &ca->io_error_work);
+ }
+ 
+ enum ask_yn {
+@@ -530,35 +546,59 @@ void bch2_flush_fsck_errs(struct bch_fs *c)
+ 	mutex_unlock(&c->fsck_error_msgs_lock);
+ }
+ 
+-int bch2_inum_err_msg_trans(struct btree_trans *trans, struct printbuf *out, subvol_inum inum)
++int bch2_inum_offset_err_msg_trans(struct btree_trans *trans, struct printbuf *out,
++				    subvol_inum inum, u64 offset)
+ {
+ 	u32 restart_count = trans->restart_count;
+ 	int ret = 0;
+ 
+-	/* XXX: we don't yet attempt to print paths when we don't know the subvol */
+-	if (inum.subvol)
+-		ret = lockrestart_do(trans, bch2_inum_to_path(trans, inum, out));
++	if (inum.subvol) {
++		ret = bch2_inum_to_path(trans, inum, out);
++		if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
++			return ret;
++	}
+ 	if (!inum.subvol || ret)
+ 		prt_printf(out, "inum %llu:%llu", inum.subvol, inum.inum);
++	prt_printf(out, " offset %llu: ", offset);
+ 
+ 	return trans_was_restarted(trans, restart_count);
+ }
+ 
+-int bch2_inum_offset_err_msg_trans(struct btree_trans *trans, struct printbuf *out,
+-				    subvol_inum inum, u64 offset)
++void bch2_inum_offset_err_msg(struct bch_fs *c, struct printbuf *out,
++			      subvol_inum inum, u64 offset)
+ {
+-	int ret = bch2_inum_err_msg_trans(trans, out, inum);
+-	prt_printf(out, " offset %llu: ", offset);
+-	return ret;
++	bch2_trans_do(c, bch2_inum_offset_err_msg_trans(trans, out, inum, offset));
+ }
+ 
+-void bch2_inum_err_msg(struct bch_fs *c, struct printbuf *out, subvol_inum inum)
++int bch2_inum_snap_offset_err_msg_trans(struct btree_trans *trans, struct printbuf *out,
++					struct bpos pos)
+ {
+-	bch2_trans_run(c, bch2_inum_err_msg_trans(trans, out, inum));
++	struct bch_fs *c = trans->c;
++	int ret = 0;
++
++	if (!bch2_snapshot_is_leaf(c, pos.snapshot))
++		prt_str(out, "(multiple snapshots) ");
++
++	subvol_inum inum = {
++		.subvol	= bch2_snapshot_tree_oldest_subvol(c, pos.snapshot),
++		.inum	= pos.inode,
++	};
++
++	if (inum.subvol) {
++		ret = bch2_inum_to_path(trans, inum, out);
++		if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
++			return ret;
++	}
++
++	if (!inum.subvol || ret)
++		prt_printf(out, "inum %llu:%u", pos.inode, pos.snapshot);
++
++	prt_printf(out, " offset %llu: ", pos.offset << 8);
++	return 0;
+ }
+ 
+-void bch2_inum_offset_err_msg(struct bch_fs *c, struct printbuf *out,
+-			      subvol_inum inum, u64 offset)
++void bch2_inum_snap_offset_err_msg(struct bch_fs *c, struct printbuf *out,
++				  struct bpos pos)
+ {
+-	bch2_trans_run(c, bch2_inum_offset_err_msg_trans(trans, out, inum, offset));
++	bch2_trans_do(c, bch2_inum_snap_offset_err_msg_trans(trans, out, pos));
+ }
+diff --git a/fs/bcachefs/error.h b/fs/bcachefs/error.h
+index 7acf2a27ca28..7d3f0e2a5fd6 100644
+--- a/fs/bcachefs/error.h
++++ b/fs/bcachefs/error.h
+@@ -216,32 +216,43 @@ void bch2_io_error_work(struct work_struct *);
+ /* Does the error handling without logging a message */
+ void bch2_io_error(struct bch_dev *, enum bch_member_error_type);
+ 
+-#define bch2_dev_io_err_on(cond, ca, _type, ...)			\
+-({									\
+-	bool _ret = (cond);						\
+-									\
+-	if (_ret) {							\
+-		bch_err_dev_ratelimited(ca, __VA_ARGS__);		\
+-		bch2_io_error(ca, _type);				\
+-	}								\
+-	_ret;								\
+-})
+-
+-#define bch2_dev_inum_io_err_on(cond, ca, _type, ...)			\
+-({									\
+-	bool _ret = (cond);						\
+-									\
+-	if (_ret) {							\
+-		bch_err_inum_offset_ratelimited(ca, __VA_ARGS__);	\
+-		bch2_io_error(ca, _type);				\
+-	}								\
+-	_ret;								\
+-})
++#ifndef CONFIG_BCACHEFS_NO_LATENCY_ACCT
++void bch2_latency_acct(struct bch_dev *, u64, int);
++#else
++static inline void bch2_latency_acct(struct bch_dev *ca, u64 submit_time, int rw) {}
++#endif
++
++static inline void bch2_account_io_success_fail(struct bch_dev *ca,
++						enum bch_member_error_type type,
++						bool success)
++{
++	if (likely(success)) {
++		if (type == BCH_MEMBER_ERROR_write &&
++		    ca->write_errors_start)
++			ca->write_errors_start = 0;
++	} else {
++		bch2_io_error(ca, type);
++	}
++}
++
++static inline void bch2_account_io_completion(struct bch_dev *ca,
++					      enum bch_member_error_type type,
++					      u64 submit_time, bool success)
++{
++	if (unlikely(!ca))
++		return;
++
++	if (type != BCH_MEMBER_ERROR_checksum)
++		bch2_latency_acct(ca, submit_time, type);
++
++	bch2_account_io_success_fail(ca, type, success);
++}
+ 
+-int bch2_inum_err_msg_trans(struct btree_trans *, struct printbuf *, subvol_inum);
+ int bch2_inum_offset_err_msg_trans(struct btree_trans *, struct printbuf *, subvol_inum, u64);
+ 
+-void bch2_inum_err_msg(struct bch_fs *, struct printbuf *, subvol_inum);
+ void bch2_inum_offset_err_msg(struct bch_fs *, struct printbuf *, subvol_inum, u64);
+ 
++int bch2_inum_snap_offset_err_msg_trans(struct btree_trans *, struct printbuf *, struct bpos);
++void bch2_inum_snap_offset_err_msg(struct bch_fs *, struct printbuf *, struct bpos);
++
+ #endif /* _BCACHEFS_ERROR_H */
+diff --git a/fs/bcachefs/extents.c b/fs/bcachefs/extents.c
+index 2d8042f853dc..ca2073db472b 100644
+--- a/fs/bcachefs/extents.c
++++ b/fs/bcachefs/extents.c
+@@ -28,6 +28,13 @@
+ #include "trace.h"
+ #include "util.h"
+ 
++static const char * const bch2_extent_flags_strs[] = {
++#define x(n, v)	[BCH_EXTENT_FLAG_##n] = #n,
++	BCH_EXTENT_FLAGS()
++#undef x
++	NULL,
++};
++
+ static unsigned bch2_crc_field_size_max[] = {
+ 	[BCH_EXTENT_ENTRY_crc32] = CRC32_SIZE_MAX,
+ 	[BCH_EXTENT_ENTRY_crc64] = CRC64_SIZE_MAX,
+@@ -51,7 +58,8 @@ struct bch_dev_io_failures *bch2_dev_io_failures(struct bch_io_failures *f,
+ }
+ 
+ void bch2_mark_io_failure(struct bch_io_failures *failed,
+-			  struct extent_ptr_decoded *p)
++			  struct extent_ptr_decoded *p,
++			  bool csum_error)
+ {
+ 	struct bch_dev_io_failures *f = bch2_dev_io_failures(failed, p->ptr.dev);
+ 
+@@ -59,53 +67,57 @@ void bch2_mark_io_failure(struct bch_io_failures *failed,
+ 		BUG_ON(failed->nr >= ARRAY_SIZE(failed->devs));
+ 
+ 		f = &failed->devs[failed->nr++];
+-		f->dev		= p->ptr.dev;
+-		f->idx		= p->idx;
+-		f->nr_failed	= 1;
+-		f->nr_retries	= 0;
+-	} else if (p->idx != f->idx) {
+-		f->idx		= p->idx;
+-		f->nr_failed	= 1;
+-		f->nr_retries	= 0;
+-	} else {
+-		f->nr_failed++;
++		memset(f, 0, sizeof(*f));
++		f->dev = p->ptr.dev;
+ 	}
++
++	if (p->do_ec_reconstruct)
++		f->failed_ec = true;
++	else if (!csum_error)
++		f->failed_io = true;
++	else
++		f->failed_csum_nr++;
+ }
+ 
+-static inline u64 dev_latency(struct bch_fs *c, unsigned dev)
++static inline u64 dev_latency(struct bch_dev *ca)
+ {
+-	struct bch_dev *ca = bch2_dev_rcu(c, dev);
+ 	return ca ? atomic64_read(&ca->cur_latency[READ]) : S64_MAX;
+ }
+ 
++static inline int dev_failed(struct bch_dev *ca)
++{
++	return !ca || ca->mi.state == BCH_MEMBER_STATE_failed;
++}
++
+ /*
+  * returns true if p1 is better than p2:
+  */
+ static inline bool ptr_better(struct bch_fs *c,
+ 			      const struct extent_ptr_decoded p1,
+-			      const struct extent_ptr_decoded p2)
++			      u64 p1_latency,
++			      struct bch_dev *ca1,
++			      const struct extent_ptr_decoded p2,
++			      u64 p2_latency)
+ {
+-	if (likely(!p1.idx && !p2.idx)) {
+-		u64 l1 = dev_latency(c, p1.ptr.dev);
+-		u64 l2 = dev_latency(c, p2.ptr.dev);
++	struct bch_dev *ca2 = bch2_dev_rcu(c, p2.ptr.dev);
+ 
+-		/*
+-		 * Square the latencies, to bias more in favor of the faster
+-		 * device - we never want to stop issuing reads to the slower
+-		 * device altogether, so that we can update our latency numbers:
+-		 */
+-		l1 *= l1;
+-		l2 *= l2;
++	int failed_delta = dev_failed(ca1) - dev_failed(ca2);
++	if (unlikely(failed_delta))
++		return failed_delta < 0;
+ 
+-		/* Pick at random, biased in favor of the faster device: */
++	if (unlikely(bch2_force_reconstruct_read))
++		return p1.do_ec_reconstruct > p2.do_ec_reconstruct;
+ 
+-		return bch2_get_random_u64_below(l1 + l2) > l1;
+-	}
++	if (unlikely(p1.do_ec_reconstruct || p2.do_ec_reconstruct))
++		return p1.do_ec_reconstruct < p2.do_ec_reconstruct;
+ 
+-	if (bch2_force_reconstruct_read)
+-		return p1.idx > p2.idx;
++	int crc_retry_delta = (int) p1.crc_retry_nr - (int) p2.crc_retry_nr;
++	if (unlikely(crc_retry_delta))
++		return crc_retry_delta < 0;
+ 
+-	return p1.idx < p2.idx;
++	/* Pick at random, biased in favor of the faster device: */
++
++	return bch2_get_random_u64_below(p1_latency + p2_latency) > p1_latency;
+ }
+ 
+ /*
+@@ -115,64 +127,104 @@ static inline bool ptr_better(struct bch_fs *c,
+  */
+ int bch2_bkey_pick_read_device(struct bch_fs *c, struct bkey_s_c k,
+ 			       struct bch_io_failures *failed,
+-			       struct extent_ptr_decoded *pick)
++			       struct extent_ptr_decoded *pick,
++			       int dev)
+ {
+-	struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
+-	const union bch_extent_entry *entry;
+-	struct extent_ptr_decoded p;
+-	struct bch_dev_io_failures *f;
+-	int ret = 0;
++	bool have_csum_errors = false, have_io_errors = false, have_missing_devs = false;
++	bool have_dirty_ptrs = false, have_pick = false;
+ 
+ 	if (k.k->type == KEY_TYPE_error)
+ 		return -BCH_ERR_key_type_error;
+ 
+ 	rcu_read_lock();
++	struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
++	const union bch_extent_entry *entry;
++	struct extent_ptr_decoded p;
++	u64 pick_latency;
++
+ 	bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
++		have_dirty_ptrs |= !p.ptr.cached;
++
+ 		/*
+ 		 * Unwritten extent: no need to actually read, treat it as a
+ 		 * hole and return 0s:
+ 		 */
+ 		if (p.ptr.unwritten) {
+-			ret = 0;
+-			break;
++			rcu_read_unlock();
++			return 0;
+ 		}
+ 
+-		/*
+-		 * If there are any dirty pointers it's an error if we can't
+-		 * read:
+-		 */
+-		if (!ret && !p.ptr.cached)
+-			ret = -BCH_ERR_no_device_to_read_from;
++		/* Are we being asked to read from a specific device? */
++		if (dev >= 0 && p.ptr.dev != dev)
++			continue;
+ 
+ 		struct bch_dev *ca = bch2_dev_rcu(c, p.ptr.dev);
+ 
+ 		if (p.ptr.cached && (!ca || dev_ptr_stale_rcu(ca, &p.ptr)))
+ 			continue;
+ 
+-		f = failed ? bch2_dev_io_failures(failed, p.ptr.dev) : NULL;
+-		if (f)
+-			p.idx = f->nr_failed < f->nr_retries
+-				? f->idx
+-				: f->idx + 1;
++		struct bch_dev_io_failures *f =
++			unlikely(failed) ? bch2_dev_io_failures(failed, p.ptr.dev) : NULL;
++		if (unlikely(f)) {
++			p.crc_retry_nr	   = f->failed_csum_nr;
++			p.has_ec	  &= ~f->failed_ec;
+ 
+-		if (!p.idx && (!ca || !bch2_dev_is_readable(ca)))
+-			p.idx++;
++			if (ca && ca->mi.state != BCH_MEMBER_STATE_failed) {
++				have_io_errors	|= f->failed_io;
++				have_io_errors	|= f->failed_ec;
++			}
++			have_csum_errors	|= !!f->failed_csum_nr;
+ 
+-		if (!p.idx && p.has_ec && bch2_force_reconstruct_read)
+-			p.idx++;
++			if (p.has_ec && (f->failed_io || f->failed_csum_nr))
++				p.do_ec_reconstruct = true;
++			else if (f->failed_io ||
++				 f->failed_csum_nr > c->opts.checksum_err_retry_nr)
++				continue;
++		}
+ 
+-		if (p.idx > (unsigned) p.has_ec)
+-			continue;
++		have_missing_devs |= ca && !bch2_dev_is_online(ca);
+ 
+-		if (ret > 0 && !ptr_better(c, p, *pick))
+-			continue;
++		if (!ca || !bch2_dev_is_online(ca)) {
++			if (!p.has_ec)
++				continue;
++			p.do_ec_reconstruct = true;
++		}
++
++		if (bch2_force_reconstruct_read && p.has_ec)
++			p.do_ec_reconstruct = true;
+ 
+-		*pick = p;
+-		ret = 1;
++		u64 p_latency = dev_latency(ca);
++		/*
++		 * Square the latencies, to bias more in favor of the faster
++		 * device - we never want to stop issuing reads to the slower
++		 * device altogether, so that we can update our latency numbers:
++		 */
++		p_latency *= p_latency;
++
++		if (!have_pick ||
++		    ptr_better(c,
++			       p, p_latency, ca,
++			       *pick, pick_latency)) {
++			*pick = p;
++			pick_latency = p_latency;
++			have_pick = true;
++		}
+ 	}
+ 	rcu_read_unlock();
+ 
+-	return ret;
++	if (have_pick)
++		return 1;
++	if (!have_dirty_ptrs)
++		return 0;
++	if (have_missing_devs)
++		return -BCH_ERR_no_device_to_read_from;
++	if (have_csum_errors)
++		return -BCH_ERR_data_read_csum_err;
++	if (have_io_errors)
++		return -BCH_ERR_data_read_io_err;
++
++	WARN_ONCE(1, "unhandled error case in %s\n", __func__);
++	return -EINVAL;
+ }
+ 
+ /* KEY_TYPE_btree_ptr: */
+@@ -536,29 +588,35 @@ static void bch2_extent_crc_pack(union bch_extent_crc *dst,
+ 				 struct bch_extent_crc_unpacked src,
+ 				 enum bch_extent_entry_type type)
+ {
+-#define set_common_fields(_dst, _src)					\
+-		_dst.type		= 1 << type;			\
+-		_dst.csum_type		= _src.csum_type,		\
+-		_dst.compression_type	= _src.compression_type,	\
+-		_dst._compressed_size	= _src.compressed_size - 1,	\
+-		_dst._uncompressed_size	= _src.uncompressed_size - 1,	\
+-		_dst.offset		= _src.offset
++#define common_fields(_src)						\
++		.type			= BIT(type),			\
++		.csum_type		= _src.csum_type,		\
++		.compression_type	= _src.compression_type,	\
++		._compressed_size	= _src.compressed_size - 1,	\
++		._uncompressed_size	= _src.uncompressed_size - 1,	\
++		.offset			= _src.offset
+ 
+ 	switch (type) {
+ 	case BCH_EXTENT_ENTRY_crc32:
+-		set_common_fields(dst->crc32, src);
+-		dst->crc32.csum		= (u32 __force) *((__le32 *) &src.csum.lo);
++		dst->crc32		= (struct bch_extent_crc32) {
++			common_fields(src),
++			.csum		= (u32 __force) *((__le32 *) &src.csum.lo),
++		};
+ 		break;
+ 	case BCH_EXTENT_ENTRY_crc64:
+-		set_common_fields(dst->crc64, src);
+-		dst->crc64.nonce	= src.nonce;
+-		dst->crc64.csum_lo	= (u64 __force) src.csum.lo;
+-		dst->crc64.csum_hi	= (u64 __force) *((__le16 *) &src.csum.hi);
++		dst->crc64		= (struct bch_extent_crc64) {
++			common_fields(src),
++			.nonce		= src.nonce,
++			.csum_lo	= (u64 __force) src.csum.lo,
++			.csum_hi	= (u64 __force) *((__le16 *) &src.csum.hi),
++		};
+ 		break;
+ 	case BCH_EXTENT_ENTRY_crc128:
+-		set_common_fields(dst->crc128, src);
+-		dst->crc128.nonce	= src.nonce;
+-		dst->crc128.csum	= src.csum;
++		dst->crc128		= (struct bch_extent_crc128) {
++			common_fields(src),
++			.nonce		= src.nonce,
++			.csum		= src.csum,
++		};
+ 		break;
+ 	default:
+ 		BUG();
+@@ -997,7 +1055,7 @@ static bool want_cached_ptr(struct bch_fs *c, struct bch_io_opts *opts,
+ 
+ 	struct bch_dev *ca = bch2_dev_rcu_noerror(c, ptr->dev);
+ 
+-	return ca && bch2_dev_is_readable(ca) && !dev_ptr_stale_rcu(ca, ptr);
++	return ca && bch2_dev_is_healthy(ca) && !dev_ptr_stale_rcu(ca, ptr);
+ }
+ 
+ void bch2_extent_ptr_set_cached(struct bch_fs *c,
+@@ -1220,6 +1278,10 @@ void bch2_bkey_ptrs_to_text(struct printbuf *out, struct bch_fs *c,
+ 			bch2_extent_rebalance_to_text(out, c, &entry->rebalance);
+ 			break;
+ 
++		case BCH_EXTENT_ENTRY_flags:
++			prt_bitflags(out, bch2_extent_flags_strs, entry->flags.flags);
++			break;
++
+ 		default:
+ 			prt_printf(out, "(invalid extent entry %.16llx)", *((u64 *) entry));
+ 			return;
+@@ -1381,6 +1443,11 @@ int bch2_bkey_ptrs_validate(struct bch_fs *c, struct bkey_s_c k,
+ #endif
+ 			break;
+ 		}
++		case BCH_EXTENT_ENTRY_flags:
++			bkey_fsck_err_on(entry != ptrs.start,
++					 c, extent_flags_not_at_start,
++					 "extent flags entry not at start");
++			break;
+ 		}
+ 	}
+ 
+@@ -1447,6 +1514,28 @@ void bch2_ptr_swab(struct bkey_s k)
+ 	}
+ }
+ 
++int bch2_bkey_extent_flags_set(struct bch_fs *c, struct bkey_i *k, u64 flags)
++{
++	int ret = bch2_request_incompat_feature(c, bcachefs_metadata_version_extent_flags);
++	if (ret)
++		return ret;
++
++	struct bkey_ptrs ptrs = bch2_bkey_ptrs(bkey_i_to_s(k));
++
++	if (ptrs.start != ptrs.end &&
++	    extent_entry_type(ptrs.start) == BCH_EXTENT_ENTRY_flags) {
++		ptrs.start->flags.flags = flags;
++	} else {
++		struct bch_extent_flags f = {
++			.type	= BIT(BCH_EXTENT_ENTRY_flags),
++			.flags	= flags,
++		};
++		__extent_entry_insert(k, ptrs.start, (union bch_extent_entry *) &f);
++	}
++
++	return 0;
++}
++
+ /* Generic extent code: */
+ 
+ int bch2_cut_front_s(struct bpos where, struct bkey_s k)
+@@ -1492,8 +1581,8 @@ int bch2_cut_front_s(struct bpos where, struct bkey_s k)
+ 				entry->crc128.offset += sub;
+ 				break;
+ 			case BCH_EXTENT_ENTRY_stripe_ptr:
+-				break;
+ 			case BCH_EXTENT_ENTRY_rebalance:
++			case BCH_EXTENT_ENTRY_flags:
+ 				break;
+ 			}
+ 
+diff --git a/fs/bcachefs/extents.h b/fs/bcachefs/extents.h
+index 204d765dd74c..e78a39e7e18f 100644
+--- a/fs/bcachefs/extents.h
++++ b/fs/bcachefs/extents.h
+@@ -320,8 +320,9 @@ static inline struct bkey_ptrs bch2_bkey_ptrs(struct bkey_s k)
+ ({									\
+ 	__label__ out;							\
+ 									\
+-	(_ptr).idx	= 0;						\
+-	(_ptr).has_ec	= false;					\
++	(_ptr).has_ec			= false;			\
++	(_ptr).do_ec_reconstruct	= false;			\
++	(_ptr).crc_retry_nr		= 0;				\
+ 									\
+ 	__bkey_extent_entry_for_each_from(_entry, _end, _entry)		\
+ 		switch (__extent_entry_type(_entry)) {			\
+@@ -401,10 +402,10 @@ out:									\
+ struct bch_dev_io_failures *bch2_dev_io_failures(struct bch_io_failures *,
+ 						 unsigned);
+ void bch2_mark_io_failure(struct bch_io_failures *,
+-			  struct extent_ptr_decoded *);
++			  struct extent_ptr_decoded *, bool);
+ int bch2_bkey_pick_read_device(struct bch_fs *, struct bkey_s_c,
+ 			       struct bch_io_failures *,
+-			       struct extent_ptr_decoded *);
++			       struct extent_ptr_decoded *, int);
+ 
+ /* KEY_TYPE_btree_ptr: */
+ 
+@@ -753,4 +754,19 @@ static inline void bch2_key_resize(struct bkey *k, unsigned new_size)
+ 	k->size = new_size;
+ }
+ 
++static inline u64 bch2_bkey_extent_ptrs_flags(struct bkey_ptrs_c ptrs)
++{
++	if (ptrs.start != ptrs.end &&
++	    extent_entry_type(ptrs.start) == BCH_EXTENT_ENTRY_flags)
++		return ptrs.start->flags.flags;
++	return 0;
++}
++
++static inline u64 bch2_bkey_extent_flags(struct bkey_s_c k)
++{
++	return bch2_bkey_extent_ptrs_flags(bch2_bkey_ptrs_c(k));
++}
++
++int bch2_bkey_extent_flags_set(struct bch_fs *, struct bkey_i *, u64);
++
+ #endif /* _BCACHEFS_EXTENTS_H */
+diff --git a/fs/bcachefs/extents_format.h b/fs/bcachefs/extents_format.h
+index c198dfc376d6..74c0252cbd98 100644
+--- a/fs/bcachefs/extents_format.h
++++ b/fs/bcachefs/extents_format.h
+@@ -79,8 +79,9 @@
+ 	x(crc64,		2)		\
+ 	x(crc128,		3)		\
+ 	x(stripe_ptr,		4)		\
+-	x(rebalance,		5)
+-#define BCH_EXTENT_ENTRY_MAX	6
++	x(rebalance,		5)		\
++	x(flags,		6)
++#define BCH_EXTENT_ENTRY_MAX	7
+ 
+ enum bch_extent_entry_type {
+ #define x(f, n) BCH_EXTENT_ENTRY_##f = n,
+@@ -201,6 +202,25 @@ struct bch_extent_stripe_ptr {
+ #endif
+ };
+ 
++#define BCH_EXTENT_FLAGS()		\
++	x(poisoned,		0)
++
++enum bch_extent_flags_e {
++#define x(n, v)	BCH_EXTENT_FLAG_##n = v,
++	BCH_EXTENT_FLAGS()
++#undef x
++};
++
++struct bch_extent_flags {
++#if defined(__LITTLE_ENDIAN_BITFIELD)
++	__u64			type:7,
++				flags:57;
++#elif defined (__BIG_ENDIAN_BITFIELD)
++	__u64			flags:57,
++				type:7;
++#endif
++};
++
+ /* bch_extent_rebalance: */
+ #include "rebalance_format.h"
+ 
+diff --git a/fs/bcachefs/extents_types.h b/fs/bcachefs/extents_types.h
+index 43d6c341ecca..e51529dca4c2 100644
+--- a/fs/bcachefs/extents_types.h
++++ b/fs/bcachefs/extents_types.h
+@@ -20,8 +20,9 @@ struct bch_extent_crc_unpacked {
+ };
+ 
+ struct extent_ptr_decoded {
+-	unsigned			idx;
+ 	bool				has_ec;
++	bool				do_ec_reconstruct;
++	u8				crc_retry_nr;
+ 	struct bch_extent_crc_unpacked	crc;
+ 	struct bch_extent_ptr		ptr;
+ 	struct bch_extent_stripe_ptr	ec;
+@@ -31,10 +32,10 @@ struct bch_io_failures {
+ 	u8			nr;
+ 	struct bch_dev_io_failures {
+ 		u8		dev;
+-		u8		idx;
+-		u8		nr_failed;
+-		u8		nr_retries;
+-	}			devs[BCH_REPLICAS_MAX];
++		unsigned	failed_csum_nr:6,
++				failed_io:1,
++				failed_ec:1;
++	}			devs[BCH_REPLICAS_MAX + 1];
+ };
+ 
+ #endif /* _BCACHEFS_EXTENTS_TYPES_H */
+diff --git a/fs/bcachefs/eytzinger.c b/fs/bcachefs/eytzinger.c
+index 2eaffe37b5e7..0e742555cb0a 100644
+--- a/fs/bcachefs/eytzinger.c
++++ b/fs/bcachefs/eytzinger.c
+@@ -148,89 +148,99 @@ static int do_cmp(const void *a, const void *b, cmp_r_func_t cmp, const void *pr
+ 	return cmp(a, b, priv);
+ }
+ 
+-static inline int eytzinger0_do_cmp(void *base, size_t n, size_t size,
++static inline int eytzinger1_do_cmp(void *base1, size_t n, size_t size,
+ 			 cmp_r_func_t cmp_func, const void *priv,
+ 			 size_t l, size_t r)
+ {
+-	return do_cmp(base + inorder_to_eytzinger0(l, n) * size,
+-		      base + inorder_to_eytzinger0(r, n) * size,
++	return do_cmp(base1 + inorder_to_eytzinger1(l, n) * size,
++		      base1 + inorder_to_eytzinger1(r, n) * size,
+ 		      cmp_func, priv);
+ }
+ 
+-static inline void eytzinger0_do_swap(void *base, size_t n, size_t size,
++static inline void eytzinger1_do_swap(void *base1, size_t n, size_t size,
+ 			   swap_r_func_t swap_func, const void *priv,
+ 			   size_t l, size_t r)
+ {
+-	do_swap(base + inorder_to_eytzinger0(l, n) * size,
+-		base + inorder_to_eytzinger0(r, n) * size,
++	do_swap(base1 + inorder_to_eytzinger1(l, n) * size,
++		base1 + inorder_to_eytzinger1(r, n) * size,
+ 		size, swap_func, priv);
+ }
+ 
+-void eytzinger0_sort_r(void *base, size_t n, size_t size,
+-		       cmp_r_func_t cmp_func,
+-		       swap_r_func_t swap_func,
+-		       const void *priv)
++static void eytzinger1_sort_r(void *base1, size_t n, size_t size,
++			      cmp_r_func_t cmp_func,
++			      swap_r_func_t swap_func,
++			      const void *priv)
+ {
+-	int i, j, k;
++	unsigned i, j, k;
+ 
+ 	/* called from 'sort' without swap function, let's pick the default */
+ 	if (swap_func == SWAP_WRAPPER && !((struct wrapper *)priv)->swap_func)
+ 		swap_func = NULL;
+ 
+ 	if (!swap_func) {
+-		if (is_aligned(base, size, 8))
++		if (is_aligned(base1, size, 8))
+ 			swap_func = SWAP_WORDS_64;
+-		else if (is_aligned(base, size, 4))
++		else if (is_aligned(base1, size, 4))
+ 			swap_func = SWAP_WORDS_32;
+ 		else
+ 			swap_func = SWAP_BYTES;
+ 	}
+ 
+ 	/* heapify */
+-	for (i = n / 2 - 1; i >= 0; --i) {
++	for (i = n / 2; i >= 1; --i) {
+ 		/* Find the sift-down path all the way to the leaves. */
+-		for (j = i; k = j * 2 + 1, k + 1 < n;)
+-			j = eytzinger0_do_cmp(base, n, size, cmp_func, priv, k, k + 1) > 0 ? k : k + 1;
++		for (j = i; k = j * 2, k < n;)
++			j = eytzinger1_do_cmp(base1, n, size, cmp_func, priv, k, k + 1) > 0 ? k : k + 1;
+ 
+ 		/* Special case for the last leaf with no sibling. */
+-		if (j * 2 + 2 == n)
+-			j = j * 2 + 1;
++		if (j * 2 == n)
++			j *= 2;
+ 
+ 		/* Backtrack to the correct location. */
+-		while (j != i && eytzinger0_do_cmp(base, n, size, cmp_func, priv, i, j) >= 0)
+-			j = (j - 1) / 2;
++		while (j != i && eytzinger1_do_cmp(base1, n, size, cmp_func, priv, i, j) >= 0)
++			j /= 2;
+ 
+ 		/* Shift the element into its correct place. */
+ 		for (k = j; j != i;) {
+-			j = (j - 1) / 2;
+-			eytzinger0_do_swap(base, n, size, swap_func, priv, j, k);
++			j /= 2;
++			eytzinger1_do_swap(base1, n, size, swap_func, priv, j, k);
+ 		}
+ 	}
+ 
+ 	/* sort */
+-	for (i = n - 1; i > 0; --i) {
+-		eytzinger0_do_swap(base, n, size, swap_func, priv, 0, i);
++	for (i = n; i > 1; --i) {
++		eytzinger1_do_swap(base1, n, size, swap_func, priv, 1, i);
+ 
+ 		/* Find the sift-down path all the way to the leaves. */
+-		for (j = 0; k = j * 2 + 1, k + 1 < i;)
+-			j = eytzinger0_do_cmp(base, n, size, cmp_func, priv, k, k + 1) > 0 ? k : k + 1;
++		for (j = 1; k = j * 2, k + 1 < i;)
++			j = eytzinger1_do_cmp(base1, n, size, cmp_func, priv, k, k + 1) > 0 ? k : k + 1;
+ 
+ 		/* Special case for the last leaf with no sibling. */
+-		if (j * 2 + 2 == i)
+-			j = j * 2 + 1;
++		if (j * 2 + 1 == i)
++			j *= 2;
+ 
+ 		/* Backtrack to the correct location. */
+-		while (j && eytzinger0_do_cmp(base, n, size, cmp_func, priv, 0, j) >= 0)
+-			j = (j - 1) / 2;
++		while (j >= 1 && eytzinger1_do_cmp(base1, n, size, cmp_func, priv, 1, j) >= 0)
++			j /= 2;
+ 
+ 		/* Shift the element into its correct place. */
+-		for (k = j; j;) {
+-			j = (j - 1) / 2;
+-			eytzinger0_do_swap(base, n, size, swap_func, priv, j, k);
++		for (k = j; j > 1;) {
++			j /= 2;
++			eytzinger1_do_swap(base1, n, size, swap_func, priv, j, k);
+ 		}
+ 	}
+ }
+ 
++void eytzinger0_sort_r(void *base, size_t n, size_t size,
++		       cmp_r_func_t cmp_func,
++		       swap_r_func_t swap_func,
++		       const void *priv)
++{
++	void *base1 = base - size;
++
++	return eytzinger1_sort_r(base1, n, size, cmp_func, swap_func, priv);
++}
++
+ void eytzinger0_sort(void *base, size_t n, size_t size,
+ 		     cmp_func_t cmp_func,
+ 		     swap_func_t swap_func)
+diff --git a/fs/bcachefs/eytzinger.h b/fs/bcachefs/eytzinger.h
+index 0541192d7bc0..643c1f716061 100644
+--- a/fs/bcachefs/eytzinger.h
++++ b/fs/bcachefs/eytzinger.h
+@@ -6,6 +6,7 @@
+ #include <linux/log2.h>
+ 
+ #ifdef EYTZINGER_DEBUG
++#include <linux/bug.h>
+ #define EYTZINGER_BUG_ON(cond)		BUG_ON(cond)
+ #else
+ #define EYTZINGER_BUG_ON(cond)
+@@ -56,24 +57,14 @@ static inline unsigned eytzinger1_last(unsigned size)
+ 	return rounddown_pow_of_two(size + 1) - 1;
+ }
+ 
+-/*
+- * eytzinger1_next() and eytzinger1_prev() have the nice properties that
+- *
+- * eytzinger1_next(0) == eytzinger1_first())
+- * eytzinger1_prev(0) == eytzinger1_last())
+- *
+- * eytzinger1_prev(eytzinger1_first()) == 0
+- * eytzinger1_next(eytzinger1_last()) == 0
+- */
+-
+ static inline unsigned eytzinger1_next(unsigned i, unsigned size)
+ {
+-	EYTZINGER_BUG_ON(i > size);
++	EYTZINGER_BUG_ON(i == 0 || i > size);
+ 
+ 	if (eytzinger1_right_child(i) <= size) {
+ 		i = eytzinger1_right_child(i);
+ 
+-		i <<= __fls(size + 1) - __fls(i);
++		i <<= __fls(size) - __fls(i);
+ 		i >>= i > size;
+ 	} else {
+ 		i >>= ffz(i) + 1;
+@@ -84,12 +75,12 @@ static inline unsigned eytzinger1_next(unsigned i, unsigned size)
+ 
+ static inline unsigned eytzinger1_prev(unsigned i, unsigned size)
+ {
+-	EYTZINGER_BUG_ON(i > size);
++	EYTZINGER_BUG_ON(i == 0 || i > size);
+ 
+ 	if (eytzinger1_left_child(i) <= size) {
+ 		i = eytzinger1_left_child(i) + 1;
+ 
+-		i <<= __fls(size + 1) - __fls(i);
++		i <<= __fls(size) - __fls(i);
+ 		i -= 1;
+ 		i >>= i > size;
+ 	} else {
+@@ -243,73 +234,63 @@ static inline unsigned inorder_to_eytzinger0(unsigned i, unsigned size)
+ 	     (_i) != -1;				\
+ 	     (_i) = eytzinger0_next((_i), (_size)))
+ 
++#define eytzinger0_for_each_prev(_i, _size)		\
++	for (unsigned (_i) = eytzinger0_last((_size));	\
++	     (_i) != -1;				\
++	     (_i) = eytzinger0_prev((_i), (_size)))
++
+ /* return greatest node <= @search, or -1 if not found */
+ static inline int eytzinger0_find_le(void *base, size_t nr, size_t size,
+ 				     cmp_func_t cmp, const void *search)
+ {
+-	unsigned i, n = 0;
+-
+-	if (!nr)
+-		return -1;
+-
+-	do {
+-		i = n;
+-		n = eytzinger0_child(i, cmp(base + i * size, search) <= 0);
+-	} while (n < nr);
+-
+-	if (n & 1) {
+-		/*
+-		 * @i was greater than @search, return previous node:
+-		 *
+-		 * if @i was leftmost/smallest element,
+-		 * eytzinger0_prev(eytzinger0_first())) returns -1, as expected
+-		 */
+-		return eytzinger0_prev(i, nr);
+-	} else {
+-		return i;
+-	}
++	void *base1 = base - size;
++	unsigned n = 1;
++
++	while (n <= nr)
++		n = eytzinger1_child(n, cmp(base1 + n * size, search) <= 0);
++	n >>= __ffs(n) + 1;
++	return n - 1;
+ }
+ 
++/* return smallest node > @search, or -1 if not found */
+ static inline int eytzinger0_find_gt(void *base, size_t nr, size_t size,
+ 				     cmp_func_t cmp, const void *search)
+ {
+-	ssize_t idx = eytzinger0_find_le(base, nr, size, cmp, search);
++	void *base1 = base - size;
++	unsigned n = 1;
+ 
+-	/*
+-	 * if eytitzinger0_find_le() returned -1 - no element was <= search - we
+-	 * want to return the first element; next/prev identities mean this work
+-	 * as expected
+-	 *
+-	 * similarly if find_le() returns last element, we should return -1;
+-	 * identities mean this all works out:
+-	 */
+-	return eytzinger0_next(idx, nr);
++	while (n <= nr)
++		n = eytzinger1_child(n, cmp(base1 + n * size, search) <= 0);
++	n >>= __ffs(n + 1) + 1;
++	return n - 1;
+ }
+ 
++/* return smallest node >= @search, or -1 if not found */
+ static inline int eytzinger0_find_ge(void *base, size_t nr, size_t size,
+ 				     cmp_func_t cmp, const void *search)
+ {
+-	ssize_t idx = eytzinger0_find_le(base, nr, size, cmp, search);
+-
+-	if (idx < nr && !cmp(base + idx * size, search))
+-		return idx;
++	void *base1 = base - size;
++	unsigned n = 1;
+ 
+-	return eytzinger0_next(idx, nr);
++	while (n <= nr)
++		n = eytzinger1_child(n, cmp(base1 + n * size, search) < 0);
++	n >>= __ffs(n + 1) + 1;
++	return n - 1;
+ }
+ 
+ #define eytzinger0_find(base, nr, size, _cmp, search)			\
+ ({									\
+-	void *_base		= (base);				\
++	size_t _size		= (size);				\
++	void *_base1		= (void *)(base) - _size;		\
+ 	const void *_search	= (search);				\
+ 	size_t _nr		= (nr);					\
+-	size_t _size		= (size);				\
+-	size_t _i		= 0;					\
++	size_t _i		= 1;					\
+ 	int _res;							\
+ 									\
+-	while (_i < _nr &&						\
+-	       (_res = _cmp(_search, _base + _i * _size)))		\
+-		_i = eytzinger0_child(_i, _res > 0);			\
+-	_i;								\
++	while (_i <= _nr &&						\
++	       (_res = _cmp(_search, _base1 + _i * _size)))		\
++		_i = eytzinger1_child(_i, _res > 0);			\
++	_i - 1;								\
+ })
+ 
+ void eytzinger0_sort_r(void *, size_t, size_t,
+diff --git a/fs/bcachefs/fs-io-buffered.c b/fs/bcachefs/fs-io-buffered.c
+index ab1d5db2fa56..a03e2c780cba 100644
+--- a/fs/bcachefs/fs-io-buffered.c
++++ b/fs/bcachefs/fs-io-buffered.c
+@@ -110,11 +110,21 @@ static int readpage_bio_extend(struct btree_trans *trans,
+ 			if (!get_more)
+ 				break;
+ 
++			unsigned sectors_remaining = sectors_this_extent - bio_sectors(bio);
++
++			if (sectors_remaining < PAGE_SECTORS << mapping_min_folio_order(iter->mapping))
++				break;
++
++			unsigned order = ilog2(rounddown_pow_of_two(sectors_remaining) / PAGE_SECTORS);
++
++			/* ensure proper alignment */
++			order = min(order, __ffs(folio_offset|BIT(31)));
++
+ 			folio = xa_load(&iter->mapping->i_pages, folio_offset);
+ 			if (folio && !xa_is_value(folio))
+ 				break;
+ 
+-			folio = filemap_alloc_folio(readahead_gfp_mask(iter->mapping), 0);
++			folio = filemap_alloc_folio(readahead_gfp_mask(iter->mapping), order);
+ 			if (!folio)
+ 				break;
+ 
+@@ -149,12 +159,10 @@ static void bchfs_read(struct btree_trans *trans,
+ 	struct bch_fs *c = trans->c;
+ 	struct btree_iter iter;
+ 	struct bkey_buf sk;
+-	int flags = BCH_READ_RETRY_IF_STALE|
+-		BCH_READ_MAY_PROMOTE;
++	int flags = BCH_READ_retry_if_stale|
++		BCH_READ_may_promote;
+ 	int ret = 0;
+ 
+-	rbio->c = c;
+-	rbio->start_time = local_clock();
+ 	rbio->subvol = inum.subvol;
+ 
+ 	bch2_bkey_buf_init(&sk);
+@@ -211,17 +219,17 @@ static void bchfs_read(struct btree_trans *trans,
+ 		swap(rbio->bio.bi_iter.bi_size, bytes);
+ 
+ 		if (rbio->bio.bi_iter.bi_size == bytes)
+-			flags |= BCH_READ_LAST_FRAGMENT;
++			flags |= BCH_READ_last_fragment;
+ 
+ 		bch2_bio_page_state_set(&rbio->bio, k);
+ 
+ 		bch2_read_extent(trans, rbio, iter.pos,
+ 				 data_btree, k, offset_into_extent, flags);
++		swap(rbio->bio.bi_iter.bi_size, bytes);
+ 
+-		if (flags & BCH_READ_LAST_FRAGMENT)
++		if (flags & BCH_READ_last_fragment)
+ 			break;
+ 
+-		swap(rbio->bio.bi_iter.bi_size, bytes);
+ 		bio_advance(&rbio->bio, bytes);
+ err:
+ 		if (ret &&
+@@ -232,7 +240,8 @@ static void bchfs_read(struct btree_trans *trans,
+ 
+ 	if (ret) {
+ 		struct printbuf buf = PRINTBUF;
+-		bch2_inum_offset_err_msg_trans(trans, &buf, inum, iter.pos.offset << 9);
++		lockrestart_do(trans,
++			bch2_inum_offset_err_msg_trans(trans, &buf, inum, iter.pos.offset << 9));
+ 		prt_printf(&buf, "read error %i from btree lookup", ret);
+ 		bch_err_ratelimited(c, "%s", buf.buf);
+ 		printbuf_exit(&buf);
+@@ -280,12 +289,13 @@ void bch2_readahead(struct readahead_control *ractl)
+ 		struct bch_read_bio *rbio =
+ 			rbio_init(bio_alloc_bioset(NULL, n, REQ_OP_READ,
+ 						   GFP_KERNEL, &c->bio_read),
+-				  opts);
++				  c,
++				  opts,
++				  bch2_readpages_end_io);
+ 
+ 		readpage_iter_advance(&readpages_iter);
+ 
+ 		rbio->bio.bi_iter.bi_sector = folio_sector(folio);
+-		rbio->bio.bi_end_io = bch2_readpages_end_io;
+ 		BUG_ON(!bio_add_folio(&rbio->bio, folio, folio_size(folio), 0));
+ 
+ 		bchfs_read(trans, rbio, inode_inum(inode),
+@@ -323,10 +333,10 @@ int bch2_read_single_folio(struct folio *folio, struct address_space *mapping)
+ 	bch2_inode_opts_get(&opts, c, &inode->ei_inode);
+ 
+ 	rbio = rbio_init(bio_alloc_bioset(NULL, 1, REQ_OP_READ, GFP_KERNEL, &c->bio_read),
+-			 opts);
++			 c,
++			 opts,
++			 bch2_read_single_folio_end_io);
+ 	rbio->bio.bi_private = &done;
+-	rbio->bio.bi_end_io = bch2_read_single_folio_end_io;
+-
+ 	rbio->bio.bi_opf = REQ_OP_READ|REQ_SYNC;
+ 	rbio->bio.bi_iter.bi_sector = folio_sector(folio);
+ 	BUG_ON(!bio_add_folio(&rbio->bio, folio, folio_size(folio), 0));
+@@ -420,7 +430,7 @@ static void bch2_writepage_io_done(struct bch_write_op *op)
+ 		}
+ 	}
+ 
+-	if (io->op.flags & BCH_WRITE_WROTE_DATA_INLINE) {
++	if (io->op.flags & BCH_WRITE_wrote_data_inline) {
+ 		bio_for_each_folio_all(fi, bio) {
+ 			struct bch_folio *s;
+ 
+diff --git a/fs/bcachefs/fs-io-direct.c b/fs/bcachefs/fs-io-direct.c
+index 2089c36b5866..535bc5fcbcc0 100644
+--- a/fs/bcachefs/fs-io-direct.c
++++ b/fs/bcachefs/fs-io-direct.c
+@@ -73,6 +73,7 @@ static int bch2_direct_IO_read(struct kiocb *req, struct iov_iter *iter)
+ 	struct blk_plug plug;
+ 	loff_t offset = req->ki_pos;
+ 	bool sync = is_sync_kiocb(req);
++	bool split = false;
+ 	size_t shorten;
+ 	ssize_t ret;
+ 
+@@ -99,8 +100,6 @@ static int bch2_direct_IO_read(struct kiocb *req, struct iov_iter *iter)
+ 			       GFP_KERNEL,
+ 			       &c->dio_read_bioset);
+ 
+-	bio->bi_end_io = bch2_direct_IO_read_endio;
+-
+ 	dio = container_of(bio, struct dio_read, rbio.bio);
+ 	closure_init(&dio->cl, NULL);
+ 
+@@ -133,12 +132,13 @@ static int bch2_direct_IO_read(struct kiocb *req, struct iov_iter *iter)
+ 
+ 	goto start;
+ 	while (iter->count) {
++		split = true;
++
+ 		bio = bio_alloc_bioset(NULL,
+ 				       bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS),
+ 				       REQ_OP_READ,
+ 				       GFP_KERNEL,
+ 				       &c->bio_read);
+-		bio->bi_end_io		= bch2_direct_IO_read_split_endio;
+ start:
+ 		bio->bi_opf		= REQ_OP_READ|REQ_SYNC;
+ 		bio->bi_iter.bi_sector	= offset >> 9;
+@@ -160,7 +160,15 @@ static int bch2_direct_IO_read(struct kiocb *req, struct iov_iter *iter)
+ 		if (iter->count)
+ 			closure_get(&dio->cl);
+ 
+-		bch2_read(c, rbio_init(bio, opts), inode_inum(inode));
++		struct bch_read_bio *rbio =
++			rbio_init(bio,
++				  c,
++				  opts,
++				  split
++				  ? bch2_direct_IO_read_split_endio
++				  : bch2_direct_IO_read_endio);
++
++		bch2_read(c, rbio, inode_inum(inode));
+ 	}
+ 
+ 	blk_finish_plug(&plug);
+@@ -511,8 +519,8 @@ static __always_inline long bch2_dio_write_loop(struct dio_write *dio)
+ 		dio->op.devs_need_flush	= &inode->ei_devs_need_flush;
+ 
+ 		if (sync)
+-			dio->op.flags |= BCH_WRITE_SYNC;
+-		dio->op.flags |= BCH_WRITE_CHECK_ENOSPC;
++			dio->op.flags |= BCH_WRITE_sync;
++		dio->op.flags |= BCH_WRITE_check_enospc;
+ 
+ 		ret = bch2_quota_reservation_add(c, inode, &dio->quota_res,
+ 						 bio_sectors(bio), true);
+diff --git a/fs/bcachefs/fs-ioctl.c b/fs/bcachefs/fs-ioctl.c
+index 15725b4ce393..e3a3230fc652 100644
+--- a/fs/bcachefs/fs-ioctl.c
++++ b/fs/bcachefs/fs-ioctl.c
+@@ -5,8 +5,8 @@
+ #include "chardev.h"
+ #include "dirent.h"
+ #include "fs.h"
+-#include "fs-common.h"
+ #include "fs-ioctl.h"
++#include "namei.h"
+ #include "quota.h"
+ 
+ #include <linux/compat.h>
+@@ -54,6 +54,32 @@ static int bch2_inode_flags_set(struct btree_trans *trans,
+ 	    (newflags & (BCH_INODE_nodump|BCH_INODE_noatime)) != newflags)
+ 		return -EINVAL;
+ 
++	if ((newflags ^ oldflags) & BCH_INODE_casefolded) {
++#ifdef CONFIG_UNICODE
++		int ret = 0;
++		/* Not supported on individual files. */
++		if (!S_ISDIR(bi->bi_mode))
++			return -EOPNOTSUPP;
++
++		/*
++		 * Make sure the dir is empty, as otherwise we'd need to
++		 * rehash everything and update the dirent keys.
++		 */
++		ret = bch2_empty_dir_trans(trans, inode_inum(inode));
++		if (ret < 0)
++			return ret;
++
++		ret = bch2_request_incompat_feature(c,bcachefs_metadata_version_casefolding);
++		if (ret)
++			return ret;
++
++		bch2_check_set_feature(c, BCH_FEATURE_casefolding);
++#else
++		printk(KERN_ERR "Cannot use casefolding on a kernel without CONFIG_UNICODE\n");
++		return -EOPNOTSUPP;
++#endif
++	}
++
+ 	if (s->set_projinherit) {
+ 		bi->bi_fields_set &= ~(1 << Inode_opt_project);
+ 		bi->bi_fields_set |= ((int) s->projinherit << Inode_opt_project);
+@@ -218,7 +244,7 @@ static int bch2_ioc_reinherit_attrs(struct bch_fs *c,
+ 	int ret = 0;
+ 	subvol_inum inum;
+ 
+-	kname = kmalloc(BCH_NAME_MAX + 1, GFP_KERNEL);
++	kname = kmalloc(BCH_NAME_MAX, GFP_KERNEL);
+ 	if (!kname)
+ 		return -ENOMEM;
+ 
+diff --git a/fs/bcachefs/fs-ioctl.h b/fs/bcachefs/fs-ioctl.h
+index d30f9bb056fd..ecd3bfdcde21 100644
+--- a/fs/bcachefs/fs-ioctl.h
++++ b/fs/bcachefs/fs-ioctl.h
+@@ -6,19 +6,21 @@
+ 
+ /* bcachefs inode flags -> vfs inode flags: */
+ static const __maybe_unused unsigned bch_flags_to_vfs[] = {
+-	[__BCH_INODE_sync]	= S_SYNC,
+-	[__BCH_INODE_immutable]	= S_IMMUTABLE,
+-	[__BCH_INODE_append]	= S_APPEND,
+-	[__BCH_INODE_noatime]	= S_NOATIME,
++	[__BCH_INODE_sync]		= S_SYNC,
++	[__BCH_INODE_immutable]		= S_IMMUTABLE,
++	[__BCH_INODE_append]		= S_APPEND,
++	[__BCH_INODE_noatime]		= S_NOATIME,
++	[__BCH_INODE_casefolded]	= S_CASEFOLD,
+ };
+ 
+ /* bcachefs inode flags -> FS_IOC_GETFLAGS: */
+ static const __maybe_unused unsigned bch_flags_to_uflags[] = {
+-	[__BCH_INODE_sync]	= FS_SYNC_FL,
+-	[__BCH_INODE_immutable]	= FS_IMMUTABLE_FL,
+-	[__BCH_INODE_append]	= FS_APPEND_FL,
+-	[__BCH_INODE_nodump]	= FS_NODUMP_FL,
+-	[__BCH_INODE_noatime]	= FS_NOATIME_FL,
++	[__BCH_INODE_sync]		= FS_SYNC_FL,
++	[__BCH_INODE_immutable]		= FS_IMMUTABLE_FL,
++	[__BCH_INODE_append]		= FS_APPEND_FL,
++	[__BCH_INODE_nodump]		= FS_NODUMP_FL,
++	[__BCH_INODE_noatime]		= FS_NOATIME_FL,
++	[__BCH_INODE_casefolded]	= FS_CASEFOLD_FL,
+ };
+ 
+ /* bcachefs inode flags -> FS_IOC_FSGETXATTR: */
+diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c
+index 90ade8f648d9..fbca200f7636 100644
+--- a/fs/bcachefs/fs.c
++++ b/fs/bcachefs/fs.c
+@@ -11,7 +11,6 @@
+ #include "errcode.h"
+ #include "extents.h"
+ #include "fs.h"
+-#include "fs-common.h"
+ #include "fs-io.h"
+ #include "fs-ioctl.h"
+ #include "fs-io-buffered.h"
+@@ -22,6 +21,7 @@
+ #include "io_read.h"
+ #include "journal.h"
+ #include "keylist.h"
++#include "namei.h"
+ #include "quota.h"
+ #include "rebalance.h"
+ #include "snapshot.h"
+@@ -641,7 +641,9 @@ static struct bch_inode_info *bch2_lookup_trans(struct btree_trans *trans,
+ 	if (ret)
+ 		return ERR_PTR(ret);
+ 
+-	ret = bch2_dirent_read_target(trans, dir, bkey_s_c_to_dirent(k), &inum);
++	struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
++
++	ret = bch2_dirent_read_target(trans, dir, d, &inum);
+ 	if (ret > 0)
+ 		ret = -ENOENT;
+ 	if (ret)
+@@ -651,30 +653,30 @@ static struct bch_inode_info *bch2_lookup_trans(struct btree_trans *trans,
+ 	if (inode)
+ 		goto out;
+ 
++	/*
++	 * Note: if check/repair needs it, we commit before
++	 * bch2_inode_hash_init_insert(), as after that point we can't take a
++	 * restart - not in the top level loop with a commit_do(), like we
++	 * usually do:
++	 */
++
+ 	struct bch_subvolume subvol;
+ 	struct bch_inode_unpacked inode_u;
+ 	ret =   bch2_subvolume_get(trans, inum.subvol, true, &subvol) ?:
+ 		bch2_inode_find_by_inum_nowarn_trans(trans, inum, &inode_u) ?:
++		bch2_check_dirent_target(trans, &dirent_iter, d, &inode_u, false) ?:
++		bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) ?:
+ 		PTR_ERR_OR_ZERO(inode = bch2_inode_hash_init_insert(trans, inum, &inode_u, &subvol));
+ 
++	/*
++	 * don't remove it: check_inodes might find another inode that points
++	 * back to this dirent
++	 */
+ 	bch2_fs_inconsistent_on(bch2_err_matches(ret, ENOENT),
+ 				c, "dirent to missing inode:\n  %s",
+-				(bch2_bkey_val_to_text(&buf, c, k), buf.buf));
++				(bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf));
+ 	if (ret)
+ 		goto err;
+-
+-	/* regular files may have hardlinks: */
+-	if (bch2_fs_inconsistent_on(bch2_inode_should_have_single_bp(&inode_u) &&
+-				    !bkey_eq(k.k->p, POS(inode_u.bi_dir, inode_u.bi_dir_offset)),
+-				    c,
+-				    "dirent points to inode that does not point back:\n  %s",
+-				    (bch2_bkey_val_to_text(&buf, c, k),
+-				     prt_printf(&buf, "\n  "),
+-				     bch2_inode_unpacked_to_text(&buf, &inode_u),
+-				     buf.buf))) {
+-		ret = -ENOENT;
+-		goto err;
+-	}
+ out:
+ 	bch2_trans_iter_exit(trans, &dirent_iter);
+ 	printbuf_exit(&buf);
+@@ -698,6 +700,23 @@ static struct dentry *bch2_lookup(struct inode *vdir, struct dentry *dentry,
+ 	if (IS_ERR(inode))
+ 		inode = NULL;
+ 
++#ifdef CONFIG_UNICODE
++	if (!inode && IS_CASEFOLDED(vdir)) {
++		/*
++		 * Do not cache a negative dentry in casefolded directories
++		 * as it would need to be invalidated in the following situation:
++		 * - Lookup file "blAH" in a casefolded directory
++		 * - Creation of file "BLAH" in a casefolded directory
++		 * - Lookup file "blAH" in a casefolded directory
++		 * which would fail if we had a negative dentry.
++		 *
++		 * We should come back to this when VFS has a method to handle
++		 * this edgecase.
++		 */
++		return NULL;
++	}
++#endif
++
+ 	return d_splice_alias(&inode->v, dentry);
+ }
+ 
+@@ -1802,7 +1821,8 @@ static void bch2_vfs_inode_init(struct btree_trans *trans,
+ 		break;
+ 	}
+ 
+-	mapping_set_large_folios(inode->v.i_mapping);
++	mapping_set_folio_min_order(inode->v.i_mapping,
++				    get_order(trans->c->opts.block_size));
+ }
+ 
+ static void bch2_free_inode(struct inode *vinode)
+@@ -2008,44 +2028,6 @@ static struct bch_fs *bch2_path_to_fs(const char *path)
+ 	return c ?: ERR_PTR(-ENOENT);
+ }
+ 
+-static int bch2_remount(struct super_block *sb, int *flags,
+-			struct bch_opts opts)
+-{
+-	struct bch_fs *c = sb->s_fs_info;
+-	int ret = 0;
+-
+-	opt_set(opts, read_only, (*flags & SB_RDONLY) != 0);
+-
+-	if (opts.read_only != c->opts.read_only) {
+-		down_write(&c->state_lock);
+-
+-		if (opts.read_only) {
+-			bch2_fs_read_only(c);
+-
+-			sb->s_flags |= SB_RDONLY;
+-		} else {
+-			ret = bch2_fs_read_write(c);
+-			if (ret) {
+-				bch_err(c, "error going rw: %i", ret);
+-				up_write(&c->state_lock);
+-				ret = -EINVAL;
+-				goto err;
+-			}
+-
+-			sb->s_flags &= ~SB_RDONLY;
+-		}
+-
+-		c->opts.read_only = opts.read_only;
+-
+-		up_write(&c->state_lock);
+-	}
+-
+-	if (opt_defined(opts, errors))
+-		c->opts.errors = opts.errors;
+-err:
+-	return bch2_err_class(ret);
+-}
+-
+ static int bch2_show_devname(struct seq_file *seq, struct dentry *root)
+ {
+ 	struct bch_fs *c = root->d_sb->s_fs_info;
+@@ -2192,6 +2174,9 @@ static int bch2_fs_get_tree(struct fs_context *fc)
+ 	if (ret)
+ 		goto err;
+ 
++	if (opt_defined(opts, discard))
++		set_bit(BCH_FS_discard_mount_opt_set, &c->flags);
++
+ 	/* Some options can't be parsed until after the fs is started: */
+ 	opts = bch2_opts_empty();
+ 	ret = bch2_parse_mount_opts(c, &opts, NULL, opts_parse->parse_later.buf);
+@@ -2200,9 +2185,10 @@ static int bch2_fs_get_tree(struct fs_context *fc)
+ 
+ 	bch2_opts_apply(&c->opts, opts);
+ 
+-	ret = bch2_fs_start(c);
+-	if (ret)
+-		goto err_stop_fs;
++	/*
++	 * need to initialise sb and set c->vfs_sb _before_ starting fs,
++	 * for blk_holder_ops
++	 */
+ 
+ 	sb = sget(fc->fs_type, NULL, bch2_set_super, fc->sb_flags|SB_NOSEC, c);
+ 	ret = PTR_ERR_OR_ZERO(sb);
+@@ -2264,6 +2250,10 @@ static int bch2_fs_get_tree(struct fs_context *fc)
+ 
+ 	sb->s_shrink->seeks = 0;
+ 
++	ret = bch2_fs_start(c);
++	if (ret)
++		goto err_put_super;
++
+ 	vinode = bch2_vfs_inode_get(c, BCACHEFS_ROOT_SUBVOL_INUM);
+ 	ret = PTR_ERR_OR_ZERO(vinode);
+ 	bch_err_msg(c, ret, "mounting: error getting root inode");
+@@ -2351,8 +2341,39 @@ static int bch2_fs_reconfigure(struct fs_context *fc)
+ {
+ 	struct super_block *sb = fc->root->d_sb;
+ 	struct bch2_opts_parse *opts = fc->fs_private;
++	struct bch_fs *c = sb->s_fs_info;
++	int ret = 0;
++
++	opt_set(opts->opts, read_only, (fc->sb_flags & SB_RDONLY) != 0);
+ 
+-	return bch2_remount(sb, &fc->sb_flags, opts->opts);
++	if (opts->opts.read_only != c->opts.read_only) {
++		down_write(&c->state_lock);
++
++		if (opts->opts.read_only) {
++			bch2_fs_read_only(c);
++
++			sb->s_flags |= SB_RDONLY;
++		} else {
++			ret = bch2_fs_read_write(c);
++			if (ret) {
++				bch_err(c, "error going rw: %i", ret);
++				up_write(&c->state_lock);
++				ret = -EINVAL;
++				goto err;
++			}
++
++			sb->s_flags &= ~SB_RDONLY;
++		}
++
++		c->opts.read_only = opts->opts.read_only;
++
++		up_write(&c->state_lock);
++	}
++
++	if (opt_defined(opts->opts, errors))
++		c->opts.errors = opts->opts.errors;
++err:
++	return bch2_err_class(ret);
+ }
+ 
+ static const struct fs_context_operations bch2_context_ops = {
+diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c
+index 0e85131d0af8..091057023fc5 100644
+--- a/fs/bcachefs/fsck.c
++++ b/fs/bcachefs/fsck.c
+@@ -10,10 +10,10 @@
+ #include "dirent.h"
+ #include "error.h"
+ #include "fs.h"
+-#include "fs-common.h"
+ #include "fsck.h"
+ #include "inode.h"
+ #include "keylist.h"
++#include "namei.h"
+ #include "recovery_passes.h"
+ #include "snapshot.h"
+ #include "super.h"
+@@ -23,13 +23,6 @@
+ #include <linux/bsearch.h>
+ #include <linux/dcache.h> /* struct qstr */
+ 
+-static bool inode_points_to_dirent(struct bch_inode_unpacked *inode,
+-				   struct bkey_s_c_dirent d)
+-{
+-	return  inode->bi_dir		== d.k->p.inode &&
+-		inode->bi_dir_offset	== d.k->p.offset;
+-}
+-
+ static int dirent_points_to_inode_nowarn(struct bkey_s_c_dirent d,
+ 					 struct bch_inode_unpacked *inode)
+ {
+@@ -116,29 +109,6 @@ static int subvol_lookup(struct btree_trans *trans, u32 subvol,
+ 	return ret;
+ }
+ 
+-static int lookup_first_inode(struct btree_trans *trans, u64 inode_nr,
+-			      struct bch_inode_unpacked *inode)
+-{
+-	struct btree_iter iter;
+-	struct bkey_s_c k;
+-	int ret;
+-
+-	for_each_btree_key_norestart(trans, iter, BTREE_ID_inodes, POS(0, inode_nr),
+-				     BTREE_ITER_all_snapshots, k, ret) {
+-		if (k.k->p.offset != inode_nr)
+-			break;
+-		if (!bkey_is_inode(k.k))
+-			continue;
+-		ret = bch2_inode_unpack(k, inode);
+-		goto found;
+-	}
+-	ret = -BCH_ERR_ENOENT_inode;
+-found:
+-	bch_err_msg(trans->c, ret, "fetching inode %llu", inode_nr);
+-	bch2_trans_iter_exit(trans, &iter);
+-	return ret;
+-}
+-
+ static int lookup_inode(struct btree_trans *trans, u64 inode_nr, u32 snapshot,
+ 			struct bch_inode_unpacked *inode)
+ {
+@@ -179,32 +149,6 @@ static int lookup_dirent_in_snapshot(struct btree_trans *trans,
+ 	return 0;
+ }
+ 
+-static int __remove_dirent(struct btree_trans *trans, struct bpos pos)
+-{
+-	struct bch_fs *c = trans->c;
+-	struct btree_iter iter;
+-	struct bch_inode_unpacked dir_inode;
+-	struct bch_hash_info dir_hash_info;
+-	int ret;
+-
+-	ret = lookup_first_inode(trans, pos.inode, &dir_inode);
+-	if (ret)
+-		goto err;
+-
+-	dir_hash_info = bch2_hash_info_init(c, &dir_inode);
+-
+-	bch2_trans_iter_init(trans, &iter, BTREE_ID_dirents, pos, BTREE_ITER_intent);
+-
+-	ret =   bch2_btree_iter_traverse(&iter) ?:
+-		bch2_hash_delete_at(trans, bch2_dirent_hash_desc,
+-				    &dir_hash_info, &iter,
+-				    BTREE_UPDATE_internal_snapshot_node);
+-	bch2_trans_iter_exit(trans, &iter);
+-err:
+-	bch_err_fn(c, ret);
+-	return ret;
+-}
+-
+ /*
+  * Find any subvolume associated with a tree of snapshots
+  * We can't rely on master_subvol - it might have been deleted.
+@@ -548,7 +492,7 @@ static int remove_backpointer(struct btree_trans *trans,
+ 				     SPOS(inode->bi_dir, inode->bi_dir_offset, inode->bi_snapshot));
+ 	int ret = bkey_err(d) ?:
+ 		  dirent_points_to_inode(c, d, inode) ?:
+-		  __remove_dirent(trans, d.k->p);
++		  bch2_fsck_remove_dirent(trans, d.k->p);
+ 	bch2_trans_iter_exit(trans, &iter);
+ 	return ret;
+ }
+@@ -1985,169 +1929,6 @@ static int check_subdir_dirents_count(struct btree_trans *trans, struct inode_wa
+ 		trans_was_restarted(trans, restart_count);
+ }
+ 
+-noinline_for_stack
+-static int check_dirent_inode_dirent(struct btree_trans *trans,
+-				   struct btree_iter *iter,
+-				   struct bkey_s_c_dirent d,
+-				   struct bch_inode_unpacked *target)
+-{
+-	struct bch_fs *c = trans->c;
+-	struct printbuf buf = PRINTBUF;
+-	struct btree_iter bp_iter = { NULL };
+-	int ret = 0;
+-
+-	if (inode_points_to_dirent(target, d))
+-		return 0;
+-
+-	if (!target->bi_dir &&
+-	    !target->bi_dir_offset) {
+-		fsck_err_on(S_ISDIR(target->bi_mode),
+-			    trans, inode_dir_missing_backpointer,
+-			    "directory with missing backpointer\n%s",
+-			    (printbuf_reset(&buf),
+-			     bch2_bkey_val_to_text(&buf, c, d.s_c),
+-			     prt_printf(&buf, "\n"),
+-			     bch2_inode_unpacked_to_text(&buf, target),
+-			     buf.buf));
+-
+-		fsck_err_on(target->bi_flags & BCH_INODE_unlinked,
+-			    trans, inode_unlinked_but_has_dirent,
+-			    "inode unlinked but has dirent\n%s",
+-			    (printbuf_reset(&buf),
+-			     bch2_bkey_val_to_text(&buf, c, d.s_c),
+-			     prt_printf(&buf, "\n"),
+-			     bch2_inode_unpacked_to_text(&buf, target),
+-			     buf.buf));
+-
+-		target->bi_flags &= ~BCH_INODE_unlinked;
+-		target->bi_dir		= d.k->p.inode;
+-		target->bi_dir_offset	= d.k->p.offset;
+-		return __bch2_fsck_write_inode(trans, target);
+-	}
+-
+-	if (bch2_inode_should_have_single_bp(target) &&
+-	    !fsck_err(trans, inode_wrong_backpointer,
+-		      "dirent points to inode that does not point back:\n  %s",
+-		      (bch2_bkey_val_to_text(&buf, c, d.s_c),
+-		       prt_printf(&buf, "\n  "),
+-		       bch2_inode_unpacked_to_text(&buf, target),
+-		       buf.buf)))
+-		goto err;
+-
+-	struct bkey_s_c_dirent bp_dirent = dirent_get_by_pos(trans, &bp_iter,
+-			      SPOS(target->bi_dir, target->bi_dir_offset, target->bi_snapshot));
+-	ret = bkey_err(bp_dirent);
+-	if (ret && !bch2_err_matches(ret, ENOENT))
+-		goto err;
+-
+-	bool backpointer_exists = !ret;
+-	ret = 0;
+-
+-	if (fsck_err_on(!backpointer_exists,
+-			trans, inode_wrong_backpointer,
+-			"inode %llu:%u has wrong backpointer:\n"
+-			"got       %llu:%llu\n"
+-			"should be %llu:%llu",
+-			target->bi_inum, target->bi_snapshot,
+-			target->bi_dir,
+-			target->bi_dir_offset,
+-			d.k->p.inode,
+-			d.k->p.offset)) {
+-		target->bi_dir		= d.k->p.inode;
+-		target->bi_dir_offset	= d.k->p.offset;
+-		ret = __bch2_fsck_write_inode(trans, target);
+-		goto out;
+-	}
+-
+-	bch2_bkey_val_to_text(&buf, c, d.s_c);
+-	prt_newline(&buf);
+-	if (backpointer_exists)
+-		bch2_bkey_val_to_text(&buf, c, bp_dirent.s_c);
+-
+-	if (fsck_err_on(backpointer_exists &&
+-			(S_ISDIR(target->bi_mode) ||
+-			 target->bi_subvol),
+-			trans, inode_dir_multiple_links,
+-			"%s %llu:%u with multiple links\n%s",
+-			S_ISDIR(target->bi_mode) ? "directory" : "subvolume",
+-			target->bi_inum, target->bi_snapshot, buf.buf)) {
+-		ret = __remove_dirent(trans, d.k->p);
+-		goto out;
+-	}
+-
+-	/*
+-	 * hardlinked file with nlink 0:
+-	 * We're just adjusting nlink here so check_nlinks() will pick
+-	 * it up, it ignores inodes with nlink 0
+-	 */
+-	if (fsck_err_on(backpointer_exists && !target->bi_nlink,
+-			trans, inode_multiple_links_but_nlink_0,
+-			"inode %llu:%u type %s has multiple links but i_nlink 0\n%s",
+-			target->bi_inum, target->bi_snapshot, bch2_d_types[d.v->d_type], buf.buf)) {
+-		target->bi_nlink++;
+-		target->bi_flags &= ~BCH_INODE_unlinked;
+-		ret = __bch2_fsck_write_inode(trans, target);
+-		if (ret)
+-			goto err;
+-	}
+-out:
+-err:
+-fsck_err:
+-	bch2_trans_iter_exit(trans, &bp_iter);
+-	printbuf_exit(&buf);
+-	bch_err_fn(c, ret);
+-	return ret;
+-}
+-
+-noinline_for_stack
+-static int check_dirent_target(struct btree_trans *trans,
+-			       struct btree_iter *iter,
+-			       struct bkey_s_c_dirent d,
+-			       struct bch_inode_unpacked *target)
+-{
+-	struct bch_fs *c = trans->c;
+-	struct bkey_i_dirent *n;
+-	struct printbuf buf = PRINTBUF;
+-	int ret = 0;
+-
+-	ret = check_dirent_inode_dirent(trans, iter, d, target);
+-	if (ret)
+-		goto err;
+-
+-	if (fsck_err_on(d.v->d_type != inode_d_type(target),
+-			trans, dirent_d_type_wrong,
+-			"incorrect d_type: got %s, should be %s:\n%s",
+-			bch2_d_type_str(d.v->d_type),
+-			bch2_d_type_str(inode_d_type(target)),
+-			(printbuf_reset(&buf),
+-			 bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf))) {
+-		n = bch2_trans_kmalloc(trans, bkey_bytes(d.k));
+-		ret = PTR_ERR_OR_ZERO(n);
+-		if (ret)
+-			goto err;
+-
+-		bkey_reassemble(&n->k_i, d.s_c);
+-		n->v.d_type = inode_d_type(target);
+-		if (n->v.d_type == DT_SUBVOL) {
+-			n->v.d_parent_subvol = cpu_to_le32(target->bi_parent_subvol);
+-			n->v.d_child_subvol = cpu_to_le32(target->bi_subvol);
+-		} else {
+-			n->v.d_inum = cpu_to_le64(target->bi_inum);
+-		}
+-
+-		ret = bch2_trans_update(trans, iter, &n->k_i, 0);
+-		if (ret)
+-			goto err;
+-
+-		d = dirent_i_to_s_c(n);
+-	}
+-err:
+-fsck_err:
+-	printbuf_exit(&buf);
+-	bch_err_fn(c, ret);
+-	return ret;
+-}
+-
+ /* find a subvolume that's a descendent of @snapshot: */
+ static int find_snapshot_subvol(struct btree_trans *trans, u32 snapshot, u32 *subvolid)
+ {
+@@ -2247,7 +2028,7 @@ static int check_dirent_to_subvol(struct btree_trans *trans, struct btree_iter *
+ 		if (fsck_err(trans, dirent_to_missing_subvol,
+ 			     "dirent points to missing subvolume\n%s",
+ 			     (bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf)))
+-			return __remove_dirent(trans, d.k->p);
++			return bch2_fsck_remove_dirent(trans, d.k->p);
+ 		ret = 0;
+ 		goto out;
+ 	}
+@@ -2291,7 +2072,7 @@ static int check_dirent_to_subvol(struct btree_trans *trans, struct btree_iter *
+ 			goto err;
+ 	}
+ 
+-	ret = check_dirent_target(trans, iter, d, &subvol_root);
++	ret = bch2_check_dirent_target(trans, iter, d, &subvol_root, true);
+ 	if (ret)
+ 		goto err;
+ out:
+@@ -2378,13 +2159,13 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
+ 				(printbuf_reset(&buf),
+ 				 bch2_bkey_val_to_text(&buf, c, k),
+ 				 buf.buf))) {
+-			ret = __remove_dirent(trans, d.k->p);
++			ret = bch2_fsck_remove_dirent(trans, d.k->p);
+ 			if (ret)
+ 				goto err;
+ 		}
+ 
+ 		darray_for_each(target->inodes, i) {
+-			ret = check_dirent_target(trans, iter, d, &i->inode);
++			ret = bch2_check_dirent_target(trans, iter, d, &i->inode, true);
+ 			if (ret)
+ 				goto err;
+ 		}
+diff --git a/fs/bcachefs/inode.c b/fs/bcachefs/inode.c
+index 339b80770f1d..80051073f613 100644
+--- a/fs/bcachefs/inode.c
++++ b/fs/bcachefs/inode.c
+@@ -731,10 +731,9 @@ int bch2_trigger_inode(struct btree_trans *trans,
+ 		bkey_s_to_inode_v3(new).v->bi_journal_seq = cpu_to_le64(trans->journal_res.seq);
+ 	}
+ 
+-	s64 nr = bkey_is_inode(new.k) - bkey_is_inode(old.k);
+-	if ((flags & (BTREE_TRIGGER_transactional|BTREE_TRIGGER_gc)) && nr) {
+-		struct disk_accounting_pos acc = { .type = BCH_DISK_ACCOUNTING_nr_inodes };
+-		int ret = bch2_disk_accounting_mod(trans, &acc, &nr, 1, flags & BTREE_TRIGGER_gc);
++	s64 nr[1] = { bkey_is_inode(new.k) - bkey_is_inode(old.k) };
++	if ((flags & (BTREE_TRIGGER_transactional|BTREE_TRIGGER_gc)) && nr[0]) {
++		int ret = bch2_disk_accounting_mod2(trans, flags & BTREE_TRIGGER_gc, nr, nr_inodes);
+ 		if (ret)
+ 			return ret;
+ 	}
+@@ -868,19 +867,6 @@ void bch2_inode_init(struct bch_fs *c, struct bch_inode_unpacked *inode_u,
+ 			     uid, gid, mode, rdev, parent);
+ }
+ 
+-static inline u32 bkey_generation(struct bkey_s_c k)
+-{
+-	switch (k.k->type) {
+-	case KEY_TYPE_inode:
+-	case KEY_TYPE_inode_v2:
+-		BUG();
+-	case KEY_TYPE_inode_generation:
+-		return le32_to_cpu(bkey_s_c_to_inode_generation(k).v->bi_generation);
+-	default:
+-		return 0;
+-	}
+-}
+-
+ static struct bkey_i_inode_alloc_cursor *
+ bch2_inode_alloc_cursor_get(struct btree_trans *trans, u64 cpu, u64 *min, u64 *max)
+ {
+@@ -1092,7 +1078,7 @@ int bch2_inode_rm(struct bch_fs *c, subvol_inum inum)
+ 		bch2_fs_inconsistent(c,
+ 				     "inode %llu:%u not found when deleting",
+ 				     inum.inum, snapshot);
+-		ret = -EIO;
++		ret = -BCH_ERR_ENOENT_inode;
+ 		goto err;
+ 	}
+ 
+@@ -1256,7 +1242,7 @@ static noinline int __bch2_inode_rm_snapshot(struct btree_trans *trans, u64 inum
+ 		bch2_fs_inconsistent(c,
+ 				     "inode %llu:%u not found when deleting",
+ 				     inum, snapshot);
+-		ret = -EIO;
++		ret = -BCH_ERR_ENOENT_inode;
+ 		goto err;
+ 	}
+ 
+diff --git a/fs/bcachefs/inode.h b/fs/bcachefs/inode.h
+index 428b9be6af34..f82cfbf460d0 100644
+--- a/fs/bcachefs/inode.h
++++ b/fs/bcachefs/inode.h
+@@ -277,6 +277,7 @@ static inline bool bch2_inode_should_have_single_bp(struct bch_inode_unpacked *i
+ 	bool inode_has_bp = inode->bi_dir || inode->bi_dir_offset;
+ 
+ 	return S_ISDIR(inode->bi_mode) ||
++		inode->bi_subvol ||
+ 		(!inode->bi_nlink && inode_has_bp);
+ }
+ 
+diff --git a/fs/bcachefs/inode_format.h b/fs/bcachefs/inode_format.h
+index b99a5bf1a75e..117110af1e3f 100644
+--- a/fs/bcachefs/inode_format.h
++++ b/fs/bcachefs/inode_format.h
+@@ -137,7 +137,8 @@ enum inode_opt_id {
+ 	x(i_sectors_dirty,		6)	\
+ 	x(unlinked,			7)	\
+ 	x(backptr_untrusted,		8)	\
+-	x(has_child_snapshot,		9)
++	x(has_child_snapshot,		9)	\
++	x(casefolded,			10)
+ 
+ /* bits 20+ reserved for packed fields below: */
+ 
+diff --git a/fs/bcachefs/io_misc.c b/fs/bcachefs/io_misc.c
+index 5353979117b0..6b842c8d21be 100644
+--- a/fs/bcachefs/io_misc.c
++++ b/fs/bcachefs/io_misc.c
+@@ -115,7 +115,8 @@ int bch2_extent_fallocate(struct btree_trans *trans,
+ 		bch2_increment_clock(c, sectors_allocated, WRITE);
+ 	if (should_print_err(ret)) {
+ 		struct printbuf buf = PRINTBUF;
+-		bch2_inum_offset_err_msg_trans(trans, &buf, inum, iter->pos.offset << 9);
++		lockrestart_do(trans,
++			bch2_inum_offset_err_msg_trans(trans, &buf, inum, iter->pos.offset << 9));
+ 		prt_printf(&buf, "fallocate error: %s", bch2_err_str(ret));
+ 		bch_err_ratelimited(c, "%s", buf.buf);
+ 		printbuf_exit(&buf);
+diff --git a/fs/bcachefs/io_read.c b/fs/bcachefs/io_read.c
+index aa91fcf51eec..a04dffa45f5f 100644
+--- a/fs/bcachefs/io_read.c
++++ b/fs/bcachefs/io_read.c
+@@ -25,8 +25,15 @@
+ #include "subvolume.h"
+ #include "trace.h"
+ 
++#include <linux/random.h>
+ #include <linux/sched/mm.h>
+ 
++#ifdef CONFIG_BCACHEFS_DEBUG
++static unsigned bch2_read_corrupt_ratio;
++module_param_named(read_corrupt_ratio, bch2_read_corrupt_ratio, uint, 0644);
++MODULE_PARM_DESC(read_corrupt_ratio, "");
++#endif
++
+ #ifndef CONFIG_BCACHEFS_NO_LATENCY_ACCT
+ 
+ static bool bch2_target_congested(struct bch_fs *c, u16 target)
+@@ -80,6 +87,7 @@ struct promote_op {
+ 	struct rhash_head	hash;
+ 	struct bpos		pos;
+ 
++	struct work_struct	work;
+ 	struct data_update	write;
+ 	struct bio_vec		bi_inline_vecs[]; /* must be last */
+ };
+@@ -96,6 +104,33 @@ static inline bool have_io_error(struct bch_io_failures *failed)
+ 	return failed && failed->nr;
+ }
+ 
++static inline struct data_update *rbio_data_update(struct bch_read_bio *rbio)
++{
++	EBUG_ON(rbio->split);
++
++	return rbio->data_update
++		? container_of(rbio, struct data_update, rbio)
++		: NULL;
++}
++
++static bool ptr_being_rewritten(struct bch_read_bio *orig, unsigned dev)
++{
++	struct data_update *u = rbio_data_update(orig);
++	if (!u)
++		return false;
++
++	struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(bkey_i_to_s_c(u->k.k));
++	unsigned i = 0;
++	bkey_for_each_ptr(ptrs, ptr) {
++		if (ptr->dev == dev &&
++		    u->data_opts.rewrite_ptrs & BIT(i))
++			return true;
++		i++;
++	}
++
++	return false;
++}
++
+ static inline int should_promote(struct bch_fs *c, struct bkey_s_c k,
+ 				  struct bpos pos,
+ 				  struct bch_io_opts opts,
+@@ -105,7 +140,7 @@ static inline int should_promote(struct bch_fs *c, struct bkey_s_c k,
+ 	if (!have_io_error(failed)) {
+ 		BUG_ON(!opts.promote_target);
+ 
+-		if (!(flags & BCH_READ_MAY_PROMOTE))
++		if (!(flags & BCH_READ_may_promote))
+ 			return -BCH_ERR_nopromote_may_not;
+ 
+ 		if (bch2_bkey_has_target(c, k, opts.promote_target))
+@@ -125,98 +160,93 @@ static inline int should_promote(struct bch_fs *c, struct bkey_s_c k,
+ 	return 0;
+ }
+ 
+-static void promote_free(struct bch_fs *c, struct promote_op *op)
++static noinline void promote_free(struct bch_read_bio *rbio)
+ {
+-	int ret;
++	struct promote_op *op = container_of(rbio, struct promote_op, write.rbio);
++	struct bch_fs *c = rbio->c;
++
++	int ret = rhashtable_remove_fast(&c->promote_table, &op->hash,
++					 bch_promote_params);
++	BUG_ON(ret);
+ 
+ 	bch2_data_update_exit(&op->write);
+ 
+-	ret = rhashtable_remove_fast(&c->promote_table, &op->hash,
+-				     bch_promote_params);
+-	BUG_ON(ret);
+ 	bch2_write_ref_put(c, BCH_WRITE_REF_promote);
+ 	kfree_rcu(op, rcu);
+ }
+ 
+ static void promote_done(struct bch_write_op *wop)
+ {
+-	struct promote_op *op =
+-		container_of(wop, struct promote_op, write.op);
+-	struct bch_fs *c = op->write.op.c;
++	struct promote_op *op = container_of(wop, struct promote_op, write.op);
++	struct bch_fs *c = op->write.rbio.c;
+ 
+-	bch2_time_stats_update(&c->times[BCH_TIME_data_promote],
+-			       op->start_time);
+-	promote_free(c, op);
++	bch2_time_stats_update(&c->times[BCH_TIME_data_promote], op->start_time);
++	promote_free(&op->write.rbio);
+ }
+ 
+-static void promote_start(struct promote_op *op, struct bch_read_bio *rbio)
++static void promote_start_work(struct work_struct *work)
+ {
+-	struct bio *bio = &op->write.op.wbio.bio;
++	struct promote_op *op = container_of(work, struct promote_op, work);
+ 
+-	trace_and_count(op->write.op.c, read_promote, &rbio->bio);
++	bch2_data_update_read_done(&op->write);
++}
+ 
+-	/* we now own pages: */
+-	BUG_ON(!rbio->bounce);
+-	BUG_ON(rbio->bio.bi_vcnt > bio->bi_max_vecs);
++static noinline void promote_start(struct bch_read_bio *rbio)
++{
++	struct promote_op *op = container_of(rbio, struct promote_op, write.rbio);
+ 
+-	memcpy(bio->bi_io_vec, rbio->bio.bi_io_vec,
+-	       sizeof(struct bio_vec) * rbio->bio.bi_vcnt);
+-	swap(bio->bi_vcnt, rbio->bio.bi_vcnt);
++	trace_and_count(op->write.op.c, io_read_promote, &rbio->bio);
+ 
+-	bch2_data_update_read_done(&op->write, rbio->pick.crc);
++	INIT_WORK(&op->work, promote_start_work);
++	queue_work(rbio->c->write_ref_wq, &op->work);
+ }
+ 
+-static struct promote_op *__promote_alloc(struct btree_trans *trans,
+-					  enum btree_id btree_id,
+-					  struct bkey_s_c k,
+-					  struct bpos pos,
+-					  struct extent_ptr_decoded *pick,
+-					  struct bch_io_opts opts,
+-					  unsigned sectors,
+-					  struct bch_read_bio **rbio,
+-					  struct bch_io_failures *failed)
++static struct bch_read_bio *__promote_alloc(struct btree_trans *trans,
++					    enum btree_id btree_id,
++					    struct bkey_s_c k,
++					    struct bpos pos,
++					    struct extent_ptr_decoded *pick,
++					    unsigned sectors,
++					    struct bch_read_bio *orig,
++					    struct bch_io_failures *failed)
+ {
+ 	struct bch_fs *c = trans->c;
+-	struct promote_op *op = NULL;
+-	struct bio *bio;
+-	unsigned pages = DIV_ROUND_UP(sectors, PAGE_SECTORS);
+ 	int ret;
+ 
+-	if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_promote))
+-		return ERR_PTR(-BCH_ERR_nopromote_no_writes);
++	struct data_update_opts update_opts = { .write_flags = BCH_WRITE_alloc_nowait };
+ 
+-	op = kzalloc(struct_size(op, bi_inline_vecs, pages), GFP_KERNEL);
+-	if (!op) {
+-		ret = -BCH_ERR_nopromote_enomem;
+-		goto err;
+-	}
++	if (!have_io_error(failed)) {
++		update_opts.target = orig->opts.promote_target;
++		update_opts.extra_replicas = 1;
++		update_opts.write_flags |= BCH_WRITE_cached;
++		update_opts.write_flags |= BCH_WRITE_only_specified_devs;
++	} else {
++		update_opts.target = orig->opts.foreground_target;
+ 
+-	op->start_time = local_clock();
+-	op->pos = pos;
++		struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
++		unsigned ptr_bit = 1;
++		bkey_for_each_ptr(ptrs, ptr) {
++			if (bch2_dev_io_failures(failed, ptr->dev) &&
++			    !ptr_being_rewritten(orig, ptr->dev))
++				update_opts.rewrite_ptrs |= ptr_bit;
++			ptr_bit <<= 1;
++		}
+ 
+-	/*
+-	 * We don't use the mempool here because extents that aren't
+-	 * checksummed or compressed can be too big for the mempool:
+-	 */
+-	*rbio = kzalloc(sizeof(struct bch_read_bio) +
+-			sizeof(struct bio_vec) * pages,
+-			GFP_KERNEL);
+-	if (!*rbio) {
+-		ret = -BCH_ERR_nopromote_enomem;
+-		goto err;
++		if (!update_opts.rewrite_ptrs)
++			return NULL;
+ 	}
+ 
+-	rbio_init(&(*rbio)->bio, opts);
+-	bio_init(&(*rbio)->bio, NULL, (*rbio)->bio.bi_inline_vecs, pages, 0);
++	if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_promote))
++		return ERR_PTR(-BCH_ERR_nopromote_no_writes);
+ 
+-	if (bch2_bio_alloc_pages(&(*rbio)->bio, sectors << 9, GFP_KERNEL)) {
++	struct promote_op *op = kzalloc(sizeof(*op), GFP_KERNEL);
++	if (!op) {
+ 		ret = -BCH_ERR_nopromote_enomem;
+-		goto err;
++		goto err_put;
+ 	}
+ 
+-	(*rbio)->bounce		= true;
+-	(*rbio)->split		= true;
+-	(*rbio)->kmalloc	= true;
++	op->start_time = local_clock();
++	op->pos = pos;
+ 
+ 	if (rhashtable_lookup_insert_fast(&c->promote_table, &op->hash,
+ 					  bch_promote_params)) {
+@@ -224,68 +254,54 @@ static struct promote_op *__promote_alloc(struct btree_trans *trans,
+ 		goto err;
+ 	}
+ 
+-	bio = &op->write.op.wbio.bio;
+-	bio_init(bio, NULL, bio->bi_inline_vecs, pages, 0);
+-
+-	struct data_update_opts update_opts = {};
+-
+-	if (!have_io_error(failed)) {
+-		update_opts.target = opts.promote_target;
+-		update_opts.extra_replicas = 1;
+-		update_opts.write_flags = BCH_WRITE_ALLOC_NOWAIT|BCH_WRITE_CACHED;
+-	} else {
+-		update_opts.target = opts.foreground_target;
+-
+-		struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
+-		unsigned ptr_bit = 1;
+-		bkey_for_each_ptr(ptrs, ptr) {
+-			if (bch2_dev_io_failures(failed, ptr->dev))
+-				update_opts.rewrite_ptrs |= ptr_bit;
+-			ptr_bit <<= 1;
+-		}
+-	}
+-
+ 	ret = bch2_data_update_init(trans, NULL, NULL, &op->write,
+ 			writepoint_hashed((unsigned long) current),
+-			opts,
++			&orig->opts,
+ 			update_opts,
+ 			btree_id, k);
+ 	/*
+ 	 * possible errors: -BCH_ERR_nocow_lock_blocked,
+ 	 * -BCH_ERR_ENOSPC_disk_reservation:
+ 	 */
+-	if (ret) {
+-		BUG_ON(rhashtable_remove_fast(&c->promote_table, &op->hash,
+-					      bch_promote_params));
+-		goto err;
+-	}
++	if (ret)
++		goto err_remove_hash;
+ 
++	rbio_init_fragment(&op->write.rbio.bio, orig);
++	op->write.rbio.bounce	= true;
++	op->write.rbio.promote	= true;
+ 	op->write.op.end_io = promote_done;
+ 
+-	return op;
++	return &op->write.rbio;
++err_remove_hash:
++	BUG_ON(rhashtable_remove_fast(&c->promote_table, &op->hash,
++				      bch_promote_params));
+ err:
+-	if (*rbio)
+-		bio_free_pages(&(*rbio)->bio);
+-	kfree(*rbio);
+-	*rbio = NULL;
++	bio_free_pages(&op->write.op.wbio.bio);
+ 	/* We may have added to the rhashtable and thus need rcu freeing: */
+ 	kfree_rcu(op, rcu);
++err_put:
+ 	bch2_write_ref_put(c, BCH_WRITE_REF_promote);
+ 	return ERR_PTR(ret);
+ }
+ 
+ noinline
+-static struct promote_op *promote_alloc(struct btree_trans *trans,
++static struct bch_read_bio *promote_alloc(struct btree_trans *trans,
+ 					struct bvec_iter iter,
+ 					struct bkey_s_c k,
+ 					struct extent_ptr_decoded *pick,
+-					struct bch_io_opts opts,
+ 					unsigned flags,
+-					struct bch_read_bio **rbio,
++					struct bch_read_bio *orig,
+ 					bool *bounce,
+ 					bool *read_full,
+ 					struct bch_io_failures *failed)
+ {
++	/*
++	 * We're in the retry path, but we don't know what to repair yet, and we
++	 * don't want to do a promote here:
++	 */
++	if (failed && !failed->nr)
++		return NULL;
++
+ 	struct bch_fs *c = trans->c;
+ 	/*
+ 	 * if failed != NULL we're not actually doing a promote, we're
+@@ -301,18 +317,21 @@ static struct promote_op *promote_alloc(struct btree_trans *trans,
+ 	struct bpos pos = promote_full
+ 		? bkey_start_pos(k.k)
+ 		: POS(k.k->p.inode, iter.bi_sector);
+-	struct promote_op *promote;
+ 	int ret;
+ 
+-	ret = should_promote(c, k, pos, opts, flags, failed);
++	ret = should_promote(c, k, pos, orig->opts, flags, failed);
+ 	if (ret)
+ 		goto nopromote;
+ 
+-	promote = __promote_alloc(trans,
+-				  k.k->type == KEY_TYPE_reflink_v
+-				  ? BTREE_ID_reflink
+-				  : BTREE_ID_extents,
+-				  k, pos, pick, opts, sectors, rbio, failed);
++	struct bch_read_bio *promote =
++		__promote_alloc(trans,
++				k.k->type == KEY_TYPE_reflink_v
++				? BTREE_ID_reflink
++				: BTREE_ID_extents,
++				k, pos, pick, sectors, orig, failed);
++	if (!promote)
++		return NULL;
++
+ 	ret = PTR_ERR_OR_ZERO(promote);
+ 	if (ret)
+ 		goto nopromote;
+@@ -321,7 +340,7 @@ static struct promote_op *promote_alloc(struct btree_trans *trans,
+ 	*read_full	= promote_full;
+ 	return promote;
+ nopromote:
+-	trace_read_nopromote(c, ret);
++	trace_io_read_nopromote(c, ret);
+ 	return NULL;
+ }
+ 
+@@ -330,9 +349,17 @@ static struct promote_op *promote_alloc(struct btree_trans *trans,
+ static int bch2_read_err_msg_trans(struct btree_trans *trans, struct printbuf *out,
+ 				   struct bch_read_bio *rbio, struct bpos read_pos)
+ {
+-	return bch2_inum_offset_err_msg_trans(trans, out,
+-		(subvol_inum) { rbio->subvol, read_pos.inode },
+-		read_pos.offset << 9);
++	int ret = lockrestart_do(trans,
++		bch2_inum_offset_err_msg_trans(trans, out,
++				(subvol_inum) { rbio->subvol, read_pos.inode },
++				read_pos.offset << 9));
++	if (ret)
++		return ret;
++
++	if (rbio->data_update)
++		prt_str(out, "(internal move) ");
++
++	return 0;
+ }
+ 
+ static void bch2_read_err_msg(struct bch_fs *c, struct printbuf *out,
+@@ -341,10 +368,6 @@ static void bch2_read_err_msg(struct bch_fs *c, struct printbuf *out,
+ 	bch2_trans_run(c, bch2_read_err_msg_trans(trans, out, rbio, read_pos));
+ }
+ 
+-#define READ_RETRY_AVOID	1
+-#define READ_RETRY		2
+-#define READ_ERR		3
+-
+ enum rbio_context {
+ 	RBIO_CONTEXT_NULL,
+ 	RBIO_CONTEXT_HIGHPRI,
+@@ -375,20 +398,25 @@ static inline struct bch_read_bio *bch2_rbio_free(struct bch_read_bio *rbio)
+ {
+ 	BUG_ON(rbio->bounce && !rbio->split);
+ 
+-	if (rbio->promote)
+-		promote_free(rbio->c, rbio->promote);
+-	rbio->promote = NULL;
+-
+-	if (rbio->bounce)
+-		bch2_bio_free_pages_pool(rbio->c, &rbio->bio);
++	if (rbio->have_ioref) {
++		struct bch_dev *ca = bch2_dev_have_ref(rbio->c, rbio->pick.ptr.dev);
++		percpu_ref_put(&ca->io_ref);
++	}
+ 
+ 	if (rbio->split) {
+ 		struct bch_read_bio *parent = rbio->parent;
+ 
+-		if (rbio->kmalloc)
+-			kfree(rbio);
+-		else
++		if (unlikely(rbio->promote)) {
++			if (!rbio->bio.bi_status)
++				promote_start(rbio);
++			else
++				promote_free(rbio);
++		} else {
++			if (rbio->bounce)
++				bch2_bio_free_pages_pool(rbio->c, &rbio->bio);
++
+ 			bio_put(&rbio->bio);
++		}
+ 
+ 		rbio = parent;
+ 	}
+@@ -408,61 +436,115 @@ static void bch2_rbio_done(struct bch_read_bio *rbio)
+ 	bio_endio(&rbio->bio);
+ }
+ 
+-static void bch2_read_retry_nodecode(struct bch_fs *c, struct bch_read_bio *rbio,
+-				     struct bvec_iter bvec_iter,
+-				     struct bch_io_failures *failed,
+-				     unsigned flags)
++static void get_rbio_extent(struct btree_trans *trans,
++			    struct bch_read_bio *rbio,
++			    struct bkey_buf *sk)
+ {
+-	struct btree_trans *trans = bch2_trans_get(c);
+ 	struct btree_iter iter;
+-	struct bkey_buf sk;
+ 	struct bkey_s_c k;
+-	int ret;
++	int ret = lockrestart_do(trans,
++			bkey_err(k = bch2_bkey_get_iter(trans, &iter,
++						rbio->data_btree, rbio->data_pos, 0)));
++	if (ret)
++		return;
++
++	struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
++	bkey_for_each_ptr(ptrs, ptr)
++		if (bch2_extent_ptr_eq(*ptr, rbio->pick.ptr)) {
++			bch2_bkey_buf_reassemble(sk, trans->c, k);
++			break;
++		}
+ 
+-	flags &= ~BCH_READ_LAST_FRAGMENT;
+-	flags |= BCH_READ_MUST_CLONE;
++	bch2_trans_iter_exit(trans, &iter);
++}
+ 
+-	bch2_bkey_buf_init(&sk);
++static noinline int maybe_poison_extent(struct btree_trans *trans, struct bch_read_bio *rbio,
++					enum btree_id btree, struct bkey_s_c read_k)
++{
++	struct bch_fs *c = trans->c;
++
++	struct data_update *u = rbio_data_update(rbio);
++	if (u)
++		read_k = bkey_i_to_s_c(u->k.k);
++
++	u64 flags = bch2_bkey_extent_flags(read_k);
++	if (flags & BIT_ULL(BCH_EXTENT_FLAG_poisoned))
++		return 0;
++
++	struct btree_iter iter;
++	struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter, btree, bkey_start_pos(read_k.k),
++					       BTREE_ITER_intent);
++	int ret = bkey_err(k);
++	if (ret)
++		return ret;
++
++	if (!bkey_and_val_eq(k, read_k))
++		goto out;
++
++	struct bkey_i *new = bch2_trans_kmalloc(trans,
++					bkey_bytes(k.k) + sizeof(struct bch_extent_flags));
++	ret =   PTR_ERR_OR_ZERO(new) ?:
++		(bkey_reassemble(new, k), 0) ?:
++		bch2_bkey_extent_flags_set(c, new, flags|BIT_ULL(BCH_EXTENT_FLAG_poisoned)) ?:
++		bch2_trans_update(trans, &iter, new, BTREE_UPDATE_internal_snapshot_node) ?:
++		bch2_trans_commit(trans, NULL, NULL, 0);
++
++	/*
++	 * Propagate key change back to data update path, in particular so it
++	 * knows the extent has been poisoned and it's safe to change the
++	 * checksum
++	 */
++	if (u && !ret)
++		bch2_bkey_buf_copy(&u->k, c, new);
++out:
++	bch2_trans_iter_exit(trans, &iter);
++	return ret;
++}
+ 
+-	bch2_trans_iter_init(trans, &iter, rbio->data_btree,
+-			     rbio->read_pos, BTREE_ITER_slots);
++static noinline int bch2_read_retry_nodecode(struct btree_trans *trans,
++					struct bch_read_bio *rbio,
++					struct bvec_iter bvec_iter,
++					struct bch_io_failures *failed,
++					unsigned flags)
++{
++	struct data_update *u = container_of(rbio, struct data_update, rbio);
+ retry:
+ 	bch2_trans_begin(trans);
+-	rbio->bio.bi_status = 0;
+ 
+-	ret = lockrestart_do(trans, bkey_err(k = bch2_btree_iter_peek_slot(&iter)));
++	struct btree_iter iter;
++	struct bkey_s_c k;
++	int ret = lockrestart_do(trans,
++		bkey_err(k = bch2_bkey_get_iter(trans, &iter,
++				u->btree_id, bkey_start_pos(&u->k.k->k),
++				0)));
+ 	if (ret)
+ 		goto err;
+ 
+-	bch2_bkey_buf_reassemble(&sk, c, k);
+-	k = bkey_i_to_s_c(sk.k);
+-
+-	if (!bch2_bkey_matches_ptr(c, k,
+-				   rbio->pick.ptr,
+-				   rbio->data_pos.offset -
+-				   rbio->pick.crc.offset)) {
++	if (!bkey_and_val_eq(k, bkey_i_to_s_c(u->k.k))) {
+ 		/* extent we wanted to read no longer exists: */
+-		rbio->hole = true;
+-		goto out;
++		rbio->ret = -BCH_ERR_data_read_key_overwritten;
++		goto err;
+ 	}
+ 
+ 	ret = __bch2_read_extent(trans, rbio, bvec_iter,
+-				 rbio->read_pos,
+-				 rbio->data_btree,
+-				 k, 0, failed, flags);
+-	if (ret == READ_RETRY)
+-		goto retry;
+-	if (ret)
+-		goto err;
+-out:
+-	bch2_rbio_done(rbio);
+-	bch2_trans_iter_exit(trans, &iter);
+-	bch2_trans_put(trans);
+-	bch2_bkey_buf_exit(&sk, c);
+-	return;
++				 bkey_start_pos(&u->k.k->k),
++				 u->btree_id,
++				 bkey_i_to_s_c(u->k.k),
++				 0, failed, flags, -1);
+ err:
+-	rbio->bio.bi_status = BLK_STS_IOERR;
+-	goto out;
++	bch2_trans_iter_exit(trans, &iter);
++
++	if (bch2_err_matches(ret, BCH_ERR_transaction_restart) ||
++	    bch2_err_matches(ret, BCH_ERR_data_read_retry))
++		goto retry;
++
++	if (ret) {
++		rbio->bio.bi_status	= BLK_STS_IOERR;
++		rbio->ret		= ret;
++	}
++
++	BUG_ON(atomic_read(&rbio->bio.__bi_remaining) != 1);
++	return ret;
+ }
+ 
+ static void bch2_rbio_retry(struct work_struct *work)
+@@ -478,44 +560,88 @@ static void bch2_rbio_retry(struct work_struct *work)
+ 	};
+ 	struct bch_io_failures failed = { .nr = 0 };
+ 
+-	trace_and_count(c, read_retry, &rbio->bio);
++	struct btree_trans *trans = bch2_trans_get(c);
++
++	struct bkey_buf sk;
++	bch2_bkey_buf_init(&sk);
++	bkey_init(&sk.k->k);
+ 
+-	if (rbio->retry == READ_RETRY_AVOID)
+-		bch2_mark_io_failure(&failed, &rbio->pick);
++	trace_io_read_retry(&rbio->bio);
++	this_cpu_add(c->counters[BCH_COUNTER_io_read_retry],
++		     bvec_iter_sectors(rbio->bvec_iter));
+ 
+-	rbio->bio.bi_status = 0;
++	get_rbio_extent(trans, rbio, &sk);
++
++	if (!bkey_deleted(&sk.k->k) &&
++	    bch2_err_matches(rbio->ret, BCH_ERR_data_read_retry_avoid))
++		bch2_mark_io_failure(&failed, &rbio->pick,
++				     rbio->ret == -BCH_ERR_data_read_retry_csum_err);
++
++	if (!rbio->split) {
++		rbio->bio.bi_status	= 0;
++		rbio->ret		= 0;
++	}
++
++	unsigned subvol		= rbio->subvol;
++	struct bpos read_pos	= rbio->read_pos;
+ 
+ 	rbio = bch2_rbio_free(rbio);
+ 
+-	flags |= BCH_READ_IN_RETRY;
+-	flags &= ~BCH_READ_MAY_PROMOTE;
++	flags |= BCH_READ_in_retry;
++	flags &= ~BCH_READ_may_promote;
++	flags &= ~BCH_READ_last_fragment;
++	flags |= BCH_READ_must_clone;
+ 
+-	if (flags & BCH_READ_NODECODE) {
+-		bch2_read_retry_nodecode(c, rbio, iter, &failed, flags);
++	int ret = rbio->data_update
++		? bch2_read_retry_nodecode(trans, rbio, iter, &failed, flags)
++		: __bch2_read(trans, rbio, iter, inum, &failed, &sk, flags);
++
++	if (ret) {
++		rbio->ret = ret;
++		rbio->bio.bi_status = BLK_STS_IOERR;
+ 	} else {
+-		flags &= ~BCH_READ_LAST_FRAGMENT;
+-		flags |= BCH_READ_MUST_CLONE;
++		struct printbuf buf = PRINTBUF;
+ 
+-		__bch2_read(c, rbio, iter, inum, &failed, flags);
++		lockrestart_do(trans,
++			bch2_inum_offset_err_msg_trans(trans, &buf,
++					(subvol_inum) { subvol, read_pos.inode },
++					read_pos.offset << 9));
++		if (rbio->data_update)
++			prt_str(&buf, "(internal move) ");
++		prt_str(&buf, "successful retry");
++
++		bch_err_ratelimited(c, "%s", buf.buf);
++		printbuf_exit(&buf);
+ 	}
++
++	bch2_rbio_done(rbio);
++	bch2_bkey_buf_exit(&sk, c);
++	bch2_trans_put(trans);
+ }
+ 
+-static void bch2_rbio_error(struct bch_read_bio *rbio, int retry,
+-			    blk_status_t error)
++static void bch2_rbio_error(struct bch_read_bio *rbio,
++			    int ret, blk_status_t blk_error)
+ {
+-	rbio->retry = retry;
++	BUG_ON(ret >= 0);
++
++	rbio->ret		= ret;
++	rbio->bio.bi_status	= blk_error;
+ 
+-	if (rbio->flags & BCH_READ_IN_RETRY)
++	bch2_rbio_parent(rbio)->saw_error = true;
++
++	if (rbio->flags & BCH_READ_in_retry)
+ 		return;
+ 
+-	if (retry == READ_ERR) {
++	if (bch2_err_matches(ret, BCH_ERR_data_read_retry)) {
++		bch2_rbio_punt(rbio, bch2_rbio_retry,
++			       RBIO_CONTEXT_UNBOUND, system_unbound_wq);
++	} else {
+ 		rbio = bch2_rbio_free(rbio);
+ 
+-		rbio->bio.bi_status = error;
++		rbio->ret		= ret;
++		rbio->bio.bi_status	= blk_error;
++
+ 		bch2_rbio_done(rbio);
+-	} else {
+-		bch2_rbio_punt(rbio, bch2_rbio_retry,
+-			       RBIO_CONTEXT_UNBOUND, system_unbound_wq);
+ 	}
+ }
+ 
+@@ -531,15 +657,13 @@ static void bch2_read_io_err(struct work_struct *work)
+ 	bch2_read_err_msg(c, &buf, rbio, rbio->read_pos);
+ 	prt_printf(&buf, "data read error: %s", bch2_blk_status_to_str(bio->bi_status));
+ 
+-	if (ca) {
+-		bch2_io_error(ca, BCH_MEMBER_ERROR_read);
++	if (ca)
+ 		bch_err_ratelimited(ca, "%s", buf.buf);
+-	} else {
++	else
+ 		bch_err_ratelimited(c, "%s", buf.buf);
+-	}
+ 
+ 	printbuf_exit(&buf);
+-	bch2_rbio_error(rbio, READ_RETRY_AVOID, bio->bi_status);
++	bch2_rbio_error(rbio, -BCH_ERR_data_read_retry_io_err, bio->bi_status);
+ }
+ 
+ static int __bch2_rbio_narrow_crcs(struct btree_trans *trans,
+@@ -621,14 +745,12 @@ static void bch2_read_csum_err(struct work_struct *work)
+ 	bch2_csum_err_msg(&buf, crc.csum_type, rbio->pick.crc.csum, csum);
+ 
+ 	struct bch_dev *ca = rbio->have_ioref ? bch2_dev_have_ref(c, rbio->pick.ptr.dev) : NULL;
+-	if (ca) {
+-		bch2_io_error(ca, BCH_MEMBER_ERROR_checksum);
++	if (ca)
+ 		bch_err_ratelimited(ca, "%s", buf.buf);
+-	} else {
++	else
+ 		bch_err_ratelimited(c, "%s", buf.buf);
+-	}
+ 
+-	bch2_rbio_error(rbio, READ_RETRY_AVOID, BLK_STS_IOERR);
++	bch2_rbio_error(rbio, -BCH_ERR_data_read_retry_csum_err, BLK_STS_IOERR);
+ 	printbuf_exit(&buf);
+ }
+ 
+@@ -648,7 +770,7 @@ static void bch2_read_decompress_err(struct work_struct *work)
+ 	else
+ 		bch_err_ratelimited(c, "%s", buf.buf);
+ 
+-	bch2_rbio_error(rbio, READ_ERR, BLK_STS_IOERR);
++	bch2_rbio_error(rbio, -BCH_ERR_data_read_decompress_err, BLK_STS_IOERR);
+ 	printbuf_exit(&buf);
+ }
+ 
+@@ -668,7 +790,7 @@ static void bch2_read_decrypt_err(struct work_struct *work)
+ 	else
+ 		bch_err_ratelimited(c, "%s", buf.buf);
+ 
+-	bch2_rbio_error(rbio, READ_ERR, BLK_STS_IOERR);
++	bch2_rbio_error(rbio, -BCH_ERR_data_read_decrypt_err, BLK_STS_IOERR);
+ 	printbuf_exit(&buf);
+ }
+ 
+@@ -678,9 +800,11 @@ static void __bch2_read_endio(struct work_struct *work)
+ 	struct bch_read_bio *rbio =
+ 		container_of(work, struct bch_read_bio, work);
+ 	struct bch_fs *c	= rbio->c;
+-	struct bio *src		= &rbio->bio;
+-	struct bio *dst		= &bch2_rbio_parent(rbio)->bio;
+-	struct bvec_iter dst_iter = rbio->bvec_iter;
++	struct bch_dev *ca = rbio->have_ioref ? bch2_dev_have_ref(c, rbio->pick.ptr.dev) : NULL;
++	struct bch_read_bio *parent	= bch2_rbio_parent(rbio);
++	struct bio *src			= &rbio->bio;
++	struct bio *dst			= &parent->bio;
++	struct bvec_iter dst_iter	= rbio->bvec_iter;
+ 	struct bch_extent_crc_unpacked crc = rbio->pick.crc;
+ 	struct nonce nonce = extent_nonce(rbio->version, crc);
+ 	unsigned nofs_flags;
+@@ -698,8 +822,26 @@ static void __bch2_read_endio(struct work_struct *work)
+ 		src->bi_iter			= rbio->bvec_iter;
+ 	}
+ 
++	bch2_maybe_corrupt_bio(src, bch2_read_corrupt_ratio);
++
+ 	csum = bch2_checksum_bio(c, crc.csum_type, nonce, src);
+-	if (bch2_crc_cmp(csum, rbio->pick.crc.csum) && !c->opts.no_data_io)
++	bool csum_good = !bch2_crc_cmp(csum, rbio->pick.crc.csum) || c->opts.no_data_io;
++
++	/*
++	 * Checksum error: if the bio wasn't bounced, we may have been
++	 * reading into buffers owned by userspace (that userspace can
++	 * scribble over) - retry the read, bouncing it this time:
++	 */
++	if (!csum_good && !rbio->bounce && (rbio->flags & BCH_READ_user_mapped)) {
++		rbio->flags |= BCH_READ_must_bounce;
++		bch2_rbio_error(rbio, -BCH_ERR_data_read_retry_csum_err_maybe_userspace,
++				BLK_STS_IOERR);
++		goto out;
++	}
++
++	bch2_account_io_completion(ca, BCH_MEMBER_ERROR_checksum, 0, csum_good);
++
++	if (!csum_good)
+ 		goto csum_err;
+ 
+ 	/*
+@@ -712,32 +854,40 @@ static void __bch2_read_endio(struct work_struct *work)
+ 	if (unlikely(rbio->narrow_crcs))
+ 		bch2_rbio_narrow_crcs(rbio);
+ 
+-	if (rbio->flags & BCH_READ_NODECODE)
+-		goto nodecode;
++	if (likely(!parent->data_update)) {
++		/* Adjust crc to point to subset of data we want: */
++		crc.offset     += rbio->offset_into_extent;
++		crc.live_size	= bvec_iter_sectors(rbio->bvec_iter);
+ 
+-	/* Adjust crc to point to subset of data we want: */
+-	crc.offset     += rbio->offset_into_extent;
+-	crc.live_size	= bvec_iter_sectors(rbio->bvec_iter);
++		if (crc_is_compressed(crc)) {
++			ret = bch2_encrypt_bio(c, crc.csum_type, nonce, src);
++			if (ret)
++				goto decrypt_err;
+ 
+-	if (crc_is_compressed(crc)) {
+-		ret = bch2_encrypt_bio(c, crc.csum_type, nonce, src);
+-		if (ret)
+-			goto decrypt_err;
++			if (bch2_bio_uncompress(c, src, dst, dst_iter, crc) &&
++			    !c->opts.no_data_io)
++				goto decompression_err;
++		} else {
++			/* don't need to decrypt the entire bio: */
++			nonce = nonce_add(nonce, crc.offset << 9);
++			bio_advance(src, crc.offset << 9);
+ 
+-		if (bch2_bio_uncompress(c, src, dst, dst_iter, crc) &&
+-		    !c->opts.no_data_io)
+-			goto decompression_err;
+-	} else {
+-		/* don't need to decrypt the entire bio: */
+-		nonce = nonce_add(nonce, crc.offset << 9);
+-		bio_advance(src, crc.offset << 9);
++			BUG_ON(src->bi_iter.bi_size < dst_iter.bi_size);
++			src->bi_iter.bi_size = dst_iter.bi_size;
+ 
+-		BUG_ON(src->bi_iter.bi_size < dst_iter.bi_size);
+-		src->bi_iter.bi_size = dst_iter.bi_size;
++			ret = bch2_encrypt_bio(c, crc.csum_type, nonce, src);
++			if (ret)
++				goto decrypt_err;
+ 
+-		ret = bch2_encrypt_bio(c, crc.csum_type, nonce, src);
+-		if (ret)
+-			goto decrypt_err;
++			if (rbio->bounce) {
++				struct bvec_iter src_iter = src->bi_iter;
++
++				bio_copy_data_iter(dst, &dst_iter, src, &src_iter);
++			}
++		}
++	} else {
++		if (rbio->split)
++			rbio->parent->pick = rbio->pick;
+ 
+ 		if (rbio->bounce) {
+ 			struct bvec_iter src_iter = src->bi_iter;
+@@ -754,12 +904,9 @@ static void __bch2_read_endio(struct work_struct *work)
+ 		ret = bch2_encrypt_bio(c, crc.csum_type, nonce, src);
+ 		if (ret)
+ 			goto decrypt_err;
+-
+-		promote_start(rbio->promote, rbio);
+-		rbio->promote = NULL;
+ 	}
+-nodecode:
+-	if (likely(!(rbio->flags & BCH_READ_IN_RETRY))) {
++
++	if (likely(!(rbio->flags & BCH_READ_in_retry))) {
+ 		rbio = bch2_rbio_free(rbio);
+ 		bch2_rbio_done(rbio);
+ 	}
+@@ -767,17 +914,6 @@ static void __bch2_read_endio(struct work_struct *work)
+ 	memalloc_nofs_restore(nofs_flags);
+ 	return;
+ csum_err:
+-	/*
+-	 * Checksum error: if the bio wasn't bounced, we may have been
+-	 * reading into buffers owned by userspace (that userspace can
+-	 * scribble over) - retry the read, bouncing it this time:
+-	 */
+-	if (!rbio->bounce && (rbio->flags & BCH_READ_USER_MAPPED)) {
+-		rbio->flags |= BCH_READ_MUST_BOUNCE;
+-		bch2_rbio_error(rbio, READ_RETRY, BLK_STS_IOERR);
+-		goto out;
+-	}
+-
+ 	bch2_rbio_punt(rbio, bch2_read_csum_err, RBIO_CONTEXT_UNBOUND, system_unbound_wq);
+ 	goto out;
+ decompression_err:
+@@ -797,10 +933,8 @@ static void bch2_read_endio(struct bio *bio)
+ 	struct workqueue_struct *wq = NULL;
+ 	enum rbio_context context = RBIO_CONTEXT_NULL;
+ 
+-	if (rbio->have_ioref) {
+-		bch2_latency_acct(ca, rbio->submit_time, READ);
+-		percpu_ref_put(&ca->io_ref);
+-	}
++	bch2_account_io_completion(ca, BCH_MEMBER_ERROR_read,
++				   rbio->submit_time, !bio->bi_status);
+ 
+ 	if (!rbio->split)
+ 		rbio->bio.bi_end_io = rbio->end_io;
+@@ -810,14 +944,14 @@ static void bch2_read_endio(struct bio *bio)
+ 		return;
+ 	}
+ 
+-	if (((rbio->flags & BCH_READ_RETRY_IF_STALE) && race_fault()) ||
++	if (((rbio->flags & BCH_READ_retry_if_stale) && race_fault()) ||
+ 	    (ca && dev_ptr_stale(ca, &rbio->pick.ptr))) {
+-		trace_and_count(c, read_reuse_race, &rbio->bio);
++		trace_and_count(c, io_read_reuse_race, &rbio->bio);
+ 
+-		if (rbio->flags & BCH_READ_RETRY_IF_STALE)
+-			bch2_rbio_error(rbio, READ_RETRY, BLK_STS_AGAIN);
++		if (rbio->flags & BCH_READ_retry_if_stale)
++			bch2_rbio_error(rbio, -BCH_ERR_data_read_ptr_stale_retry, BLK_STS_AGAIN);
+ 		else
+-			bch2_rbio_error(rbio, READ_ERR, BLK_STS_AGAIN);
++			bch2_rbio_error(rbio, -BCH_ERR_data_read_ptr_stale_race, BLK_STS_AGAIN);
+ 		return;
+ 	}
+ 
+@@ -883,15 +1017,15 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
+ 		       struct bvec_iter iter, struct bpos read_pos,
+ 		       enum btree_id data_btree, struct bkey_s_c k,
+ 		       unsigned offset_into_extent,
+-		       struct bch_io_failures *failed, unsigned flags)
++		       struct bch_io_failures *failed, unsigned flags, int dev)
+ {
+ 	struct bch_fs *c = trans->c;
+ 	struct extent_ptr_decoded pick;
+ 	struct bch_read_bio *rbio = NULL;
+-	struct promote_op *promote = NULL;
+ 	bool bounce = false, read_full = false, narrow_crcs = false;
+ 	struct bpos data_pos = bkey_start_pos(k.k);
+-	int pick_ret;
++	struct data_update *u = rbio_data_update(orig);
++	int ret = 0;
+ 
+ 	if (bkey_extent_is_inline_data(k.k)) {
+ 		unsigned bytes = min_t(unsigned, iter.bi_size,
+@@ -902,19 +1036,35 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
+ 		swap(iter.bi_size, bytes);
+ 		bio_advance_iter(&orig->bio, &iter, bytes);
+ 		zero_fill_bio_iter(&orig->bio, iter);
++		this_cpu_add(c->counters[BCH_COUNTER_io_read_inline],
++			     bvec_iter_sectors(iter));
+ 		goto out_read_done;
+ 	}
++
++	if ((bch2_bkey_extent_flags(k) & BIT_ULL(BCH_EXTENT_FLAG_poisoned)) &&
++	    !orig->data_update)
++		return -BCH_ERR_extent_poisened;
+ retry_pick:
+-	pick_ret = bch2_bkey_pick_read_device(c, k, failed, &pick);
++	ret = bch2_bkey_pick_read_device(c, k, failed, &pick, dev);
+ 
+ 	/* hole or reservation - just zero fill: */
+-	if (!pick_ret)
++	if (!ret)
+ 		goto hole;
+ 
+-	if (unlikely(pick_ret < 0)) {
++	if (unlikely(ret < 0)) {
++		if (ret == -BCH_ERR_data_read_csum_err) {
++			int ret2 = maybe_poison_extent(trans, orig, data_btree, k);
++			if (ret2) {
++				ret = ret2;
++				goto err;
++			}
++
++			trace_and_count(c, io_read_fail_and_poison, &orig->bio);
++		}
++
+ 		struct printbuf buf = PRINTBUF;
+ 		bch2_read_err_msg_trans(trans, &buf, orig, read_pos);
+-		prt_printf(&buf, "no device to read from: %s\n  ", bch2_err_str(pick_ret));
++		prt_printf(&buf, "%s\n  ", bch2_err_str(ret));
+ 		bch2_bkey_val_to_text(&buf, c, k);
+ 
+ 		bch_err_ratelimited(c, "%s", buf.buf);
+@@ -930,6 +1080,7 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
+ 
+ 		bch_err_ratelimited(c, "%s", buf.buf);
+ 		printbuf_exit(&buf);
++		ret = -BCH_ERR_data_read_no_encryption_key;
+ 		goto err;
+ 	}
+ 
+@@ -941,56 +1092,57 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
+ 	 * retry path, don't check here, it'll be caught in bch2_read_endio()
+ 	 * and we'll end up in the retry path:
+ 	 */
+-	if ((flags & BCH_READ_IN_RETRY) &&
++	if ((flags & BCH_READ_in_retry) &&
+ 	    !pick.ptr.cached &&
+ 	    ca &&
+ 	    unlikely(dev_ptr_stale(ca, &pick.ptr))) {
+ 		read_from_stale_dirty_pointer(trans, ca, k, pick.ptr);
+-		bch2_mark_io_failure(failed, &pick);
++		bch2_mark_io_failure(failed, &pick, false);
+ 		percpu_ref_put(&ca->io_ref);
+ 		goto retry_pick;
+ 	}
+ 
+-	if (flags & BCH_READ_NODECODE) {
++	if (likely(!u)) {
++		if (!(flags & BCH_READ_last_fragment) ||
++		    bio_flagged(&orig->bio, BIO_CHAIN))
++			flags |= BCH_READ_must_clone;
++
++		narrow_crcs = !(flags & BCH_READ_in_retry) &&
++			bch2_can_narrow_extent_crcs(k, pick.crc);
++
++		if (narrow_crcs && (flags & BCH_READ_user_mapped))
++			flags |= BCH_READ_must_bounce;
++
++		EBUG_ON(offset_into_extent + bvec_iter_sectors(iter) > k.k->size);
++
++		if (crc_is_compressed(pick.crc) ||
++		    (pick.crc.csum_type != BCH_CSUM_none &&
++		     (bvec_iter_sectors(iter) != pick.crc.uncompressed_size ||
++		      (bch2_csum_type_is_encryption(pick.crc.csum_type) &&
++		       (flags & BCH_READ_user_mapped)) ||
++		      (flags & BCH_READ_must_bounce)))) {
++			read_full = true;
++			bounce = true;
++		}
++	} else {
+ 		/*
+ 		 * can happen if we retry, and the extent we were going to read
+ 		 * has been merged in the meantime:
+ 		 */
+-		if (pick.crc.compressed_size > orig->bio.bi_vcnt * PAGE_SECTORS) {
++		if (pick.crc.compressed_size > u->op.wbio.bio.bi_iter.bi_size) {
+ 			if (ca)
+ 				percpu_ref_put(&ca->io_ref);
+-			goto hole;
++			rbio->ret = -BCH_ERR_data_read_buffer_too_small;
++			goto out_read_done;
+ 		}
+ 
+ 		iter.bi_size	= pick.crc.compressed_size << 9;
+-		goto get_bio;
+-	}
+-
+-	if (!(flags & BCH_READ_LAST_FRAGMENT) ||
+-	    bio_flagged(&orig->bio, BIO_CHAIN))
+-		flags |= BCH_READ_MUST_CLONE;
+-
+-	narrow_crcs = !(flags & BCH_READ_IN_RETRY) &&
+-		bch2_can_narrow_extent_crcs(k, pick.crc);
+-
+-	if (narrow_crcs && (flags & BCH_READ_USER_MAPPED))
+-		flags |= BCH_READ_MUST_BOUNCE;
+-
+-	EBUG_ON(offset_into_extent + bvec_iter_sectors(iter) > k.k->size);
+-
+-	if (crc_is_compressed(pick.crc) ||
+-	    (pick.crc.csum_type != BCH_CSUM_none &&
+-	     (bvec_iter_sectors(iter) != pick.crc.uncompressed_size ||
+-	      (bch2_csum_type_is_encryption(pick.crc.csum_type) &&
+-	       (flags & BCH_READ_USER_MAPPED)) ||
+-	      (flags & BCH_READ_MUST_BOUNCE)))) {
+ 		read_full = true;
+-		bounce = true;
+ 	}
+ 
+ 	if (orig->opts.promote_target || have_io_error(failed))
+-		promote = promote_alloc(trans, iter, k, &pick, orig->opts, flags,
+-					&rbio, &bounce, &read_full, failed);
++		rbio = promote_alloc(trans, iter, k, &pick, flags, orig,
++				     &bounce, &read_full, failed);
+ 
+ 	if (!read_full) {
+ 		EBUG_ON(crc_is_compressed(pick.crc));
+@@ -1009,7 +1161,7 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
+ 		pick.crc.offset			= 0;
+ 		pick.crc.live_size		= bvec_iter_sectors(iter);
+ 	}
+-get_bio:
++
+ 	if (rbio) {
+ 		/*
+ 		 * promote already allocated bounce rbio:
+@@ -1024,17 +1176,16 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
+ 	} else if (bounce) {
+ 		unsigned sectors = pick.crc.compressed_size;
+ 
+-		rbio = rbio_init(bio_alloc_bioset(NULL,
++		rbio = rbio_init_fragment(bio_alloc_bioset(NULL,
+ 						  DIV_ROUND_UP(sectors, PAGE_SECTORS),
+ 						  0,
+ 						  GFP_NOFS,
+ 						  &c->bio_read_split),
+-				 orig->opts);
++				 orig);
+ 
+ 		bch2_bio_alloc_pages_pool(c, &rbio->bio, sectors << 9);
+ 		rbio->bounce	= true;
+-		rbio->split	= true;
+-	} else if (flags & BCH_READ_MUST_CLONE) {
++	} else if (flags & BCH_READ_must_clone) {
+ 		/*
+ 		 * Have to clone if there were any splits, due to error
+ 		 * reporting issues (if a split errored, and retrying didn't
+@@ -1043,11 +1194,10 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
+ 		 * from the whole bio, in which case we don't want to retry and
+ 		 * lose the error)
+ 		 */
+-		rbio = rbio_init(bio_alloc_clone(NULL, &orig->bio, GFP_NOFS,
++		rbio = rbio_init_fragment(bio_alloc_clone(NULL, &orig->bio, GFP_NOFS,
+ 						 &c->bio_read_split),
+-				 orig->opts);
++				 orig);
+ 		rbio->bio.bi_iter = iter;
+-		rbio->split	= true;
+ 	} else {
+ 		rbio = orig;
+ 		rbio->bio.bi_iter = iter;
+@@ -1056,67 +1206,64 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
+ 
+ 	EBUG_ON(bio_sectors(&rbio->bio) != pick.crc.compressed_size);
+ 
+-	rbio->c			= c;
+ 	rbio->submit_time	= local_clock();
+-	if (rbio->split)
+-		rbio->parent	= orig;
+-	else
++	if (!rbio->split)
+ 		rbio->end_io	= orig->bio.bi_end_io;
+ 	rbio->bvec_iter		= iter;
+ 	rbio->offset_into_extent= offset_into_extent;
+ 	rbio->flags		= flags;
+ 	rbio->have_ioref	= ca != NULL;
+ 	rbio->narrow_crcs	= narrow_crcs;
+-	rbio->hole		= 0;
+-	rbio->retry		= 0;
++	rbio->ret		= 0;
+ 	rbio->context		= 0;
+-	/* XXX: only initialize this if needed */
+-	rbio->devs_have		= bch2_bkey_devs(k);
+ 	rbio->pick		= pick;
+ 	rbio->subvol		= orig->subvol;
+ 	rbio->read_pos		= read_pos;
+ 	rbio->data_btree	= data_btree;
+ 	rbio->data_pos		= data_pos;
+ 	rbio->version		= k.k->bversion;
+-	rbio->promote		= promote;
+ 	INIT_WORK(&rbio->work, NULL);
+ 
+-	if (flags & BCH_READ_NODECODE)
+-		orig->pick = pick;
+-
+ 	rbio->bio.bi_opf	= orig->bio.bi_opf;
+ 	rbio->bio.bi_iter.bi_sector = pick.ptr.offset;
+ 	rbio->bio.bi_end_io	= bch2_read_endio;
+ 
++	/* XXX: also nvme read recovery level */
++	if (unlikely(failed && bch2_dev_io_failures(failed, pick.ptr.dev)))
++		rbio->bio.bi_opf |= REQ_FUA;
++
+ 	if (rbio->bounce)
+-		trace_and_count(c, read_bounce, &rbio->bio);
++		trace_and_count(c, io_read_bounce, &rbio->bio);
+ 
+-	this_cpu_add(c->counters[BCH_COUNTER_io_read], bio_sectors(&rbio->bio));
++	if (!u)
++		this_cpu_add(c->counters[BCH_COUNTER_io_read], bio_sectors(&rbio->bio));
++	else
++		this_cpu_add(c->counters[BCH_COUNTER_io_move_read], bio_sectors(&rbio->bio));
+ 	bch2_increment_clock(c, bio_sectors(&rbio->bio), READ);
+ 
+ 	/*
+ 	 * If it's being moved internally, we don't want to flag it as a cache
+ 	 * hit:
+ 	 */
+-	if (ca && pick.ptr.cached && !(flags & BCH_READ_NODECODE))
++	if (ca && pick.ptr.cached && !u)
+ 		bch2_bucket_io_time_reset(trans, pick.ptr.dev,
+ 			PTR_BUCKET_NR(ca, &pick.ptr), READ);
+ 
+-	if (!(flags & (BCH_READ_IN_RETRY|BCH_READ_LAST_FRAGMENT))) {
++	if (!(flags & (BCH_READ_in_retry|BCH_READ_last_fragment))) {
+ 		bio_inc_remaining(&orig->bio);
+-		trace_and_count(c, read_split, &orig->bio);
++		trace_and_count(c, io_read_split, &orig->bio);
+ 	}
+ 
+ 	/*
+ 	 * Unlock the iterator while the btree node's lock is still in
+ 	 * cache, before doing the IO:
+ 	 */
+-	if (!(flags & BCH_READ_IN_RETRY))
++	if (!(flags & BCH_READ_in_retry))
+ 		bch2_trans_unlock(trans);
+ 	else
+ 		bch2_trans_unlock_long(trans);
+ 
+-	if (!rbio->pick.idx) {
++	if (likely(!rbio->pick.do_ec_reconstruct)) {
+ 		if (unlikely(!rbio->have_ioref)) {
+ 			struct printbuf buf = PRINTBUF;
+ 			bch2_read_err_msg_trans(trans, &buf, rbio, read_pos);
+@@ -1126,7 +1273,9 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
+ 			bch_err_ratelimited(c, "%s", buf.buf);
+ 			printbuf_exit(&buf);
+ 
+-			bch2_rbio_error(rbio, READ_RETRY_AVOID, BLK_STS_IOERR);
++			bch2_rbio_error(rbio,
++					-BCH_ERR_data_read_retry_device_offline,
++					BLK_STS_IOERR);
+ 			goto out;
+ 		}
+ 
+@@ -1135,10 +1284,10 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
+ 		bio_set_dev(&rbio->bio, ca->disk_sb.bdev);
+ 
+ 		if (unlikely(c->opts.no_data_io)) {
+-			if (likely(!(flags & BCH_READ_IN_RETRY)))
++			if (likely(!(flags & BCH_READ_in_retry)))
+ 				bio_endio(&rbio->bio);
+ 		} else {
+-			if (likely(!(flags & BCH_READ_IN_RETRY)))
++			if (likely(!(flags & BCH_READ_in_retry)))
+ 				submit_bio(&rbio->bio);
+ 			else
+ 				submit_bio_wait(&rbio->bio);
+@@ -1152,15 +1301,16 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
+ 	} else {
+ 		/* Attempting reconstruct read: */
+ 		if (bch2_ec_read_extent(trans, rbio, k)) {
+-			bch2_rbio_error(rbio, READ_RETRY_AVOID, BLK_STS_IOERR);
++			bch2_rbio_error(rbio, -BCH_ERR_data_read_retry_ec_reconstruct_err,
++					BLK_STS_IOERR);
+ 			goto out;
+ 		}
+ 
+-		if (likely(!(flags & BCH_READ_IN_RETRY)))
++		if (likely(!(flags & BCH_READ_in_retry)))
+ 			bio_endio(&rbio->bio);
+ 	}
+ out:
+-	if (likely(!(flags & BCH_READ_IN_RETRY))) {
++	if (likely(!(flags & BCH_READ_in_retry))) {
+ 		return 0;
+ 	} else {
+ 		bch2_trans_unlock(trans);
+@@ -1170,54 +1320,57 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
+ 		rbio->context = RBIO_CONTEXT_UNBOUND;
+ 		bch2_read_endio(&rbio->bio);
+ 
+-		ret = rbio->retry;
++		ret = rbio->ret;
+ 		rbio = bch2_rbio_free(rbio);
+ 
+-		if (ret == READ_RETRY_AVOID) {
+-			bch2_mark_io_failure(failed, &pick);
+-			ret = READ_RETRY;
+-		}
+-
+-		if (!ret)
+-			goto out_read_done;
++		if (bch2_err_matches(ret, BCH_ERR_data_read_retry_avoid))
++			bch2_mark_io_failure(failed, &pick,
++					ret == -BCH_ERR_data_read_retry_csum_err);
+ 
+ 		return ret;
+ 	}
+ 
+ err:
+-	if (flags & BCH_READ_IN_RETRY)
+-		return READ_ERR;
++	if (flags & BCH_READ_in_retry)
++		return ret;
+ 
+-	orig->bio.bi_status = BLK_STS_IOERR;
++	orig->bio.bi_status	= BLK_STS_IOERR;
++	orig->ret		= ret;
+ 	goto out_read_done;
+ 
+ hole:
++	this_cpu_add(c->counters[BCH_COUNTER_io_read_hole],
++		     bvec_iter_sectors(iter));
+ 	/*
+-	 * won't normally happen in the BCH_READ_NODECODE
+-	 * (bch2_move_extent()) path, but if we retry and the extent we wanted
+-	 * to read no longer exists we have to signal that:
++	 * won't normally happen in the data update (bch2_move_extent()) path,
++	 * but if we retry and the extent we wanted to read no longer exists we
++	 * have to signal that:
+ 	 */
+-	if (flags & BCH_READ_NODECODE)
+-		orig->hole = true;
++	if (u)
++		orig->ret = -BCH_ERR_data_read_key_overwritten;
+ 
+ 	zero_fill_bio_iter(&orig->bio, iter);
+ out_read_done:
+-	if (flags & BCH_READ_LAST_FRAGMENT)
++	if ((flags & BCH_READ_last_fragment) &&
++	    !(flags & BCH_READ_in_retry))
+ 		bch2_rbio_done(orig);
+ 	return 0;
+ }
+ 
+-void __bch2_read(struct bch_fs *c, struct bch_read_bio *rbio,
+-		 struct bvec_iter bvec_iter, subvol_inum inum,
+-		 struct bch_io_failures *failed, unsigned flags)
++int __bch2_read(struct btree_trans *trans, struct bch_read_bio *rbio,
++		struct bvec_iter bvec_iter, subvol_inum inum,
++		struct bch_io_failures *failed,
++		struct bkey_buf *prev_read,
++		unsigned flags)
+ {
+-	struct btree_trans *trans = bch2_trans_get(c);
++	struct bch_fs *c = trans->c;
+ 	struct btree_iter iter;
+ 	struct bkey_buf sk;
+ 	struct bkey_s_c k;
++	enum btree_id data_btree;
+ 	int ret;
+ 
+-	BUG_ON(flags & BCH_READ_NODECODE);
++	EBUG_ON(rbio->data_update);
+ 
+ 	bch2_bkey_buf_init(&sk);
+ 	bch2_trans_iter_init(trans, &iter, BTREE_ID_extents,
+@@ -1225,7 +1378,7 @@ void __bch2_read(struct bch_fs *c, struct bch_read_bio *rbio,
+ 			     BTREE_ITER_slots);
+ 
+ 	while (1) {
+-		enum btree_id data_btree = BTREE_ID_extents;
++		data_btree = BTREE_ID_extents;
+ 
+ 		bch2_trans_begin(trans);
+ 
+@@ -1257,6 +1410,12 @@ void __bch2_read(struct bch_fs *c, struct bch_read_bio *rbio,
+ 
+ 		k = bkey_i_to_s_c(sk.k);
+ 
++		if (unlikely(flags & BCH_READ_in_retry)) {
++			if (!bkey_and_val_eq(k, bkey_i_to_s_c(prev_read->k)))
++				failed->nr = 0;
++			bch2_bkey_buf_copy(prev_read, c, sk.k);
++		}
++
+ 		/*
+ 		 * With indirect extents, the amount of data to read is the min
+ 		 * of the original extent and the indirect extent:
+@@ -1267,42 +1426,49 @@ void __bch2_read(struct bch_fs *c, struct bch_read_bio *rbio,
+ 		swap(bvec_iter.bi_size, bytes);
+ 
+ 		if (bvec_iter.bi_size == bytes)
+-			flags |= BCH_READ_LAST_FRAGMENT;
++			flags |= BCH_READ_last_fragment;
+ 
+ 		ret = __bch2_read_extent(trans, rbio, bvec_iter, iter.pos,
+ 					 data_btree, k,
+-					 offset_into_extent, failed, flags);
++					 offset_into_extent, failed, flags, -1);
++		swap(bvec_iter.bi_size, bytes);
++
+ 		if (ret)
+ 			goto err;
+ 
+-		if (flags & BCH_READ_LAST_FRAGMENT)
++		if (flags & BCH_READ_last_fragment)
+ 			break;
+ 
+-		swap(bvec_iter.bi_size, bytes);
+ 		bio_advance_iter(&rbio->bio, &bvec_iter, bytes);
+ err:
++		if (ret == -BCH_ERR_data_read_retry_csum_err_maybe_userspace)
++			flags |= BCH_READ_must_bounce;
++
+ 		if (ret &&
+ 		    !bch2_err_matches(ret, BCH_ERR_transaction_restart) &&
+-		    ret != READ_RETRY &&
+-		    ret != READ_RETRY_AVOID)
++		    !bch2_err_matches(ret, BCH_ERR_data_read_retry))
+ 			break;
+ 	}
+ 
+-	bch2_trans_iter_exit(trans, &iter);
+-
+-	if (ret) {
++	if (unlikely(ret)) {
+ 		struct printbuf buf = PRINTBUF;
+-		bch2_inum_offset_err_msg_trans(trans, &buf, inum, bvec_iter.bi_sector << 9);
+-		prt_printf(&buf, "read error %i from btree lookup", ret);
++		lockrestart_do(trans,
++			bch2_inum_offset_err_msg_trans(trans, &buf, inum,
++						       bvec_iter.bi_sector << 9));
++		prt_printf(&buf, "read error: %s", bch2_err_str(ret));
+ 		bch_err_ratelimited(c, "%s", buf.buf);
+ 		printbuf_exit(&buf);
+ 
+-		rbio->bio.bi_status = BLK_STS_IOERR;
+-		bch2_rbio_done(rbio);
++		rbio->bio.bi_status	= BLK_STS_IOERR;
++		rbio->ret		= ret;
++
++		if (!(flags & BCH_READ_in_retry))
++			bch2_rbio_done(rbio);
+ 	}
+ 
+-	bch2_trans_put(trans);
++	bch2_trans_iter_exit(trans, &iter);
+ 	bch2_bkey_buf_exit(&sk, c);
++	return ret;
+ }
+ 
+ void bch2_fs_io_read_exit(struct bch_fs *c)
+diff --git a/fs/bcachefs/io_read.h b/fs/bcachefs/io_read.h
+index a82e8a94ccb6..1a85b092fd1d 100644
+--- a/fs/bcachefs/io_read.h
++++ b/fs/bcachefs/io_read.h
+@@ -3,6 +3,7 @@
+ #define _BCACHEFS_IO_READ_H
+ 
+ #include "bkey_buf.h"
++#include "btree_iter.h"
+ #include "reflink.h"
+ 
+ struct bch_read_bio {
+@@ -35,19 +36,18 @@ struct bch_read_bio {
+ 	u16			flags;
+ 	union {
+ 	struct {
+-	u16			bounce:1,
++	u16			data_update:1,
++				promote:1,
++				bounce:1,
+ 				split:1,
+-				kmalloc:1,
+ 				have_ioref:1,
+ 				narrow_crcs:1,
+-				hole:1,
+-				retry:2,
++				saw_error:1,
+ 				context:2;
+ 	};
+ 	u16			_state;
+ 	};
+-
+-	struct bch_devs_list	devs_have;
++	s16			ret;
+ 
+ 	struct extent_ptr_decoded pick;
+ 
+@@ -65,8 +65,6 @@ struct bch_read_bio {
+ 	struct bpos		data_pos;
+ 	struct bversion		version;
+ 
+-	struct promote_op	*promote;
+-
+ 	struct bch_io_opts	opts;
+ 
+ 	struct work_struct	work;
+@@ -108,61 +106,90 @@ static inline int bch2_read_indirect_extent(struct btree_trans *trans,
+ 	return 0;
+ }
+ 
++#define BCH_READ_FLAGS()		\
++	x(retry_if_stale)		\
++	x(may_promote)			\
++	x(user_mapped)			\
++	x(last_fragment)		\
++	x(must_bounce)			\
++	x(must_clone)			\
++	x(in_retry)
++
++enum __bch_read_flags {
++#define x(n)	__BCH_READ_##n,
++	BCH_READ_FLAGS()
++#undef x
++};
++
+ enum bch_read_flags {
+-	BCH_READ_RETRY_IF_STALE		= 1 << 0,
+-	BCH_READ_MAY_PROMOTE		= 1 << 1,
+-	BCH_READ_USER_MAPPED		= 1 << 2,
+-	BCH_READ_NODECODE		= 1 << 3,
+-	BCH_READ_LAST_FRAGMENT		= 1 << 4,
+-
+-	/* internal: */
+-	BCH_READ_MUST_BOUNCE		= 1 << 5,
+-	BCH_READ_MUST_CLONE		= 1 << 6,
+-	BCH_READ_IN_RETRY		= 1 << 7,
++#define x(n)	BCH_READ_##n = BIT(__BCH_READ_##n),
++	BCH_READ_FLAGS()
++#undef x
+ };
+ 
+ int __bch2_read_extent(struct btree_trans *, struct bch_read_bio *,
+ 		       struct bvec_iter, struct bpos, enum btree_id,
+ 		       struct bkey_s_c, unsigned,
+-		       struct bch_io_failures *, unsigned);
++		       struct bch_io_failures *, unsigned, int);
+ 
+ static inline void bch2_read_extent(struct btree_trans *trans,
+ 			struct bch_read_bio *rbio, struct bpos read_pos,
+ 			enum btree_id data_btree, struct bkey_s_c k,
+ 			unsigned offset_into_extent, unsigned flags)
+ {
+-	__bch2_read_extent(trans, rbio, rbio->bio.bi_iter, read_pos,
+-			   data_btree, k, offset_into_extent, NULL, flags);
++	int ret = __bch2_read_extent(trans, rbio, rbio->bio.bi_iter, read_pos,
++				     data_btree, k, offset_into_extent, NULL, flags, -1);
++	/* __bch2_read_extent only returns errors if BCH_READ_in_retry is set */
++	WARN(ret, "unhandled error from __bch2_read_extent()");
+ }
+ 
+-void __bch2_read(struct bch_fs *, struct bch_read_bio *, struct bvec_iter,
+-		 subvol_inum, struct bch_io_failures *, unsigned flags);
++int __bch2_read(struct btree_trans *, struct bch_read_bio *, struct bvec_iter,
++		subvol_inum,
++		struct bch_io_failures *, struct bkey_buf *, unsigned flags);
+ 
+ static inline void bch2_read(struct bch_fs *c, struct bch_read_bio *rbio,
+ 			     subvol_inum inum)
+ {
+-	struct bch_io_failures failed = { .nr = 0 };
+-
+ 	BUG_ON(rbio->_state);
+ 
+-	rbio->c = c;
+-	rbio->start_time = local_clock();
+ 	rbio->subvol = inum.subvol;
+ 
+-	__bch2_read(c, rbio, rbio->bio.bi_iter, inum, &failed,
+-		    BCH_READ_RETRY_IF_STALE|
+-		    BCH_READ_MAY_PROMOTE|
+-		    BCH_READ_USER_MAPPED);
++	bch2_trans_run(c,
++		__bch2_read(trans, rbio, rbio->bio.bi_iter, inum, NULL, NULL,
++			    BCH_READ_retry_if_stale|
++			    BCH_READ_may_promote|
++			    BCH_READ_user_mapped));
++}
++
++static inline struct bch_read_bio *rbio_init_fragment(struct bio *bio,
++						      struct bch_read_bio *orig)
++{
++	struct bch_read_bio *rbio = to_rbio(bio);
++
++	rbio->c			= orig->c;
++	rbio->_state		= 0;
++	rbio->flags		= 0;
++	rbio->ret		= 0;
++	rbio->split		= true;
++	rbio->parent		= orig;
++	rbio->opts		= orig->opts;
++	return rbio;
+ }
+ 
+ static inline struct bch_read_bio *rbio_init(struct bio *bio,
+-					     struct bch_io_opts opts)
++					     struct bch_fs *c,
++					     struct bch_io_opts opts,
++					     bio_end_io_t end_io)
+ {
+ 	struct bch_read_bio *rbio = to_rbio(bio);
+ 
+-	rbio->_state	= 0;
+-	rbio->promote	= NULL;
+-	rbio->opts	= opts;
++	rbio->start_time	= local_clock();
++	rbio->c			= c;
++	rbio->_state		= 0;
++	rbio->flags		= 0;
++	rbio->ret		= 0;
++	rbio->opts		= opts;
++	rbio->bio.bi_end_io	= end_io;
+ 	return rbio;
+ }
+ 
+diff --git a/fs/bcachefs/io_write.c b/fs/bcachefs/io_write.c
+index 03892388832b..07b55839768e 100644
+--- a/fs/bcachefs/io_write.c
++++ b/fs/bcachefs/io_write.c
+@@ -34,6 +34,12 @@
+ #include <linux/random.h>
+ #include <linux/sched/mm.h>
+ 
++#ifdef CONFIG_BCACHEFS_DEBUG
++static unsigned bch2_write_corrupt_ratio;
++module_param_named(write_corrupt_ratio, bch2_write_corrupt_ratio, uint, 0644);
++MODULE_PARM_DESC(write_corrupt_ratio, "");
++#endif
++
+ #ifndef CONFIG_BCACHEFS_NO_LATENCY_ACCT
+ 
+ static inline void bch2_congested_acct(struct bch_dev *ca, u64 io_latency,
+@@ -374,7 +380,7 @@ static int bch2_write_index_default(struct bch_write_op *op)
+ 			bch2_extent_update(trans, inum, &iter, sk.k,
+ 					&op->res,
+ 					op->new_i_size, &op->i_sectors_delta,
+-					op->flags & BCH_WRITE_CHECK_ENOSPC);
++					op->flags & BCH_WRITE_check_enospc);
+ 		bch2_trans_iter_exit(trans, &iter);
+ 
+ 		if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
+@@ -396,29 +402,36 @@ static int bch2_write_index_default(struct bch_write_op *op)
+ 
+ /* Writes */
+ 
+-static void __bch2_write_op_error(struct printbuf *out, struct bch_write_op *op,
+-				  u64 offset)
++void bch2_write_op_error(struct bch_write_op *op, u64 offset, const char *fmt, ...)
+ {
+-	bch2_inum_offset_err_msg(op->c, out,
+-				 (subvol_inum) { op->subvol, op->pos.inode, },
+-				 offset << 9);
+-	prt_printf(out, "write error%s: ",
+-		   op->flags & BCH_WRITE_MOVE ? "(internal move)" : "");
+-}
++	struct printbuf buf = PRINTBUF;
+ 
+-void bch2_write_op_error(struct printbuf *out, struct bch_write_op *op)
+-{
+-	__bch2_write_op_error(out, op, op->pos.offset);
+-}
++	if (op->subvol) {
++		bch2_inum_offset_err_msg(op->c, &buf,
++					 (subvol_inum) { op->subvol, op->pos.inode, },
++					 offset << 9);
++	} else {
++		struct bpos pos = op->pos;
++		pos.offset = offset;
++		bch2_inum_snap_offset_err_msg(op->c, &buf, pos);
++	}
+ 
+-static void bch2_write_op_error_trans(struct btree_trans *trans, struct printbuf *out,
+-				      struct bch_write_op *op, u64 offset)
+-{
+-	bch2_inum_offset_err_msg_trans(trans, out,
+-				       (subvol_inum) { op->subvol, op->pos.inode, },
+-				       offset << 9);
+-	prt_printf(out, "write error%s: ",
+-		   op->flags & BCH_WRITE_MOVE ? "(internal move)" : "");
++	prt_str(&buf, "write error: ");
++
++	va_list args;
++	va_start(args, fmt);
++	prt_vprintf(&buf, fmt, args);
++	va_end(args);
++
++	if (op->flags & BCH_WRITE_move) {
++		struct data_update *u = container_of(op, struct data_update, op);
++
++		prt_printf(&buf, "\n  from internal move ");
++		bch2_bkey_val_to_text(&buf, op->c, bkey_i_to_s_c(u->k.k));
++	}
++
++	bch_err_ratelimited(op->c, "%s", buf.buf);
++	printbuf_exit(&buf);
+ }
+ 
+ void bch2_submit_wbio_replicas(struct bch_write_bio *wbio, struct bch_fs *c,
+@@ -493,7 +506,7 @@ static void bch2_write_done(struct closure *cl)
+ 	bch2_time_stats_update(&c->times[BCH_TIME_data_write], op->start_time);
+ 	bch2_disk_reservation_put(c, &op->res);
+ 
+-	if (!(op->flags & BCH_WRITE_MOVE))
++	if (!(op->flags & BCH_WRITE_move))
+ 		bch2_write_ref_put(c, BCH_WRITE_REF_write);
+ 	bch2_keylist_free(&op->insert_keys, op->inline_keys);
+ 
+@@ -516,7 +529,7 @@ static noinline int bch2_write_drop_io_error_ptrs(struct bch_write_op *op)
+ 					    test_bit(ptr->dev, op->failed.d));
+ 
+ 			if (!bch2_bkey_nr_ptrs(bkey_i_to_s_c(src)))
+-				return -EIO;
++				return -BCH_ERR_data_write_io;
+ 		}
+ 
+ 		if (dst != src)
+@@ -539,7 +552,7 @@ static void __bch2_write_index(struct bch_write_op *op)
+ 	unsigned dev;
+ 	int ret = 0;
+ 
+-	if (unlikely(op->flags & BCH_WRITE_IO_ERROR)) {
++	if (unlikely(op->flags & BCH_WRITE_io_error)) {
+ 		ret = bch2_write_drop_io_error_ptrs(op);
+ 		if (ret)
+ 			goto err;
+@@ -548,7 +561,7 @@ static void __bch2_write_index(struct bch_write_op *op)
+ 	if (!bch2_keylist_empty(keys)) {
+ 		u64 sectors_start = keylist_sectors(keys);
+ 
+-		ret = !(op->flags & BCH_WRITE_MOVE)
++		ret = !(op->flags & BCH_WRITE_move)
+ 			? bch2_write_index_default(op)
+ 			: bch2_data_update_index_update(op);
+ 
+@@ -560,11 +573,8 @@ static void __bch2_write_index(struct bch_write_op *op)
+ 		if (unlikely(ret && !bch2_err_matches(ret, EROFS))) {
+ 			struct bkey_i *insert = bch2_keylist_front(&op->insert_keys);
+ 
+-			struct printbuf buf = PRINTBUF;
+-			__bch2_write_op_error(&buf, op, bkey_start_offset(&insert->k));
+-			prt_printf(&buf, "btree update error: %s", bch2_err_str(ret));
+-			bch_err_ratelimited(c, "%s", buf.buf);
+-			printbuf_exit(&buf);
++			bch2_write_op_error(op, bkey_start_offset(&insert->k),
++					    "btree update error: %s", bch2_err_str(ret));
+ 		}
+ 
+ 		if (ret)
+@@ -573,21 +583,29 @@ static void __bch2_write_index(struct bch_write_op *op)
+ out:
+ 	/* If some a bucket wasn't written, we can't erasure code it: */
+ 	for_each_set_bit(dev, op->failed.d, BCH_SB_MEMBERS_MAX)
+-		bch2_open_bucket_write_error(c, &op->open_buckets, dev);
++		bch2_open_bucket_write_error(c, &op->open_buckets, dev, -BCH_ERR_data_write_io);
+ 
+ 	bch2_open_buckets_put(c, &op->open_buckets);
+ 	return;
+ err:
+ 	keys->top = keys->keys;
+ 	op->error = ret;
+-	op->flags |= BCH_WRITE_SUBMITTED;
++	op->flags |= BCH_WRITE_submitted;
+ 	goto out;
+ }
+ 
+ static inline void __wp_update_state(struct write_point *wp, enum write_point_state state)
+ {
+ 	if (state != wp->state) {
++		struct task_struct *p = current;
+ 		u64 now = ktime_get_ns();
++		u64 runtime = p->se.sum_exec_runtime +
++			(now - p->se.exec_start);
++
++		if (state == WRITE_POINT_runnable)
++			wp->last_runtime = runtime;
++		else if (wp->state == WRITE_POINT_runnable)
++			wp->time[WRITE_POINT_running] += runtime - wp->last_runtime;
+ 
+ 		if (wp->last_state_change &&
+ 		    time_after64(now, wp->last_state_change))
+@@ -601,7 +619,7 @@ static inline void wp_update_state(struct write_point *wp, bool running)
+ {
+ 	enum write_point_state state;
+ 
+-	state = running			 ? WRITE_POINT_running :
++	state = running			 ? WRITE_POINT_runnable:
+ 		!list_empty(&wp->writes) ? WRITE_POINT_waiting_io
+ 					 : WRITE_POINT_stopped;
+ 
+@@ -615,8 +633,8 @@ static CLOSURE_CALLBACK(bch2_write_index)
+ 	struct workqueue_struct *wq = index_update_wq(op);
+ 	unsigned long flags;
+ 
+-	if ((op->flags & BCH_WRITE_SUBMITTED) &&
+-	    (op->flags & BCH_WRITE_MOVE))
++	if ((op->flags & BCH_WRITE_submitted) &&
++	    (op->flags & BCH_WRITE_move))
+ 		bch2_bio_free_pages_pool(op->c, &op->wbio.bio);
+ 
+ 	spin_lock_irqsave(&wp->writes_lock, flags);
+@@ -654,11 +672,11 @@ void bch2_write_point_do_index_updates(struct work_struct *work)
+ 		if (!op)
+ 			break;
+ 
+-		op->flags |= BCH_WRITE_IN_WORKER;
++		op->flags |= BCH_WRITE_in_worker;
+ 
+ 		__bch2_write_index(op);
+ 
+-		if (!(op->flags & BCH_WRITE_SUBMITTED))
++		if (!(op->flags & BCH_WRITE_submitted))
+ 			__bch2_write(op);
+ 		else
+ 			bch2_write_done(&op->cl);
+@@ -676,13 +694,17 @@ static void bch2_write_endio(struct bio *bio)
+ 		? bch2_dev_have_ref(c, wbio->dev)
+ 		: NULL;
+ 
+-	if (bch2_dev_inum_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_write,
++	bch2_account_io_completion(ca, BCH_MEMBER_ERROR_write,
++				   wbio->submit_time, !bio->bi_status);
++
++	if (bio->bi_status) {
++		bch_err_inum_offset_ratelimited(ca,
+ 				    op->pos.inode,
+ 				    wbio->inode_offset << 9,
+ 				    "data write error: %s",
+-				    bch2_blk_status_to_str(bio->bi_status))) {
++				    bch2_blk_status_to_str(bio->bi_status));
+ 		set_bit(wbio->dev, op->failed.d);
+-		op->flags |= BCH_WRITE_IO_ERROR;
++		op->flags |= BCH_WRITE_io_error;
+ 	}
+ 
+ 	if (wbio->nocow) {
+@@ -692,10 +714,8 @@ static void bch2_write_endio(struct bio *bio)
+ 		set_bit(wbio->dev, op->devs_need_flush->d);
+ 	}
+ 
+-	if (wbio->have_ioref) {
+-		bch2_latency_acct(ca, wbio->submit_time, WRITE);
++	if (wbio->have_ioref)
+ 		percpu_ref_put(&ca->io_ref);
+-	}
+ 
+ 	if (wbio->bounce)
+ 		bch2_bio_free_pages_pool(c, bio);
+@@ -729,7 +749,7 @@ static void init_append_extent(struct bch_write_op *op,
+ 		bch2_extent_crc_append(&e->k_i, crc);
+ 
+ 	bch2_alloc_sectors_append_ptrs_inlined(op->c, wp, &e->k_i, crc.compressed_size,
+-				       op->flags & BCH_WRITE_CACHED);
++				       op->flags & BCH_WRITE_cached);
+ 
+ 	bch2_keylist_push(&op->insert_keys);
+ }
+@@ -789,7 +809,6 @@ static int bch2_write_rechecksum(struct bch_fs *c,
+ {
+ 	struct bio *bio = &op->wbio.bio;
+ 	struct bch_extent_crc_unpacked new_crc;
+-	int ret;
+ 
+ 	/* bch2_rechecksum_bio() can't encrypt or decrypt data: */
+ 
+@@ -797,10 +816,10 @@ static int bch2_write_rechecksum(struct bch_fs *c,
+ 	    bch2_csum_type_is_encryption(new_csum_type))
+ 		new_csum_type = op->crc.csum_type;
+ 
+-	ret = bch2_rechecksum_bio(c, bio, op->version, op->crc,
+-				  NULL, &new_crc,
+-				  op->crc.offset, op->crc.live_size,
+-				  new_csum_type);
++	int ret = bch2_rechecksum_bio(c, bio, op->version, op->crc,
++				      NULL, &new_crc,
++				      op->crc.offset, op->crc.live_size,
++				      new_csum_type);
+ 	if (ret)
+ 		return ret;
+ 
+@@ -810,44 +829,12 @@ static int bch2_write_rechecksum(struct bch_fs *c,
+ 	return 0;
+ }
+ 
+-static int bch2_write_decrypt(struct bch_write_op *op)
+-{
+-	struct bch_fs *c = op->c;
+-	struct nonce nonce = extent_nonce(op->version, op->crc);
+-	struct bch_csum csum;
+-	int ret;
+-
+-	if (!bch2_csum_type_is_encryption(op->crc.csum_type))
+-		return 0;
+-
+-	/*
+-	 * If we need to decrypt data in the write path, we'll no longer be able
+-	 * to verify the existing checksum (poly1305 mac, in this case) after
+-	 * it's decrypted - this is the last point we'll be able to reverify the
+-	 * checksum:
+-	 */
+-	csum = bch2_checksum_bio(c, op->crc.csum_type, nonce, &op->wbio.bio);
+-	if (bch2_crc_cmp(op->crc.csum, csum) && !c->opts.no_data_io)
+-		return -EIO;
+-
+-	ret = bch2_encrypt_bio(c, op->crc.csum_type, nonce, &op->wbio.bio);
+-	op->crc.csum_type = 0;
+-	op->crc.csum = (struct bch_csum) { 0, 0 };
+-	return ret;
+-}
+-
+-static enum prep_encoded_ret {
+-	PREP_ENCODED_OK,
+-	PREP_ENCODED_ERR,
+-	PREP_ENCODED_CHECKSUM_ERR,
+-	PREP_ENCODED_DO_WRITE,
+-} bch2_write_prep_encoded_data(struct bch_write_op *op, struct write_point *wp)
++static noinline int bch2_write_prep_encoded_data(struct bch_write_op *op, struct write_point *wp)
+ {
+ 	struct bch_fs *c = op->c;
+ 	struct bio *bio = &op->wbio.bio;
+-
+-	if (!(op->flags & BCH_WRITE_DATA_ENCODED))
+-		return PREP_ENCODED_OK;
++	struct bch_csum csum;
++	int ret = 0;
+ 
+ 	BUG_ON(bio_sectors(bio) != op->crc.compressed_size);
+ 
+@@ -858,12 +845,13 @@ static enum prep_encoded_ret {
+ 	    (op->crc.compression_type == bch2_compression_opt_to_type(op->compression_opt) ||
+ 	     op->incompressible)) {
+ 		if (!crc_is_compressed(op->crc) &&
+-		    op->csum_type != op->crc.csum_type &&
+-		    bch2_write_rechecksum(c, op, op->csum_type) &&
+-		    !c->opts.no_data_io)
+-			return PREP_ENCODED_CHECKSUM_ERR;
++		    op->csum_type != op->crc.csum_type) {
++			ret = bch2_write_rechecksum(c, op, op->csum_type);
++			if (ret)
++				return ret;
++		}
+ 
+-		return PREP_ENCODED_DO_WRITE;
++		return 1;
+ 	}
+ 
+ 	/*
+@@ -871,20 +859,24 @@ static enum prep_encoded_ret {
+ 	 * is, we have to decompress it:
+ 	 */
+ 	if (crc_is_compressed(op->crc)) {
+-		struct bch_csum csum;
+-
+-		if (bch2_write_decrypt(op))
+-			return PREP_ENCODED_CHECKSUM_ERR;
+-
+ 		/* Last point we can still verify checksum: */
+-		csum = bch2_checksum_bio(c, op->crc.csum_type,
+-					 extent_nonce(op->version, op->crc),
+-					 bio);
++		struct nonce nonce = extent_nonce(op->version, op->crc);
++		csum = bch2_checksum_bio(c, op->crc.csum_type, nonce, bio);
+ 		if (bch2_crc_cmp(op->crc.csum, csum) && !c->opts.no_data_io)
+-			return PREP_ENCODED_CHECKSUM_ERR;
++			goto csum_err;
++
++		if (bch2_csum_type_is_encryption(op->crc.csum_type)) {
++			ret = bch2_encrypt_bio(c, op->crc.csum_type, nonce, bio);
++			if (ret)
++				return ret;
++
++			op->crc.csum_type = 0;
++			op->crc.csum = (struct bch_csum) { 0, 0 };
++		}
+ 
+-		if (bch2_bio_uncompress_inplace(op, bio))
+-			return PREP_ENCODED_ERR;
++		ret = bch2_bio_uncompress_inplace(op, bio);
++		if (ret)
++			return ret;
+ 	}
+ 
+ 	/*
+@@ -896,22 +888,44 @@ static enum prep_encoded_ret {
+ 	 * If the data is checksummed and we're only writing a subset,
+ 	 * rechecksum and adjust bio to point to currently live data:
+ 	 */
+-	if ((op->crc.live_size != op->crc.uncompressed_size ||
+-	     op->crc.csum_type != op->csum_type) &&
+-	    bch2_write_rechecksum(c, op, op->csum_type) &&
+-	    !c->opts.no_data_io)
+-		return PREP_ENCODED_CHECKSUM_ERR;
++	if (op->crc.live_size != op->crc.uncompressed_size ||
++	    op->crc.csum_type != op->csum_type) {
++		ret = bch2_write_rechecksum(c, op, op->csum_type);
++		if (ret)
++			return ret;
++	}
+ 
+ 	/*
+ 	 * If we want to compress the data, it has to be decrypted:
+ 	 */
+-	if ((op->compression_opt ||
+-	     bch2_csum_type_is_encryption(op->crc.csum_type) !=
+-	     bch2_csum_type_is_encryption(op->csum_type)) &&
+-	    bch2_write_decrypt(op))
+-		return PREP_ENCODED_CHECKSUM_ERR;
++	if (bch2_csum_type_is_encryption(op->crc.csum_type) &&
++	    (op->compression_opt || op->crc.csum_type != op->csum_type)) {
++		struct nonce nonce = extent_nonce(op->version, op->crc);
++		csum = bch2_checksum_bio(c, op->crc.csum_type, nonce, bio);
++		if (bch2_crc_cmp(op->crc.csum, csum) && !c->opts.no_data_io)
++			goto csum_err;
++
++		ret = bch2_encrypt_bio(c, op->crc.csum_type, nonce, bio);
++		if (ret)
++			return ret;
++
++		op->crc.csum_type = 0;
++		op->crc.csum = (struct bch_csum) { 0, 0 };
++	}
+ 
+-	return PREP_ENCODED_OK;
++	return 0;
++csum_err:
++	bch2_write_op_error(op, op->pos.offset,
++		"error verifying existing checksum while moving existing data (memory corruption?)\n"
++		"  expected %0llx:%0llx got %0llx:%0llx type %s",
++		op->crc.csum.hi,
++		op->crc.csum.lo,
++		csum.hi,
++		csum.lo,
++		op->crc.csum_type < BCH_CSUM_NR
++		? __bch2_csum_types[op->crc.csum_type]
++		: "(unknown)");
++	return -BCH_ERR_data_write_csum;
+ }
+ 
+ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
+@@ -926,43 +940,51 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
+ 	bool page_alloc_failed = false;
+ 	int ret, more = 0;
+ 
++	if (op->incompressible)
++		op->compression_opt = 0;
++
+ 	BUG_ON(!bio_sectors(src));
+ 
+ 	ec_buf = bch2_writepoint_ec_buf(c, wp);
+ 
+-	switch (bch2_write_prep_encoded_data(op, wp)) {
+-	case PREP_ENCODED_OK:
+-		break;
+-	case PREP_ENCODED_ERR:
+-		ret = -EIO;
+-		goto err;
+-	case PREP_ENCODED_CHECKSUM_ERR:
+-		goto csum_err;
+-	case PREP_ENCODED_DO_WRITE:
+-		/* XXX look for bug here */
+-		if (ec_buf) {
+-			dst = bch2_write_bio_alloc(c, wp, src,
+-						   &page_alloc_failed,
+-						   ec_buf);
+-			bio_copy_data(dst, src);
+-			bounce = true;
++	if (unlikely(op->flags & BCH_WRITE_data_encoded)) {
++		ret = bch2_write_prep_encoded_data(op, wp);
++		if (ret < 0)
++			goto err;
++		if (ret) {
++			if (ec_buf) {
++				dst = bch2_write_bio_alloc(c, wp, src,
++							   &page_alloc_failed,
++							   ec_buf);
++				bio_copy_data(dst, src);
++				bounce = true;
++			}
++			init_append_extent(op, wp, op->version, op->crc);
++			goto do_write;
+ 		}
+-		init_append_extent(op, wp, op->version, op->crc);
+-		goto do_write;
+ 	}
+ 
+ 	if (ec_buf ||
+ 	    op->compression_opt ||
+ 	    (op->csum_type &&
+-	     !(op->flags & BCH_WRITE_PAGES_STABLE)) ||
++	     !(op->flags & BCH_WRITE_pages_stable)) ||
+ 	    (bch2_csum_type_is_encryption(op->csum_type) &&
+-	     !(op->flags & BCH_WRITE_PAGES_OWNED))) {
++	     !(op->flags & BCH_WRITE_pages_owned))) {
+ 		dst = bch2_write_bio_alloc(c, wp, src,
+ 					   &page_alloc_failed,
+ 					   ec_buf);
+ 		bounce = true;
+ 	}
+ 
++#ifdef CONFIG_BCACHEFS_DEBUG
++	unsigned write_corrupt_ratio = READ_ONCE(bch2_write_corrupt_ratio);
++	if (!bounce && write_corrupt_ratio) {
++		dst = bch2_write_bio_alloc(c, wp, src,
++					   &page_alloc_failed,
++					   ec_buf);
++		bounce = true;
++	}
++#endif
+ 	saved_iter = dst->bi_iter;
+ 
+ 	do {
+@@ -976,7 +998,7 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
+ 			break;
+ 
+ 		BUG_ON(op->compression_opt &&
+-		       (op->flags & BCH_WRITE_DATA_ENCODED) &&
++		       (op->flags & BCH_WRITE_data_encoded) &&
+ 		       bch2_csum_type_is_encryption(op->crc.csum_type));
+ 		BUG_ON(op->compression_opt && !bounce);
+ 
+@@ -1014,7 +1036,7 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
+ 			}
+ 		}
+ 
+-		if ((op->flags & BCH_WRITE_DATA_ENCODED) &&
++		if ((op->flags & BCH_WRITE_data_encoded) &&
+ 		    !crc_is_compressed(crc) &&
+ 		    bch2_csum_type_is_encryption(op->crc.csum_type) ==
+ 		    bch2_csum_type_is_encryption(op->csum_type)) {
+@@ -1032,12 +1054,13 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
+ 			 * data can't be modified (by userspace) while it's in
+ 			 * flight.
+ 			 */
+-			if (bch2_rechecksum_bio(c, src, version, op->crc,
++			ret = bch2_rechecksum_bio(c, src, version, op->crc,
+ 					&crc, &op->crc,
+ 					src_len >> 9,
+ 					bio_sectors(src) - (src_len >> 9),
+-					op->csum_type))
+-				goto csum_err;
++					op->csum_type);
++			if (ret)
++				goto err;
+ 			/*
+ 			 * rchecksum_bio sets compression_type on crc from op->crc,
+ 			 * this isn't always correct as sometimes we're changing
+@@ -1046,13 +1069,13 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
+ 			crc.compression_type = compression_type;
+ 			crc.nonce = nonce;
+ 		} else {
+-			if ((op->flags & BCH_WRITE_DATA_ENCODED) &&
+-			    bch2_rechecksum_bio(c, src, version, op->crc,
++			if ((op->flags & BCH_WRITE_data_encoded) &&
++			    (ret = bch2_rechecksum_bio(c, src, version, op->crc,
+ 					NULL, &op->crc,
+ 					src_len >> 9,
+ 					bio_sectors(src) - (src_len >> 9),
+-					op->crc.csum_type))
+-				goto csum_err;
++					op->crc.csum_type)))
++				goto err;
+ 
+ 			crc.compressed_size	= dst_len >> 9;
+ 			crc.uncompressed_size	= src_len >> 9;
+@@ -1072,6 +1095,14 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
+ 
+ 		init_append_extent(op, wp, version, crc);
+ 
++#ifdef CONFIG_BCACHEFS_DEBUG
++		if (write_corrupt_ratio) {
++			swap(dst->bi_iter.bi_size, dst_len);
++			bch2_maybe_corrupt_bio(dst, write_corrupt_ratio);
++			swap(dst->bi_iter.bi_size, dst_len);
++		}
++#endif
++
+ 		if (dst != src)
+ 			bio_advance(dst, dst_len);
+ 		bio_advance(src, src_len);
+@@ -1103,16 +1134,6 @@ static int bch2_write_extent(struct bch_write_op *op, struct write_point *wp,
+ do_write:
+ 	*_dst = dst;
+ 	return more;
+-csum_err:
+-	{
+-		struct printbuf buf = PRINTBUF;
+-		bch2_write_op_error(&buf, op);
+-		prt_printf(&buf, "error verifying existing checksum while rewriting existing data (memory corruption?)");
+-		bch_err_ratelimited(c, "%s", buf.buf);
+-		printbuf_exit(&buf);
+-	}
+-
+-	ret = -EIO;
+ err:
+ 	if (to_wbio(dst)->bounce)
+ 		bch2_bio_free_pages_pool(c, dst);
+@@ -1190,39 +1211,36 @@ static void bch2_nocow_write_convert_unwritten(struct bch_write_op *op)
+ {
+ 	struct bch_fs *c = op->c;
+ 	struct btree_trans *trans = bch2_trans_get(c);
++	int ret = 0;
+ 
+ 	for_each_keylist_key(&op->insert_keys, orig) {
+-		int ret = for_each_btree_key_max_commit(trans, iter, BTREE_ID_extents,
++		ret = for_each_btree_key_max_commit(trans, iter, BTREE_ID_extents,
+ 				     bkey_start_pos(&orig->k), orig->k.p,
+ 				     BTREE_ITER_intent, k,
+ 				     NULL, NULL, BCH_TRANS_COMMIT_no_enospc, ({
+ 			bch2_nocow_write_convert_one_unwritten(trans, &iter, orig, k, op->new_i_size);
+ 		}));
+-
+-		if (ret && !bch2_err_matches(ret, EROFS)) {
+-			struct bkey_i *insert = bch2_keylist_front(&op->insert_keys);
+-
+-			struct printbuf buf = PRINTBUF;
+-			bch2_write_op_error_trans(trans, &buf, op, bkey_start_offset(&insert->k));
+-			prt_printf(&buf, "btree update error: %s", bch2_err_str(ret));
+-			bch_err_ratelimited(c, "%s", buf.buf);
+-			printbuf_exit(&buf);
+-		}
+-
+-		if (ret) {
+-			op->error = ret;
++		if (ret)
+ 			break;
+-		}
+ 	}
+ 
+ 	bch2_trans_put(trans);
++
++	if (ret && !bch2_err_matches(ret, EROFS)) {
++		struct bkey_i *insert = bch2_keylist_front(&op->insert_keys);
++		bch2_write_op_error(op, bkey_start_offset(&insert->k),
++				    "btree update error: %s", bch2_err_str(ret));
++	}
++
++	if (ret)
++		op->error = ret;
+ }
+ 
+ static void __bch2_nocow_write_done(struct bch_write_op *op)
+ {
+-	if (unlikely(op->flags & BCH_WRITE_IO_ERROR)) {
+-		op->error = -EIO;
+-	} else if (unlikely(op->flags & BCH_WRITE_CONVERT_UNWRITTEN))
++	if (unlikely(op->flags & BCH_WRITE_io_error)) {
++		op->error = -BCH_ERR_data_write_io;
++	} else if (unlikely(op->flags & BCH_WRITE_convert_unwritten))
+ 		bch2_nocow_write_convert_unwritten(op);
+ }
+ 
+@@ -1251,7 +1269,7 @@ static void bch2_nocow_write(struct bch_write_op *op)
+ 	struct bucket_to_lock *stale_at;
+ 	int stale, ret;
+ 
+-	if (op->flags & BCH_WRITE_MOVE)
++	if (op->flags & BCH_WRITE_move)
+ 		return;
+ 
+ 	darray_init(&buckets);
+@@ -1309,7 +1327,7 @@ static void bch2_nocow_write(struct bch_write_op *op)
+ 						   }), GFP_KERNEL|__GFP_NOFAIL);
+ 
+ 			if (ptr->unwritten)
+-				op->flags |= BCH_WRITE_CONVERT_UNWRITTEN;
++				op->flags |= BCH_WRITE_convert_unwritten;
+ 		}
+ 
+ 		/* Unlock before taking nocow locks, doing IO: */
+@@ -1317,7 +1335,7 @@ static void bch2_nocow_write(struct bch_write_op *op)
+ 		bch2_trans_unlock(trans);
+ 
+ 		bch2_cut_front(op->pos, op->insert_keys.top);
+-		if (op->flags & BCH_WRITE_CONVERT_UNWRITTEN)
++		if (op->flags & BCH_WRITE_convert_unwritten)
+ 			bch2_cut_back(POS(op->pos.inode, op->pos.offset + bio_sectors(bio)), op->insert_keys.top);
+ 
+ 		darray_for_each(buckets, i) {
+@@ -1342,7 +1360,7 @@ static void bch2_nocow_write(struct bch_write_op *op)
+ 			wbio_init(bio)->put_bio = true;
+ 			bio->bi_opf = op->wbio.bio.bi_opf;
+ 		} else {
+-			op->flags |= BCH_WRITE_SUBMITTED;
++			op->flags |= BCH_WRITE_submitted;
+ 		}
+ 
+ 		op->pos.offset += bio_sectors(bio);
+@@ -1352,11 +1370,12 @@ static void bch2_nocow_write(struct bch_write_op *op)
+ 		bio->bi_private	= &op->cl;
+ 		bio->bi_opf |= REQ_OP_WRITE;
+ 		closure_get(&op->cl);
++
+ 		bch2_submit_wbio_replicas(to_wbio(bio), c, BCH_DATA_user,
+ 					  op->insert_keys.top, true);
+ 
+ 		bch2_keylist_push(&op->insert_keys);
+-		if (op->flags & BCH_WRITE_SUBMITTED)
++		if (op->flags & BCH_WRITE_submitted)
+ 			break;
+ 		bch2_btree_iter_advance(&iter);
+ 	}
+@@ -1370,21 +1389,18 @@ static void bch2_nocow_write(struct bch_write_op *op)
+ 	darray_exit(&buckets);
+ 
+ 	if (ret) {
+-		struct printbuf buf = PRINTBUF;
+-		bch2_write_op_error(&buf, op);
+-		prt_printf(&buf, "%s(): btree lookup error: %s", __func__, bch2_err_str(ret));
+-		bch_err_ratelimited(c, "%s", buf.buf);
+-		printbuf_exit(&buf);
++		bch2_write_op_error(op, op->pos.offset,
++				    "%s(): btree lookup error: %s", __func__, bch2_err_str(ret));
+ 		op->error = ret;
+-		op->flags |= BCH_WRITE_SUBMITTED;
++		op->flags |= BCH_WRITE_submitted;
+ 	}
+ 
+ 	/* fallback to cow write path? */
+-	if (!(op->flags & BCH_WRITE_SUBMITTED)) {
++	if (!(op->flags & BCH_WRITE_submitted)) {
+ 		closure_sync(&op->cl);
+ 		__bch2_nocow_write_done(op);
+ 		op->insert_keys.top = op->insert_keys.keys;
+-	} else if (op->flags & BCH_WRITE_SYNC) {
++	} else if (op->flags & BCH_WRITE_sync) {
+ 		closure_sync(&op->cl);
+ 		bch2_nocow_write_done(&op->cl.work);
+ 	} else {
+@@ -1414,7 +1430,7 @@ static void bch2_nocow_write(struct bch_write_op *op)
+ 				    "pointer to invalid bucket in nocow path on device %llu\n  %s",
+ 				    stale_at->b.inode,
+ 				    (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
+-		ret = -EIO;
++		ret = -BCH_ERR_data_write_invalid_ptr;
+ 	} else {
+ 		/* We can retry this: */
+ 		ret = -BCH_ERR_transaction_restart;
+@@ -1436,7 +1452,7 @@ static void __bch2_write(struct bch_write_op *op)
+ 
+ 	if (unlikely(op->opts.nocow && c->opts.nocow_enabled)) {
+ 		bch2_nocow_write(op);
+-		if (op->flags & BCH_WRITE_SUBMITTED)
++		if (op->flags & BCH_WRITE_submitted)
+ 			goto out_nofs_restore;
+ 	}
+ again:
+@@ -1466,7 +1482,7 @@ static void __bch2_write(struct bch_write_op *op)
+ 		ret = bch2_trans_run(c, lockrestart_do(trans,
+ 			bch2_alloc_sectors_start_trans(trans,
+ 				op->target,
+-				op->opts.erasure_code && !(op->flags & BCH_WRITE_CACHED),
++				op->opts.erasure_code && !(op->flags & BCH_WRITE_cached),
+ 				op->write_point,
+ 				&op->devs_have,
+ 				op->nr_replicas,
+@@ -1489,16 +1505,12 @@ static void __bch2_write(struct bch_write_op *op)
+ 		bch2_alloc_sectors_done_inlined(c, wp);
+ err:
+ 		if (ret <= 0) {
+-			op->flags |= BCH_WRITE_SUBMITTED;
++			op->flags |= BCH_WRITE_submitted;
+ 
+ 			if (unlikely(ret < 0)) {
+-				if (!(op->flags & BCH_WRITE_ALLOC_NOWAIT)) {
+-					struct printbuf buf = PRINTBUF;
+-					bch2_write_op_error(&buf, op);
+-					prt_printf(&buf, "%s(): %s", __func__, bch2_err_str(ret));
+-					bch_err_ratelimited(c, "%s", buf.buf);
+-					printbuf_exit(&buf);
+-				}
++				if (!(op->flags & BCH_WRITE_alloc_nowait))
++					bch2_write_op_error(op, op->pos.offset,
++							    "%s(): %s", __func__, bch2_err_str(ret));
+ 				op->error = ret;
+ 				break;
+ 			}
+@@ -1524,14 +1536,14 @@ static void __bch2_write(struct bch_write_op *op)
+ 	 * synchronously here if we weren't able to submit all of the IO at
+ 	 * once, as that signals backpressure to the caller.
+ 	 */
+-	if ((op->flags & BCH_WRITE_SYNC) ||
+-	    (!(op->flags & BCH_WRITE_SUBMITTED) &&
+-	     !(op->flags & BCH_WRITE_IN_WORKER))) {
++	if ((op->flags & BCH_WRITE_sync) ||
++	    (!(op->flags & BCH_WRITE_submitted) &&
++	     !(op->flags & BCH_WRITE_in_worker))) {
+ 		bch2_wait_on_allocator(c, &op->cl);
+ 
+ 		__bch2_write_index(op);
+ 
+-		if (!(op->flags & BCH_WRITE_SUBMITTED))
++		if (!(op->flags & BCH_WRITE_submitted))
+ 			goto again;
+ 		bch2_write_done(&op->cl);
+ 	} else {
+@@ -1552,8 +1564,8 @@ static void bch2_write_data_inline(struct bch_write_op *op, unsigned data_len)
+ 
+ 	memset(&op->failed, 0, sizeof(op->failed));
+ 
+-	op->flags |= BCH_WRITE_WROTE_DATA_INLINE;
+-	op->flags |= BCH_WRITE_SUBMITTED;
++	op->flags |= BCH_WRITE_wrote_data_inline;
++	op->flags |= BCH_WRITE_submitted;
+ 
+ 	bch2_check_set_feature(op->c, BCH_FEATURE_inline_data);
+ 
+@@ -1616,8 +1628,8 @@ CLOSURE_CALLBACK(bch2_write)
+ 	BUG_ON(!op->write_point.v);
+ 	BUG_ON(bkey_eq(op->pos, POS_MAX));
+ 
+-	if (op->flags & BCH_WRITE_ONLY_SPECIFIED_DEVS)
+-		op->flags |= BCH_WRITE_ALLOC_NOWAIT;
++	if (op->flags & BCH_WRITE_only_specified_devs)
++		op->flags |= BCH_WRITE_alloc_nowait;
+ 
+ 	op->nr_replicas_required = min_t(unsigned, op->nr_replicas_required, op->nr_replicas);
+ 	op->start_time = local_clock();
+@@ -1625,11 +1637,8 @@ CLOSURE_CALLBACK(bch2_write)
+ 	wbio_init(bio)->put_bio = false;
+ 
+ 	if (unlikely(bio->bi_iter.bi_size & (c->opts.block_size - 1))) {
+-		struct printbuf buf = PRINTBUF;
+-		bch2_write_op_error(&buf, op);
+-		prt_printf(&buf, "misaligned write");
+-		printbuf_exit(&buf);
+-		op->error = -EIO;
++		bch2_write_op_error(op, op->pos.offset, "misaligned write");
++		op->error = -BCH_ERR_data_write_misaligned;
+ 		goto err;
+ 	}
+ 
+@@ -1638,13 +1647,14 @@ CLOSURE_CALLBACK(bch2_write)
+ 		goto err;
+ 	}
+ 
+-	if (!(op->flags & BCH_WRITE_MOVE) &&
++	if (!(op->flags & BCH_WRITE_move) &&
+ 	    !bch2_write_ref_tryget(c, BCH_WRITE_REF_write)) {
+ 		op->error = -BCH_ERR_erofs_no_writes;
+ 		goto err;
+ 	}
+ 
+-	this_cpu_add(c->counters[BCH_COUNTER_io_write], bio_sectors(bio));
++	if (!(op->flags & BCH_WRITE_move))
++		this_cpu_add(c->counters[BCH_COUNTER_io_write], bio_sectors(bio));
+ 	bch2_increment_clock(c, bio_sectors(bio), WRITE);
+ 
+ 	data_len = min_t(u64, bio->bi_iter.bi_size,
+@@ -1675,20 +1685,26 @@ static const char * const bch2_write_flags[] = {
+ 
+ void bch2_write_op_to_text(struct printbuf *out, struct bch_write_op *op)
+ {
+-	prt_str(out, "pos: ");
++	if (!out->nr_tabstops)
++		printbuf_tabstop_push(out, 32);
++
++	prt_printf(out, "pos:\t");
+ 	bch2_bpos_to_text(out, op->pos);
+ 	prt_newline(out);
+ 	printbuf_indent_add(out, 2);
+ 
+-	prt_str(out, "started: ");
++	prt_printf(out, "started:\t");
+ 	bch2_pr_time_units(out, local_clock() - op->start_time);
+ 	prt_newline(out);
+ 
+-	prt_str(out, "flags: ");
++	prt_printf(out, "flags:\t");
+ 	prt_bitflags(out, bch2_write_flags, op->flags);
+ 	prt_newline(out);
+ 
+-	prt_printf(out, "ref: %u\n", closure_nr_remaining(&op->cl));
++	prt_printf(out, "nr_replicas:\t%u\n", op->nr_replicas);
++	prt_printf(out, "nr_replicas_required:\t%u\n", op->nr_replicas_required);
++
++	prt_printf(out, "ref:\t%u\n", closure_nr_remaining(&op->cl));
+ 
+ 	printbuf_indent_sub(out, 2);
+ }
+diff --git a/fs/bcachefs/io_write.h b/fs/bcachefs/io_write.h
+index b4626013abc8..b8ab19a1e1da 100644
+--- a/fs/bcachefs/io_write.h
++++ b/fs/bcachefs/io_write.h
+@@ -11,33 +11,27 @@
+ void bch2_bio_free_pages_pool(struct bch_fs *, struct bio *);
+ void bch2_bio_alloc_pages_pool(struct bch_fs *, struct bio *, size_t);
+ 
+-#ifndef CONFIG_BCACHEFS_NO_LATENCY_ACCT
+-void bch2_latency_acct(struct bch_dev *, u64, int);
+-#else
+-static inline void bch2_latency_acct(struct bch_dev *ca, u64 submit_time, int rw) {}
+-#endif
+-
+ void bch2_submit_wbio_replicas(struct bch_write_bio *, struct bch_fs *,
+ 			       enum bch_data_type, const struct bkey_i *, bool);
+ 
+-void bch2_write_op_error(struct printbuf *out, struct bch_write_op *op);
++__printf(3, 4)
++void bch2_write_op_error(struct bch_write_op *op, u64, const char *, ...);
+ 
+ #define BCH_WRITE_FLAGS()		\
+-	x(ALLOC_NOWAIT)			\
+-	x(CACHED)			\
+-	x(DATA_ENCODED)			\
+-	x(PAGES_STABLE)			\
+-	x(PAGES_OWNED)			\
+-	x(ONLY_SPECIFIED_DEVS)		\
+-	x(WROTE_DATA_INLINE)		\
+-	x(FROM_INTERNAL)		\
+-	x(CHECK_ENOSPC)			\
+-	x(SYNC)				\
+-	x(MOVE)				\
+-	x(IN_WORKER)			\
+-	x(SUBMITTED)			\
+-	x(IO_ERROR)			\
+-	x(CONVERT_UNWRITTEN)
++	x(alloc_nowait)			\
++	x(cached)			\
++	x(data_encoded)			\
++	x(pages_stable)			\
++	x(pages_owned)			\
++	x(only_specified_devs)		\
++	x(wrote_data_inline)		\
++	x(check_enospc)			\
++	x(sync)				\
++	x(move)				\
++	x(in_worker)			\
++	x(submitted)			\
++	x(io_error)			\
++	x(convert_unwritten)
+ 
+ enum __bch_write_flags {
+ #define x(f)	__BCH_WRITE_##f,
+diff --git a/fs/bcachefs/io_write_types.h b/fs/bcachefs/io_write_types.h
+index 6e878a6f2f0b..3ef6df9145ef 100644
+--- a/fs/bcachefs/io_write_types.h
++++ b/fs/bcachefs/io_write_types.h
+@@ -64,7 +64,7 @@ struct bch_write_op {
+ 	struct bpos		pos;
+ 	struct bversion		version;
+ 
+-	/* For BCH_WRITE_DATA_ENCODED: */
++	/* For BCH_WRITE_data_encoded: */
+ 	struct bch_extent_crc_unpacked crc;
+ 
+ 	struct write_point_specifier write_point;
+diff --git a/fs/bcachefs/journal.c b/fs/bcachefs/journal.c
+index 05b1250619ec..ecb97d435f6a 100644
+--- a/fs/bcachefs/journal.c
++++ b/fs/bcachefs/journal.c
+@@ -20,13 +20,6 @@
+ #include "journal_seq_blacklist.h"
+ #include "trace.h"
+ 
+-static const char * const bch2_journal_errors[] = {
+-#define x(n)	#n,
+-	JOURNAL_ERRORS()
+-#undef x
+-	NULL
+-};
+-
+ static inline bool journal_seq_unwritten(struct journal *j, u64 seq)
+ {
+ 	return seq > j->seq_ondisk;
+@@ -56,14 +49,20 @@ static void bch2_journal_buf_to_text(struct printbuf *out, struct journal *j, u6
+ 	prt_printf(out, "seq:\t%llu\n", seq);
+ 	printbuf_indent_add(out, 2);
+ 
+-	prt_printf(out, "refcount:\t%u\n", journal_state_count(s, i));
++	if (!buf->write_started)
++		prt_printf(out, "refcount:\t%u\n", journal_state_count(s, i & JOURNAL_STATE_BUF_MASK));
+ 
+-	prt_printf(out, "size:\t");
+-	prt_human_readable_u64(out, vstruct_bytes(buf->data));
+-	prt_newline(out);
++	struct closure *cl = &buf->io;
++	int r = atomic_read(&cl->remaining);
++	prt_printf(out, "io:\t%pS r %i\n", cl->fn, r & CLOSURE_REMAINING_MASK);
++
++	if (buf->data) {
++		prt_printf(out, "size:\t");
++		prt_human_readable_u64(out, vstruct_bytes(buf->data));
++		prt_newline(out);
++	}
+ 
+-	prt_printf(out, "expires:\t");
+-	prt_printf(out, "%li jiffies\n", buf->expires - jiffies);
++	prt_printf(out, "expires:\t%li jiffies\n", buf->expires - jiffies);
+ 
+ 	prt_printf(out, "flags:\t");
+ 	if (buf->noflush)
+@@ -87,6 +86,9 @@ static void bch2_journal_buf_to_text(struct printbuf *out, struct journal *j, u6
+ 
+ static void bch2_journal_bufs_to_text(struct printbuf *out, struct journal *j)
+ {
++	lockdep_assert_held(&j->lock);
++	out->atomic++;
++
+ 	if (!out->nr_tabstops)
+ 		printbuf_tabstop_push(out, 24);
+ 
+@@ -95,6 +97,8 @@ static void bch2_journal_bufs_to_text(struct printbuf *out, struct journal *j)
+ 	     seq++)
+ 		bch2_journal_buf_to_text(out, j, seq);
+ 	prt_printf(out, "last buf %s\n", journal_entry_is_open(j) ? "open" : "closed");
++
++	--out->atomic;
+ }
+ 
+ static inline struct journal_buf *
+@@ -104,10 +108,8 @@ journal_seq_to_buf(struct journal *j, u64 seq)
+ 
+ 	EBUG_ON(seq > journal_cur_seq(j));
+ 
+-	if (journal_seq_unwritten(j, seq)) {
++	if (journal_seq_unwritten(j, seq))
+ 		buf = j->buf + (seq & JOURNAL_BUF_MASK);
+-		EBUG_ON(le64_to_cpu(buf->data->seq) != seq);
+-	}
+ 	return buf;
+ }
+ 
+@@ -139,8 +141,10 @@ journal_error_check_stuck(struct journal *j, int error, unsigned flags)
+ 	bool stuck = false;
+ 	struct printbuf buf = PRINTBUF;
+ 
+-	if (!(error == JOURNAL_ERR_journal_full ||
+-	      error == JOURNAL_ERR_journal_pin_full) ||
++	buf.atomic++;
++
++	if (!(error == -BCH_ERR_journal_full ||
++	      error == -BCH_ERR_journal_pin_full) ||
+ 	    nr_unwritten_journal_entries(j) ||
+ 	    (flags & BCH_WATERMARK_MASK) != BCH_WATERMARK_reclaim)
+ 		return stuck;
+@@ -167,9 +171,9 @@ journal_error_check_stuck(struct journal *j, int error, unsigned flags)
+ 	spin_unlock(&j->lock);
+ 
+ 	bch_err(c, "Journal stuck! Hava a pre-reservation but journal full (error %s)",
+-		bch2_journal_errors[error]);
++		bch2_err_str(error));
+ 	bch2_journal_debug_to_text(&buf, j);
+-	bch_err(c, "%s", buf.buf);
++	bch2_print_string_as_lines(KERN_ERR, buf.buf);
+ 
+ 	printbuf_reset(&buf);
+ 	bch2_journal_pins_to_text(&buf, j);
+@@ -195,7 +199,8 @@ void bch2_journal_do_writes(struct journal *j)
+ 		if (w->write_started)
+ 			continue;
+ 
+-		if (!journal_state_count(j->reservations, idx)) {
++		if (!journal_state_seq_count(j, j->reservations, seq)) {
++			j->seq_write_started = seq;
+ 			w->write_started = true;
+ 			closure_call(&w->io, bch2_journal_write, j->wq, NULL);
+ 		}
+@@ -306,7 +311,7 @@ static void __journal_entry_close(struct journal *j, unsigned closed_val, bool t
+ 
+ 	bch2_journal_space_available(j);
+ 
+-	__bch2_journal_buf_put(j, old.idx, le64_to_cpu(buf->data->seq));
++	__bch2_journal_buf_put(j, le64_to_cpu(buf->data->seq));
+ }
+ 
+ void bch2_journal_halt(struct journal *j)
+@@ -377,29 +382,41 @@ static int journal_entry_open(struct journal *j)
+ 	BUG_ON(BCH_SB_CLEAN(c->disk_sb.sb));
+ 
+ 	if (j->blocked)
+-		return JOURNAL_ERR_blocked;
++		return -BCH_ERR_journal_blocked;
+ 
+ 	if (j->cur_entry_error)
+ 		return j->cur_entry_error;
+ 
+-	if (bch2_journal_error(j))
+-		return JOURNAL_ERR_insufficient_devices; /* -EROFS */
++	int ret = bch2_journal_error(j);
++	if (unlikely(ret))
++		return ret;
+ 
+ 	if (!fifo_free(&j->pin))
+-		return JOURNAL_ERR_journal_pin_full;
++		return -BCH_ERR_journal_pin_full;
+ 
+ 	if (nr_unwritten_journal_entries(j) == ARRAY_SIZE(j->buf))
+-		return JOURNAL_ERR_max_in_flight;
++		return -BCH_ERR_journal_max_in_flight;
++
++	if (atomic64_read(&j->seq) - j->seq_write_started == JOURNAL_STATE_BUF_NR)
++		return -BCH_ERR_journal_max_open;
+ 
+ 	if (journal_cur_seq(j) >= JOURNAL_SEQ_MAX) {
+ 		bch_err(c, "cannot start: journal seq overflow");
+ 		if (bch2_fs_emergency_read_only_locked(c))
+ 			bch_err(c, "fatal error - emergency read only");
+-		return JOURNAL_ERR_insufficient_devices; /* -EROFS */
++		return -BCH_ERR_journal_shutdown;
+ 	}
+ 
++	if (!j->free_buf && !buf->data)
++		return -BCH_ERR_journal_buf_enomem; /* will retry after write completion frees up a buf */
++
+ 	BUG_ON(!j->cur_entry_sectors);
+ 
++	if (!buf->data) {
++		swap(buf->data,		j->free_buf);
++		swap(buf->buf_size,	j->free_buf_size);
++	}
++
+ 	buf->expires		=
+ 		(journal_cur_seq(j) == j->flushed_seq_ondisk
+ 		 ? jiffies
+@@ -415,7 +432,7 @@ static int journal_entry_open(struct journal *j)
+ 	u64s = clamp_t(int, u64s, 0, JOURNAL_ENTRY_CLOSED_VAL - 1);
+ 
+ 	if (u64s <= (ssize_t) j->early_journal_entries.nr)
+-		return JOURNAL_ERR_journal_full;
++		return -BCH_ERR_journal_full;
+ 
+ 	if (fifo_empty(&j->pin) && j->reclaim_thread)
+ 		wake_up_process(j->reclaim_thread);
+@@ -464,7 +481,7 @@ static int journal_entry_open(struct journal *j)
+ 
+ 		new.idx++;
+ 		BUG_ON(journal_state_count(new, new.idx));
+-		BUG_ON(new.idx != (journal_cur_seq(j) & JOURNAL_BUF_MASK));
++		BUG_ON(new.idx != (journal_cur_seq(j) & JOURNAL_STATE_BUF_MASK));
+ 
+ 		journal_state_inc(&new);
+ 
+@@ -514,6 +531,33 @@ static void journal_write_work(struct work_struct *work)
+ 	spin_unlock(&j->lock);
+ }
+ 
++static void journal_buf_prealloc(struct journal *j)
++{
++	if (j->free_buf &&
++	    j->free_buf_size >= j->buf_size_want)
++		return;
++
++	unsigned buf_size = j->buf_size_want;
++
++	spin_unlock(&j->lock);
++	void *buf = kvmalloc(buf_size, GFP_NOFS);
++	spin_lock(&j->lock);
++
++	if (buf &&
++	    (!j->free_buf ||
++	     buf_size > j->free_buf_size)) {
++		swap(buf,	j->free_buf);
++		swap(buf_size,	j->free_buf_size);
++	}
++
++	if (unlikely(buf)) {
++		spin_unlock(&j->lock);
++		/* kvfree can sleep */
++		kvfree(buf);
++		spin_lock(&j->lock);
++	}
++}
++
+ static int __journal_res_get(struct journal *j, struct journal_res *res,
+ 			     unsigned flags)
+ {
+@@ -525,25 +569,28 @@ static int __journal_res_get(struct journal *j, struct journal_res *res,
+ 	if (journal_res_get_fast(j, res, flags))
+ 		return 0;
+ 
+-	if (bch2_journal_error(j))
+-		return -BCH_ERR_erofs_journal_err;
++	ret = bch2_journal_error(j);
++	if (unlikely(ret))
++		return ret;
+ 
+ 	if (j->blocked)
+-		return -BCH_ERR_journal_res_get_blocked;
++		return -BCH_ERR_journal_blocked;
+ 
+ 	if ((flags & BCH_WATERMARK_MASK) < j->watermark) {
+-		ret = JOURNAL_ERR_journal_full;
++		ret = -BCH_ERR_journal_full;
+ 		can_discard = j->can_discard;
+ 		goto out;
+ 	}
+ 
+ 	if (nr_unwritten_journal_entries(j) == ARRAY_SIZE(j->buf) && !journal_entry_is_open(j)) {
+-		ret = JOURNAL_ERR_max_in_flight;
++		ret = -BCH_ERR_journal_max_in_flight;
+ 		goto out;
+ 	}
+ 
+ 	spin_lock(&j->lock);
+ 
++	journal_buf_prealloc(j);
++
+ 	/*
+ 	 * Recheck after taking the lock, so we don't race with another thread
+ 	 * that just did journal_entry_open() and call bch2_journal_entry_close()
+@@ -566,25 +613,48 @@ static int __journal_res_get(struct journal *j, struct journal_res *res,
+ 		j->buf_size_want = max(j->buf_size_want, buf->buf_size << 1);
+ 
+ 	__journal_entry_close(j, JOURNAL_ENTRY_CLOSED_VAL, false);
+-	ret = journal_entry_open(j) ?: JOURNAL_ERR_retry;
++	ret = journal_entry_open(j) ?: -BCH_ERR_journal_retry_open;
+ unlock:
+ 	can_discard = j->can_discard;
+ 	spin_unlock(&j->lock);
+ out:
+-	if (ret == JOURNAL_ERR_retry)
+-		goto retry;
+-	if (!ret)
++	if (likely(!ret))
+ 		return 0;
++	if (ret == -BCH_ERR_journal_retry_open)
++		goto retry;
+ 
+ 	if (journal_error_check_stuck(j, ret, flags))
+-		ret = -BCH_ERR_journal_res_get_blocked;
++		ret = -BCH_ERR_journal_stuck;
++
++	if (ret == -BCH_ERR_journal_max_in_flight &&
++	    track_event_change(&c->times[BCH_TIME_blocked_journal_max_in_flight], true) &&
++	    trace_journal_entry_full_enabled()) {
++		struct printbuf buf = PRINTBUF;
++
++		bch2_printbuf_make_room(&buf, 4096);
++
++		spin_lock(&j->lock);
++		prt_printf(&buf, "seq %llu\n", journal_cur_seq(j));
++		bch2_journal_bufs_to_text(&buf, j);
++		spin_unlock(&j->lock);
+ 
+-	if (ret == JOURNAL_ERR_max_in_flight &&
+-	    track_event_change(&c->times[BCH_TIME_blocked_journal_max_in_flight], true)) {
++		trace_journal_entry_full(c, buf.buf);
++		printbuf_exit(&buf);
++		count_event(c, journal_entry_full);
++	}
+ 
++	if (ret == -BCH_ERR_journal_max_open &&
++	    track_event_change(&c->times[BCH_TIME_blocked_journal_max_open], true) &&
++	    trace_journal_entry_full_enabled()) {
+ 		struct printbuf buf = PRINTBUF;
++
++		bch2_printbuf_make_room(&buf, 4096);
++
++		spin_lock(&j->lock);
+ 		prt_printf(&buf, "seq %llu\n", journal_cur_seq(j));
+ 		bch2_journal_bufs_to_text(&buf, j);
++		spin_unlock(&j->lock);
++
+ 		trace_journal_entry_full(c, buf.buf);
+ 		printbuf_exit(&buf);
+ 		count_event(c, journal_entry_full);
+@@ -594,8 +664,8 @@ static int __journal_res_get(struct journal *j, struct journal_res *res,
+ 	 * Journal is full - can't rely on reclaim from work item due to
+ 	 * freezing:
+ 	 */
+-	if ((ret == JOURNAL_ERR_journal_full ||
+-	     ret == JOURNAL_ERR_journal_pin_full) &&
++	if ((ret == -BCH_ERR_journal_full ||
++	     ret == -BCH_ERR_journal_pin_full) &&
+ 	    !(flags & JOURNAL_RES_GET_NONBLOCK)) {
+ 		if (can_discard) {
+ 			bch2_journal_do_discards(j);
+@@ -608,9 +678,7 @@ static int __journal_res_get(struct journal *j, struct journal_res *res,
+ 		}
+ 	}
+ 
+-	return ret == JOURNAL_ERR_insufficient_devices
+-		? -BCH_ERR_erofs_journal_err
+-		: -BCH_ERR_journal_res_get_blocked;
++	return ret;
+ }
+ 
+ static unsigned max_dev_latency(struct bch_fs *c)
+@@ -640,7 +708,7 @@ int bch2_journal_res_get_slowpath(struct journal *j, struct journal_res *res,
+ 	int ret;
+ 
+ 	if (closure_wait_event_timeout(&j->async_wait,
+-		   (ret = __journal_res_get(j, res, flags)) != -BCH_ERR_journal_res_get_blocked ||
++		   !bch2_err_matches(ret = __journal_res_get(j, res, flags), BCH_ERR_operation_blocked) ||
+ 		   (flags & JOURNAL_RES_GET_NONBLOCK),
+ 		   HZ))
+ 		return ret;
+@@ -654,19 +722,19 @@ int bch2_journal_res_get_slowpath(struct journal *j, struct journal_res *res,
+ 	remaining_wait = max(0, remaining_wait - HZ);
+ 
+ 	if (closure_wait_event_timeout(&j->async_wait,
+-		   (ret = __journal_res_get(j, res, flags)) != -BCH_ERR_journal_res_get_blocked ||
++		   !bch2_err_matches(ret = __journal_res_get(j, res, flags), BCH_ERR_operation_blocked) ||
+ 		   (flags & JOURNAL_RES_GET_NONBLOCK),
+ 		   remaining_wait))
+ 		return ret;
+ 
++	bch_err(c, "Journal stuck? Waited for 10 seconds, err %s", bch2_err_str(ret));
+ 	struct printbuf buf = PRINTBUF;
+ 	bch2_journal_debug_to_text(&buf, j);
+-	bch_err(c, "Journal stuck? Waited for 10 seconds...\n%s",
+-		buf.buf);
++	bch2_print_string_as_lines(KERN_ERR, buf.buf);
+ 	printbuf_exit(&buf);
+ 
+ 	closure_wait_event(&j->async_wait,
+-		   (ret = __journal_res_get(j, res, flags)) != -BCH_ERR_journal_res_get_blocked ||
++		   !bch2_err_matches(ret = __journal_res_get(j, res, flags), BCH_ERR_operation_blocked) ||
+ 		   (flags & JOURNAL_RES_GET_NONBLOCK));
+ 	return ret;
+ }
+@@ -687,7 +755,6 @@ void bch2_journal_entry_res_resize(struct journal *j,
+ 		goto out;
+ 
+ 	j->cur_entry_u64s = max_t(int, 0, j->cur_entry_u64s - d);
+-	smp_mb();
+ 	state = READ_ONCE(j->reservations);
+ 
+ 	if (state.cur_entry_offset < JOURNAL_ENTRY_CLOSED_VAL &&
+@@ -907,7 +974,7 @@ int bch2_journal_meta(struct journal *j)
+ 	struct bch_fs *c = container_of(j, struct bch_fs, journal);
+ 
+ 	if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_journal))
+-		return -EROFS;
++		return -BCH_ERR_erofs_no_writes;
+ 
+ 	int ret = __bch2_journal_meta(j);
+ 	bch2_write_ref_put(c, BCH_WRITE_REF_journal);
+@@ -951,7 +1018,8 @@ static void __bch2_journal_block(struct journal *j)
+ 			new.cur_entry_offset = JOURNAL_ENTRY_BLOCKED_VAL;
+ 		} while (!atomic64_try_cmpxchg(&j->reservations.counter, &old.v, new.v));
+ 
+-		journal_cur_buf(j)->data->u64s = cpu_to_le32(old.cur_entry_offset);
++		if (old.cur_entry_offset < JOURNAL_ENTRY_BLOCKED_VAL)
++			journal_cur_buf(j)->data->u64s = cpu_to_le32(old.cur_entry_offset);
+ 	}
+ }
+ 
+@@ -992,7 +1060,7 @@ static struct journal_buf *__bch2_next_write_buffer_flush_journal_buf(struct jou
+ 				*blocked = true;
+ 			}
+ 
+-			ret = journal_state_count(s, idx) > open
++			ret = journal_state_count(s, idx & JOURNAL_STATE_BUF_MASK) > open
+ 				? ERR_PTR(-EAGAIN)
+ 				: buf;
+ 			break;
+@@ -1349,6 +1417,7 @@ int bch2_fs_journal_start(struct journal *j, u64 cur_seq)
+ 	j->replay_journal_seq_end = cur_seq;
+ 	j->last_seq_ondisk	= last_seq;
+ 	j->flushed_seq_ondisk	= cur_seq - 1;
++	j->seq_write_started	= cur_seq - 1;
+ 	j->seq_ondisk		= cur_seq - 1;
+ 	j->pin.front		= last_seq;
+ 	j->pin.back		= cur_seq;
+@@ -1389,8 +1458,7 @@ int bch2_fs_journal_start(struct journal *j, u64 cur_seq)
+ 	set_bit(JOURNAL_running, &j->flags);
+ 	j->last_flush_write = jiffies;
+ 
+-	j->reservations.idx = j->reservations.unwritten_idx = journal_cur_seq(j);
+-	j->reservations.unwritten_idx++;
++	j->reservations.idx = journal_cur_seq(j);
+ 
+ 	c->last_bucket_seq_cleanup = journal_cur_seq(j);
+ 
+@@ -1443,7 +1511,7 @@ int bch2_dev_journal_init(struct bch_dev *ca, struct bch_sb *sb)
+ 	unsigned nr_bvecs = DIV_ROUND_UP(JOURNAL_ENTRY_SIZE_MAX, PAGE_SIZE);
+ 
+ 	for (unsigned i = 0; i < ARRAY_SIZE(ja->bio); i++) {
+-		ja->bio[i] = kmalloc(struct_size(ja->bio[i], bio.bi_inline_vecs,
++		ja->bio[i] = kzalloc(struct_size(ja->bio[i], bio.bi_inline_vecs,
+ 				     nr_bvecs), GFP_KERNEL);
+ 		if (!ja->bio[i])
+ 			return -BCH_ERR_ENOMEM_dev_journal_init;
+@@ -1482,6 +1550,7 @@ void bch2_fs_journal_exit(struct journal *j)
+ 
+ 	for (unsigned i = 0; i < ARRAY_SIZE(j->buf); i++)
+ 		kvfree(j->buf[i].data);
++	kvfree(j->free_buf);
+ 	free_fifo(&j->pin);
+ }
+ 
+@@ -1508,13 +1577,13 @@ int bch2_fs_journal_init(struct journal *j)
+ 	if (!(init_fifo(&j->pin, JOURNAL_PIN, GFP_KERNEL)))
+ 		return -BCH_ERR_ENOMEM_journal_pin_fifo;
+ 
+-	for (unsigned i = 0; i < ARRAY_SIZE(j->buf); i++) {
+-		j->buf[i].buf_size = JOURNAL_ENTRY_SIZE_MIN;
+-		j->buf[i].data = kvmalloc(j->buf[i].buf_size, GFP_KERNEL);
+-		if (!j->buf[i].data)
+-			return -BCH_ERR_ENOMEM_journal_buf;
++	j->free_buf_size = j->buf_size_want = JOURNAL_ENTRY_SIZE_MIN;
++	j->free_buf = kvmalloc(j->free_buf_size, GFP_KERNEL);
++	if (!j->free_buf)
++		return -BCH_ERR_ENOMEM_journal_buf;
++
++	for (unsigned i = 0; i < ARRAY_SIZE(j->buf); i++)
+ 		j->buf[i].idx = i;
+-	}
+ 
+ 	j->pin.front = j->pin.back = 1;
+ 
+@@ -1564,6 +1633,7 @@ void __bch2_journal_debug_to_text(struct printbuf *out, struct journal *j)
+ 	prt_printf(out, "average write size:\t");
+ 	prt_human_readable_u64(out, nr_writes ? div64_u64(j->entry_bytes_written, nr_writes) : 0);
+ 	prt_newline(out);
++	prt_printf(out, "free buf:\t%u\n",			j->free_buf ? j->free_buf_size : 0);
+ 	prt_printf(out, "nr direct reclaim:\t%llu\n",		j->nr_direct_reclaim);
+ 	prt_printf(out, "nr background reclaim:\t%llu\n",	j->nr_background_reclaim);
+ 	prt_printf(out, "reclaim kicked:\t%u\n",		j->reclaim_kicked);
+@@ -1571,7 +1641,7 @@ void __bch2_journal_debug_to_text(struct printbuf *out, struct journal *j)
+ 	       ? jiffies_to_msecs(j->next_reclaim - jiffies) : 0);
+ 	prt_printf(out, "blocked:\t%u\n",			j->blocked);
+ 	prt_printf(out, "current entry sectors:\t%u\n",		j->cur_entry_sectors);
+-	prt_printf(out, "current entry error:\t%s\n",		bch2_journal_errors[j->cur_entry_error]);
++	prt_printf(out, "current entry error:\t%s\n",		bch2_err_str(j->cur_entry_error));
+ 	prt_printf(out, "current entry:\t");
+ 
+ 	switch (s.cur_entry_offset) {
+diff --git a/fs/bcachefs/journal.h b/fs/bcachefs/journal.h
+index 107f7f901cd9..47828771f9c2 100644
+--- a/fs/bcachefs/journal.h
++++ b/fs/bcachefs/journal.h
+@@ -121,11 +121,6 @@ static inline void journal_wake(struct journal *j)
+ 	closure_wake_up(&j->async_wait);
+ }
+ 
+-static inline struct journal_buf *journal_cur_buf(struct journal *j)
+-{
+-	return j->buf + j->reservations.idx;
+-}
+-
+ /* Sequence number of oldest dirty journal entry */
+ 
+ static inline u64 journal_last_seq(struct journal *j)
+@@ -143,6 +138,15 @@ static inline u64 journal_last_unwritten_seq(struct journal *j)
+ 	return j->seq_ondisk + 1;
+ }
+ 
++static inline struct journal_buf *journal_cur_buf(struct journal *j)
++{
++	unsigned idx = (journal_cur_seq(j) &
++			JOURNAL_BUF_MASK &
++			~JOURNAL_STATE_BUF_MASK) + j->reservations.idx;
++
++	return j->buf + idx;
++}
++
+ static inline int journal_state_count(union journal_res_state s, int idx)
+ {
+ 	switch (idx) {
+@@ -154,6 +158,15 @@ static inline int journal_state_count(union journal_res_state s, int idx)
+ 	BUG();
+ }
+ 
++static inline int journal_state_seq_count(struct journal *j,
++					  union journal_res_state s, u64 seq)
++{
++	if (journal_cur_seq(j) - seq < JOURNAL_STATE_BUF_NR)
++		return journal_state_count(s, seq & JOURNAL_STATE_BUF_MASK);
++	else
++		return 0;
++}
++
+ static inline void journal_state_inc(union journal_res_state *s)
+ {
+ 	s->buf0_count += s->idx == 0;
+@@ -193,7 +206,7 @@ bch2_journal_add_entry_noreservation(struct journal_buf *buf, size_t u64s)
+ static inline struct jset_entry *
+ journal_res_entry(struct journal *j, struct journal_res *res)
+ {
+-	return vstruct_idx(j->buf[res->idx].data, res->offset);
++	return vstruct_idx(j->buf[res->seq & JOURNAL_BUF_MASK].data, res->offset);
+ }
+ 
+ static inline unsigned journal_entry_init(struct jset_entry *entry, unsigned type,
+@@ -267,8 +280,9 @@ bool bch2_journal_entry_close(struct journal *);
+ void bch2_journal_do_writes(struct journal *);
+ void bch2_journal_buf_put_final(struct journal *, u64);
+ 
+-static inline void __bch2_journal_buf_put(struct journal *j, unsigned idx, u64 seq)
++static inline void __bch2_journal_buf_put(struct journal *j, u64 seq)
+ {
++	unsigned idx = seq & JOURNAL_STATE_BUF_MASK;
+ 	union journal_res_state s;
+ 
+ 	s = journal_state_buf_put(j, idx);
+@@ -276,8 +290,9 @@ static inline void __bch2_journal_buf_put(struct journal *j, unsigned idx, u64 s
+ 		bch2_journal_buf_put_final(j, seq);
+ }
+ 
+-static inline void bch2_journal_buf_put(struct journal *j, unsigned idx, u64 seq)
++static inline void bch2_journal_buf_put(struct journal *j, u64 seq)
+ {
++	unsigned idx = seq & JOURNAL_STATE_BUF_MASK;
+ 	union journal_res_state s;
+ 
+ 	s = journal_state_buf_put(j, idx);
+@@ -306,7 +321,7 @@ static inline void bch2_journal_res_put(struct journal *j,
+ 				       BCH_JSET_ENTRY_btree_keys,
+ 				       0, 0, 0);
+ 
+-	bch2_journal_buf_put(j, res->idx, res->seq);
++	bch2_journal_buf_put(j, res->seq);
+ 
+ 	res->ref = 0;
+ }
+@@ -335,8 +350,10 @@ static inline int journal_res_get_fast(struct journal *j,
+ 
+ 		/*
+ 		 * Check if there is still room in the current journal
+-		 * entry:
++		 * entry, smp_rmb() guarantees that reads from reservations.counter
++		 * occur before accessing cur_entry_u64s:
+ 		 */
++		smp_rmb();
+ 		if (new.cur_entry_offset + res->u64s > j->cur_entry_u64s)
+ 			return 0;
+ 
+@@ -361,9 +378,9 @@ static inline int journal_res_get_fast(struct journal *j,
+ 				       &old.v, new.v));
+ 
+ 	res->ref	= true;
+-	res->idx	= old.idx;
+ 	res->offset	= old.cur_entry_offset;
+-	res->seq	= le64_to_cpu(j->buf[old.idx].data->seq);
++	res->seq	= journal_cur_seq(j);
++	res->seq -= (res->seq - old.idx) & JOURNAL_STATE_BUF_MASK;
+ 	return 1;
+ }
+ 
+@@ -390,6 +407,7 @@ static inline int bch2_journal_res_get(struct journal *j, struct journal_res *re
+ 				    (flags & JOURNAL_RES_GET_NONBLOCK) != 0,
+ 				    NULL, _THIS_IP_);
+ 		EBUG_ON(!res->ref);
++		BUG_ON(!res->seq);
+ 	}
+ 	return 0;
+ }
+diff --git a/fs/bcachefs/journal_io.c b/fs/bcachefs/journal_io.c
+index 11c39e0c34f4..4ed6137f0439 100644
+--- a/fs/bcachefs/journal_io.c
++++ b/fs/bcachefs/journal_io.c
+@@ -1041,13 +1041,19 @@ static int journal_read_bucket(struct bch_dev *ca,
+ 			bio->bi_iter.bi_sector = offset;
+ 			bch2_bio_map(bio, buf->data, sectors_read << 9);
+ 
++			u64 submit_time = local_clock();
+ 			ret = submit_bio_wait(bio);
+ 			kfree(bio);
+ 
+-			if (bch2_dev_io_err_on(ret, ca, BCH_MEMBER_ERROR_read,
+-					       "journal read error: sector %llu",
+-					       offset) ||
+-			    bch2_meta_read_fault("journal")) {
++			if (!ret && bch2_meta_read_fault("journal"))
++				ret = -BCH_ERR_EIO_fault_injected;
++
++			bch2_account_io_completion(ca, BCH_MEMBER_ERROR_read,
++						   submit_time, !ret);
++
++			if (ret) {
++				bch_err_dev_ratelimited(ca,
++					"journal read error: sector %llu", offset);
+ 				/*
+ 				 * We don't error out of the recovery process
+ 				 * here, since the relevant journal entry may be
+@@ -1110,13 +1116,16 @@ static int journal_read_bucket(struct bch_dev *ca,
+ 		struct bch_csum csum;
+ 		csum_good = jset_csum_good(c, j, &csum);
+ 
+-		if (bch2_dev_io_err_on(!csum_good, ca, BCH_MEMBER_ERROR_checksum,
+-				       "%s",
+-				       (printbuf_reset(&err),
+-					prt_str(&err, "journal "),
+-					bch2_csum_err_msg(&err, csum_type, j->csum, csum),
+-					err.buf)))
++		bch2_account_io_completion(ca, BCH_MEMBER_ERROR_checksum, 0, csum_good);
++
++		if (!csum_good) {
++			bch_err_dev_ratelimited(ca, "%s",
++				(printbuf_reset(&err),
++				 prt_str(&err, "journal "),
++				 bch2_csum_err_msg(&err, csum_type, j->csum, csum),
++				 err.buf));
+ 			saw_bad = true;
++		}
+ 
+ 		ret = bch2_encrypt(c, JSET_CSUM_TYPE(j), journal_nonce(j),
+ 			     j->encrypted_start,
+@@ -1515,7 +1524,7 @@ static void __journal_write_alloc(struct journal *j,
+  * @j:		journal object
+  * @w:		journal buf (entry to be written)
+  *
+- * Returns: 0 on success, or -EROFS on failure
++ * Returns: 0 on success, or -BCH_ERR_insufficient_devices on failure
+  */
+ static int journal_write_alloc(struct journal *j, struct journal_buf *w)
+ {
+@@ -1600,18 +1609,12 @@ static void journal_buf_realloc(struct journal *j, struct journal_buf *buf)
+ 	kvfree(new_buf);
+ }
+ 
+-static inline struct journal_buf *journal_last_unwritten_buf(struct journal *j)
+-{
+-	return j->buf + (journal_last_unwritten_seq(j) & JOURNAL_BUF_MASK);
+-}
+-
+ static CLOSURE_CALLBACK(journal_write_done)
+ {
+ 	closure_type(w, struct journal_buf, io);
+ 	struct journal *j = container_of(w, struct journal, buf[w->idx]);
+ 	struct bch_fs *c = container_of(j, struct bch_fs, journal);
+ 	struct bch_replicas_padded replicas;
+-	union journal_res_state old, new;
+ 	u64 seq = le64_to_cpu(w->data->seq);
+ 	int err = 0;
+ 
+@@ -1621,12 +1624,11 @@ static CLOSURE_CALLBACK(journal_write_done)
+ 
+ 	if (!w->devs_written.nr) {
+ 		bch_err(c, "unable to write journal to sufficient devices");
+-		err = -EIO;
++		err = -BCH_ERR_journal_write_err;
+ 	} else {
+ 		bch2_devlist_to_replicas(&replicas.e, BCH_DATA_journal,
+ 					 w->devs_written);
+-		if (bch2_mark_replicas(c, &replicas.e))
+-			err = -EIO;
++		err = bch2_mark_replicas(c, &replicas.e);
+ 	}
+ 
+ 	if (err)
+@@ -1641,7 +1643,23 @@ static CLOSURE_CALLBACK(journal_write_done)
+ 		j->err_seq	= seq;
+ 	w->write_done = true;
+ 
++	if (!j->free_buf || j->free_buf_size < w->buf_size) {
++		swap(j->free_buf,	w->data);
++		swap(j->free_buf_size,	w->buf_size);
++	}
++
++	if (w->data) {
++		void *buf = w->data;
++		w->data = NULL;
++		w->buf_size = 0;
++
++		spin_unlock(&j->lock);
++		kvfree(buf);
++		spin_lock(&j->lock);
++	}
++
+ 	bool completed = false;
++	bool do_discards = false;
+ 
+ 	for (seq = journal_last_unwritten_seq(j);
+ 	     seq <= journal_cur_seq(j);
+@@ -1650,11 +1668,10 @@ static CLOSURE_CALLBACK(journal_write_done)
+ 		if (!w->write_done)
+ 			break;
+ 
+-		if (!j->err_seq && !JSET_NO_FLUSH(w->data)) {
++		if (!j->err_seq && !w->noflush) {
+ 			j->flushed_seq_ondisk = seq;
+ 			j->last_seq_ondisk = w->last_seq;
+ 
+-			bch2_do_discards(c);
+ 			closure_wake_up(&c->freelist_wait);
+ 			bch2_reset_alloc_cursors(c);
+ 		}
+@@ -1671,16 +1688,6 @@ static CLOSURE_CALLBACK(journal_write_done)
+ 		if (j->watermark != BCH_WATERMARK_stripe)
+ 			journal_reclaim_kick(&c->journal);
+ 
+-		old.v = atomic64_read(&j->reservations.counter);
+-		do {
+-			new.v = old.v;
+-			BUG_ON(journal_state_count(new, new.unwritten_idx));
+-			BUG_ON(new.unwritten_idx != (seq & JOURNAL_BUF_MASK));
+-
+-			new.unwritten_idx++;
+-		} while (!atomic64_try_cmpxchg(&j->reservations.counter,
+-					       &old.v, new.v));
+-
+ 		closure_wake_up(&w->wait);
+ 		completed = true;
+ 	}
+@@ -1695,7 +1702,7 @@ static CLOSURE_CALLBACK(journal_write_done)
+ 	}
+ 
+ 	if (journal_last_unwritten_seq(j) == journal_cur_seq(j) &&
+-		   new.cur_entry_offset < JOURNAL_ENTRY_CLOSED_VAL) {
++	    j->reservations.cur_entry_offset < JOURNAL_ENTRY_CLOSED_VAL) {
+ 		struct journal_buf *buf = journal_cur_buf(j);
+ 		long delta = buf->expires - jiffies;
+ 
+@@ -1715,6 +1722,9 @@ static CLOSURE_CALLBACK(journal_write_done)
+ 	 */
+ 	bch2_journal_do_writes(j);
+ 	spin_unlock(&j->lock);
++
++	if (do_discards)
++		bch2_do_discards(c);
+ }
+ 
+ static void journal_write_endio(struct bio *bio)
+@@ -1724,13 +1734,16 @@ static void journal_write_endio(struct bio *bio)
+ 	struct journal *j = &ca->fs->journal;
+ 	struct journal_buf *w = j->buf + jbio->buf_idx;
+ 
+-	if (bch2_dev_io_err_on(bio->bi_status, ca, BCH_MEMBER_ERROR_write,
++	bch2_account_io_completion(ca, BCH_MEMBER_ERROR_write,
++				   jbio->submit_time, !bio->bi_status);
++
++	if (bio->bi_status) {
++		bch_err_dev_ratelimited(ca,
+ 			       "error writing journal entry %llu: %s",
+ 			       le64_to_cpu(w->data->seq),
+-			       bch2_blk_status_to_str(bio->bi_status)) ||
+-	    bch2_meta_write_fault("journal")) {
+-		unsigned long flags;
++			       bch2_blk_status_to_str(bio->bi_status));
+ 
++		unsigned long flags;
+ 		spin_lock_irqsave(&j->err_lock, flags);
+ 		bch2_dev_list_drop_dev(&w->devs_written, ca->dev_idx);
+ 		spin_unlock_irqrestore(&j->err_lock, flags);
+@@ -1759,7 +1772,11 @@ static CLOSURE_CALLBACK(journal_write_submit)
+ 			     sectors);
+ 
+ 		struct journal_device *ja = &ca->journal;
+-		struct bio *bio = &ja->bio[w->idx]->bio;
++		struct journal_bio *jbio = ja->bio[w->idx];
++		struct bio *bio = &jbio->bio;
++
++		jbio->submit_time	= local_clock();
++
+ 		bio_reset(bio, ca->disk_sb.bdev, REQ_OP_WRITE|REQ_SYNC|REQ_META);
+ 		bio->bi_iter.bi_sector	= ptr->offset;
+ 		bio->bi_end_io		= journal_write_endio;
+@@ -1791,6 +1808,10 @@ static CLOSURE_CALLBACK(journal_write_preflush)
+ 	struct journal *j = container_of(w, struct journal, buf[w->idx]);
+ 	struct bch_fs *c = container_of(j, struct bch_fs, journal);
+ 
++	/*
++	 * Wait for previous journal writes to comelete; they won't necessarily
++	 * be flushed if they're still in flight
++	 */
+ 	if (j->seq_ondisk + 1 != le64_to_cpu(w->data->seq)) {
+ 		spin_lock(&j->lock);
+ 		if (j->seq_ondisk + 1 != le64_to_cpu(w->data->seq)) {
+@@ -1984,7 +2005,7 @@ static int bch2_journal_write_pick_flush(struct journal *j, struct journal_buf *
+ 	 * write anything at all.
+ 	 */
+ 	if (error && test_bit(JOURNAL_need_flush_write, &j->flags))
+-		return -EIO;
++		return error;
+ 
+ 	if (error ||
+ 	    w->noflush ||
+diff --git a/fs/bcachefs/journal_reclaim.c b/fs/bcachefs/journal_reclaim.c
+index d373cd181a7f..5d1547aa118a 100644
+--- a/fs/bcachefs/journal_reclaim.c
++++ b/fs/bcachefs/journal_reclaim.c
+@@ -226,7 +226,7 @@ void bch2_journal_space_available(struct journal *j)
+ 
+ 		bch_err(c, "%s", buf.buf);
+ 		printbuf_exit(&buf);
+-		ret = JOURNAL_ERR_insufficient_devices;
++		ret = -BCH_ERR_insufficient_journal_devices;
+ 		goto out;
+ 	}
+ 
+@@ -240,7 +240,7 @@ void bch2_journal_space_available(struct journal *j)
+ 	total		= j->space[journal_space_total].total;
+ 
+ 	if (!j->space[journal_space_discarded].next_entry)
+-		ret = JOURNAL_ERR_journal_full;
++		ret = -BCH_ERR_journal_full;
+ 
+ 	if ((j->space[journal_space_clean_ondisk].next_entry <
+ 	     j->space[journal_space_clean_ondisk].total) &&
+@@ -645,7 +645,6 @@ static u64 journal_seq_to_flush(struct journal *j)
+  * @j:		journal object
+  * @direct:	direct or background reclaim?
+  * @kicked:	requested to run since we last ran?
+- * Returns:	0 on success, or -EIO if the journal has been shutdown
+  *
+  * Background journal reclaim writes out btree nodes. It should be run
+  * early enough so that we never completely run out of journal buckets.
+@@ -685,10 +684,9 @@ static int __bch2_journal_reclaim(struct journal *j, bool direct, bool kicked)
+ 		if (kthread && kthread_should_stop())
+ 			break;
+ 
+-		if (bch2_journal_error(j)) {
+-			ret = -EIO;
++		ret = bch2_journal_error(j);
++		if (ret)
+ 			break;
+-		}
+ 
+ 		bch2_journal_do_discards(j);
+ 
+diff --git a/fs/bcachefs/journal_seq_blacklist.c b/fs/bcachefs/journal_seq_blacklist.c
+index 1f25c111c54c..e463d2d95359 100644
+--- a/fs/bcachefs/journal_seq_blacklist.c
++++ b/fs/bcachefs/journal_seq_blacklist.c
+@@ -231,15 +231,14 @@ bool bch2_blacklist_entries_gc(struct bch_fs *c)
+ 	struct journal_seq_blacklist_table *t = c->journal_seq_blacklist_table;
+ 	BUG_ON(nr != t->nr);
+ 
+-	unsigned i;
+-	for (src = bl->start, i = t->nr == 0 ? 0 : eytzinger0_first(t->nr);
+-	     src < bl->start + nr;
+-	     src++, i = eytzinger0_next(i, nr)) {
++	src = bl->start;
++	eytzinger0_for_each(i, nr) {
+ 		BUG_ON(t->entries[i].start	!= le64_to_cpu(src->start));
+ 		BUG_ON(t->entries[i].end	!= le64_to_cpu(src->end));
+ 
+ 		if (t->entries[i].dirty || t->entries[i].end >= c->journal.oldest_seq_found_ondisk)
+ 			*dst++ = *src;
++		src++;
+ 	}
+ 
+ 	unsigned new_nr = dst - bl->start;
+diff --git a/fs/bcachefs/journal_types.h b/fs/bcachefs/journal_types.h
+index 1ef3a28ed6ab..8e0eba776b9d 100644
+--- a/fs/bcachefs/journal_types.h
++++ b/fs/bcachefs/journal_types.h
+@@ -12,7 +12,11 @@
+ /* btree write buffer steals 8 bits for its own purposes: */
+ #define JOURNAL_SEQ_MAX		((1ULL << 56) - 1)
+ 
+-#define JOURNAL_BUF_BITS	2
++#define JOURNAL_STATE_BUF_BITS	2
++#define JOURNAL_STATE_BUF_NR	(1U << JOURNAL_STATE_BUF_BITS)
++#define JOURNAL_STATE_BUF_MASK	(JOURNAL_STATE_BUF_NR - 1)
++
++#define JOURNAL_BUF_BITS	4
+ #define JOURNAL_BUF_NR		(1U << JOURNAL_BUF_BITS)
+ #define JOURNAL_BUF_MASK	(JOURNAL_BUF_NR - 1)
+ 
+@@ -82,7 +86,6 @@ struct journal_entry_pin {
+ 
+ struct journal_res {
+ 	bool			ref;
+-	u8			idx;
+ 	u16			u64s;
+ 	u32			offset;
+ 	u64			seq;
+@@ -98,9 +101,8 @@ union journal_res_state {
+ 	};
+ 
+ 	struct {
+-		u64		cur_entry_offset:20,
++		u64		cur_entry_offset:22,
+ 				idx:2,
+-				unwritten_idx:2,
+ 				buf0_count:10,
+ 				buf1_count:10,
+ 				buf2_count:10,
+@@ -110,13 +112,13 @@ union journal_res_state {
+ 
+ /* bytes: */
+ #define JOURNAL_ENTRY_SIZE_MIN		(64U << 10) /* 64k */
+-#define JOURNAL_ENTRY_SIZE_MAX		(4U  << 20) /* 4M */
++#define JOURNAL_ENTRY_SIZE_MAX		(4U  << 22) /* 16M */
+ 
+ /*
+  * We stash some journal state as sentinal values in cur_entry_offset:
+  * note - cur_entry_offset is in units of u64s
+  */
+-#define JOURNAL_ENTRY_OFFSET_MAX	((1U << 20) - 1)
++#define JOURNAL_ENTRY_OFFSET_MAX	((1U << 22) - 1)
+ 
+ #define JOURNAL_ENTRY_BLOCKED_VAL	(JOURNAL_ENTRY_OFFSET_MAX - 2)
+ #define JOURNAL_ENTRY_CLOSED_VAL	(JOURNAL_ENTRY_OFFSET_MAX - 1)
+@@ -149,28 +151,12 @@ enum journal_flags {
+ #undef x
+ };
+ 
+-/* Reasons we may fail to get a journal reservation: */
+-#define JOURNAL_ERRORS()		\
+-	x(ok)				\
+-	x(retry)			\
+-	x(blocked)			\
+-	x(max_in_flight)		\
+-	x(journal_full)			\
+-	x(journal_pin_full)		\
+-	x(journal_stuck)		\
+-	x(insufficient_devices)
+-
+-enum journal_errors {
+-#define x(n)	JOURNAL_ERR_##n,
+-	JOURNAL_ERRORS()
+-#undef x
+-};
+-
+ typedef DARRAY(u64)		darray_u64;
+ 
+ struct journal_bio {
+ 	struct bch_dev		*ca;
+ 	unsigned		buf_idx;
++	u64			submit_time;
+ 
+ 	struct bio		bio;
+ };
+@@ -199,7 +185,7 @@ struct journal {
+ 	 * 0, or -ENOSPC if waiting on journal reclaim, or -EROFS if
+ 	 * insufficient devices:
+ 	 */
+-	enum journal_errors	cur_entry_error;
++	int			cur_entry_error;
+ 	unsigned		cur_entry_offset_if_blocked;
+ 
+ 	unsigned		buf_size_want;
+@@ -220,6 +206,8 @@ struct journal {
+ 	 * other is possibly being written out.
+ 	 */
+ 	struct journal_buf	buf[JOURNAL_BUF_NR];
++	void			*free_buf;
++	unsigned		free_buf_size;
+ 
+ 	spinlock_t		lock;
+ 
+@@ -237,6 +225,7 @@ struct journal {
+ 	/* Sequence number of most recent journal entry (last entry in @pin) */
+ 	atomic64_t		seq;
+ 
++	u64			seq_write_started;
+ 	/* seq, last_seq from the most recent journal entry successfully written */
+ 	u64			seq_ondisk;
+ 	u64			flushed_seq_ondisk;
+diff --git a/fs/bcachefs/lru.c b/fs/bcachefs/lru.c
+index ce794d55818f..a299d9ec8ee4 100644
+--- a/fs/bcachefs/lru.c
++++ b/fs/bcachefs/lru.c
+@@ -6,6 +6,7 @@
+ #include "btree_iter.h"
+ #include "btree_update.h"
+ #include "btree_write_buffer.h"
++#include "ec.h"
+ #include "error.h"
+ #include "lru.h"
+ #include "recovery.h"
+@@ -59,9 +60,9 @@ int bch2_lru_set(struct btree_trans *trans, u16 lru_id, u64 dev_bucket, u64 time
+ 	return __bch2_lru_set(trans, lru_id, dev_bucket, time, KEY_TYPE_set);
+ }
+ 
+-int bch2_lru_change(struct btree_trans *trans,
+-		    u16 lru_id, u64 dev_bucket,
+-		    u64 old_time, u64 new_time)
++int __bch2_lru_change(struct btree_trans *trans,
++		      u16 lru_id, u64 dev_bucket,
++		      u64 old_time, u64 new_time)
+ {
+ 	if (old_time == new_time)
+ 		return 0;
+@@ -78,7 +79,9 @@ static const char * const bch2_lru_types[] = {
+ };
+ 
+ int bch2_lru_check_set(struct btree_trans *trans,
+-		       u16 lru_id, u64 time,
++		       u16 lru_id,
++		       u64 dev_bucket,
++		       u64 time,
+ 		       struct bkey_s_c referring_k,
+ 		       struct bkey_buf *last_flushed)
+ {
+@@ -87,9 +90,7 @@ int bch2_lru_check_set(struct btree_trans *trans,
+ 	struct btree_iter lru_iter;
+ 	struct bkey_s_c lru_k =
+ 		bch2_bkey_get_iter(trans, &lru_iter, BTREE_ID_lru,
+-				   lru_pos(lru_id,
+-					   bucket_to_u64(referring_k.k->p),
+-					   time), 0);
++				   lru_pos(lru_id, dev_bucket, time), 0);
+ 	int ret = bkey_err(lru_k);
+ 	if (ret)
+ 		return ret;
+@@ -104,7 +105,7 @@ int bch2_lru_check_set(struct btree_trans *trans,
+ 			     "  %s",
+ 			     bch2_lru_types[lru_type(lru_k)],
+ 			     (bch2_bkey_val_to_text(&buf, c, referring_k), buf.buf))) {
+-			ret = bch2_lru_set(trans, lru_id, bucket_to_u64(referring_k.k->p), time);
++			ret = bch2_lru_set(trans, lru_id, dev_bucket, time);
+ 			if (ret)
+ 				goto err;
+ 		}
+@@ -116,49 +117,73 @@ int bch2_lru_check_set(struct btree_trans *trans,
+ 	return ret;
+ }
+ 
++static struct bbpos lru_pos_to_bp(struct bkey_s_c lru_k)
++{
++	enum bch_lru_type type = lru_type(lru_k);
++
++	switch (type) {
++	case BCH_LRU_read:
++	case BCH_LRU_fragmentation:
++		return BBPOS(BTREE_ID_alloc, u64_to_bucket(lru_k.k->p.offset));
++	case BCH_LRU_stripes:
++		return BBPOS(BTREE_ID_stripes, POS(0, lru_k.k->p.offset));
++	default:
++		BUG();
++	}
++}
++
++static u64 bkey_lru_type_idx(struct bch_fs *c,
++			     enum bch_lru_type type,
++			     struct bkey_s_c k)
++{
++	struct bch_alloc_v4 a_convert;
++	const struct bch_alloc_v4 *a;
++
++	switch (type) {
++	case BCH_LRU_read:
++		a = bch2_alloc_to_v4(k, &a_convert);
++		return alloc_lru_idx_read(*a);
++	case BCH_LRU_fragmentation: {
++		a = bch2_alloc_to_v4(k, &a_convert);
++
++		rcu_read_lock();
++		struct bch_dev *ca = bch2_dev_rcu_noerror(c, k.k->p.inode);
++		u64 idx = ca
++			? alloc_lru_idx_fragmentation(*a, ca)
++			: 0;
++		rcu_read_unlock();
++		return idx;
++	}
++	case BCH_LRU_stripes:
++		return k.k->type == KEY_TYPE_stripe
++			? stripe_lru_pos(bkey_s_c_to_stripe(k).v)
++			: 0;
++	default:
++		BUG();
++	}
++}
++
+ static int bch2_check_lru_key(struct btree_trans *trans,
+ 			      struct btree_iter *lru_iter,
+ 			      struct bkey_s_c lru_k,
+ 			      struct bkey_buf *last_flushed)
+ {
+ 	struct bch_fs *c = trans->c;
+-	struct btree_iter iter;
+-	struct bkey_s_c k;
+-	struct bch_alloc_v4 a_convert;
+-	const struct bch_alloc_v4 *a;
+ 	struct printbuf buf1 = PRINTBUF;
+ 	struct printbuf buf2 = PRINTBUF;
+-	enum bch_lru_type type = lru_type(lru_k);
+-	struct bpos alloc_pos = u64_to_bucket(lru_k.k->p.offset);
+-	u64 idx;
+-	int ret;
+-
+-	struct bch_dev *ca = bch2_dev_bucket_tryget_noerror(c, alloc_pos);
+ 
+-	if (fsck_err_on(!ca,
+-			trans, lru_entry_to_invalid_bucket,
+-			"lru key points to nonexistent device:bucket %llu:%llu",
+-			alloc_pos.inode, alloc_pos.offset))
+-		return bch2_btree_bit_mod_buffered(trans, BTREE_ID_lru, lru_iter->pos, false);
++	struct bbpos bp = lru_pos_to_bp(lru_k);
+ 
+-	k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_alloc, alloc_pos, 0);
+-	ret = bkey_err(k);
++	struct btree_iter iter;
++	struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter, bp.btree, bp.pos, 0);
++	int ret = bkey_err(k);
+ 	if (ret)
+ 		goto err;
+ 
+-	a = bch2_alloc_to_v4(k, &a_convert);
+-
+-	switch (type) {
+-	case BCH_LRU_read:
+-		idx = alloc_lru_idx_read(*a);
+-		break;
+-	case BCH_LRU_fragmentation:
+-		idx = alloc_lru_idx_fragmentation(*a, ca);
+-		break;
+-	}
++	enum bch_lru_type type = lru_type(lru_k);
++	u64 idx = bkey_lru_type_idx(c, type, k);
+ 
+-	if (lru_k.k->type != KEY_TYPE_set ||
+-	    lru_pos_time(lru_k.k->p) != idx) {
++	if (lru_pos_time(lru_k.k->p) != idx) {
+ 		ret = bch2_btree_write_buffer_maybe_flush(trans, lru_k, last_flushed);
+ 		if (ret)
+ 			goto err;
+@@ -176,7 +201,6 @@ static int bch2_check_lru_key(struct btree_trans *trans,
+ err:
+ fsck_err:
+ 	bch2_trans_iter_exit(trans, &iter);
+-	bch2_dev_put(ca);
+ 	printbuf_exit(&buf2);
+ 	printbuf_exit(&buf1);
+ 	return ret;
+diff --git a/fs/bcachefs/lru.h b/fs/bcachefs/lru.h
+index f31a6cf1514c..8abd0aa2083a 100644
+--- a/fs/bcachefs/lru.h
++++ b/fs/bcachefs/lru.h
+@@ -28,9 +28,14 @@ static inline enum bch_lru_type lru_type(struct bkey_s_c l)
+ {
+ 	u16 lru_id = l.k->p.inode >> 48;
+ 
+-	if (lru_id == BCH_LRU_FRAGMENTATION_START)
++	switch (lru_id) {
++	case BCH_LRU_BUCKET_FRAGMENTATION:
+ 		return BCH_LRU_fragmentation;
+-	return BCH_LRU_read;
++	case BCH_LRU_STRIPE_FRAGMENTATION:
++		return BCH_LRU_stripes;
++	default:
++		return BCH_LRU_read;
++	}
+ }
+ 
+ int bch2_lru_validate(struct bch_fs *, struct bkey_s_c, struct bkey_validate_context);
+@@ -46,10 +51,19 @@ void bch2_lru_pos_to_text(struct printbuf *, struct bpos);
+ 
+ int bch2_lru_del(struct btree_trans *, u16, u64, u64);
+ int bch2_lru_set(struct btree_trans *, u16, u64, u64);
+-int bch2_lru_change(struct btree_trans *, u16, u64, u64, u64);
++int __bch2_lru_change(struct btree_trans *, u16, u64, u64, u64);
++
++static inline int bch2_lru_change(struct btree_trans *trans,
++		      u16 lru_id, u64 dev_bucket,
++		      u64 old_time, u64 new_time)
++{
++	return old_time != new_time
++		? __bch2_lru_change(trans, lru_id, dev_bucket, old_time, new_time)
++		: 0;
++}
+ 
+ struct bkey_buf;
+-int bch2_lru_check_set(struct btree_trans *, u16, u64, struct bkey_s_c, struct bkey_buf *);
++int bch2_lru_check_set(struct btree_trans *, u16, u64, u64, struct bkey_s_c, struct bkey_buf *);
+ 
+ int bch2_check_lrus(struct bch_fs *);
+ 
+diff --git a/fs/bcachefs/lru_format.h b/fs/bcachefs/lru_format.h
+index f372cb3b8cda..b7392ad8e41f 100644
+--- a/fs/bcachefs/lru_format.h
++++ b/fs/bcachefs/lru_format.h
+@@ -9,7 +9,8 @@ struct bch_lru {
+ 
+ #define BCH_LRU_TYPES()		\
+ 	x(read)			\
+-	x(fragmentation)
++	x(fragmentation)	\
++	x(stripes)
+ 
+ enum bch_lru_type {
+ #define x(n) BCH_LRU_##n,
+@@ -17,7 +18,8 @@ enum bch_lru_type {
+ #undef x
+ };
+ 
+-#define BCH_LRU_FRAGMENTATION_START	((1U << 16) - 1)
++#define BCH_LRU_BUCKET_FRAGMENTATION	((1U << 16) - 1)
++#define BCH_LRU_STRIPE_FRAGMENTATION	((1U << 16) - 2)
+ 
+ #define LRU_TIME_BITS			48
+ #define LRU_TIME_MAX			((1ULL << LRU_TIME_BITS) - 1)
+diff --git a/fs/bcachefs/migrate.c b/fs/bcachefs/migrate.c
+index ddc187fb693d..57ad662871ba 100644
+--- a/fs/bcachefs/migrate.c
++++ b/fs/bcachefs/migrate.c
+@@ -15,6 +15,7 @@
+ #include "keylist.h"
+ #include "migrate.h"
+ #include "move.h"
++#include "progress.h"
+ #include "replicas.h"
+ #include "super-io.h"
+ 
+@@ -76,7 +77,9 @@ static int bch2_dev_usrdata_drop_key(struct btree_trans *trans,
+ 	return 0;
+ }
+ 
+-static int bch2_dev_usrdata_drop(struct bch_fs *c, unsigned dev_idx, int flags)
++static int bch2_dev_usrdata_drop(struct bch_fs *c,
++				 struct progress_indicator_state *progress,
++				 unsigned dev_idx, int flags)
+ {
+ 	struct btree_trans *trans = bch2_trans_get(c);
+ 	enum btree_id id;
+@@ -88,8 +91,10 @@ static int bch2_dev_usrdata_drop(struct bch_fs *c, unsigned dev_idx, int flags)
+ 
+ 		ret = for_each_btree_key_commit(trans, iter, id, POS_MIN,
+ 				BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k,
+-				NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
+-			bch2_dev_usrdata_drop_key(trans, &iter, k, dev_idx, flags));
++				NULL, NULL, BCH_TRANS_COMMIT_no_enospc, ({
++			bch2_progress_update_iter(trans, progress, &iter, "dropping user data");
++			bch2_dev_usrdata_drop_key(trans, &iter, k, dev_idx, flags);
++		}));
+ 		if (ret)
+ 			break;
+ 	}
+@@ -99,7 +104,9 @@ static int bch2_dev_usrdata_drop(struct bch_fs *c, unsigned dev_idx, int flags)
+ 	return ret;
+ }
+ 
+-static int bch2_dev_metadata_drop(struct bch_fs *c, unsigned dev_idx, int flags)
++static int bch2_dev_metadata_drop(struct bch_fs *c,
++				  struct progress_indicator_state *progress,
++				  unsigned dev_idx, int flags)
+ {
+ 	struct btree_trans *trans;
+ 	struct btree_iter iter;
+@@ -125,6 +132,8 @@ static int bch2_dev_metadata_drop(struct bch_fs *c, unsigned dev_idx, int flags)
+ 		while (bch2_trans_begin(trans),
+ 		       (b = bch2_btree_iter_peek_node(&iter)) &&
+ 		       !(ret = PTR_ERR_OR_ZERO(b))) {
++			bch2_progress_update_iter(trans, progress, &iter, "dropping metadata");
++
+ 			if (!bch2_bkey_has_device_c(bkey_i_to_s_c(&b->key), dev_idx))
+ 				goto next;
+ 
+@@ -169,6 +178,11 @@ static int bch2_dev_metadata_drop(struct bch_fs *c, unsigned dev_idx, int flags)
+ 
+ int bch2_dev_data_drop(struct bch_fs *c, unsigned dev_idx, int flags)
+ {
+-	return bch2_dev_usrdata_drop(c, dev_idx, flags) ?:
+-		bch2_dev_metadata_drop(c, dev_idx, flags);
++	struct progress_indicator_state progress;
++	bch2_progress_init(&progress, c,
++			   BIT_ULL(BTREE_ID_extents)|
++			   BIT_ULL(BTREE_ID_reflink));
++
++	return bch2_dev_usrdata_drop(c, &progress, dev_idx, flags) ?:
++		bch2_dev_metadata_drop(c, &progress, dev_idx, flags);
+ }
+diff --git a/fs/bcachefs/move.c b/fs/bcachefs/move.c
+index 160b4374160a..66d1c055a2e3 100644
+--- a/fs/bcachefs/move.c
++++ b/fs/bcachefs/move.c
+@@ -38,28 +38,28 @@ const char * const bch2_data_ops_strs[] = {
+ 	NULL
+ };
+ 
+-static void trace_move_extent2(struct bch_fs *c, struct bkey_s_c k,
++static void trace_io_move2(struct bch_fs *c, struct bkey_s_c k,
+ 			       struct bch_io_opts *io_opts,
+ 			       struct data_update_opts *data_opts)
+ {
+-	if (trace_move_extent_enabled()) {
++	if (trace_io_move_enabled()) {
+ 		struct printbuf buf = PRINTBUF;
+ 
+ 		bch2_bkey_val_to_text(&buf, c, k);
+ 		prt_newline(&buf);
+ 		bch2_data_update_opts_to_text(&buf, c, io_opts, data_opts);
+-		trace_move_extent(c, buf.buf);
++		trace_io_move(c, buf.buf);
+ 		printbuf_exit(&buf);
+ 	}
+ }
+ 
+-static void trace_move_extent_read2(struct bch_fs *c, struct bkey_s_c k)
++static void trace_io_move_read2(struct bch_fs *c, struct bkey_s_c k)
+ {
+-	if (trace_move_extent_read_enabled()) {
++	if (trace_io_move_read_enabled()) {
+ 		struct printbuf buf = PRINTBUF;
+ 
+ 		bch2_bkey_val_to_text(&buf, c, k);
+-		trace_move_extent_read(c, buf.buf);
++		trace_io_move_read(c, buf.buf);
+ 		printbuf_exit(&buf);
+ 	}
+ }
+@@ -74,11 +74,7 @@ struct moving_io {
+ 	unsigned			read_sectors;
+ 	unsigned			write_sectors;
+ 
+-	struct bch_read_bio		rbio;
+-
+ 	struct data_update		write;
+-	/* Must be last since it is variable size */
+-	struct bio_vec			bi_inline_vecs[];
+ };
+ 
+ static void move_free(struct moving_io *io)
+@@ -88,43 +84,86 @@ static void move_free(struct moving_io *io)
+ 	if (io->b)
+ 		atomic_dec(&io->b->count);
+ 
+-	bch2_data_update_exit(&io->write);
+-
+ 	mutex_lock(&ctxt->lock);
+ 	list_del(&io->io_list);
+ 	wake_up(&ctxt->wait);
+ 	mutex_unlock(&ctxt->lock);
+ 
++	if (!io->write.data_opts.scrub) {
++		bch2_data_update_exit(&io->write);
++	} else {
++		bch2_bio_free_pages_pool(io->write.op.c, &io->write.op.wbio.bio);
++		kfree(io->write.bvecs);
++	}
+ 	kfree(io);
+ }
+ 
+ static void move_write_done(struct bch_write_op *op)
+ {
+ 	struct moving_io *io = container_of(op, struct moving_io, write.op);
++	struct bch_fs *c = op->c;
+ 	struct moving_context *ctxt = io->write.ctxt;
+ 
+-	if (io->write.op.error)
++	if (op->error) {
++		if (trace_io_move_write_fail_enabled()) {
++			struct printbuf buf = PRINTBUF;
++
++			bch2_write_op_to_text(&buf, op);
++			prt_printf(&buf, "ret\t%s\n", bch2_err_str(op->error));
++			trace_io_move_write_fail(c, buf.buf);
++			printbuf_exit(&buf);
++		}
++		this_cpu_inc(c->counters[BCH_COUNTER_io_move_write_fail]);
++
+ 		ctxt->write_error = true;
++	}
+ 
+-	atomic_sub(io->write_sectors, &io->write.ctxt->write_sectors);
+-	atomic_dec(&io->write.ctxt->write_ios);
++	atomic_sub(io->write_sectors, &ctxt->write_sectors);
++	atomic_dec(&ctxt->write_ios);
+ 	move_free(io);
+ 	closure_put(&ctxt->cl);
+ }
+ 
+ static void move_write(struct moving_io *io)
+ {
+-	if (unlikely(io->rbio.bio.bi_status || io->rbio.hole)) {
++	struct bch_fs *c = io->write.op.c;
++	struct moving_context *ctxt = io->write.ctxt;
++	struct bch_read_bio *rbio = &io->write.rbio;
++
++	if (ctxt->stats) {
++		if (rbio->bio.bi_status)
++			atomic64_add(io->write.rbio.bvec_iter.bi_size >> 9,
++				     &ctxt->stats->sectors_error_uncorrected);
++		else if (rbio->saw_error)
++			atomic64_add(io->write.rbio.bvec_iter.bi_size >> 9,
++				     &ctxt->stats->sectors_error_corrected);
++	}
++
++	/*
++	 * If the extent has been bitrotted, we're going to have to give it a
++	 * new checksum in order to move it - but the poison bit will ensure
++	 * that userspace still gets the appropriate error.
++	 */
++	if (unlikely(rbio->ret == -BCH_ERR_data_read_csum_err &&
++		     (bch2_bkey_extent_flags(bkey_i_to_s_c(io->write.k.k)) & BIT_ULL(BCH_EXTENT_FLAG_poisoned)))) {
++		struct bch_extent_crc_unpacked crc = rbio->pick.crc;
++		struct nonce nonce = extent_nonce(rbio->version, crc);
++
++		rbio->pick.crc.csum	= bch2_checksum_bio(c, rbio->pick.crc.csum_type,
++							    nonce, &rbio->bio);
++		rbio->ret		= 0;
++	}
++
++	if (unlikely(rbio->ret || io->write.data_opts.scrub)) {
+ 		move_free(io);
+ 		return;
+ 	}
+ 
+-	if (trace_move_extent_write_enabled()) {
+-		struct bch_fs *c = io->write.op.c;
++	if (trace_io_move_write_enabled()) {
+ 		struct printbuf buf = PRINTBUF;
+ 
+ 		bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(io->write.k.k));
+-		trace_move_extent_write(c, buf.buf);
++		trace_io_move_write(c, buf.buf);
+ 		printbuf_exit(&buf);
+ 	}
+ 
+@@ -132,7 +171,7 @@ static void move_write(struct moving_io *io)
+ 	atomic_add(io->write_sectors, &io->write.ctxt->write_sectors);
+ 	atomic_inc(&io->write.ctxt->write_ios);
+ 
+-	bch2_data_update_read_done(&io->write, io->rbio.pick.crc);
++	bch2_data_update_read_done(&io->write);
+ }
+ 
+ struct moving_io *bch2_moving_ctxt_next_pending_write(struct moving_context *ctxt)
+@@ -145,7 +184,7 @@ struct moving_io *bch2_moving_ctxt_next_pending_write(struct moving_context *ctx
+ 
+ static void move_read_endio(struct bio *bio)
+ {
+-	struct moving_io *io = container_of(bio, struct moving_io, rbio.bio);
++	struct moving_io *io = container_of(bio, struct moving_io, write.rbio.bio);
+ 	struct moving_context *ctxt = io->write.ctxt;
+ 
+ 	atomic_sub(io->read_sectors, &ctxt->read_sectors);
+@@ -258,14 +297,10 @@ int bch2_move_extent(struct moving_context *ctxt,
+ {
+ 	struct btree_trans *trans = ctxt->trans;
+ 	struct bch_fs *c = trans->c;
+-	struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
+-	struct moving_io *io;
+-	const union bch_extent_entry *entry;
+-	struct extent_ptr_decoded p;
+-	unsigned sectors = k.k->size, pages;
+ 	int ret = -ENOMEM;
+ 
+-	trace_move_extent2(c, k, &io_opts, &data_opts);
++	trace_io_move2(c, k, &io_opts, &data_opts);
++	this_cpu_add(c->counters[BCH_COUNTER_io_move], k.k->size);
+ 
+ 	if (ctxt->stats)
+ 		ctxt->stats->pos = BBPOS(iter->btree_id, iter->pos);
+@@ -273,7 +308,8 @@ int bch2_move_extent(struct moving_context *ctxt,
+ 	bch2_data_update_opts_normalize(k, &data_opts);
+ 
+ 	if (!data_opts.rewrite_ptrs &&
+-	    !data_opts.extra_replicas) {
++	    !data_opts.extra_replicas &&
++	    !data_opts.scrub) {
+ 		if (data_opts.kill_ptrs)
+ 			return bch2_extent_drop_ptrs(trans, iter, k, &io_opts, &data_opts);
+ 		return 0;
+@@ -285,13 +321,7 @@ int bch2_move_extent(struct moving_context *ctxt,
+ 	 */
+ 	bch2_trans_unlock(trans);
+ 
+-	/* write path might have to decompress data: */
+-	bkey_for_each_ptr_decode(k.k, ptrs, p, entry)
+-		sectors = max_t(unsigned, sectors, p.crc.uncompressed_size);
+-
+-	pages = DIV_ROUND_UP(sectors, PAGE_SECTORS);
+-	io = kzalloc(sizeof(struct moving_io) +
+-		     sizeof(struct bio_vec) * pages, GFP_KERNEL);
++	struct moving_io *io = kzalloc(sizeof(struct moving_io), GFP_KERNEL);
+ 	if (!io)
+ 		goto err;
+ 
+@@ -300,31 +330,27 @@ int bch2_move_extent(struct moving_context *ctxt,
+ 	io->read_sectors	= k.k->size;
+ 	io->write_sectors	= k.k->size;
+ 
+-	bio_init(&io->write.op.wbio.bio, NULL, io->bi_inline_vecs, pages, 0);
+-	io->write.op.wbio.bio.bi_ioprio =
+-		     IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
+-
+-	if (bch2_bio_alloc_pages(&io->write.op.wbio.bio, sectors << 9,
+-				 GFP_KERNEL))
+-		goto err_free;
++	if (!data_opts.scrub) {
++		ret = bch2_data_update_init(trans, iter, ctxt, &io->write, ctxt->wp,
++					    &io_opts, data_opts, iter->btree_id, k);
++		if (ret)
++			goto err_free;
+ 
+-	io->rbio.c		= c;
+-	io->rbio.opts		= io_opts;
+-	bio_init(&io->rbio.bio, NULL, io->bi_inline_vecs, pages, 0);
+-	io->rbio.bio.bi_vcnt = pages;
+-	io->rbio.bio.bi_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
+-	io->rbio.bio.bi_iter.bi_size = sectors << 9;
++		io->write.op.end_io	= move_write_done;
++	} else {
++		bch2_bkey_buf_init(&io->write.k);
++		bch2_bkey_buf_reassemble(&io->write.k, c, k);
+ 
+-	io->rbio.bio.bi_opf		= REQ_OP_READ;
+-	io->rbio.bio.bi_iter.bi_sector	= bkey_start_offset(k.k);
+-	io->rbio.bio.bi_end_io		= move_read_endio;
++		io->write.op.c		= c;
++		io->write.data_opts	= data_opts;
+ 
+-	ret = bch2_data_update_init(trans, iter, ctxt, &io->write, ctxt->wp,
+-				    io_opts, data_opts, iter->btree_id, k);
+-	if (ret)
+-		goto err_free_pages;
++		ret = bch2_data_update_bios_init(&io->write, c, &io_opts);
++		if (ret)
++			goto err_free;
++	}
+ 
+-	io->write.op.end_io = move_write_done;
++	io->write.rbio.bio.bi_end_io = move_read_endio;
++	io->write.rbio.bio.bi_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
+ 
+ 	if (ctxt->rate)
+ 		bch2_ratelimit_increment(ctxt->rate, k.k->size);
+@@ -339,9 +365,7 @@ int bch2_move_extent(struct moving_context *ctxt,
+ 		atomic_inc(&io->b->count);
+ 	}
+ 
+-	this_cpu_add(c->counters[BCH_COUNTER_io_move], k.k->size);
+-	this_cpu_add(c->counters[BCH_COUNTER_move_extent_read], k.k->size);
+-	trace_move_extent_read2(c, k);
++	trace_io_move_read2(c, k);
+ 
+ 	mutex_lock(&ctxt->lock);
+ 	atomic_add(io->read_sectors, &ctxt->read_sectors);
+@@ -356,33 +380,33 @@ int bch2_move_extent(struct moving_context *ctxt,
+ 	 * ctxt when doing wakeup
+ 	 */
+ 	closure_get(&ctxt->cl);
+-	bch2_read_extent(trans, &io->rbio,
+-			 bkey_start_pos(k.k),
+-			 iter->btree_id, k, 0,
+-			 BCH_READ_NODECODE|
+-			 BCH_READ_LAST_FRAGMENT);
++	__bch2_read_extent(trans, &io->write.rbio,
++			   io->write.rbio.bio.bi_iter,
++			   bkey_start_pos(k.k),
++			   iter->btree_id, k, 0,
++			   NULL,
++			   BCH_READ_last_fragment,
++			   data_opts.scrub ?  data_opts.read_dev : -1);
+ 	return 0;
+-err_free_pages:
+-	bio_free_pages(&io->write.op.wbio.bio);
+ err_free:
+ 	kfree(io);
+ err:
+-	if (ret == -BCH_ERR_data_update_done)
++	if (bch2_err_matches(ret, BCH_ERR_data_update_done))
+ 		return 0;
+ 
+ 	if (bch2_err_matches(ret, EROFS) ||
+ 	    bch2_err_matches(ret, BCH_ERR_transaction_restart))
+ 		return ret;
+ 
+-	count_event(c, move_extent_start_fail);
++	count_event(c, io_move_start_fail);
+ 
+-	if (trace_move_extent_start_fail_enabled()) {
++	if (trace_io_move_start_fail_enabled()) {
+ 		struct printbuf buf = PRINTBUF;
+ 
+ 		bch2_bkey_val_to_text(&buf, c, k);
+ 		prt_str(&buf, ": ");
+ 		prt_str(&buf, bch2_err_str(ret));
+-		trace_move_extent_start_fail(c, buf.buf);
++		trace_io_move_start_fail(c, buf.buf);
+ 		printbuf_exit(&buf);
+ 	}
+ 	return ret;
+@@ -518,6 +542,37 @@ int bch2_move_ratelimit(struct moving_context *ctxt)
+ 	return 0;
+ }
+ 
++/*
++ * Move requires non extents iterators, and there's also no need for it to
++ * signal indirect_extent_missing_error:
++ */
++static struct bkey_s_c bch2_lookup_indirect_extent_for_move(struct btree_trans *trans,
++					    struct btree_iter *iter,
++					    struct bkey_s_c_reflink_p p)
++{
++	if (unlikely(REFLINK_P_ERROR(p.v)))
++		return bkey_s_c_null;
++
++	struct bpos reflink_pos = POS(0, REFLINK_P_IDX(p.v));
++
++	bch2_trans_iter_init(trans, iter,
++			     BTREE_ID_reflink, reflink_pos,
++			     BTREE_ITER_not_extents);
++
++	struct bkey_s_c k = bch2_btree_iter_peek(iter);
++	if (!k.k || bkey_err(k)) {
++		bch2_trans_iter_exit(trans, iter);
++		return k;
++	}
++
++	if (bkey_lt(reflink_pos, bkey_start_pos(k.k))) {
++		bch2_trans_iter_exit(trans, iter);
++		return bkey_s_c_null;
++	}
++
++	return k;
++}
++
+ static int bch2_move_data_btree(struct moving_context *ctxt,
+ 				struct bpos start,
+ 				struct bpos end,
+@@ -551,6 +606,7 @@ static int bch2_move_data_btree(struct moving_context *ctxt,
+ 	bch2_trans_begin(trans);
+ 	bch2_trans_iter_init(trans, &iter, btree_id, start,
+ 			     BTREE_ITER_prefetch|
++			     BTREE_ITER_not_extents|
+ 			     BTREE_ITER_all_snapshots);
+ 
+ 	if (ctxt->rate)
+@@ -581,17 +637,16 @@ static int bch2_move_data_btree(struct moving_context *ctxt,
+ 		    k.k->type == KEY_TYPE_reflink_p &&
+ 		    REFLINK_P_MAY_UPDATE_OPTIONS(bkey_s_c_to_reflink_p(k).v)) {
+ 			struct bkey_s_c_reflink_p p = bkey_s_c_to_reflink_p(k);
+-			s64 offset_into_extent	= iter.pos.offset - bkey_start_offset(k.k);
+ 
+ 			bch2_trans_iter_exit(trans, &reflink_iter);
+-			k = bch2_lookup_indirect_extent(trans, &reflink_iter, &offset_into_extent, p, true, 0);
++			k = bch2_lookup_indirect_extent_for_move(trans, &reflink_iter, p);
+ 			ret = bkey_err(k);
+ 			if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
+ 				continue;
+ 			if (ret)
+ 				break;
+ 
+-			if (bkey_deleted(k.k))
++			if (!k.k)
+ 				goto next_nondata;
+ 
+ 			/*
+@@ -627,7 +682,7 @@ static int bch2_move_data_btree(struct moving_context *ctxt,
+ 			if (bch2_err_matches(ret2, BCH_ERR_transaction_restart))
+ 				continue;
+ 
+-			if (ret2 == -ENOMEM) {
++			if (bch2_err_matches(ret2, ENOMEM)) {
+ 				/* memory allocation failure, wait for some IO to finish */
+ 				bch2_move_ctxt_wait_for_io(ctxt);
+ 				continue;
+@@ -689,21 +744,22 @@ int bch2_move_data(struct bch_fs *c,
+ 		   bool wait_on_copygc,
+ 		   move_pred_fn pred, void *arg)
+ {
+-
+ 	struct moving_context ctxt;
+-	int ret;
+ 
+ 	bch2_moving_ctxt_init(&ctxt, c, rate, stats, wp, wait_on_copygc);
+-	ret = __bch2_move_data(&ctxt, start, end, pred, arg);
++	int ret = __bch2_move_data(&ctxt, start, end, pred, arg);
+ 	bch2_moving_ctxt_exit(&ctxt);
+ 
+ 	return ret;
+ }
+ 
+-int bch2_evacuate_bucket(struct moving_context *ctxt,
+-			   struct move_bucket_in_flight *bucket_in_flight,
+-			   struct bpos bucket, int gen,
+-			   struct data_update_opts _data_opts)
++static int __bch2_move_data_phys(struct moving_context *ctxt,
++			struct move_bucket_in_flight *bucket_in_flight,
++			unsigned dev,
++			u64 bucket_start,
++			u64 bucket_end,
++			unsigned data_types,
++			move_pred_fn pred, void *arg)
+ {
+ 	struct btree_trans *trans = ctxt->trans;
+ 	struct bch_fs *c = trans->c;
+@@ -712,16 +768,19 @@ int bch2_evacuate_bucket(struct moving_context *ctxt,
+ 	struct btree_iter iter = {}, bp_iter = {};
+ 	struct bkey_buf sk;
+ 	struct bkey_s_c k;
+-	struct data_update_opts data_opts;
+-	unsigned sectors_moved = 0;
+ 	struct bkey_buf last_flushed;
+ 	int ret = 0;
+ 
+-	struct bch_dev *ca = bch2_dev_tryget(c, bucket.inode);
++	struct bch_dev *ca = bch2_dev_tryget(c, dev);
+ 	if (!ca)
+ 		return 0;
+ 
+-	trace_bucket_evacuate(c, &bucket);
++	bucket_end = min(bucket_end, ca->mi.nbuckets);
++
++	struct bpos bp_start	= bucket_pos_to_bp_start(ca, POS(dev, bucket_start));
++	struct bpos bp_end	= bucket_pos_to_bp_end(ca, POS(dev, bucket_end));
++	bch2_dev_put(ca);
++	ca = NULL;
+ 
+ 	bch2_bkey_buf_init(&last_flushed);
+ 	bkey_init(&last_flushed.k->k);
+@@ -732,8 +791,7 @@ int bch2_evacuate_bucket(struct moving_context *ctxt,
+ 	 */
+ 	bch2_trans_begin(trans);
+ 
+-	bch2_trans_iter_init(trans, &bp_iter, BTREE_ID_backpointers,
+-			     bucket_pos_to_bp_start(ca, bucket), 0);
++	bch2_trans_iter_init(trans, &bp_iter, BTREE_ID_backpointers, bp_start, 0);
+ 
+ 	bch_err_msg(c, ret, "looking up alloc key");
+ 	if (ret)
+@@ -757,7 +815,7 @@ int bch2_evacuate_bucket(struct moving_context *ctxt,
+ 		if (ret)
+ 			goto err;
+ 
+-		if (!k.k || bkey_gt(k.k->p, bucket_pos_to_bp_end(ca, bucket)))
++		if (!k.k || bkey_gt(k.k->p, bp_end))
+ 			break;
+ 
+ 		if (k.k->type != KEY_TYPE_backpointer)
+@@ -765,107 +823,148 @@ int bch2_evacuate_bucket(struct moving_context *ctxt,
+ 
+ 		struct bkey_s_c_backpointer bp = bkey_s_c_to_backpointer(k);
+ 
+-		if (!bp.v->level) {
+-			k = bch2_backpointer_get_key(trans, bp, &iter, 0, &last_flushed);
+-			ret = bkey_err(k);
+-			if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
+-				continue;
+-			if (ret)
+-				goto err;
+-			if (!k.k)
+-				goto next;
++		if (ctxt->stats)
++			ctxt->stats->offset = bp.k->p.offset >> MAX_EXTENT_COMPRESS_RATIO_SHIFT;
++
++		if (!(data_types & BIT(bp.v->data_type)))
++			goto next;
++
++		if (!bp.v->level && bp.v->btree_id == BTREE_ID_stripes)
++			goto next;
+ 
+-			bch2_bkey_buf_reassemble(&sk, c, k);
+-			k = bkey_i_to_s_c(sk.k);
++		k = bch2_backpointer_get_key(trans, bp, &iter, 0, &last_flushed);
++		ret = bkey_err(k);
++		if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
++			continue;
++		if (ret)
++			goto err;
++		if (!k.k)
++			goto next;
+ 
++		if (!bp.v->level) {
+ 			ret = bch2_move_get_io_opts_one(trans, &io_opts, &iter, k);
+ 			if (ret) {
+ 				bch2_trans_iter_exit(trans, &iter);
+ 				continue;
+ 			}
++		}
+ 
+-			data_opts = _data_opts;
+-			data_opts.target	= io_opts.background_target;
+-			data_opts.rewrite_ptrs = 0;
+-
+-			unsigned sectors = bp.v->bucket_len; /* move_extent will drop locks */
+-			unsigned i = 0;
+-			const union bch_extent_entry *entry;
+-			struct extent_ptr_decoded p;
+-			bkey_for_each_ptr_decode(k.k, bch2_bkey_ptrs_c(k), p, entry) {
+-				if (p.ptr.dev == bucket.inode) {
+-					if (p.ptr.cached) {
+-						bch2_trans_iter_exit(trans, &iter);
+-						goto next;
+-					}
+-					data_opts.rewrite_ptrs |= 1U << i;
+-					break;
+-				}
+-				i++;
+-			}
+-
+-			ret = bch2_move_extent(ctxt, bucket_in_flight,
+-					       &iter, k, io_opts, data_opts);
++		struct data_update_opts data_opts = {};
++		if (!pred(c, arg, k, &io_opts, &data_opts)) {
+ 			bch2_trans_iter_exit(trans, &iter);
++			goto next;
++		}
+ 
+-			if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
+-				continue;
+-			if (ret == -ENOMEM) {
+-				/* memory allocation failure, wait for some IO to finish */
+-				bch2_move_ctxt_wait_for_io(ctxt);
+-				continue;
+-			}
+-			if (ret)
+-				goto err;
+-
+-			if (ctxt->stats)
+-				atomic64_add(sectors, &ctxt->stats->sectors_seen);
+-			sectors_moved += sectors;
+-		} else {
+-			struct btree *b;
++		if (data_opts.scrub &&
++		    !bch2_dev_idx_is_online(c, data_opts.read_dev)) {
++			bch2_trans_iter_exit(trans, &iter);
++			ret = -BCH_ERR_device_offline;
++			break;
++		}
+ 
+-			b = bch2_backpointer_get_node(trans, bp, &iter, &last_flushed);
+-			ret = PTR_ERR_OR_ZERO(b);
+-			if (ret == -BCH_ERR_backpointer_to_overwritten_btree_node)
+-				goto next;
+-			if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
+-				continue;
+-			if (ret)
+-				goto err;
+-			if (!b)
+-				goto next;
++		bch2_bkey_buf_reassemble(&sk, c, k);
++		k = bkey_i_to_s_c(sk.k);
+ 
+-			unsigned sectors = btree_ptr_sectors_written(bkey_i_to_s_c(&b->key));
++		/* move_extent will drop locks */
++		unsigned sectors = bp.v->bucket_len;
+ 
+-			ret = bch2_btree_node_rewrite(trans, &iter, b, 0);
+-			bch2_trans_iter_exit(trans, &iter);
++		if (!bp.v->level)
++			ret = bch2_move_extent(ctxt, bucket_in_flight, &iter, k, io_opts, data_opts);
++		else if (!data_opts.scrub)
++			ret = bch2_btree_node_rewrite_pos(trans, bp.v->btree_id, bp.v->level, k.k->p, 0);
++		else
++			ret = bch2_btree_node_scrub(trans, bp.v->btree_id, bp.v->level, k, data_opts.read_dev);
+ 
+-			if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
+-				continue;
+-			if (ret)
+-				goto err;
++		bch2_trans_iter_exit(trans, &iter);
+ 
+-			if (ctxt->rate)
+-				bch2_ratelimit_increment(ctxt->rate, sectors);
+-			if (ctxt->stats) {
+-				atomic64_add(sectors, &ctxt->stats->sectors_seen);
+-				atomic64_add(sectors, &ctxt->stats->sectors_moved);
+-			}
+-			sectors_moved += btree_sectors(c);
++		if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
++			continue;
++		if (ret == -ENOMEM) {
++			/* memory allocation failure, wait for some IO to finish */
++			bch2_move_ctxt_wait_for_io(ctxt);
++			continue;
+ 		}
++		if (ret)
++			goto err;
++
++		if (ctxt->stats)
++			atomic64_add(sectors, &ctxt->stats->sectors_seen);
+ next:
+ 		bch2_btree_iter_advance(&bp_iter);
+ 	}
+-
+-	trace_evacuate_bucket(c, &bucket, sectors_moved, ca->mi.bucket_size, ret);
+ err:
+ 	bch2_trans_iter_exit(trans, &bp_iter);
+-	bch2_dev_put(ca);
+ 	bch2_bkey_buf_exit(&sk, c);
+ 	bch2_bkey_buf_exit(&last_flushed, c);
+ 	return ret;
+ }
+ 
++static int bch2_move_data_phys(struct bch_fs *c,
++			       unsigned dev,
++			       u64 start,
++			       u64 end,
++			       unsigned data_types,
++			       struct bch_ratelimit *rate,
++			       struct bch_move_stats *stats,
++			       struct write_point_specifier wp,
++			       bool wait_on_copygc,
++			       move_pred_fn pred, void *arg)
++{
++	struct moving_context ctxt;
++
++	bch2_trans_run(c, bch2_btree_write_buffer_flush_sync(trans));
++
++	bch2_moving_ctxt_init(&ctxt, c, rate, stats, wp, wait_on_copygc);
++	ctxt.stats->phys = true;
++	ctxt.stats->data_type = (int) DATA_PROGRESS_DATA_TYPE_phys;
++
++	int ret = __bch2_move_data_phys(&ctxt, NULL, dev, start, end, data_types, pred, arg);
++	bch2_moving_ctxt_exit(&ctxt);
++
++	return ret;
++}
++
++struct evacuate_bucket_arg {
++	struct bpos		bucket;
++	int			gen;
++	struct data_update_opts	data_opts;
++};
++
++static bool evacuate_bucket_pred(struct bch_fs *c, void *_arg, struct bkey_s_c k,
++				 struct bch_io_opts *io_opts,
++				 struct data_update_opts *data_opts)
++{
++	struct evacuate_bucket_arg *arg = _arg;
++
++	*data_opts = arg->data_opts;
++
++	unsigned i = 0;
++	bkey_for_each_ptr(bch2_bkey_ptrs_c(k), ptr) {
++		if (ptr->dev == arg->bucket.inode &&
++		    (arg->gen < 0 || arg->gen == ptr->gen) &&
++		    !ptr->cached)
++			data_opts->rewrite_ptrs |= BIT(i);
++		i++;
++	}
++
++	return data_opts->rewrite_ptrs != 0;
++}
++
++int bch2_evacuate_bucket(struct moving_context *ctxt,
++			   struct move_bucket_in_flight *bucket_in_flight,
++			   struct bpos bucket, int gen,
++			   struct data_update_opts data_opts)
++{
++	struct evacuate_bucket_arg arg = { bucket, gen, data_opts, };
++
++	return __bch2_move_data_phys(ctxt, bucket_in_flight,
++				   bucket.inode,
++				   bucket.offset,
++				   bucket.offset + 1,
++				   ~0,
++				   evacuate_bucket_pred, &arg);
++}
++
+ typedef bool (*move_btree_pred)(struct bch_fs *, void *,
+ 				struct btree *, struct bch_io_opts *,
+ 				struct data_update_opts *);
+@@ -1007,14 +1106,6 @@ static bool rereplicate_btree_pred(struct bch_fs *c, void *arg,
+ 	return rereplicate_pred(c, arg, bkey_i_to_s_c(&b->key), io_opts, data_opts);
+ }
+ 
+-static bool migrate_btree_pred(struct bch_fs *c, void *arg,
+-			       struct btree *b,
+-			       struct bch_io_opts *io_opts,
+-			       struct data_update_opts *data_opts)
+-{
+-	return migrate_pred(c, arg, bkey_i_to_s_c(&b->key), io_opts, data_opts);
+-}
+-
+ /*
+  * Ancient versions of bcachefs produced packed formats which could represent
+  * keys that the in memory format cannot represent; this checks for those
+@@ -1104,6 +1195,30 @@ static bool drop_extra_replicas_btree_pred(struct bch_fs *c, void *arg,
+ 	return drop_extra_replicas_pred(c, arg, bkey_i_to_s_c(&b->key), io_opts, data_opts);
+ }
+ 
++static bool scrub_pred(struct bch_fs *c, void *_arg,
++		       struct bkey_s_c k,
++		       struct bch_io_opts *io_opts,
++		       struct data_update_opts *data_opts)
++{
++	struct bch_ioctl_data *arg = _arg;
++
++	if (k.k->type != KEY_TYPE_btree_ptr_v2) {
++		struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
++		const union bch_extent_entry *entry;
++		struct extent_ptr_decoded p;
++		bkey_for_each_ptr_decode(k.k, ptrs, p, entry)
++			if (p.ptr.dev == arg->migrate.dev) {
++				if (!p.crc.csum_type)
++					return false;
++				break;
++			}
++	}
++
++	data_opts->scrub	= true;
++	data_opts->read_dev	= arg->migrate.dev;
++	return true;
++}
++
+ int bch2_data_job(struct bch_fs *c,
+ 		  struct bch_move_stats *stats,
+ 		  struct bch_ioctl_data op)
+@@ -1118,6 +1233,22 @@ int bch2_data_job(struct bch_fs *c,
+ 	bch2_move_stats_init(stats, bch2_data_ops_strs[op.op]);
+ 
+ 	switch (op.op) {
++	case BCH_DATA_OP_scrub:
++		/*
++		 * prevent tests from spuriously failing, make sure we see all
++		 * btree nodes that need to be repaired
++		 */
++		bch2_btree_interior_updates_flush(c);
++
++		ret = bch2_move_data_phys(c, op.scrub.dev, 0, U64_MAX,
++					  op.scrub.data_types,
++					  NULL,
++					  stats,
++					  writepoint_hashed((unsigned long) current),
++					  false,
++					  scrub_pred, &op) ?: ret;
++		break;
++
+ 	case BCH_DATA_OP_rereplicate:
+ 		stats->data_type = BCH_DATA_journal;
+ 		ret = bch2_journal_flush_device_pins(&c->journal, -1);
+@@ -1137,14 +1268,14 @@ int bch2_data_job(struct bch_fs *c,
+ 
+ 		stats->data_type = BCH_DATA_journal;
+ 		ret = bch2_journal_flush_device_pins(&c->journal, op.migrate.dev);
+-		ret = bch2_move_btree(c, start, end,
+-				      migrate_btree_pred, &op, stats) ?: ret;
+-		ret = bch2_move_data(c, start, end,
+-				     NULL,
+-				     stats,
+-				     writepoint_hashed((unsigned long) current),
+-				     true,
+-				     migrate_pred, &op) ?: ret;
++		ret = bch2_move_data_phys(c, op.migrate.dev, 0, U64_MAX,
++					  ~0,
++					  NULL,
++					  stats,
++					  writepoint_hashed((unsigned long) current),
++					  true,
++					  migrate_pred, &op) ?: ret;
++		bch2_btree_interior_updates_flush(c);
+ 		ret = bch2_replicas_gc2(c) ?: ret;
+ 		break;
+ 	case BCH_DATA_OP_rewrite_old_nodes:
+@@ -1176,17 +1307,17 @@ void bch2_move_stats_to_text(struct printbuf *out, struct bch_move_stats *stats)
+ 	prt_newline(out);
+ 	printbuf_indent_add(out, 2);
+ 
+-	prt_printf(out, "keys moved:  %llu\n",	atomic64_read(&stats->keys_moved));
+-	prt_printf(out, "keys raced:  %llu\n",	atomic64_read(&stats->keys_raced));
+-	prt_printf(out, "bytes seen:  ");
++	prt_printf(out, "keys moved:\t%llu\n",	atomic64_read(&stats->keys_moved));
++	prt_printf(out, "keys raced:\t%llu\n",	atomic64_read(&stats->keys_raced));
++	prt_printf(out, "bytes seen:\t");
+ 	prt_human_readable_u64(out, atomic64_read(&stats->sectors_seen) << 9);
+ 	prt_newline(out);
+ 
+-	prt_printf(out, "bytes moved: ");
++	prt_printf(out, "bytes moved:\t");
+ 	prt_human_readable_u64(out, atomic64_read(&stats->sectors_moved) << 9);
+ 	prt_newline(out);
+ 
+-	prt_printf(out, "bytes raced: ");
++	prt_printf(out, "bytes raced:\t");
+ 	prt_human_readable_u64(out, atomic64_read(&stats->sectors_raced) << 9);
+ 	prt_newline(out);
+ 
+@@ -1195,7 +1326,8 @@ void bch2_move_stats_to_text(struct printbuf *out, struct bch_move_stats *stats)
+ 
+ static void bch2_moving_ctxt_to_text(struct printbuf *out, struct bch_fs *c, struct moving_context *ctxt)
+ {
+-	struct moving_io *io;
++	if (!out->nr_tabstops)
++		printbuf_tabstop_push(out, 32);
+ 
+ 	bch2_move_stats_to_text(out, ctxt->stats);
+ 	printbuf_indent_add(out, 2);
+@@ -1215,8 +1347,9 @@ static void bch2_moving_ctxt_to_text(struct printbuf *out, struct bch_fs *c, str
+ 	printbuf_indent_add(out, 2);
+ 
+ 	mutex_lock(&ctxt->lock);
++	struct moving_io *io;
+ 	list_for_each_entry(io, &ctxt->ios, io_list)
+-		bch2_write_op_to_text(out, &io->write.op);
++		bch2_data_update_inflight_to_text(out, &io->write);
+ 	mutex_unlock(&ctxt->lock);
+ 
+ 	printbuf_indent_sub(out, 4);
+diff --git a/fs/bcachefs/move_types.h b/fs/bcachefs/move_types.h
+index e22841ef31e4..807f779f6f76 100644
+--- a/fs/bcachefs/move_types.h
++++ b/fs/bcachefs/move_types.h
+@@ -3,22 +3,36 @@
+ #define _BCACHEFS_MOVE_TYPES_H
+ 
+ #include "bbpos_types.h"
++#include "bcachefs_ioctl.h"
+ 
+ struct bch_move_stats {
+-	enum bch_data_type	data_type;
+-	struct bbpos		pos;
+ 	char			name[32];
++	bool			phys;
++	enum bch_ioctl_data_event_ret	ret;
++
++	union {
++	struct {
++		enum bch_data_type	data_type;
++		struct bbpos		pos;
++	};
++	struct {
++		unsigned		dev;
++		u64			offset;
++	};
++	};
+ 
+ 	atomic64_t		keys_moved;
+ 	atomic64_t		keys_raced;
+ 	atomic64_t		sectors_seen;
+ 	atomic64_t		sectors_moved;
+ 	atomic64_t		sectors_raced;
++	atomic64_t		sectors_error_corrected;
++	atomic64_t		sectors_error_uncorrected;
+ };
+ 
+ struct move_bucket_key {
+ 	struct bpos		bucket;
+-	u8			gen;
++	unsigned		gen;
+ };
+ 
+ struct move_bucket {
+diff --git a/fs/bcachefs/movinggc.c b/fs/bcachefs/movinggc.c
+index 6718dc37c5a3..5126c870ce5b 100644
+--- a/fs/bcachefs/movinggc.c
++++ b/fs/bcachefs/movinggc.c
+@@ -167,8 +167,8 @@ static int bch2_copygc_get_buckets(struct moving_context *ctxt,
+ 	bch2_trans_begin(trans);
+ 
+ 	ret = for_each_btree_key_max(trans, iter, BTREE_ID_lru,
+-				  lru_pos(BCH_LRU_FRAGMENTATION_START, 0, 0),
+-				  lru_pos(BCH_LRU_FRAGMENTATION_START, U64_MAX, LRU_TIME_MAX),
++				  lru_pos(BCH_LRU_BUCKET_FRAGMENTATION, 0, 0),
++				  lru_pos(BCH_LRU_BUCKET_FRAGMENTATION, U64_MAX, LRU_TIME_MAX),
+ 				  0, k, ({
+ 		struct move_bucket b = { .k.bucket = u64_to_bucket(k.k->p.offset) };
+ 		int ret2 = 0;
+@@ -317,6 +317,17 @@ void bch2_copygc_wait_to_text(struct printbuf *out, struct bch_fs *c)
+ 	prt_printf(out, "Currently calculated wait:\t");
+ 	prt_human_readable_u64(out, bch2_copygc_wait_amount(c));
+ 	prt_newline(out);
++
++	rcu_read_lock();
++	struct task_struct *t = rcu_dereference(c->copygc_thread);
++	if (t)
++		get_task_struct(t);
++	rcu_read_unlock();
++
++	if (t) {
++		bch2_prt_task_backtrace(out, t, 0, GFP_KERNEL);
++		put_task_struct(t);
++	}
+ }
+ 
+ static int bch2_copygc_thread(void *arg)
+diff --git a/fs/bcachefs/fs-common.c b/fs/bcachefs/namei.c
+similarity index 73%
+rename from fs/bcachefs/fs-common.c
+rename to fs/bcachefs/namei.c
+index 2c3d46ac70c6..93246ad31541 100644
+--- a/fs/bcachefs/fs-common.c
++++ b/fs/bcachefs/namei.c
+@@ -4,8 +4,8 @@
+ #include "acl.h"
+ #include "btree_update.h"
+ #include "dirent.h"
+-#include "fs-common.h"
+ #include "inode.h"
++#include "namei.h"
+ #include "subvolume.h"
+ #include "xattr.h"
+ 
+@@ -47,6 +47,10 @@ int bch2_create_trans(struct btree_trans *trans,
+ 	if (ret)
+ 		goto err;
+ 
++	/* Inherit casefold state from parent. */
++	if (S_ISDIR(mode))
++		new_inode->bi_flags |= dir_u->bi_flags & BCH_INODE_casefolded;
++
+ 	if (!(flags & BCH_CREATE_SNAPSHOT)) {
+ 		/* Normal create path - allocate a new inode: */
+ 		bch2_inode_init_late(new_inode, now, uid, gid, mode, rdev, dir_u);
+@@ -153,16 +157,14 @@ int bch2_create_trans(struct btree_trans *trans,
+ 			dir_u->bi_nlink++;
+ 		dir_u->bi_mtime = dir_u->bi_ctime = now;
+ 
+-		ret = bch2_inode_write(trans, &dir_iter, dir_u);
+-		if (ret)
+-			goto err;
+-
+-		ret = bch2_dirent_create(trans, dir, &dir_hash,
+-					 dir_type,
+-					 name,
+-					 dir_target,
+-					 &dir_offset,
+-					 STR_HASH_must_create|BTREE_ITER_with_updates);
++		ret =   bch2_dirent_create(trans, dir, &dir_hash,
++					   dir_type,
++					   name,
++					   dir_target,
++					   &dir_offset,
++					   &dir_u->bi_size,
++					   STR_HASH_must_create|BTREE_ITER_with_updates) ?:
++			bch2_inode_write(trans, &dir_iter, dir_u);
+ 		if (ret)
+ 			goto err;
+ 
+@@ -225,7 +227,9 @@ int bch2_link_trans(struct btree_trans *trans,
+ 
+ 	ret = bch2_dirent_create(trans, dir, &dir_hash,
+ 				 mode_to_type(inode_u->bi_mode),
+-				 name, inum.inum, &dir_offset,
++				 name, inum.inum,
++				 &dir_offset,
++				 &dir_u->bi_size,
+ 				 STR_HASH_must_create);
+ 	if (ret)
+ 		goto err;
+@@ -417,8 +421,8 @@ int bch2_rename_trans(struct btree_trans *trans,
+ 	}
+ 
+ 	ret = bch2_dirent_rename(trans,
+-				 src_dir, &src_hash,
+-				 dst_dir, &dst_hash,
++				 src_dir, &src_hash, &src_dir_u->bi_size,
++				 dst_dir, &dst_hash, &dst_dir_u->bi_size,
+ 				 src_name, &src_inum, &src_offset,
+ 				 dst_name, &dst_inum, &dst_offset,
+ 				 mode);
+@@ -560,6 +564,8 @@ int bch2_rename_trans(struct btree_trans *trans,
+ 	return ret;
+ }
+ 
++/* inum_to_path */
++
+ static inline void prt_bytes_reversed(struct printbuf *out, const void *b, unsigned n)
+ {
+ 	bch2_printbuf_make_room(out, n);
+@@ -650,3 +656,179 @@ int bch2_inum_to_path(struct btree_trans *trans, subvol_inum inum, struct printb
+ 	prt_str_reversed(path, "(disconnected)");
+ 	goto out;
+ }
++
++/* fsck */
++
++static int bch2_check_dirent_inode_dirent(struct btree_trans *trans,
++					  struct bkey_s_c_dirent d,
++					  struct bch_inode_unpacked *target,
++					  bool in_fsck)
++{
++	struct bch_fs *c = trans->c;
++	struct printbuf buf = PRINTBUF;
++	struct btree_iter bp_iter = { NULL };
++	int ret = 0;
++
++	if (inode_points_to_dirent(target, d))
++		return 0;
++
++	if (!target->bi_dir &&
++	    !target->bi_dir_offset) {
++		fsck_err_on(S_ISDIR(target->bi_mode),
++			    trans, inode_dir_missing_backpointer,
++			    "directory with missing backpointer\n%s",
++			    (printbuf_reset(&buf),
++			     bch2_bkey_val_to_text(&buf, c, d.s_c),
++			     prt_printf(&buf, "\n"),
++			     bch2_inode_unpacked_to_text(&buf, target),
++			     buf.buf));
++
++		fsck_err_on(target->bi_flags & BCH_INODE_unlinked,
++			    trans, inode_unlinked_but_has_dirent,
++			    "inode unlinked but has dirent\n%s",
++			    (printbuf_reset(&buf),
++			     bch2_bkey_val_to_text(&buf, c, d.s_c),
++			     prt_printf(&buf, "\n"),
++			     bch2_inode_unpacked_to_text(&buf, target),
++			     buf.buf));
++
++		target->bi_flags &= ~BCH_INODE_unlinked;
++		target->bi_dir		= d.k->p.inode;
++		target->bi_dir_offset	= d.k->p.offset;
++		return __bch2_fsck_write_inode(trans, target);
++	}
++
++	if (bch2_inode_should_have_single_bp(target) &&
++	    !fsck_err(trans, inode_wrong_backpointer,
++		      "dirent points to inode that does not point back:\n  %s",
++		      (bch2_bkey_val_to_text(&buf, c, d.s_c),
++		       prt_printf(&buf, "\n  "),
++		       bch2_inode_unpacked_to_text(&buf, target),
++		       buf.buf)))
++		goto err;
++
++	struct bkey_s_c_dirent bp_dirent =
++		bch2_bkey_get_iter_typed(trans, &bp_iter, BTREE_ID_dirents,
++			      SPOS(target->bi_dir, target->bi_dir_offset, target->bi_snapshot),
++			      0, dirent);
++	ret = bkey_err(bp_dirent);
++	if (ret && !bch2_err_matches(ret, ENOENT))
++		goto err;
++
++	bool backpointer_exists = !ret;
++	ret = 0;
++
++	if (!backpointer_exists) {
++		if (fsck_err(trans, inode_wrong_backpointer,
++			     "inode %llu:%u has wrong backpointer:\n"
++			     "got       %llu:%llu\n"
++			     "should be %llu:%llu",
++			     target->bi_inum, target->bi_snapshot,
++			     target->bi_dir,
++			     target->bi_dir_offset,
++			     d.k->p.inode,
++			     d.k->p.offset)) {
++			target->bi_dir		= d.k->p.inode;
++			target->bi_dir_offset	= d.k->p.offset;
++			ret = __bch2_fsck_write_inode(trans, target);
++		}
++	} else {
++		bch2_bkey_val_to_text(&buf, c, d.s_c);
++		prt_newline(&buf);
++		bch2_bkey_val_to_text(&buf, c, bp_dirent.s_c);
++
++		if (S_ISDIR(target->bi_mode) || target->bi_subvol) {
++			/*
++			 * XXX: verify connectivity of the other dirent
++			 * up to the root before removing this one
++			 *
++			 * Additionally, bch2_lookup would need to cope with the
++			 * dirent it found being removed - or should we remove
++			 * the other one, even though the inode points to it?
++			 */
++			if (in_fsck) {
++				if (fsck_err(trans, inode_dir_multiple_links,
++					     "%s %llu:%u with multiple links\n%s",
++					     S_ISDIR(target->bi_mode) ? "directory" : "subvolume",
++					     target->bi_inum, target->bi_snapshot, buf.buf))
++					ret = bch2_fsck_remove_dirent(trans, d.k->p);
++			} else {
++				bch2_fs_inconsistent(c,
++						"%s %llu:%u with multiple links\n%s",
++						S_ISDIR(target->bi_mode) ? "directory" : "subvolume",
++						target->bi_inum, target->bi_snapshot, buf.buf);
++			}
++
++			goto out;
++		} else {
++			/*
++			 * hardlinked file with nlink 0:
++			 * We're just adjusting nlink here so check_nlinks() will pick
++			 * it up, it ignores inodes with nlink 0
++			 */
++			if (fsck_err_on(!target->bi_nlink,
++					trans, inode_multiple_links_but_nlink_0,
++					"inode %llu:%u type %s has multiple links but i_nlink 0\n%s",
++					target->bi_inum, target->bi_snapshot, bch2_d_types[d.v->d_type], buf.buf)) {
++				target->bi_nlink++;
++				target->bi_flags &= ~BCH_INODE_unlinked;
++				ret = __bch2_fsck_write_inode(trans, target);
++				if (ret)
++					goto err;
++			}
++		}
++	}
++out:
++err:
++fsck_err:
++	bch2_trans_iter_exit(trans, &bp_iter);
++	printbuf_exit(&buf);
++	bch_err_fn(c, ret);
++	return ret;
++}
++
++int __bch2_check_dirent_target(struct btree_trans *trans,
++			       struct btree_iter *dirent_iter,
++			       struct bkey_s_c_dirent d,
++			       struct bch_inode_unpacked *target,
++			       bool in_fsck)
++{
++	struct bch_fs *c = trans->c;
++	struct printbuf buf = PRINTBUF;
++	int ret = 0;
++
++	ret = bch2_check_dirent_inode_dirent(trans, d, target, in_fsck);
++	if (ret)
++		goto err;
++
++	if (fsck_err_on(d.v->d_type != inode_d_type(target),
++			trans, dirent_d_type_wrong,
++			"incorrect d_type: got %s, should be %s:\n%s",
++			bch2_d_type_str(d.v->d_type),
++			bch2_d_type_str(inode_d_type(target)),
++			(printbuf_reset(&buf),
++			 bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf))) {
++		struct bkey_i_dirent *n = bch2_trans_kmalloc(trans, bkey_bytes(d.k));
++		ret = PTR_ERR_OR_ZERO(n);
++		if (ret)
++			goto err;
++
++		bkey_reassemble(&n->k_i, d.s_c);
++		n->v.d_type = inode_d_type(target);
++		if (n->v.d_type == DT_SUBVOL) {
++			n->v.d_parent_subvol = cpu_to_le32(target->bi_parent_subvol);
++			n->v.d_child_subvol = cpu_to_le32(target->bi_subvol);
++		} else {
++			n->v.d_inum = cpu_to_le64(target->bi_inum);
++		}
++
++		ret = bch2_trans_update(trans, dirent_iter, &n->k_i, 0);
++		if (ret)
++			goto err;
++	}
++err:
++fsck_err:
++	printbuf_exit(&buf);
++	bch_err_fn(c, ret);
++	return ret;
++}
+diff --git a/fs/bcachefs/fs-common.h b/fs/bcachefs/namei.h
+similarity index 61%
+rename from fs/bcachefs/fs-common.h
+rename to fs/bcachefs/namei.h
+index 2b59210bb5e8..2e6f6364767f 100644
+--- a/fs/bcachefs/fs-common.h
++++ b/fs/bcachefs/namei.h
+@@ -1,6 +1,6 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-#ifndef _BCACHEFS_FS_COMMON_H
+-#define _BCACHEFS_FS_COMMON_H
++#ifndef _BCACHEFS_NAMEI_H
++#define _BCACHEFS_NAMEI_H
+ 
+ #include "dirent.h"
+ 
+@@ -44,4 +44,29 @@ bool bch2_reinherit_attrs(struct bch_inode_unpacked *,
+ 
+ int bch2_inum_to_path(struct btree_trans *, subvol_inum, struct printbuf *);
+ 
+-#endif /* _BCACHEFS_FS_COMMON_H */
++int __bch2_check_dirent_target(struct btree_trans *,
++			       struct btree_iter *,
++			       struct bkey_s_c_dirent,
++			       struct bch_inode_unpacked *, bool);
++
++static inline bool inode_points_to_dirent(struct bch_inode_unpacked *inode,
++					  struct bkey_s_c_dirent d)
++{
++	return  inode->bi_dir		== d.k->p.inode &&
++		inode->bi_dir_offset	== d.k->p.offset;
++}
++
++static inline int bch2_check_dirent_target(struct btree_trans *trans,
++					   struct btree_iter *dirent_iter,
++					   struct bkey_s_c_dirent d,
++					   struct bch_inode_unpacked *target,
++					   bool in_fsck)
++{
++	if (likely(inode_points_to_dirent(target, d) &&
++		   d.v->d_type == inode_d_type(target)))
++		return 0;
++
++	return __bch2_check_dirent_target(trans, dirent_iter, d, target, in_fsck);
++}
++
++#endif /* _BCACHEFS_NAMEI_H */
+diff --git a/fs/bcachefs/opts.c b/fs/bcachefs/opts.c
+index 6772faf385a5..4eea51edafca 100644
+--- a/fs/bcachefs/opts.c
++++ b/fs/bcachefs/opts.c
+@@ -44,7 +44,7 @@ const char * const __bch2_btree_ids[] = {
+ 	NULL
+ };
+ 
+-static const char * const __bch2_csum_types[] = {
++const char * const __bch2_csum_types[] = {
+ 	BCH_CSUM_TYPES()
+ 	NULL
+ };
+@@ -163,16 +163,6 @@ const char * const bch2_d_types[BCH_DT_MAX] = {
+ 	[DT_SUBVOL]	= "subvol",
+ };
+ 
+-u64 BCH2_NO_SB_OPT(const struct bch_sb *sb)
+-{
+-	BUG();
+-}
+-
+-void SET_BCH2_NO_SB_OPT(struct bch_sb *sb, u64 v)
+-{
+-	BUG();
+-}
+-
+ void bch2_opts_apply(struct bch_opts *dst, struct bch_opts src)
+ {
+ #define x(_name, ...)						\
+@@ -223,6 +213,21 @@ void bch2_opt_set_by_id(struct bch_opts *opts, enum bch_opt_id id, u64 v)
+ 	}
+ }
+ 
++/* dummy option, for options that aren't stored in the superblock */
++typedef u64 (*sb_opt_get_fn)(const struct bch_sb *);
++typedef void (*sb_opt_set_fn)(struct bch_sb *, u64);
++typedef u64 (*member_opt_get_fn)(const struct bch_member *);
++typedef void (*member_opt_set_fn)(struct bch_member *, u64);
++
++__maybe_unused static const sb_opt_get_fn	BCH2_NO_SB_OPT = NULL;
++__maybe_unused static const sb_opt_set_fn	SET_BCH2_NO_SB_OPT = NULL;
++__maybe_unused static const member_opt_get_fn	BCH2_NO_MEMBER_OPT = NULL;
++__maybe_unused static const member_opt_set_fn	SET_BCH2_NO_MEMBER_OPT = NULL;
++
++#define type_compatible_or_null(_p, _type)				\
++	__builtin_choose_expr(						\
++		__builtin_types_compatible_p(typeof(_p), typeof(_type)), _p, NULL)
++
+ const struct bch_option bch2_opt_table[] = {
+ #define OPT_BOOL()		.type = BCH_OPT_BOOL, .min = 0, .max = 2
+ #define OPT_UINT(_min, _max)	.type = BCH_OPT_UINT,			\
+@@ -239,15 +244,15 @@ const struct bch_option bch2_opt_table[] = {
+ 
+ #define x(_name, _bits, _flags, _type, _sb_opt, _default, _hint, _help)	\
+ 	[Opt_##_name] = {						\
+-		.attr	= {						\
+-			.name	= #_name,				\
+-			.mode = (_flags) & OPT_RUNTIME ? 0644 : 0444,	\
+-		},							\
+-		.flags	= _flags,					\
+-		.hint	= _hint,					\
+-		.help	= _help,					\
+-		.get_sb = _sb_opt,					\
+-		.set_sb	= SET_##_sb_opt,				\
++		.attr.name	= #_name,				\
++		.attr.mode	= (_flags) & OPT_RUNTIME ? 0644 : 0444,	\
++		.flags		= _flags,				\
++		.hint		= _hint,				\
++		.help		= _help,				\
++		.get_sb		= type_compatible_or_null(_sb_opt,	*BCH2_NO_SB_OPT),	\
++		.set_sb		= type_compatible_or_null(SET_##_sb_opt,*SET_BCH2_NO_SB_OPT),	\
++		.get_member	= type_compatible_or_null(_sb_opt,	*BCH2_NO_MEMBER_OPT),	\
++		.set_member	= type_compatible_or_null(SET_##_sb_opt,*SET_BCH2_NO_MEMBER_OPT),\
+ 		_type							\
+ 	},
+ 
+@@ -475,11 +480,18 @@ void bch2_opts_to_text(struct printbuf *out,
+ 	}
+ }
+ 
+-int bch2_opt_check_may_set(struct bch_fs *c, int id, u64 v)
++int bch2_opt_check_may_set(struct bch_fs *c, struct bch_dev *ca, int id, u64 v)
+ {
++	lockdep_assert_held(&c->state_lock);
++
+ 	int ret = 0;
+ 
+ 	switch (id) {
++	case Opt_state:
++		if (ca)
++			return __bch2_dev_set_state(c, ca, v, BCH_FORCE_IF_DEGRADED);
++		break;
++
+ 	case Opt_compression:
+ 	case Opt_background_compression:
+ 		ret = bch2_check_set_has_compressed_data(c, v);
+@@ -495,12 +507,8 @@ int bch2_opt_check_may_set(struct bch_fs *c, int id, u64 v)
+ 
+ int bch2_opts_check_may_set(struct bch_fs *c)
+ {
+-	unsigned i;
+-	int ret;
+-
+-	for (i = 0; i < bch2_opts_nr; i++) {
+-		ret = bch2_opt_check_may_set(c, i,
+-				bch2_opt_get_by_id(&c->opts, i));
++	for (unsigned i = 0; i < bch2_opts_nr; i++) {
++		int ret = bch2_opt_check_may_set(c, NULL, i, bch2_opt_get_by_id(&c->opts, i));
+ 		if (ret)
+ 			return ret;
+ 	}
+@@ -619,12 +627,25 @@ int bch2_parse_mount_opts(struct bch_fs *c, struct bch_opts *opts,
+ 	return ret;
+ }
+ 
+-u64 bch2_opt_from_sb(struct bch_sb *sb, enum bch_opt_id id)
++u64 bch2_opt_from_sb(struct bch_sb *sb, enum bch_opt_id id, int dev_idx)
+ {
+ 	const struct bch_option *opt = bch2_opt_table + id;
+ 	u64 v;
+ 
+-	v = opt->get_sb(sb);
++	if (dev_idx < 0) {
++		v = opt->get_sb(sb);
++	} else {
++		if (WARN(!bch2_member_exists(sb, dev_idx),
++			 "tried to set device option %s on nonexistent device %i",
++			 opt->attr.name, dev_idx))
++			return 0;
++
++		struct bch_member m = bch2_sb_member_get(sb, dev_idx);
++		v = opt->get_member(&m);
++	}
++
++	if (opt->flags & OPT_SB_FIELD_ONE_BIAS)
++		--v;
+ 
+ 	if (opt->flags & OPT_SB_FIELD_ILOG2)
+ 		v = 1ULL << v;
+@@ -641,35 +662,19 @@ u64 bch2_opt_from_sb(struct bch_sb *sb, enum bch_opt_id id)
+  */
+ int bch2_opts_from_sb(struct bch_opts *opts, struct bch_sb *sb)
+ {
+-	unsigned id;
+-
+-	for (id = 0; id < bch2_opts_nr; id++) {
++	for (unsigned id = 0; id < bch2_opts_nr; id++) {
+ 		const struct bch_option *opt = bch2_opt_table + id;
+ 
+-		if (opt->get_sb == BCH2_NO_SB_OPT)
+-			continue;
+-
+-		bch2_opt_set_by_id(opts, id, bch2_opt_from_sb(sb, id));
++		if (opt->get_sb)
++			bch2_opt_set_by_id(opts, id, bch2_opt_from_sb(sb, id, -1));
+ 	}
+ 
+ 	return 0;
+ }
+ 
+-struct bch_dev_sb_opt_set {
+-	void			(*set_sb)(struct bch_member *, u64);
+-};
+-
+-static const struct bch_dev_sb_opt_set bch2_dev_sb_opt_setters [] = {
+-#define x(n, set)	[Opt_##n] = { .set_sb = SET_##set },
+-	BCH_DEV_OPT_SETTERS()
+-#undef x
+-};
+-
+ void __bch2_opt_set_sb(struct bch_sb *sb, int dev_idx,
+ 		       const struct bch_option *opt, u64 v)
+ {
+-	enum bch_opt_id id = opt - bch2_opt_table;
+-
+ 	if (opt->flags & OPT_SB_FIELD_SECTORS)
+ 		v >>= 9;
+ 
+@@ -679,24 +684,16 @@ void __bch2_opt_set_sb(struct bch_sb *sb, int dev_idx,
+ 	if (opt->flags & OPT_SB_FIELD_ONE_BIAS)
+ 		v++;
+ 
+-	if (opt->flags & OPT_FS) {
+-		if (opt->set_sb != SET_BCH2_NO_SB_OPT)
+-			opt->set_sb(sb, v);
+-	}
++	if ((opt->flags & OPT_FS) && opt->set_sb && dev_idx < 0)
++		opt->set_sb(sb, v);
+ 
+-	if ((opt->flags & OPT_DEVICE) && dev_idx >= 0) {
++	if ((opt->flags & OPT_DEVICE) && opt->set_member && dev_idx >= 0) {
+ 		if (WARN(!bch2_member_exists(sb, dev_idx),
+ 			 "tried to set device option %s on nonexistent device %i",
+ 			 opt->attr.name, dev_idx))
+ 			return;
+ 
+-		struct bch_member *m = bch2_members_v2_get_mut(sb, dev_idx);
+-
+-		const struct bch_dev_sb_opt_set *set = bch2_dev_sb_opt_setters + id;
+-		if (set->set_sb)
+-			set->set_sb(m, v);
+-		else
+-			pr_err("option %s cannot be set via opt_set_sb()", opt->attr.name);
++		opt->set_member(bch2_members_v2_get_mut(sb, dev_idx), v);
+ 	}
+ }
+ 
+diff --git a/fs/bcachefs/opts.h b/fs/bcachefs/opts.h
+index 9d397fc2a1f0..9a3102f30e11 100644
+--- a/fs/bcachefs/opts.h
++++ b/fs/bcachefs/opts.h
+@@ -16,6 +16,7 @@ extern const char * const bch2_version_upgrade_opts[];
+ extern const char * const bch2_sb_features[];
+ extern const char * const bch2_sb_compat[];
+ extern const char * const __bch2_btree_ids[];
++extern const char * const __bch2_csum_types[];
+ extern const char * const __bch2_csum_opts[];
+ extern const char * const __bch2_compression_types[];
+ extern const char * const bch2_compression_opts[];
+@@ -50,10 +51,6 @@ static inline const char *bch2_d_type_str(unsigned d_type)
+  * apply the options from that struct that are defined.
+  */
+ 
+-/* dummy option, for options that aren't stored in the superblock */
+-u64 BCH2_NO_SB_OPT(const struct bch_sb *);
+-void SET_BCH2_NO_SB_OPT(struct bch_sb *, u64);
+-
+ /* When can be set: */
+ enum opt_flags {
+ 	OPT_FS			= BIT(0),	/* Filesystem option */
+@@ -132,19 +129,24 @@ enum fsck_err_opts {
+ 	  OPT_FS|OPT_FORMAT|						\
+ 	  OPT_HUMAN_READABLE|OPT_MUST_BE_POW_2|OPT_SB_FIELD_SECTORS,	\
+ 	  OPT_UINT(512, 1U << 16),					\
+-	  BCH_SB_BLOCK_SIZE,		8,				\
++	  BCH_SB_BLOCK_SIZE,		4 << 10,			\
+ 	  "size",	NULL)						\
+ 	x(btree_node_size,		u32,				\
+ 	  OPT_FS|OPT_FORMAT|						\
+ 	  OPT_HUMAN_READABLE|OPT_MUST_BE_POW_2|OPT_SB_FIELD_SECTORS,	\
+ 	  OPT_UINT(512, 1U << 20),					\
+-	  BCH_SB_BTREE_NODE_SIZE,	512,				\
++	  BCH_SB_BTREE_NODE_SIZE,	256 << 10,			\
+ 	  "size",	"Btree node size, default 256k")		\
+ 	x(errors,			u8,				\
+ 	  OPT_FS|OPT_FORMAT|OPT_MOUNT|OPT_RUNTIME,			\
+ 	  OPT_STR(bch2_error_actions),					\
+ 	  BCH_SB_ERROR_ACTION,		BCH_ON_ERROR_fix_safe,		\
+ 	  NULL,		"Action to take on filesystem error")		\
++	x(write_error_timeout,		u16,				\
++	  OPT_FS|OPT_FORMAT|OPT_MOUNT|OPT_RUNTIME,			\
++	  OPT_UINT(1, 300),						\
++	  BCH_SB_WRITE_ERROR_TIMEOUT,	30,				\
++	  NULL,		"Number of consecutive write errors allowed before kicking out a device")\
+ 	x(metadata_replicas,		u8,				\
+ 	  OPT_FS|OPT_FORMAT|OPT_MOUNT|OPT_RUNTIME,			\
+ 	  OPT_UINT(1, BCH_REPLICAS_MAX),				\
+@@ -181,6 +183,11 @@ enum fsck_err_opts {
+ 	  OPT_STR(__bch2_csum_opts),					\
+ 	  BCH_SB_DATA_CSUM_TYPE,	BCH_CSUM_OPT_crc32c,		\
+ 	  NULL,		NULL)						\
++	x(checksum_err_retry_nr,	u8,				\
++	  OPT_FS|OPT_FORMAT|OPT_MOUNT|OPT_RUNTIME,			\
++	  OPT_UINT(0, 32),						\
++	  BCH_SB_CSUM_ERR_RETRY_NR,	3,				\
++	  NULL,		NULL)						\
+ 	x(compression,			u8,				\
+ 	  OPT_FS|OPT_INODE|OPT_FORMAT|OPT_MOUNT|OPT_RUNTIME,		\
+ 	  OPT_FN(bch2_opt_compression),					\
+@@ -197,7 +204,7 @@ enum fsck_err_opts {
+ 	  BCH_SB_STR_HASH_TYPE,		BCH_STR_HASH_OPT_siphash,	\
+ 	  NULL,		"Hash function for directory entries and xattrs")\
+ 	x(metadata_target,		u16,				\
+-	  OPT_FS|OPT_INODE|OPT_FORMAT|OPT_MOUNT|OPT_RUNTIME,		\
++	  OPT_FS|OPT_FORMAT|OPT_MOUNT|OPT_RUNTIME,			\
+ 	  OPT_FN(bch2_opt_target),					\
+ 	  BCH_SB_METADATA_TARGET,	0,				\
+ 	  "(target)",	"Device or label for metadata writes")		\
+@@ -308,11 +315,6 @@ enum fsck_err_opts {
+ 	  OPT_BOOL(),							\
+ 	  BCH2_NO_SB_OPT,		false,				\
+ 	  NULL,		"Don't kick drives out when splitbrain detected")\
+-	x(discard,			u8,				\
+-	  OPT_FS|OPT_MOUNT|OPT_DEVICE,					\
+-	  OPT_BOOL(),							\
+-	  BCH2_NO_SB_OPT,		true,				\
+-	  NULL,		"Enable discard/TRIM support")			\
+ 	x(verbose,			u8,				\
+ 	  OPT_FS|OPT_MOUNT|OPT_RUNTIME,					\
+ 	  OPT_BOOL(),							\
+@@ -493,27 +495,32 @@ enum fsck_err_opts {
+ 	  BCH2_NO_SB_OPT,		false,				\
+ 	  NULL,		"Skip submit_bio() for data reads and writes, "	\
+ 			"for performance testing purposes")		\
+-	x(fs_size,			u64,				\
+-	  OPT_DEVICE,							\
++	x(state,			u64,				\
++	  OPT_DEVICE|OPT_RUNTIME,					\
++	  OPT_STR(bch2_member_states),					\
++	  BCH_MEMBER_STATE,		BCH_MEMBER_STATE_rw,		\
++	  "state",	"rw,ro,failed,spare")				\
++	x(bucket_size,			u32,				\
++	  OPT_DEVICE|OPT_HUMAN_READABLE|OPT_SB_FIELD_SECTORS,		\
+ 	  OPT_UINT(0, S64_MAX),						\
+-	  BCH2_NO_SB_OPT,		0,				\
+-	  "size",	"Size of filesystem on device")			\
+-	x(bucket,			u32,				\
+-	  OPT_DEVICE,							\
+-	  OPT_UINT(0, S64_MAX),						\
+-	  BCH2_NO_SB_OPT,		0,				\
++	  BCH_MEMBER_BUCKET_SIZE,	0,				\
+ 	  "size",	"Specifies the bucket size; must be greater than the btree node size")\
+ 	x(durability,			u8,				\
+-	  OPT_DEVICE|OPT_SB_FIELD_ONE_BIAS,				\
++	  OPT_DEVICE|OPT_RUNTIME|OPT_SB_FIELD_ONE_BIAS,			\
+ 	  OPT_UINT(0, BCH_REPLICAS_MAX),				\
+-	  BCH2_NO_SB_OPT,		1,				\
++	  BCH_MEMBER_DURABILITY,	1,				\
+ 	  "n",		"Data written to this device will be considered\n"\
+ 			"to have already been replicated n times")	\
+ 	x(data_allowed,			u8,				\
+ 	  OPT_DEVICE,							\
+ 	  OPT_BITFIELD(__bch2_data_types),				\
+-	  BCH2_NO_SB_OPT,		BIT(BCH_DATA_journal)|BIT(BCH_DATA_btree)|BIT(BCH_DATA_user),\
++	  BCH_MEMBER_DATA_ALLOWED,	BIT(BCH_DATA_journal)|BIT(BCH_DATA_btree)|BIT(BCH_DATA_user),\
+ 	  "types",	"Allowed data types for this device: journal, btree, and/or user")\
++	x(discard,			u8,				\
++	  OPT_MOUNT|OPT_DEVICE|OPT_RUNTIME,				\
++	  OPT_BOOL(),							\
++	  BCH_MEMBER_DISCARD,		true,				\
++	  NULL,		"Enable discard/TRIM support")			\
+ 	x(btree_node_prefetch,		u8,				\
+ 	  OPT_FS|OPT_MOUNT|OPT_RUNTIME,					\
+ 	  OPT_BOOL(),							\
+@@ -521,11 +528,6 @@ enum fsck_err_opts {
+ 	  NULL,		"BTREE_ITER_prefetch casuse btree nodes to be\n"\
+ 	  " prefetched sequentially")
+ 
+-#define BCH_DEV_OPT_SETTERS()						\
+-	x(discard,		BCH_MEMBER_DISCARD)			\
+-	x(durability,		BCH_MEMBER_DURABILITY)			\
+-	x(data_allowed,		BCH_MEMBER_DATA_ALLOWED)
+-
+ struct bch_opts {
+ #define x(_name, _bits, ...)	unsigned _name##_defined:1;
+ 	BCH_OPTS()
+@@ -582,8 +584,6 @@ struct printbuf;
+ 
+ struct bch_option {
+ 	struct attribute	attr;
+-	u64			(*get_sb)(const struct bch_sb *);
+-	void			(*set_sb)(struct bch_sb *, u64);
+ 	enum opt_type		type;
+ 	enum opt_flags		flags;
+ 	u64			min, max;
+@@ -595,6 +595,12 @@ struct bch_option {
+ 	const char		*hint;
+ 	const char		*help;
+ 
++	u64			(*get_sb)(const struct bch_sb *);
++	void			(*set_sb)(struct bch_sb *, u64);
++
++	u64			(*get_member)(const struct bch_member *);
++	void			(*set_member)(struct bch_member *, u64);
++
+ };
+ 
+ extern const struct bch_option bch2_opt_table[];
+@@ -603,7 +609,7 @@ bool bch2_opt_defined_by_id(const struct bch_opts *, enum bch_opt_id);
+ u64 bch2_opt_get_by_id(const struct bch_opts *, enum bch_opt_id);
+ void bch2_opt_set_by_id(struct bch_opts *, enum bch_opt_id, u64);
+ 
+-u64 bch2_opt_from_sb(struct bch_sb *, enum bch_opt_id);
++u64 bch2_opt_from_sb(struct bch_sb *, enum bch_opt_id, int);
+ int bch2_opts_from_sb(struct bch_opts *, struct bch_sb *);
+ void __bch2_opt_set_sb(struct bch_sb *, int, const struct bch_option *, u64);
+ 
+@@ -625,7 +631,7 @@ void bch2_opts_to_text(struct printbuf *,
+ 		       struct bch_fs *, struct bch_sb *,
+ 		       unsigned, unsigned, unsigned);
+ 
+-int bch2_opt_check_may_set(struct bch_fs *, int, u64);
++int bch2_opt_check_may_set(struct bch_fs *, struct bch_dev *, int, u64);
+ int bch2_opts_check_may_set(struct bch_fs *);
+ int bch2_parse_one_mount_opt(struct bch_fs *, struct bch_opts *,
+ 			     struct printbuf *, const char *, const char *);
+diff --git a/fs/bcachefs/progress.c b/fs/bcachefs/progress.c
+new file mode 100644
+index 000000000000..bafd1c91a802
+--- /dev/null
++++ b/fs/bcachefs/progress.c
+@@ -0,0 +1,63 @@
++// SPDX-License-Identifier: GPL-2.0
++#include "bcachefs.h"
++#include "bbpos.h"
++#include "disk_accounting.h"
++#include "progress.h"
++
++void bch2_progress_init(struct progress_indicator_state *s,
++			struct bch_fs *c,
++			u64 btree_id_mask)
++{
++	memset(s, 0, sizeof(*s));
++
++	s->next_print = jiffies + HZ * 10;
++
++	for (unsigned i = 0; i < BTREE_ID_NR; i++) {
++		if (!(btree_id_mask & BIT_ULL(i)))
++			continue;
++
++		struct disk_accounting_pos acc = {
++			.type		= BCH_DISK_ACCOUNTING_btree,
++			.btree.id	= i,
++		};
++
++		u64 v;
++		bch2_accounting_mem_read(c, disk_accounting_pos_to_bpos(&acc), &v, 1);
++		s->nodes_total += div64_ul(v, btree_sectors(c));
++	}
++}
++
++static inline bool progress_update_p(struct progress_indicator_state *s)
++{
++	bool ret = time_after_eq(jiffies, s->next_print);
++
++	if (ret)
++		s->next_print = jiffies + HZ * 10;
++	return ret;
++}
++
++void bch2_progress_update_iter(struct btree_trans *trans,
++			       struct progress_indicator_state *s,
++			       struct btree_iter *iter,
++			       const char *msg)
++{
++	struct bch_fs *c = trans->c;
++	struct btree *b = path_l(btree_iter_path(trans, iter))->b;
++
++	s->nodes_seen += b != s->last_node;
++	s->last_node = b;
++
++	if (progress_update_p(s)) {
++		struct printbuf buf = PRINTBUF;
++		unsigned percent = s->nodes_total
++			? div64_u64(s->nodes_seen * 100, s->nodes_total)
++			: 0;
++
++		prt_printf(&buf, "%s: %d%%, done %llu/%llu nodes, at ",
++			   msg, percent, s->nodes_seen, s->nodes_total);
++		bch2_bbpos_to_text(&buf, BBPOS(iter->btree_id, iter->pos));
++
++		bch_info(c, "%s", buf.buf);
++		printbuf_exit(&buf);
++	}
++}
+diff --git a/fs/bcachefs/progress.h b/fs/bcachefs/progress.h
+new file mode 100644
+index 000000000000..23fb1811f943
+--- /dev/null
++++ b/fs/bcachefs/progress.h
+@@ -0,0 +1,29 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#ifndef _BCACHEFS_PROGRESS_H
++#define _BCACHEFS_PROGRESS_H
++
++/*
++ * Lame progress indicators
++ *
++ * We don't like to use these because they print to the dmesg console, which is
++ * spammy - we much prefer to be wired up to a userspace programm (e.g. via
++ * thread_with_file) and have it print the progress indicator.
++ *
++ * But some code is old and doesn't support that, or runs in a context where
++ * that's not yet practical (mount).
++ */
++
++struct progress_indicator_state {
++	unsigned long		next_print;
++	u64			nodes_seen;
++	u64			nodes_total;
++	struct btree		*last_node;
++};
++
++void bch2_progress_init(struct progress_indicator_state *, struct bch_fs *, u64);
++void bch2_progress_update_iter(struct btree_trans *,
++			       struct progress_indicator_state *,
++			       struct btree_iter *,
++			       const char *);
++
++#endif /* _BCACHEFS_PROGRESS_H */
+diff --git a/fs/bcachefs/rebalance.c b/fs/bcachefs/rebalance.c
+index d0a1f5cd5c2b..10c6a7fda54c 100644
+--- a/fs/bcachefs/rebalance.c
++++ b/fs/bcachefs/rebalance.c
+@@ -26,9 +26,8 @@
+ 
+ /* bch_extent_rebalance: */
+ 
+-static const struct bch_extent_rebalance *bch2_bkey_rebalance_opts(struct bkey_s_c k)
++static const struct bch_extent_rebalance *bch2_bkey_ptrs_rebalance_opts(struct bkey_ptrs_c ptrs)
+ {
+-	struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
+ 	const union bch_extent_entry *entry;
+ 
+ 	bkey_extent_entry_for_each(ptrs, entry)
+@@ -38,6 +37,11 @@ static const struct bch_extent_rebalance *bch2_bkey_rebalance_opts(struct bkey_s
+ 	return NULL;
+ }
+ 
++static const struct bch_extent_rebalance *bch2_bkey_rebalance_opts(struct bkey_s_c k)
++{
++	return bch2_bkey_ptrs_rebalance_opts(bch2_bkey_ptrs_c(k));
++}
++
+ static inline unsigned bch2_bkey_ptrs_need_compress(struct bch_fs *c,
+ 					   struct bch_io_opts *opts,
+ 					   struct bkey_s_c k,
+@@ -91,17 +95,24 @@ static unsigned bch2_bkey_ptrs_need_rebalance(struct bch_fs *c,
+ {
+ 	struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
+ 
++	if (bch2_bkey_extent_ptrs_flags(ptrs) & BIT_ULL(BCH_EXTENT_FLAG_poisoned))
++		return 0;
++
+ 	return bch2_bkey_ptrs_need_compress(c, opts, k, ptrs) |
+ 		bch2_bkey_ptrs_need_move(c, opts, ptrs);
+ }
+ 
+ u64 bch2_bkey_sectors_need_rebalance(struct bch_fs *c, struct bkey_s_c k)
+ {
+-	const struct bch_extent_rebalance *opts = bch2_bkey_rebalance_opts(k);
++	struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
++
++	const struct bch_extent_rebalance *opts = bch2_bkey_ptrs_rebalance_opts(ptrs);
+ 	if (!opts)
+ 		return 0;
+ 
+-	struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
++	if (bch2_bkey_extent_ptrs_flags(ptrs) & BIT_ULL(BCH_EXTENT_FLAG_poisoned))
++		return 0;
++
+ 	const union bch_extent_entry *entry;
+ 	struct extent_ptr_decoded p;
+ 	u64 sectors = 0;
+@@ -341,7 +352,7 @@ static struct bkey_s_c next_rebalance_extent(struct btree_trans *trans,
+ 	memset(data_opts, 0, sizeof(*data_opts));
+ 	data_opts->rewrite_ptrs		= bch2_bkey_ptrs_need_rebalance(c, io_opts, k);
+ 	data_opts->target		= io_opts->background_target;
+-	data_opts->write_flags		|= BCH_WRITE_ONLY_SPECIFIED_DEVS;
++	data_opts->write_flags		|= BCH_WRITE_only_specified_devs;
+ 
+ 	if (!data_opts->rewrite_ptrs) {
+ 		/*
+@@ -449,7 +460,7 @@ static bool rebalance_pred(struct bch_fs *c, void *arg,
+ {
+ 	data_opts->rewrite_ptrs		= bch2_bkey_ptrs_need_rebalance(c, io_opts, k);
+ 	data_opts->target		= io_opts->background_target;
+-	data_opts->write_flags		|= BCH_WRITE_ONLY_SPECIFIED_DEVS;
++	data_opts->write_flags		|= BCH_WRITE_only_specified_devs;
+ 	return data_opts->rewrite_ptrs != 0;
+ }
+ 
+@@ -590,8 +601,19 @@ static int bch2_rebalance_thread(void *arg)
+ 
+ void bch2_rebalance_status_to_text(struct printbuf *out, struct bch_fs *c)
+ {
++	printbuf_tabstop_push(out, 32);
++
+ 	struct bch_fs_rebalance *r = &c->rebalance;
+ 
++	/* print pending work */
++	struct disk_accounting_pos acc = { .type = BCH_DISK_ACCOUNTING_rebalance_work, };
++	u64 v;
++	bch2_accounting_mem_read(c, disk_accounting_pos_to_bpos(&acc), &v, 1);
++
++	prt_printf(out, "pending work:\t");
++	prt_human_readable_u64(out, v);
++	prt_printf(out, "\n\n");
++
+ 	prt_str(out, bch2_rebalance_state_strs[r->state]);
+ 	prt_newline(out);
+ 	printbuf_indent_add(out, 2);
+@@ -600,15 +622,15 @@ void bch2_rebalance_status_to_text(struct printbuf *out, struct bch_fs *c)
+ 	case BCH_REBALANCE_waiting: {
+ 		u64 now = atomic64_read(&c->io_clock[WRITE].now);
+ 
+-		prt_str(out, "io wait duration:  ");
++		prt_printf(out, "io wait duration:\t");
+ 		bch2_prt_human_readable_s64(out, (r->wait_iotime_end - r->wait_iotime_start) << 9);
+ 		prt_newline(out);
+ 
+-		prt_str(out, "io wait remaining: ");
++		prt_printf(out, "io wait remaining:\t");
+ 		bch2_prt_human_readable_s64(out, (r->wait_iotime_end - now) << 9);
+ 		prt_newline(out);
+ 
+-		prt_str(out, "duration waited:   ");
++		prt_printf(out, "duration waited:\t");
+ 		bch2_pr_time_units(out, ktime_get_real_ns() - r->wait_wallclock_start);
+ 		prt_newline(out);
+ 		break;
+@@ -621,6 +643,18 @@ void bch2_rebalance_status_to_text(struct printbuf *out, struct bch_fs *c)
+ 		break;
+ 	}
+ 	prt_newline(out);
++
++	rcu_read_lock();
++	struct task_struct *t = rcu_dereference(c->rebalance.thread);
++	if (t)
++		get_task_struct(t);
++	rcu_read_unlock();
++
++	if (t) {
++		bch2_prt_task_backtrace(out, t, 0, GFP_KERNEL);
++		put_task_struct(t);
++	}
++
+ 	printbuf_indent_sub(out, 2);
+ }
+ 
+diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c
+index 71c786cdb192..266c5770c824 100644
+--- a/fs/bcachefs/recovery.c
++++ b/fs/bcachefs/recovery.c
+@@ -13,12 +13,12 @@
+ #include "disk_accounting.h"
+ #include "errcode.h"
+ #include "error.h"
+-#include "fs-common.h"
+ #include "journal_io.h"
+ #include "journal_reclaim.h"
+ #include "journal_seq_blacklist.h"
+ #include "logged_ops.h"
+ #include "move.h"
++#include "namei.h"
+ #include "quota.h"
+ #include "rebalance.h"
+ #include "recovery.h"
+@@ -899,7 +899,7 @@ int bch2_fs_recovery(struct bch_fs *c)
+ 	 * journal sequence numbers:
+ 	 */
+ 	if (!c->sb.clean)
+-		journal_seq += 8;
++		journal_seq += JOURNAL_BUF_NR * 4;
+ 
+ 	if (blacklist_seq != journal_seq) {
+ 		ret =   bch2_journal_log_msg(c, "blacklisting entries %llu-%llu",
+diff --git a/fs/bcachefs/recovery_passes_types.h b/fs/bcachefs/recovery_passes_types.h
+index 418557960ed6..e89b9c783285 100644
+--- a/fs/bcachefs/recovery_passes_types.h
++++ b/fs/bcachefs/recovery_passes_types.h
+@@ -24,7 +24,7 @@
+ 	x(check_topology,			 4, 0)					\
+ 	x(accounting_read,			39, PASS_ALWAYS)			\
+ 	x(alloc_read,				 0, PASS_ALWAYS)			\
+-	x(stripes_read,				 1, PASS_ALWAYS)			\
++	x(stripes_read,				 1, 0)					\
+ 	x(initialize_subvolumes,		 2, 0)					\
+ 	x(snapshots_read,			 3, PASS_ALWAYS)			\
+ 	x(check_allocations,			 5, PASS_FSCK)				\
+diff --git a/fs/bcachefs/reflink.c b/fs/bcachefs/reflink.c
+index 441e648f28b5..68172c6eba21 100644
+--- a/fs/bcachefs/reflink.c
++++ b/fs/bcachefs/reflink.c
+@@ -185,12 +185,21 @@ static int bch2_indirect_extent_missing_error(struct btree_trans *trans,
+ 	BUG_ON(missing_start	< refd_start);
+ 	BUG_ON(missing_end	> refd_end);
+ 
+-	if (fsck_err(trans, reflink_p_to_missing_reflink_v,
+-		     "pointer to missing indirect extent\n"
+-		     "  %s\n"
+-		     "  missing range %llu-%llu",
+-		     (bch2_bkey_val_to_text(&buf, c, p.s_c), buf.buf),
+-		     missing_start, missing_end)) {
++	struct bpos missing_pos = bkey_start_pos(p.k);
++	missing_pos.offset += missing_start - live_start;
++
++	prt_printf(&buf, "pointer to missing indirect extent in ");
++	ret = bch2_inum_snap_offset_err_msg_trans(trans, &buf, missing_pos);
++	if (ret)
++		goto err;
++
++	prt_printf(&buf, "-%llu\n  ", (missing_pos.offset + (missing_end - missing_start)) << 9);
++	bch2_bkey_val_to_text(&buf, c, p.s_c);
++
++	prt_printf(&buf, "\n  missing reflink btree range %llu-%llu",
++		   missing_start, missing_end);
++
++	if (fsck_err(trans, reflink_p_to_missing_reflink_v, "%s", buf.buf)) {
+ 		struct bkey_i_reflink_p *new = bch2_bkey_make_mut_noupdate_typed(trans, p.s_c, reflink_p);
+ 		ret = PTR_ERR_OR_ZERO(new);
+ 		if (ret)
+@@ -597,7 +606,7 @@ s64 bch2_remap_range(struct bch_fs *c,
+ 	u64 dst_done = 0;
+ 	u32 dst_snapshot, src_snapshot;
+ 	bool reflink_p_may_update_opts_field =
+-		bch2_request_incompat_feature(c, bcachefs_metadata_version_reflink_p_may_update_opts);
++		!bch2_request_incompat_feature(c, bcachefs_metadata_version_reflink_p_may_update_opts);
+ 	int ret = 0, ret2 = 0;
+ 
+ 	if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_reflink))
+diff --git a/fs/bcachefs/sb-counters.c b/fs/bcachefs/sb-counters.c
+index 6992e7469112..2b4b8445d418 100644
+--- a/fs/bcachefs/sb-counters.c
++++ b/fs/bcachefs/sb-counters.c
+@@ -5,7 +5,13 @@
+ 
+ /* BCH_SB_FIELD_counters */
+ 
+-static const char * const bch2_counter_names[] = {
++static const u8 counters_to_stable_map[] = {
++#define x(n, id, ...)	[BCH_COUNTER_##n] = BCH_COUNTER_STABLE_##n,
++	BCH_PERSISTENT_COUNTERS()
++#undef x
++};
++
++const char * const bch2_counter_names[] = {
+ #define x(t, n, ...) (#t),
+ 	BCH_PERSISTENT_COUNTERS()
+ #undef x
+@@ -18,13 +24,13 @@ static size_t bch2_sb_counter_nr_entries(struct bch_sb_field_counters *ctrs)
+ 		return 0;
+ 
+ 	return (__le64 *) vstruct_end(&ctrs->field) - &ctrs->d[0];
+-};
++}
+ 
+ static int bch2_sb_counters_validate(struct bch_sb *sb, struct bch_sb_field *f,
+ 				enum bch_validate_flags flags, struct printbuf *err)
+ {
+ 	return 0;
+-};
++}
+ 
+ static void bch2_sb_counters_to_text(struct printbuf *out, struct bch_sb *sb,
+ 			      struct bch_sb_field *f)
+@@ -32,50 +38,56 @@ static void bch2_sb_counters_to_text(struct printbuf *out, struct bch_sb *sb,
+ 	struct bch_sb_field_counters *ctrs = field_to_type(f, counters);
+ 	unsigned int nr = bch2_sb_counter_nr_entries(ctrs);
+ 
+-	for (unsigned i = 0; i < nr; i++)
+-		prt_printf(out, "%s \t%llu\n",
+-			   i < BCH_COUNTER_NR ? bch2_counter_names[i] : "(unknown)",
+-			   le64_to_cpu(ctrs->d[i]));
+-};
++	for (unsigned i = 0; i < BCH_COUNTER_NR; i++) {
++		unsigned stable = counters_to_stable_map[i];
++		if (stable < nr)
++			prt_printf(out, "%s \t%llu\n",
++				   bch2_counter_names[i],
++				   le64_to_cpu(ctrs->d[stable]));
++	}
++}
+ 
+ int bch2_sb_counters_to_cpu(struct bch_fs *c)
+ {
+ 	struct bch_sb_field_counters *ctrs = bch2_sb_field_get(c->disk_sb.sb, counters);
+-	unsigned int i;
+ 	unsigned int nr = bch2_sb_counter_nr_entries(ctrs);
+-	u64 val = 0;
+ 
+-	for (i = 0; i < BCH_COUNTER_NR; i++)
++	for (unsigned i = 0; i < BCH_COUNTER_NR; i++)
+ 		c->counters_on_mount[i] = 0;
+ 
+-	for (i = 0; i < min_t(unsigned int, nr, BCH_COUNTER_NR); i++) {
+-		val = le64_to_cpu(ctrs->d[i]);
+-		percpu_u64_set(&c->counters[i], val);
+-		c->counters_on_mount[i] = val;
++	for (unsigned i = 0; i < BCH_COUNTER_NR; i++) {
++		unsigned stable = counters_to_stable_map[i];
++		if (stable < nr) {
++			u64 v = le64_to_cpu(ctrs->d[stable]);
++			percpu_u64_set(&c->counters[i], v);
++			c->counters_on_mount[i] = v;
++		}
+ 	}
++
+ 	return 0;
+-};
++}
+ 
+ int bch2_sb_counters_from_cpu(struct bch_fs *c)
+ {
+ 	struct bch_sb_field_counters *ctrs = bch2_sb_field_get(c->disk_sb.sb, counters);
+ 	struct bch_sb_field_counters *ret;
+-	unsigned int i;
+ 	unsigned int nr = bch2_sb_counter_nr_entries(ctrs);
+ 
+ 	if (nr < BCH_COUNTER_NR) {
+ 		ret = bch2_sb_field_resize(&c->disk_sb, counters,
+-					       sizeof(*ctrs) / sizeof(u64) + BCH_COUNTER_NR);
+-
++					   sizeof(*ctrs) / sizeof(u64) + BCH_COUNTER_NR);
+ 		if (ret) {
+ 			ctrs = ret;
+ 			nr = bch2_sb_counter_nr_entries(ctrs);
+ 		}
+ 	}
+ 
++	for (unsigned i = 0; i < BCH_COUNTER_NR; i++) {
++		unsigned stable = counters_to_stable_map[i];
++		if (stable < nr)
++			ctrs->d[stable] = cpu_to_le64(percpu_u64_get(&c->counters[i]));
++	}
+ 
+-	for (i = 0; i < min_t(unsigned int, nr, BCH_COUNTER_NR); i++)
+-		ctrs->d[i] = cpu_to_le64(percpu_u64_get(&c->counters[i]));
+ 	return 0;
+ }
+ 
+@@ -97,3 +109,39 @@ const struct bch_sb_field_ops bch_sb_field_ops_counters = {
+ 	.validate	= bch2_sb_counters_validate,
+ 	.to_text	= bch2_sb_counters_to_text,
+ };
++
++#ifndef NO_BCACHEFS_CHARDEV
++long bch2_ioctl_query_counters(struct bch_fs *c,
++			struct bch_ioctl_query_counters __user *user_arg)
++{
++	struct bch_ioctl_query_counters arg;
++	int ret = copy_from_user_errcode(&arg, user_arg, sizeof(arg));
++	if (ret)
++		return ret;
++
++	if ((arg.flags & ~BCH_IOCTL_QUERY_COUNTERS_MOUNT) ||
++	    arg.pad)
++		return -EINVAL;
++
++	arg.nr = min(arg.nr, BCH_COUNTER_NR);
++	ret = put_user(arg.nr, &user_arg->nr);
++	if (ret)
++		return ret;
++
++	for (unsigned i = 0; i < BCH_COUNTER_NR; i++) {
++		unsigned stable = counters_to_stable_map[i];
++
++		if (stable < arg.nr) {
++			u64 v = !(arg.flags & BCH_IOCTL_QUERY_COUNTERS_MOUNT)
++				? percpu_u64_get(&c->counters[i])
++				: c->counters_on_mount[i];
++
++			ret = put_user(v, &user_arg->d[stable]);
++			if (ret)
++				return ret;
++		}
++	}
++
++	return 0;
++}
++#endif
+diff --git a/fs/bcachefs/sb-counters.h b/fs/bcachefs/sb-counters.h
+index 81f8aec9fcb1..a4329ad8dd1b 100644
+--- a/fs/bcachefs/sb-counters.h
++++ b/fs/bcachefs/sb-counters.h
+@@ -11,6 +11,10 @@ int bch2_sb_counters_from_cpu(struct bch_fs *);
+ void bch2_fs_counters_exit(struct bch_fs *);
+ int bch2_fs_counters_init(struct bch_fs *);
+ 
++extern const char * const bch2_counter_names[];
+ extern const struct bch_sb_field_ops bch_sb_field_ops_counters;
+ 
++long bch2_ioctl_query_counters(struct bch_fs *,
++			struct bch_ioctl_query_counters __user *);
++
+ #endif // _BCACHEFS_SB_COUNTERS_H
+diff --git a/fs/bcachefs/sb-counters_format.h b/fs/bcachefs/sb-counters_format.h
+index fdcf598f08b1..5c4e5de79d81 100644
+--- a/fs/bcachefs/sb-counters_format.h
++++ b/fs/bcachefs/sb-counters_format.h
+@@ -9,10 +9,25 @@ enum counters_flags {
+ 
+ #define BCH_PERSISTENT_COUNTERS()					\
+ 	x(io_read,					0,	TYPE_SECTORS)	\
++	x(io_read_inline,				80,	TYPE_SECTORS)	\
++	x(io_read_hole,					81,	TYPE_SECTORS)	\
++	x(io_read_promote,				30,	TYPE_COUNTER)	\
++	x(io_read_bounce,				31,	TYPE_COUNTER)	\
++	x(io_read_split,				33,	TYPE_COUNTER)	\
++	x(io_read_reuse_race,				34,	TYPE_COUNTER)	\
++	x(io_read_retry,				32,	TYPE_COUNTER)	\
++	x(io_read_fail_and_poison,			82,	TYPE_COUNTER)	\
+ 	x(io_write,					1,	TYPE_SECTORS)	\
+ 	x(io_move,					2,	TYPE_SECTORS)	\
++	x(io_move_read,					35,	TYPE_SECTORS)	\
++	x(io_move_write,				36,	TYPE_SECTORS)	\
++	x(io_move_finish,				37,	TYPE_SECTORS)	\
++	x(io_move_fail,					38,	TYPE_COUNTER)	\
++	x(io_move_write_fail,				82,	TYPE_COUNTER)	\
++	x(io_move_start_fail,				39,	TYPE_COUNTER)	\
+ 	x(bucket_invalidate,				3,	TYPE_COUNTER)	\
+ 	x(bucket_discard,				4,	TYPE_COUNTER)	\
++	x(bucket_discard_fast,				79,	TYPE_COUNTER)	\
+ 	x(bucket_alloc,					5,	TYPE_COUNTER)	\
+ 	x(bucket_alloc_fail,				6,	TYPE_COUNTER)	\
+ 	x(btree_cache_scan,				7,	TYPE_COUNTER)	\
+@@ -38,16 +53,6 @@ enum counters_flags {
+ 	x(journal_reclaim_finish,			27,	TYPE_COUNTER)	\
+ 	x(journal_reclaim_start,			28,	TYPE_COUNTER)	\
+ 	x(journal_write,				29,	TYPE_COUNTER)	\
+-	x(read_promote,					30,	TYPE_COUNTER)	\
+-	x(read_bounce,					31,	TYPE_COUNTER)	\
+-	x(read_split,					33,	TYPE_COUNTER)	\
+-	x(read_retry,					32,	TYPE_COUNTER)	\
+-	x(read_reuse_race,				34,	TYPE_COUNTER)	\
+-	x(move_extent_read,				35,	TYPE_SECTORS)	\
+-	x(move_extent_write,				36,	TYPE_SECTORS)	\
+-	x(move_extent_finish,				37,	TYPE_SECTORS)	\
+-	x(move_extent_fail,				38,	TYPE_COUNTER)	\
+-	x(move_extent_start_fail,			39,	TYPE_COUNTER)	\
+ 	x(copygc,					40,	TYPE_COUNTER)	\
+ 	x(copygc_wait,					41,	TYPE_COUNTER)	\
+ 	x(gc_gens_end,					42,	TYPE_COUNTER)	\
+@@ -95,6 +100,13 @@ enum bch_persistent_counters {
+ 	BCH_COUNTER_NR
+ };
+ 
++enum bch_persistent_counters_stable {
++#define x(t, n, ...) BCH_COUNTER_STABLE_##t = n,
++	BCH_PERSISTENT_COUNTERS()
++#undef x
++	BCH_COUNTER_STABLE_NR
++};
++
+ struct bch_sb_field_counters {
+ 	struct bch_sb_field	field;
+ 	__le64			d[];
+diff --git a/fs/bcachefs/sb-downgrade.c b/fs/bcachefs/sb-downgrade.c
+index 051214fdc735..acb5d845841e 100644
+--- a/fs/bcachefs/sb-downgrade.c
++++ b/fs/bcachefs/sb-downgrade.c
+@@ -90,7 +90,13 @@
+ 	  BIT_ULL(BCH_RECOVERY_PASS_check_allocations),		\
+ 	  BCH_FSCK_ERR_accounting_mismatch,			\
+ 	  BCH_FSCK_ERR_accounting_key_replicas_nr_devs_0,	\
+-	  BCH_FSCK_ERR_accounting_key_junk_at_end)
++	  BCH_FSCK_ERR_accounting_key_junk_at_end)		\
++	x(cached_backpointers,					\
++	  BIT_ULL(BCH_RECOVERY_PASS_check_extents_to_backpointers),\
++	  BCH_FSCK_ERR_ptr_to_missing_backpointer)		\
++	x(stripe_backpointers,					\
++	  BIT_ULL(BCH_RECOVERY_PASS_check_extents_to_backpointers),\
++	  BCH_FSCK_ERR_ptr_to_missing_backpointer)
+ 
+ #define DOWNGRADE_TABLE()					\
+ 	x(bucket_stripe_sectors,				\
+diff --git a/fs/bcachefs/sb-errors_format.h b/fs/bcachefs/sb-errors_format.h
+index b86ec013d7d7..1736abea9ed1 100644
+--- a/fs/bcachefs/sb-errors_format.h
++++ b/fs/bcachefs/sb-errors_format.h
+@@ -179,6 +179,7 @@ enum bch_fsck_flags {
+ 	x(ptr_crc_redundant,					160,	0)		\
+ 	x(ptr_crc_nonce_mismatch,				162,	0)		\
+ 	x(ptr_stripe_redundant,					163,	0)		\
++	x(extent_flags_not_at_start,				306,	0)		\
+ 	x(reservation_key_nr_replicas_invalid,			164,	0)		\
+ 	x(reflink_v_refcount_wrong,				165,	FSCK_AUTOFIX)	\
+ 	x(reflink_v_pos_bad,					292,	0)		\
+@@ -310,11 +311,14 @@ enum bch_fsck_flags {
+ 	x(accounting_key_replicas_nr_required_bad,		279,	FSCK_AUTOFIX)	\
+ 	x(accounting_key_replicas_devs_unsorted,		280,	FSCK_AUTOFIX)	\
+ 	x(accounting_key_version_0,				282,	FSCK_AUTOFIX)	\
++	x(accounting_key_nr_counters_wrong,			307,	FSCK_AUTOFIX)	\
+ 	x(logged_op_but_clean,					283,	FSCK_AUTOFIX)	\
+ 	x(compression_opt_not_marked_in_sb,			295,	FSCK_AUTOFIX)	\
+ 	x(compression_type_not_marked_in_sb,			296,	FSCK_AUTOFIX)	\
+ 	x(directory_size_mismatch,				303,	FSCK_AUTOFIX)	\
+-	x(MAX,							304,	0)
++	x(dirent_cf_name_too_big,				304,	0)		\
++	x(dirent_stray_data_after_cf_name,			305,	0)		\
++	x(MAX,							308,	0)
+ 
+ enum bch_sb_error_id {
+ #define x(t, n, ...) BCH_FSCK_ERR_##t = n,
+diff --git a/fs/bcachefs/sb-members.h b/fs/bcachefs/sb-members.h
+index 762083b564ee..38261638a611 100644
+--- a/fs/bcachefs/sb-members.h
++++ b/fs/bcachefs/sb-members.h
+@@ -23,7 +23,19 @@ static inline bool bch2_dev_is_online(struct bch_dev *ca)
+ 	return !percpu_ref_is_zero(&ca->io_ref);
+ }
+ 
+-static inline bool bch2_dev_is_readable(struct bch_dev *ca)
++static inline struct bch_dev *bch2_dev_rcu(struct bch_fs *, unsigned);
++
++static inline bool bch2_dev_idx_is_online(struct bch_fs *c, unsigned dev)
++{
++	rcu_read_lock();
++	struct bch_dev *ca = bch2_dev_rcu(c, dev);
++	bool ret = ca && bch2_dev_is_online(ca);
++	rcu_read_unlock();
++
++	return ret;
++}
++
++static inline bool bch2_dev_is_healthy(struct bch_dev *ca)
+ {
+ 	return bch2_dev_is_online(ca) &&
+ 		ca->mi.state != BCH_MEMBER_STATE_failed;
+@@ -271,6 +283,8 @@ static inline struct bch_dev *bch2_dev_iterate(struct bch_fs *c, struct bch_dev
+ 
+ static inline struct bch_dev *bch2_dev_get_ioref(struct bch_fs *c, unsigned dev, int rw)
+ {
++	might_sleep();
++
+ 	rcu_read_lock();
+ 	struct bch_dev *ca = bch2_dev_rcu(c, dev);
+ 	if (ca && !percpu_ref_tryget(&ca->io_ref))
+diff --git a/fs/bcachefs/sb-members_format.h b/fs/bcachefs/sb-members_format.h
+index 2adf1221a440..3affec823b3f 100644
+--- a/fs/bcachefs/sb-members_format.h
++++ b/fs/bcachefs/sb-members_format.h
+@@ -79,6 +79,7 @@ struct bch_member {
+ 
+ #define BCH_MEMBER_V1_BYTES	56
+ 
++LE16_BITMASK(BCH_MEMBER_BUCKET_SIZE,	struct bch_member, bucket_size,  0, 16)
+ LE64_BITMASK(BCH_MEMBER_STATE,		struct bch_member, flags,  0,  4)
+ /* 4-14 unused, was TIER, HAS_(META)DATA, REPLACEMENT */
+ LE64_BITMASK(BCH_MEMBER_DISCARD,	struct bch_member, flags, 14, 15)
+diff --git a/fs/bcachefs/snapshot.c b/fs/bcachefs/snapshot.c
+index c54091a28909..e7f197896db1 100644
+--- a/fs/bcachefs/snapshot.c
++++ b/fs/bcachefs/snapshot.c
+@@ -146,8 +146,9 @@ bool __bch2_snapshot_is_ancestor(struct bch_fs *c, u32 id, u32 ancestor)
+ 		goto out;
+ 	}
+ 
+-	while (id && id < ancestor - IS_ANCESTOR_BITMAP)
+-		id = get_ancestor_below(t, id, ancestor);
++	if (likely(ancestor >= IS_ANCESTOR_BITMAP))
++		while (id && id < ancestor - IS_ANCESTOR_BITMAP)
++			id = get_ancestor_below(t, id, ancestor);
+ 
+ 	ret = id && id < ancestor
+ 		? test_ancestor_bitmap(t, id, ancestor)
+@@ -389,7 +390,7 @@ static u32 bch2_snapshot_tree_next(struct bch_fs *c, u32 id)
+ 	return 0;
+ }
+ 
+-static u32 bch2_snapshot_tree_oldest_subvol(struct bch_fs *c, u32 snapshot_root)
++u32 bch2_snapshot_tree_oldest_subvol(struct bch_fs *c, u32 snapshot_root)
+ {
+ 	u32 id = snapshot_root;
+ 	u32 subvol = 0, s;
+diff --git a/fs/bcachefs/snapshot.h b/fs/bcachefs/snapshot.h
+index 00373cf32e7b..81180181d7c9 100644
+--- a/fs/bcachefs/snapshot.h
++++ b/fs/bcachefs/snapshot.h
+@@ -105,6 +105,7 @@ static inline u32 bch2_snapshot_nth_parent(struct bch_fs *c, u32 id, u32 n)
+ 	return id;
+ }
+ 
++u32 bch2_snapshot_tree_oldest_subvol(struct bch_fs *, u32);
+ u32 bch2_snapshot_skiplist_get(struct bch_fs *, u32);
+ 
+ static inline u32 bch2_snapshot_root(struct bch_fs *c, u32 id)
+diff --git a/fs/bcachefs/str_hash.c b/fs/bcachefs/str_hash.c
+index d78451c2a0c6..93e71119e5a4 100644
+--- a/fs/bcachefs/str_hash.c
++++ b/fs/bcachefs/str_hash.c
+@@ -50,7 +50,7 @@ static noinline int fsck_rename_dirent(struct btree_trans *trans,
+ 	for (unsigned i = 0; i < 1000; i++) {
+ 		unsigned len = sprintf(new->v.d_name, "%.*s.fsck_renamed-%u",
+ 				       old_name.len, old_name.name, i);
+-		unsigned u64s = BKEY_U64s + dirent_val_u64s(len);
++		unsigned u64s = BKEY_U64s + dirent_val_u64s(len, 0);
+ 
+ 		if (u64s > U8_MAX)
+ 			return -EINVAL;
+diff --git a/fs/bcachefs/str_hash.h b/fs/bcachefs/str_hash.h
+index 55a4ac7bf220..575ad1e03904 100644
+--- a/fs/bcachefs/str_hash.h
++++ b/fs/bcachefs/str_hash.h
+@@ -12,7 +12,6 @@
+ #include "super.h"
+ 
+ #include <linux/crc32c.h>
+-#include <crypto/hash.h>
+ #include <crypto/sha2.h>
+ 
+ static inline enum bch_str_hash_type
+@@ -34,6 +33,7 @@ bch2_str_hash_opt_to_type(struct bch_fs *c, enum bch_str_hash_opts opt)
+ 
+ struct bch_hash_info {
+ 	u8			type;
++	struct unicode_map 	*cf_encoding;
+ 	/*
+ 	 * For crc32 or crc64 string hashes the first key value of
+ 	 * the siphash_key (k0) is used as the key.
+@@ -47,17 +47,17 @@ bch2_hash_info_init(struct bch_fs *c, const struct bch_inode_unpacked *bi)
+ 	/* XXX ick */
+ 	struct bch_hash_info info = {
+ 		.type = INODE_STR_HASH(bi),
++#ifdef CONFIG_UNICODE
++		.cf_encoding = !!(bi->bi_flags & BCH_INODE_casefolded) ? c->cf_encoding : NULL,
++#endif
+ 		.siphash_key = { .k0 = bi->bi_hash_seed }
+ 	};
+ 
+ 	if (unlikely(info.type == BCH_STR_HASH_siphash_old)) {
+-		SHASH_DESC_ON_STACK(desc, c->sha256);
+ 		u8 digest[SHA256_DIGEST_SIZE];
+ 
+-		desc->tfm = c->sha256;
+-
+-		crypto_shash_digest(desc, (void *) &bi->bi_hash_seed,
+-				    sizeof(bi->bi_hash_seed), digest);
++		sha256((const u8 *)&bi->bi_hash_seed,
++		       sizeof(bi->bi_hash_seed), digest);
+ 		memcpy(&info.siphash_key, digest, sizeof(info.siphash_key));
+ 	}
+ 
+diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c
+index a81a7b6c0989..572b06bfa0b8 100644
+--- a/fs/bcachefs/super-io.c
++++ b/fs/bcachefs/super-io.c
+@@ -25,9 +25,6 @@
+ #include <linux/sort.h>
+ #include <linux/string_choices.h>
+ 
+-static const struct blk_holder_ops bch2_sb_handle_bdev_ops = {
+-};
+-
+ struct bch2_metadata_version {
+ 	u16		version;
+ 	const char	*name;
+@@ -69,12 +66,14 @@ enum bcachefs_metadata_version bch2_latest_compatible_version(enum bcachefs_meta
+ 	return v;
+ }
+ 
+-bool bch2_set_version_incompat(struct bch_fs *c, enum bcachefs_metadata_version version)
++int bch2_set_version_incompat(struct bch_fs *c, enum bcachefs_metadata_version version)
+ {
+-	bool ret = (c->sb.features & BIT_ULL(BCH_FEATURE_incompat_version_field)) &&
+-		   version <= c->sb.version_incompat_allowed;
++	int ret = ((c->sb.features & BIT_ULL(BCH_FEATURE_incompat_version_field)) &&
++		   version <= c->sb.version_incompat_allowed)
++		? 0
++		: -BCH_ERR_may_not_use_incompat_feature;
+ 
+-	if (ret) {
++	if (!ret) {
+ 		mutex_lock(&c->sb_lock);
+ 		SET_BCH_SB_VERSION_INCOMPAT(c->disk_sb.sb,
+ 			max(BCH_SB_VERSION_INCOMPAT(c->disk_sb.sb), version));
+@@ -366,39 +365,41 @@ static int bch2_sb_compatible(struct bch_sb *sb, struct printbuf *out)
+ 	return 0;
+ }
+ 
+-static int bch2_sb_validate(struct bch_sb_handle *disk_sb,
+-			    enum bch_validate_flags flags, struct printbuf *out)
++int bch2_sb_validate(struct bch_sb *sb, u64 read_offset,
++		     enum bch_validate_flags flags, struct printbuf *out)
+ {
+-	struct bch_sb *sb = disk_sb->sb;
+ 	struct bch_sb_field_members_v1 *mi;
+ 	enum bch_opt_id opt_id;
+-	u16 block_size;
+ 	int ret;
+ 
+ 	ret = bch2_sb_compatible(sb, out);
+ 	if (ret)
+ 		return ret;
+ 
+-	if (sb->features[1] ||
+-	    (le64_to_cpu(sb->features[0]) & (~0ULL << BCH_FEATURE_NR))) {
+-		prt_printf(out, "Filesystem has incompatible features");
++	u64 incompat = le64_to_cpu(sb->features[0]) & (~0ULL << BCH_FEATURE_NR);
++	unsigned incompat_bit = 0;
++	if (incompat)
++		incompat_bit = __ffs64(incompat);
++	else if (sb->features[1])
++		incompat_bit = 64 + __ffs64(le64_to_cpu(sb->features[1]));
++
++	if (incompat_bit) {
++		prt_printf(out, "Filesystem has incompatible feature bit %u, highest supported %s (%u)",
++			   incompat_bit,
++			   bch2_sb_features[BCH_FEATURE_NR - 1],
++			   BCH_FEATURE_NR - 1);
+ 		return -BCH_ERR_invalid_sb_features;
+ 	}
+ 
+ 	if (BCH_VERSION_MAJOR(le16_to_cpu(sb->version)) > BCH_VERSION_MAJOR(bcachefs_metadata_version_current) ||
+ 	    BCH_SB_VERSION_INCOMPAT(sb) > bcachefs_metadata_version_current) {
+-		prt_printf(out, "Filesystem has incompatible version");
++		prt_str(out, "Filesystem has incompatible version ");
++		bch2_version_to_text(out, le16_to_cpu(sb->version));
++		prt_str(out, ", current version ");
++		bch2_version_to_text(out, bcachefs_metadata_version_current);
+ 		return -BCH_ERR_invalid_sb_features;
+ 	}
+ 
+-	block_size = le16_to_cpu(sb->block_size);
+-
+-	if (block_size > PAGE_SECTORS) {
+-		prt_printf(out, "Block size too big (got %u, max %u)",
+-		       block_size, PAGE_SECTORS);
+-		return -BCH_ERR_invalid_sb_block_size;
+-	}
+-
+ 	if (bch2_is_zero(sb->user_uuid.b, sizeof(sb->user_uuid))) {
+ 		prt_printf(out, "Bad user UUID (got zeroes)");
+ 		return -BCH_ERR_invalid_sb_uuid;
+@@ -409,6 +410,13 @@ static int bch2_sb_validate(struct bch_sb_handle *disk_sb,
+ 		return -BCH_ERR_invalid_sb_uuid;
+ 	}
+ 
++	if (!(flags & BCH_VALIDATE_write) &&
++	    le64_to_cpu(sb->offset) != read_offset) {
++		prt_printf(out, "Bad sb offset (got %llu, read from %llu)",
++			   le64_to_cpu(sb->offset), read_offset);
++		return -BCH_ERR_invalid_sb_offset;
++	}
++
+ 	if (!sb->nr_devices ||
+ 	    sb->nr_devices > BCH_SB_MEMBERS_MAX) {
+ 		prt_printf(out, "Bad number of member devices %u (max %u)",
+@@ -464,6 +472,13 @@ static int bch2_sb_validate(struct bch_sb_handle *disk_sb,
+ 
+ 		if (le16_to_cpu(sb->version) <= bcachefs_metadata_version_disk_accounting_v2)
+ 			SET_BCH_SB_PROMOTE_WHOLE_EXTENTS(sb, true);
++
++		if (!BCH_SB_WRITE_ERROR_TIMEOUT(sb))
++			SET_BCH_SB_WRITE_ERROR_TIMEOUT(sb, 30);
++
++		if (le16_to_cpu(sb->version) <= bcachefs_metadata_version_extent_flags &&
++		    !BCH_SB_CSUM_ERR_RETRY_NR(sb))
++			SET_BCH_SB_CSUM_ERR_RETRY_NR(sb, 3);
+ 	}
+ 
+ #ifdef __KERNEL__
+@@ -474,8 +489,8 @@ static int bch2_sb_validate(struct bch_sb_handle *disk_sb,
+ 	for (opt_id = 0; opt_id < bch2_opts_nr; opt_id++) {
+ 		const struct bch_option *opt = bch2_opt_table + opt_id;
+ 
+-		if (opt->get_sb != BCH2_NO_SB_OPT) {
+-			u64 v = bch2_opt_from_sb(sb, opt_id);
++		if (opt->get_sb) {
++			u64 v = bch2_opt_from_sb(sb, opt_id, -1);
+ 
+ 			prt_printf(out, "Invalid option ");
+ 			ret = bch2_opt_validate(opt, v, out);
+@@ -755,7 +770,7 @@ static int __bch2_read_super(const char *path, struct bch_opts *opts,
+ 	memset(sb, 0, sizeof(*sb));
+ 	sb->mode	= BLK_OPEN_READ;
+ 	sb->have_bio	= true;
+-	sb->holder	= kmalloc(1, GFP_KERNEL);
++	sb->holder	= kzalloc(sizeof(*sb->holder), GFP_KERNEL);
+ 	if (!sb->holder)
+ 		return -ENOMEM;
+ 
+@@ -881,7 +896,7 @@ static int __bch2_read_super(const char *path, struct bch_opts *opts,
+ 
+ 	sb->have_layout = true;
+ 
+-	ret = bch2_sb_validate(sb, 0, &err);
++	ret = bch2_sb_validate(sb->sb, offset, 0, &err);
+ 	if (ret) {
+ 		bch2_print_opts(opts, KERN_ERR "bcachefs (%s): error validating superblock: %s\n",
+ 				path, err.buf);
+@@ -918,16 +933,16 @@ static void write_super_endio(struct bio *bio)
+ {
+ 	struct bch_dev *ca = bio->bi_private;
+ 
++	bch2_account_io_success_fail(ca, bio_data_dir(bio), !bio->bi_status);
++
+ 	/* XXX: return errors directly */
+ 
+-	if (bch2_dev_io_err_on(bio->bi_status, ca,
+-			       bio_data_dir(bio)
+-			       ? BCH_MEMBER_ERROR_write
+-			       : BCH_MEMBER_ERROR_read,
+-			       "superblock %s error: %s",
++	if (bio->bi_status) {
++		bch_err_dev_ratelimited(ca, "superblock %s error: %s",
+ 			       str_write_read(bio_data_dir(bio)),
+-			       bch2_blk_status_to_str(bio->bi_status)))
++			       bch2_blk_status_to_str(bio->bi_status));
+ 		ca->sb_write_error = 1;
++	}
+ 
+ 	closure_put(&ca->fs->sb_write);
+ 	percpu_ref_put(&ca->io_ref);
+@@ -1038,7 +1053,7 @@ int bch2_write_super(struct bch_fs *c)
+ 	darray_for_each(online_devices, ca) {
+ 		printbuf_reset(&err);
+ 
+-		ret = bch2_sb_validate(&(*ca)->disk_sb, BCH_VALIDATE_write, &err);
++		ret = bch2_sb_validate((*ca)->disk_sb.sb, 0, BCH_VALIDATE_write, &err);
+ 		if (ret) {
+ 			bch2_fs_inconsistent(c, "sb invalid before write: %s", err.buf);
+ 			goto out;
+@@ -1166,7 +1181,7 @@ int bch2_write_super(struct bch_fs *c)
+ 				  !can_mount_with_written), c,
+ 		": Unable to write superblock to sufficient devices (from %ps)",
+ 		(void *) _RET_IP_))
+-		ret = -1;
++		ret = -BCH_ERR_erofs_sb_err;
+ out:
+ 	/* Make new options visible after they're persistent: */
+ 	bch2_sb_update(c);
+@@ -1223,12 +1238,11 @@ void bch2_sb_upgrade(struct bch_fs *c, unsigned new_version, bool incompat)
+ 		bch2_sb_field_resize(&c->disk_sb, downgrade, 0);
+ 
+ 	c->disk_sb.sb->version = cpu_to_le16(new_version);
+-	c->disk_sb.sb->features[0] |= cpu_to_le64(BCH_SB_FEATURES_ALL);
+ 
+ 	if (incompat) {
++		c->disk_sb.sb->features[0] |= cpu_to_le64(BCH_SB_FEATURES_ALL);
+ 		SET_BCH_SB_VERSION_INCOMPAT_ALLOWED(c->disk_sb.sb,
+ 			max(BCH_SB_VERSION_INCOMPAT_ALLOWED(c->disk_sb.sb), new_version));
+-		c->disk_sb.sb->features[0] |= cpu_to_le64(BCH_FEATURE_incompat_version_field);
+ 	}
+ }
+ 
+@@ -1459,8 +1473,8 @@ void bch2_sb_to_text(struct printbuf *out, struct bch_sb *sb,
+ 		for (id = 0; id < bch2_opts_nr; id++) {
+ 			const struct bch_option *opt = bch2_opt_table + id;
+ 
+-			if (opt->get_sb != BCH2_NO_SB_OPT) {
+-				u64 v = bch2_opt_from_sb(sb, id);
++			if (opt->get_sb) {
++				u64 v = bch2_opt_from_sb(sb, id, -1);
+ 
+ 				prt_printf(out, "%s:\t", opt->attr.name);
+ 				bch2_opt_to_text(out, NULL, sb, opt, v,
+diff --git a/fs/bcachefs/super-io.h b/fs/bcachefs/super-io.h
+index b4cff9ebdebb..78f708a6fbcd 100644
+--- a/fs/bcachefs/super-io.h
++++ b/fs/bcachefs/super-io.h
+@@ -21,13 +21,13 @@ static inline bool bch2_version_compatible(u16 version)
+ void bch2_version_to_text(struct printbuf *, enum bcachefs_metadata_version);
+ enum bcachefs_metadata_version bch2_latest_compatible_version(enum bcachefs_metadata_version);
+ 
+-bool bch2_set_version_incompat(struct bch_fs *, enum bcachefs_metadata_version);
++int bch2_set_version_incompat(struct bch_fs *, enum bcachefs_metadata_version);
+ 
+-static inline bool bch2_request_incompat_feature(struct bch_fs *c,
+-						 enum bcachefs_metadata_version version)
++static inline int bch2_request_incompat_feature(struct bch_fs *c,
++						enum bcachefs_metadata_version version)
+ {
+ 	return likely(version <= c->sb.version_incompat)
+-		? true
++		? 0
+ 		: bch2_set_version_incompat(c, version);
+ }
+ 
+@@ -92,6 +92,8 @@ int bch2_sb_from_fs(struct bch_fs *, struct bch_dev *);
+ void bch2_free_super(struct bch_sb_handle *);
+ int bch2_sb_realloc(struct bch_sb_handle *, unsigned);
+ 
++int bch2_sb_validate(struct bch_sb *, u64, enum bch_validate_flags, struct printbuf *);
++
+ int bch2_read_super(const char *, struct bch_opts *, struct bch_sb_handle *);
+ int bch2_read_super_silent(const char *, struct bch_opts *, struct bch_sb_handle *);
+ int bch2_write_super(struct bch_fs *);
+diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c
+index 0459c875e189..99f9a0aaa380 100644
+--- a/fs/bcachefs/super.c
++++ b/fs/bcachefs/super.c
+@@ -75,9 +75,6 @@
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Kent Overstreet <kent.overstreet@gmail.com>");
+ MODULE_DESCRIPTION("bcachefs filesystem");
+-MODULE_SOFTDEP("pre: crc32c");
+-MODULE_SOFTDEP("pre: crc64");
+-MODULE_SOFTDEP("pre: sha256");
+ MODULE_SOFTDEP("pre: chacha20");
+ MODULE_SOFTDEP("pre: poly1305");
+ MODULE_SOFTDEP("pre: xxhash");
+@@ -718,7 +715,7 @@ static int bch2_fs_online(struct bch_fs *c)
+ 	    kobject_add(&c->time_stats, &c->kobj, "time_stats") ?:
+ #endif
+ 	    kobject_add(&c->counters_kobj, &c->kobj, "counters") ?:
+-	    bch2_opts_create_sysfs_files(&c->opts_dir);
++	    bch2_opts_create_sysfs_files(&c->opts_dir, OPT_FS);
+ 	if (ret) {
+ 		bch_err(c, "error creating sysfs objects");
+ 		return ret;
+@@ -837,6 +834,25 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
+ 	if (ret)
+ 		goto err;
+ 
++#ifdef CONFIG_UNICODE
++	/* Default encoding until we can potentially have more as an option. */
++	c->cf_encoding = utf8_load(BCH_FS_DEFAULT_UTF8_ENCODING);
++	if (IS_ERR(c->cf_encoding)) {
++		printk(KERN_ERR "Cannot load UTF-8 encoding for filesystem. Version: %u.%u.%u",
++			unicode_major(BCH_FS_DEFAULT_UTF8_ENCODING),
++			unicode_minor(BCH_FS_DEFAULT_UTF8_ENCODING),
++			unicode_rev(BCH_FS_DEFAULT_UTF8_ENCODING));
++		ret = -EINVAL;
++		goto err;
++	}
++#else
++	if (c->sb.features & BIT_ULL(BCH_FEATURE_casefolding)) {
++		printk(KERN_ERR "Cannot mount a filesystem with casefolding on a kernel without CONFIG_UNICODE\n");
++		ret = -EINVAL;
++		goto err;
++	}
++#endif
++
+ 	pr_uuid(&name, c->sb.user_uuid.b);
+ 	ret = name.allocation_failure ? -BCH_ERR_ENOMEM_fs_name_alloc : 0;
+ 	if (ret)
+@@ -1056,6 +1072,7 @@ int bch2_fs_start(struct bch_fs *c)
+ 	}
+ 
+ 	set_bit(BCH_FS_started, &c->flags);
++	wake_up(&c->ro_ref_wait);
+ 
+ 	if (c->opts.read_only) {
+ 		bch2_fs_read_only(c);
+@@ -1280,8 +1297,8 @@ static int bch2_dev_sysfs_online(struct bch_fs *c, struct bch_dev *ca)
+ 		return 0;
+ 
+ 	if (!ca->kobj.state_in_sysfs) {
+-		ret = kobject_add(&ca->kobj, &c->kobj,
+-				  "dev-%u", ca->dev_idx);
++		ret =   kobject_add(&ca->kobj, &c->kobj, "dev-%u", ca->dev_idx) ?:
++			bch2_opts_create_sysfs_files(&ca->kobj, OPT_DEVICE);
+ 		if (ret)
+ 			return ret;
+ 	}
+@@ -1412,6 +1429,13 @@ static int __bch2_dev_attach_bdev(struct bch_dev *ca, struct bch_sb_handle *sb)
+ 	ca->disk_sb = *sb;
+ 	memset(sb, 0, sizeof(*sb));
+ 
++	/*
++	 * Stash pointer to the filesystem for blk_holder_ops - note that once
++	 * attached to a filesystem, we will always close the block device
++	 * before tearing down the filesystem object.
++	 */
++	ca->disk_sb.holder->c = ca->fs;
++
+ 	ca->dev = ca->disk_sb.bdev->bd_dev;
+ 
+ 	percpu_ref_reinit(&ca->io_ref);
+@@ -1966,15 +1990,12 @@ int bch2_dev_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets)
+ 	mutex_unlock(&c->sb_lock);
+ 
+ 	if (ca->mi.freespace_initialized) {
+-		struct disk_accounting_pos acc = {
+-			.type = BCH_DISK_ACCOUNTING_dev_data_type,
+-			.dev_data_type.dev = ca->dev_idx,
+-			.dev_data_type.data_type = BCH_DATA_free,
+-		};
+ 		u64 v[3] = { nbuckets - old_nbuckets, 0, 0 };
+ 
+ 		ret   = bch2_trans_commit_do(ca->fs, NULL, NULL, 0,
+-				bch2_disk_accounting_mod(trans, &acc, v, ARRAY_SIZE(v), false)) ?:
++				bch2_disk_accounting_mod2(trans, false, v, dev_data_type,
++							  .dev = ca->dev_idx,
++							  .data_type = BCH_DATA_free)) ?:
+ 			bch2_dev_freespace_init(c, ca, old_nbuckets, nbuckets);
+ 		if (ret)
+ 			goto err;
+@@ -1998,6 +2019,102 @@ struct bch_dev *bch2_dev_lookup(struct bch_fs *c, const char *name)
+ 	return ERR_PTR(-BCH_ERR_ENOENT_dev_not_found);
+ }
+ 
++/* blk_holder_ops: */
++
++static struct bch_fs *bdev_get_fs(struct block_device *bdev)
++	__releases(&bdev->bd_holder_lock)
++{
++	struct bch_sb_handle_holder *holder = bdev->bd_holder;
++	struct bch_fs *c = holder->c;
++
++	if (c && !bch2_ro_ref_tryget(c))
++		c = NULL;
++
++	mutex_unlock(&bdev->bd_holder_lock);
++
++	if (c)
++		wait_event(c->ro_ref_wait, test_bit(BCH_FS_started, &c->flags));
++	return c;
++}
++
++/* returns with ref on ca->ref */
++static struct bch_dev *bdev_to_bch_dev(struct bch_fs *c, struct block_device *bdev)
++{
++	for_each_member_device(c, ca)
++		if (ca->disk_sb.bdev == bdev)
++			return ca;
++	return NULL;
++}
++
++static void bch2_fs_bdev_mark_dead(struct block_device *bdev, bool surprise)
++{
++	struct bch_fs *c = bdev_get_fs(bdev);
++	if (!c)
++		return;
++
++	struct super_block *sb = c->vfs_sb;
++	if (sb) {
++		/*
++		 * Not necessary, c->ro_ref guards against the filesystem being
++		 * unmounted - we only take this to avoid a warning in
++		 * sync_filesystem:
++		 */
++		down_read(&sb->s_umount);
++	}
++
++	down_write(&c->state_lock);
++	struct bch_dev *ca = bdev_to_bch_dev(c, bdev);
++	if (!ca)
++		goto unlock;
++
++	if (bch2_dev_state_allowed(c, ca, BCH_MEMBER_STATE_failed, BCH_FORCE_IF_DEGRADED)) {
++		__bch2_dev_offline(c, ca);
++	} else {
++		if (sb) {
++			if (!surprise)
++				sync_filesystem(sb);
++			shrink_dcache_sb(sb);
++			evict_inodes(sb);
++		}
++
++		bch2_journal_flush(&c->journal);
++		bch2_fs_emergency_read_only(c);
++	}
++
++	bch2_dev_put(ca);
++unlock:
++	if (sb)
++		up_read(&sb->s_umount);
++	up_write(&c->state_lock);
++	bch2_ro_ref_put(c);
++}
++
++static void bch2_fs_bdev_sync(struct block_device *bdev)
++{
++	struct bch_fs *c = bdev_get_fs(bdev);
++	if (!c)
++		return;
++
++	struct super_block *sb = c->vfs_sb;
++	if (sb) {
++		/*
++		 * Not necessary, c->ro_ref guards against the filesystem being
++		 * unmounted - we only take this to avoid a warning in
++		 * sync_filesystem:
++		 */
++		down_read(&sb->s_umount);
++		sync_filesystem(sb);
++		up_read(&sb->s_umount);
++	}
++
++	bch2_ro_ref_put(c);
++}
++
++const struct blk_holder_ops bch2_sb_handle_bdev_ops = {
++	.mark_dead		= bch2_fs_bdev_mark_dead,
++	.sync			= bch2_fs_bdev_sync,
++};
++
+ /* Filesystem open: */
+ 
+ static inline int sb_cmp(struct bch_sb *l, struct bch_sb *r)
+diff --git a/fs/bcachefs/super.h b/fs/bcachefs/super.h
+index 04f8287eff5c..23533bce5709 100644
+--- a/fs/bcachefs/super.h
++++ b/fs/bcachefs/super.h
+@@ -42,4 +42,6 @@ void bch2_fs_stop(struct bch_fs *);
+ int bch2_fs_start(struct bch_fs *);
+ struct bch_fs *bch2_fs_open(char * const *, unsigned, struct bch_opts);
+ 
++extern const struct blk_holder_ops bch2_sb_handle_bdev_ops;
++
+ #endif /* _BCACHEFS_SUPER_H */
+diff --git a/fs/bcachefs/super_types.h b/fs/bcachefs/super_types.h
+index 368a63d938cf..3a899f799d1d 100644
+--- a/fs/bcachefs/super_types.h
++++ b/fs/bcachefs/super_types.h
+@@ -2,13 +2,19 @@
+ #ifndef _BCACHEFS_SUPER_TYPES_H
+ #define _BCACHEFS_SUPER_TYPES_H
+ 
++struct bch_fs;
++
++struct bch_sb_handle_holder {
++	struct bch_fs		*c;
++};
++
+ struct bch_sb_handle {
+ 	struct bch_sb		*sb;
+ 	struct file		*s_bdev_file;
+ 	struct block_device	*bdev;
+ 	char			*sb_name;
+ 	struct bio		*bio;
+-	void			*holder;
++	struct bch_sb_handle_holder *holder;
+ 	size_t			buffer_size;
+ 	blk_mode_t		mode;
+ 	unsigned		have_layout:1;
+diff --git a/fs/bcachefs/sysfs.c b/fs/bcachefs/sysfs.c
+index a7eb1f511484..8c200b558c69 100644
+--- a/fs/bcachefs/sysfs.c
++++ b/fs/bcachefs/sysfs.c
+@@ -146,15 +146,15 @@ write_attribute(trigger_journal_writes);
+ write_attribute(trigger_btree_cache_shrink);
+ write_attribute(trigger_btree_key_cache_shrink);
+ write_attribute(trigger_freelist_wakeup);
++write_attribute(trigger_btree_updates);
+ read_attribute(gc_gens_pos);
++__sysfs_attribute(read_fua_test, 0400);
+ 
+ read_attribute(uuid);
+ read_attribute(minor);
+ read_attribute(flags);
+-read_attribute(bucket_size);
+ read_attribute(first_bucket);
+ read_attribute(nbuckets);
+-rw_attribute(durability);
+ read_attribute(io_done);
+ read_attribute(io_errors);
+ write_attribute(io_errors_reset);
+@@ -173,10 +173,8 @@ read_attribute(journal_debug);
+ read_attribute(btree_cache);
+ read_attribute(btree_key_cache);
+ read_attribute(btree_reserve_cache);
+-read_attribute(stripes_heap);
+ read_attribute(open_buckets);
+ read_attribute(open_buckets_partial);
+-read_attribute(write_points);
+ read_attribute(nocow_lock_table);
+ 
+ #ifdef BCH_WRITE_REF_DEBUG
+@@ -209,8 +207,6 @@ read_attribute(usage_base);
+ BCH_PERSISTENT_COUNTERS()
+ #undef x
+ 
+-rw_attribute(discard);
+-read_attribute(state);
+ rw_attribute(label);
+ 
+ read_attribute(copy_gc_wait);
+@@ -315,6 +311,116 @@ static void bch2_fs_usage_base_to_text(struct printbuf *out, struct bch_fs *c)
+ 	prt_printf(out, "nr_inodes:\t%llu\n",	b.nr_inodes);
+ }
+ 
++static int bch2_read_fua_test(struct printbuf *out, struct bch_dev *ca)
++{
++	struct bch_fs *c = ca->fs;
++	struct bio *bio = NULL;
++	void *buf = NULL;
++	unsigned bs = c->opts.block_size, iters;
++	u64 end, test_duration = NSEC_PER_SEC * 2;
++	struct bch2_time_stats stats_nofua, stats_fua, stats_random;
++	int ret = 0;
++
++	bch2_time_stats_init_no_pcpu(&stats_nofua);
++	bch2_time_stats_init_no_pcpu(&stats_fua);
++	bch2_time_stats_init_no_pcpu(&stats_random);
++
++	if (!bch2_dev_get_ioref(c, ca->dev_idx, READ)) {
++		prt_str(out, "offline\n");
++		return 0;
++	}
++
++	struct block_device *bdev = ca->disk_sb.bdev;
++
++	bio = bio_kmalloc(1, GFP_KERNEL);
++	if (!bio) {
++		ret = -ENOMEM;
++		goto err;
++	}
++
++	buf = kmalloc(bs, GFP_KERNEL);
++	if (!buf)
++		goto err;
++
++	end = ktime_get_ns() + test_duration;
++	for (iters = 0; iters < 1000 && time_before64(ktime_get_ns(), end); iters++) {
++		bio_init(bio, bdev, bio->bi_inline_vecs, 1, READ);
++		bch2_bio_map(bio, buf, bs);
++
++		u64 submit_time = ktime_get_ns();
++		ret = submit_bio_wait(bio);
++		bch2_time_stats_update(&stats_nofua, submit_time);
++
++		if (ret)
++			goto err;
++	}
++
++	end = ktime_get_ns() + test_duration;
++	for (iters = 0; iters < 1000 && time_before64(ktime_get_ns(), end); iters++) {
++		bio_init(bio, bdev, bio->bi_inline_vecs, 1, REQ_FUA|READ);
++		bch2_bio_map(bio, buf, bs);
++
++		u64 submit_time = ktime_get_ns();
++		ret = submit_bio_wait(bio);
++		bch2_time_stats_update(&stats_fua, submit_time);
++
++		if (ret)
++			goto err;
++	}
++
++	u64 dev_size = ca->mi.nbuckets * bucket_bytes(ca);
++
++	end = ktime_get_ns() + test_duration;
++	for (iters = 0; iters < 1000 && time_before64(ktime_get_ns(), end); iters++) {
++		bio_init(bio, bdev, bio->bi_inline_vecs, 1, READ);
++		bio->bi_iter.bi_sector = (bch2_get_random_u64_below(dev_size) & ~((u64) bs - 1)) >> 9;
++		bch2_bio_map(bio, buf, bs);
++
++		u64 submit_time = ktime_get_ns();
++		ret = submit_bio_wait(bio);
++		bch2_time_stats_update(&stats_random, submit_time);
++
++		if (ret)
++			goto err;
++	}
++
++	u64 ns_nofua		= mean_and_variance_get_mean(stats_nofua.duration_stats);
++	u64 ns_fua		= mean_and_variance_get_mean(stats_fua.duration_stats);
++	u64 ns_rand		= mean_and_variance_get_mean(stats_random.duration_stats);
++
++	u64 stddev_nofua	= mean_and_variance_get_stddev(stats_nofua.duration_stats);
++	u64 stddev_fua		= mean_and_variance_get_stddev(stats_fua.duration_stats);
++	u64 stddev_rand		= mean_and_variance_get_stddev(stats_random.duration_stats);
++
++	printbuf_tabstop_push(out, 8);
++	printbuf_tabstop_push(out, 12);
++	printbuf_tabstop_push(out, 12);
++	prt_printf(out, "This test must be run on an idle drive for accurate results\n");
++	prt_printf(out, "%s\n", dev_name(&ca->disk_sb.bdev->bd_device));
++	prt_printf(out, "fua support advertized: %s\n", bdev_fua(bdev) ? "yes" : "no");
++	prt_newline(out);
++	prt_printf(out, "ns:\tlatency\rstddev\r\n");
++	prt_printf(out, "nofua\t%llu\r%llu\r\n",	ns_nofua,	stddev_nofua);
++	prt_printf(out, "fua\t%llu\r%llu\r\n",		ns_fua,		stddev_fua);
++	prt_printf(out, "random\t%llu\r%llu\r\n",	ns_rand,	stddev_rand);
++
++	bool read_cache = ns_nofua * 2 < ns_rand;
++	bool fua_cached	= read_cache && ns_fua < (ns_nofua + ns_rand) / 2;
++
++	if (!read_cache)
++		prt_str(out, "reads don't appear to be cached - safe\n");
++	else if (!fua_cached)
++		prt_str(out, "fua reads don't appear to be cached - safe\n");
++	else
++		prt_str(out, "fua reads appear to be cached - unsafe\n");
++err:
++	kfree(buf);
++	kfree(bio);
++	percpu_ref_put(&ca->io_ref);
++	bch_err_fn(c, ret);
++	return ret;
++}
++
+ SHOW(bch2_fs)
+ {
+ 	struct bch_fs *c = container_of(kobj, struct bch_fs, kobj);
+@@ -355,18 +461,12 @@ SHOW(bch2_fs)
+ 	if (attr == &sysfs_btree_reserve_cache)
+ 		bch2_btree_reserve_cache_to_text(out, c);
+ 
+-	if (attr == &sysfs_stripes_heap)
+-		bch2_stripes_heap_to_text(out, c);
+-
+ 	if (attr == &sysfs_open_buckets)
+ 		bch2_open_buckets_to_text(out, c, NULL);
+ 
+ 	if (attr == &sysfs_open_buckets_partial)
+ 		bch2_open_buckets_partial_to_text(out, c);
+ 
+-	if (attr == &sysfs_write_points)
+-		bch2_write_points_to_text(out, c);
+-
+ 	if (attr == &sysfs_compression_stats)
+ 		bch2_compression_stats_to_text(out, c);
+ 
+@@ -415,6 +515,9 @@ STORE(bch2_fs)
+ 
+ 	/* Debugging: */
+ 
++	if (attr == &sysfs_trigger_btree_updates)
++		queue_work(c->btree_interior_update_worker, &c->btree_interior_update_work);
++
+ 	if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_sysfs))
+ 		return -EROFS;
+ 
+@@ -566,10 +669,8 @@ struct attribute *bch2_fs_internal_files[] = {
+ 	&sysfs_btree_key_cache,
+ 	&sysfs_btree_reserve_cache,
+ 	&sysfs_new_stripes,
+-	&sysfs_stripes_heap,
+ 	&sysfs_open_buckets,
+ 	&sysfs_open_buckets_partial,
+-	&sysfs_write_points,
+ #ifdef BCH_WRITE_REF_DEBUG
+ 	&sysfs_write_refs,
+ #endif
+@@ -585,6 +686,7 @@ struct attribute *bch2_fs_internal_files[] = {
+ 	&sysfs_trigger_btree_cache_shrink,
+ 	&sysfs_trigger_btree_key_cache_shrink,
+ 	&sysfs_trigger_freelist_wakeup,
++	&sysfs_trigger_btree_updates,
+ 
+ 	&sysfs_gc_gens_pos,
+ 
+@@ -604,26 +706,34 @@ struct attribute *bch2_fs_internal_files[] = {
+ 
+ /* options */
+ 
+-SHOW(bch2_fs_opts_dir)
++static ssize_t sysfs_opt_show(struct bch_fs *c,
++			      struct bch_dev *ca,
++			      enum bch_opt_id id,
++			      struct printbuf *out)
+ {
+-	struct bch_fs *c = container_of(kobj, struct bch_fs, opts_dir);
+-	const struct bch_option *opt = container_of(attr, struct bch_option, attr);
+-	int id = opt - bch2_opt_table;
+-	u64 v = bch2_opt_get_by_id(&c->opts, id);
++	const struct bch_option *opt = bch2_opt_table + id;
++	u64 v;
++
++	if (opt->flags & OPT_FS) {
++		v = bch2_opt_get_by_id(&c->opts, id);
++	} else if ((opt->flags & OPT_DEVICE) && opt->get_member)  {
++		v = bch2_opt_from_sb(c->disk_sb.sb, id, ca->dev_idx);
++	} else {
++		return -EINVAL;
++	}
+ 
+ 	bch2_opt_to_text(out, c, c->disk_sb.sb, opt, v, OPT_SHOW_FULL_LIST);
+ 	prt_char(out, '\n');
+-
+ 	return 0;
+ }
+ 
+-STORE(bch2_fs_opts_dir)
++static ssize_t sysfs_opt_store(struct bch_fs *c,
++			       struct bch_dev *ca,
++			       enum bch_opt_id id,
++			       const char *buf, size_t size)
+ {
+-	struct bch_fs *c = container_of(kobj, struct bch_fs, opts_dir);
+-	const struct bch_option *opt = container_of(attr, struct bch_option, attr);
+-	int ret, id = opt - bch2_opt_table;
+-	char *tmp;
+-	u64 v;
++	const struct bch_option *opt = bch2_opt_table + id;
++	int ret = 0;
+ 
+ 	/*
+ 	 * We don't need to take c->writes for correctness, but it eliminates an
+@@ -632,27 +742,28 @@ STORE(bch2_fs_opts_dir)
+ 	if (unlikely(!bch2_write_ref_tryget(c, BCH_WRITE_REF_sysfs)))
+ 		return -EROFS;
+ 
+-	tmp = kstrdup(buf, GFP_KERNEL);
++	down_write(&c->state_lock);
++
++	char *tmp = kstrdup(buf, GFP_KERNEL);
+ 	if (!tmp) {
+ 		ret = -ENOMEM;
+ 		goto err;
+ 	}
+ 
+-	ret = bch2_opt_parse(c, opt, strim(tmp), &v, NULL);
++	u64 v;
++	ret =   bch2_opt_parse(c, opt, strim(tmp), &v, NULL) ?:
++		bch2_opt_check_may_set(c, ca, id, v);
+ 	kfree(tmp);
+ 
+ 	if (ret < 0)
+ 		goto err;
+ 
+-	ret = bch2_opt_check_may_set(c, id, v);
+-	if (ret < 0)
+-		goto err;
+-
+-	bch2_opt_set_sb(c, NULL, opt, v);
++	bch2_opt_set_sb(c, ca, opt, v);
+ 	bch2_opt_set_by_id(&c->opts, id, v);
+ 
+ 	if (v &&
+ 	    (id == Opt_background_target ||
++	     (id == Opt_foreground_target && !c->opts.background_target) ||
+ 	     id == Opt_background_compression ||
+ 	     (id == Opt_compression && !c->opts.background_compression)))
+ 		bch2_set_rebalance_needs_scan(c, 0);
+@@ -664,27 +775,56 @@ STORE(bch2_fs_opts_dir)
+ 	    c->copygc_thread)
+ 		wake_up_process(c->copygc_thread);
+ 
++	if (id == Opt_discard && !ca) {
++		mutex_lock(&c->sb_lock);
++		for_each_member_device(c, ca)
++			opt->set_member(bch2_members_v2_get_mut(ca->disk_sb.sb, ca->dev_idx), v);
++
++		bch2_write_super(c);
++		mutex_unlock(&c->sb_lock);
++	}
++
+ 	ret = size;
+ err:
++	up_write(&c->state_lock);
+ 	bch2_write_ref_put(c, BCH_WRITE_REF_sysfs);
+ 	return ret;
+ }
++
++SHOW(bch2_fs_opts_dir)
++{
++	struct bch_fs *c = container_of(kobj, struct bch_fs, opts_dir);
++	int id = bch2_opt_lookup(attr->name);
++	if (id < 0)
++		return 0;
++
++	return sysfs_opt_show(c, NULL, id, out);
++}
++
++STORE(bch2_fs_opts_dir)
++{
++	struct bch_fs *c = container_of(kobj, struct bch_fs, opts_dir);
++	int id = bch2_opt_lookup(attr->name);
++	if (id < 0)
++		return 0;
++
++	return sysfs_opt_store(c, NULL, id, buf, size);
++}
+ SYSFS_OPS(bch2_fs_opts_dir);
+ 
+ struct attribute *bch2_fs_opts_dir_files[] = { NULL };
+ 
+-int bch2_opts_create_sysfs_files(struct kobject *kobj)
++int bch2_opts_create_sysfs_files(struct kobject *kobj, unsigned type)
+ {
+-	const struct bch_option *i;
+-	int ret;
+-
+-	for (i = bch2_opt_table;
++	for (const struct bch_option *i = bch2_opt_table;
+ 	     i < bch2_opt_table + bch2_opts_nr;
+ 	     i++) {
+-		if (!(i->flags & OPT_FS))
++		if (i->flags & OPT_HIDDEN)
++			continue;
++		if (!(i->flags & type))
+ 			continue;
+ 
+-		ret = sysfs_create_file(kobj, &i->attr);
++		int ret = sysfs_create_file(kobj, &i->attr);
+ 		if (ret)
+ 			return ret;
+ 	}
+@@ -755,11 +895,8 @@ SHOW(bch2_dev)
+ 
+ 	sysfs_printf(uuid,		"%pU\n", ca->uuid.b);
+ 
+-	sysfs_print(bucket_size,	bucket_bytes(ca));
+ 	sysfs_print(first_bucket,	ca->mi.first_bucket);
+ 	sysfs_print(nbuckets,		ca->mi.nbuckets);
+-	sysfs_print(durability,		ca->mi.durability);
+-	sysfs_print(discard,		ca->mi.discard);
+ 
+ 	if (attr == &sysfs_label) {
+ 		if (ca->mi.group)
+@@ -772,11 +909,6 @@ SHOW(bch2_dev)
+ 		prt_char(out, '\n');
+ 	}
+ 
+-	if (attr == &sysfs_state) {
+-		prt_string_option(out, bch2_member_states, ca->mi.state);
+-		prt_char(out, '\n');
+-	}
+-
+ 	if (attr == &sysfs_io_done)
+ 		dev_io_done_to_text(out, ca);
+ 
+@@ -802,6 +934,13 @@ SHOW(bch2_dev)
+ 	if (attr == &sysfs_open_buckets)
+ 		bch2_open_buckets_to_text(out, c, ca);
+ 
++	if (attr == &sysfs_read_fua_test)
++		return bch2_read_fua_test(out, ca);
++
++	int opt_id = bch2_opt_lookup(attr->name);
++	if (opt_id >= 0)
++		return sysfs_opt_show(c, ca, opt_id, out);
++
+ 	return 0;
+ }
+ 
+@@ -810,18 +949,6 @@ STORE(bch2_dev)
+ 	struct bch_dev *ca = container_of(kobj, struct bch_dev, kobj);
+ 	struct bch_fs *c = ca->fs;
+ 
+-	if (attr == &sysfs_discard) {
+-		bool v = strtoul_or_return(buf);
+-
+-		bch2_opt_set_sb(c, ca, bch2_opt_table + Opt_discard, v);
+-	}
+-
+-	if (attr == &sysfs_durability) {
+-		u64 v = strtoul_or_return(buf);
+-
+-		bch2_opt_set_sb(c, ca, bch2_opt_table + Opt_durability, v);
+-	}
+-
+ 	if (attr == &sysfs_label) {
+ 		char *tmp;
+ 		int ret;
+@@ -839,20 +966,20 @@ STORE(bch2_dev)
+ 	if (attr == &sysfs_io_errors_reset)
+ 		bch2_dev_errors_reset(ca);
+ 
++	int opt_id = bch2_opt_lookup(attr->name);
++	if (opt_id >= 0)
++		return sysfs_opt_store(c, ca, opt_id, buf, size);
++
+ 	return size;
+ }
+ SYSFS_OPS(bch2_dev);
+ 
+ struct attribute *bch2_dev_files[] = {
+ 	&sysfs_uuid,
+-	&sysfs_bucket_size,
+ 	&sysfs_first_bucket,
+ 	&sysfs_nbuckets,
+-	&sysfs_durability,
+ 
+ 	/* settings: */
+-	&sysfs_discard,
+-	&sysfs_state,
+ 	&sysfs_label,
+ 
+ 	&sysfs_has_data,
+@@ -866,6 +993,8 @@ struct attribute *bch2_dev_files[] = {
+ 	&sysfs_io_latency_stats_write,
+ 	&sysfs_congested,
+ 
++	&sysfs_read_fua_test,
++
+ 	/* debug: */
+ 	&sysfs_alloc_debug,
+ 	&sysfs_open_buckets,
+diff --git a/fs/bcachefs/sysfs.h b/fs/bcachefs/sysfs.h
+index 222cd5062702..303e0433c702 100644
+--- a/fs/bcachefs/sysfs.h
++++ b/fs/bcachefs/sysfs.h
+@@ -23,7 +23,7 @@ extern const struct sysfs_ops bch2_fs_opts_dir_sysfs_ops;
+ extern const struct sysfs_ops bch2_fs_time_stats_sysfs_ops;
+ extern const struct sysfs_ops bch2_dev_sysfs_ops;
+ 
+-int bch2_opts_create_sysfs_files(struct kobject *);
++int bch2_opts_create_sysfs_files(struct kobject *, unsigned);
+ 
+ #else
+ 
+@@ -41,7 +41,8 @@ static const struct sysfs_ops bch2_fs_opts_dir_sysfs_ops;
+ static const struct sysfs_ops bch2_fs_time_stats_sysfs_ops;
+ static const struct sysfs_ops bch2_dev_sysfs_ops;
+ 
+-static inline int bch2_opts_create_sysfs_files(struct kobject *kobj) { return 0; }
++static inline int bch2_opts_create_sysfs_files(struct kobject *kobj, unsigned type)
++{ return 0; }
+ 
+ #endif /* NO_BCACHEFS_SYSFS */
+ 
+diff --git a/fs/bcachefs/time_stats.c b/fs/bcachefs/time_stats.c
+index 3fe82757f93a..a8382d876835 100644
+--- a/fs/bcachefs/time_stats.c
++++ b/fs/bcachefs/time_stats.c
+@@ -10,6 +10,9 @@
+ #include "eytzinger.h"
+ #include "time_stats.h"
+ 
++/* disable automatic switching to percpu mode */
++#define TIME_STATS_NONPCPU	((struct time_stat_buffer *) 1)
++
+ static const struct time_unit time_units[] = {
+ 	{ "ns",		1		 },
+ 	{ "us",		NSEC_PER_USEC	 },
+@@ -123,11 +126,12 @@ void __bch2_time_stats_update(struct bch2_time_stats *stats, u64 start, u64 end)
+ {
+ 	unsigned long flags;
+ 
+-	if (!stats->buffer) {
++	if ((unsigned long) stats->buffer <= 1) {
+ 		spin_lock_irqsave(&stats->lock, flags);
+ 		time_stats_update_one(stats, start, end);
+ 
+-		if (mean_and_variance_weighted_get_mean(stats->freq_stats_weighted, TIME_STATS_MV_WEIGHT) < 32 &&
++		if (!stats->buffer &&
++		    mean_and_variance_weighted_get_mean(stats->freq_stats_weighted, TIME_STATS_MV_WEIGHT) < 32 &&
+ 		    stats->duration_stats.n > 1024)
+ 			stats->buffer =
+ 				alloc_percpu_gfp(struct time_stat_buffer,
+@@ -157,7 +161,8 @@ void bch2_time_stats_reset(struct bch2_time_stats *stats)
+ 	unsigned offset = offsetof(struct bch2_time_stats, min_duration);
+ 	memset((void *) stats + offset, 0, sizeof(*stats) - offset);
+ 
+-	if (stats->buffer) {
++	if (stats->buffer &&
++	    stats->buffer != TIME_STATS_NONPCPU) {
+ 		int cpu;
+ 		for_each_possible_cpu(cpu)
+ 			per_cpu_ptr(stats->buffer, cpu)->nr = 0;
+@@ -167,7 +172,10 @@ void bch2_time_stats_reset(struct bch2_time_stats *stats)
+ 
+ void bch2_time_stats_exit(struct bch2_time_stats *stats)
+ {
+-	free_percpu(stats->buffer);
++	if (stats->buffer != TIME_STATS_NONPCPU) {
++		free_percpu(stats->buffer);
++		stats->buffer = NULL;
++	}
+ }
+ 
+ void bch2_time_stats_init(struct bch2_time_stats *stats)
+@@ -177,3 +185,9 @@ void bch2_time_stats_init(struct bch2_time_stats *stats)
+ 	stats->min_freq = U64_MAX;
+ 	spin_lock_init(&stats->lock);
+ }
++
++void bch2_time_stats_init_no_pcpu(struct bch2_time_stats *stats)
++{
++	bch2_time_stats_init(stats);
++	stats->buffer = TIME_STATS_NONPCPU;
++}
+diff --git a/fs/bcachefs/time_stats.h b/fs/bcachefs/time_stats.h
+index dc6493f7bbab..eddb0985bab4 100644
+--- a/fs/bcachefs/time_stats.h
++++ b/fs/bcachefs/time_stats.h
+@@ -145,6 +145,7 @@ static inline bool track_event_change(struct bch2_time_stats *stats, bool v)
+ void bch2_time_stats_reset(struct bch2_time_stats *);
+ void bch2_time_stats_exit(struct bch2_time_stats *);
+ void bch2_time_stats_init(struct bch2_time_stats *);
++void bch2_time_stats_init_no_pcpu(struct bch2_time_stats *);
+ 
+ static inline void bch2_time_stats_quantiles_exit(struct bch2_time_stats_quantiles *statq)
+ {
+diff --git a/fs/bcachefs/trace.h b/fs/bcachefs/trace.h
+index c1b51009edf6..8c07189a080a 100644
+--- a/fs/bcachefs/trace.h
++++ b/fs/bcachefs/trace.h
+@@ -295,12 +295,12 @@ TRACE_EVENT(write_super,
+ 
+ /* io.c: */
+ 
+-DEFINE_EVENT(bio, read_promote,
++DEFINE_EVENT(bio, io_read_promote,
+ 	TP_PROTO(struct bio *bio),
+ 	TP_ARGS(bio)
+ );
+ 
+-TRACE_EVENT(read_nopromote,
++TRACE_EVENT(io_read_nopromote,
+ 	TP_PROTO(struct bch_fs *c, int ret),
+ 	TP_ARGS(c, ret),
+ 
+@@ -319,26 +319,55 @@ TRACE_EVENT(read_nopromote,
+ 		  __entry->ret)
+ );
+ 
+-DEFINE_EVENT(bio, read_bounce,
++DEFINE_EVENT(bio, io_read_bounce,
+ 	TP_PROTO(struct bio *bio),
+ 	TP_ARGS(bio)
+ );
+ 
+-DEFINE_EVENT(bio, read_split,
++DEFINE_EVENT(bio, io_read_split,
+ 	TP_PROTO(struct bio *bio),
+ 	TP_ARGS(bio)
+ );
+ 
+-DEFINE_EVENT(bio, read_retry,
++DEFINE_EVENT(bio, io_read_retry,
+ 	TP_PROTO(struct bio *bio),
+ 	TP_ARGS(bio)
+ );
+ 
+-DEFINE_EVENT(bio, read_reuse_race,
++DEFINE_EVENT(bio, io_read_reuse_race,
+ 	TP_PROTO(struct bio *bio),
+ 	TP_ARGS(bio)
+ );
+ 
++DEFINE_EVENT(bio, io_read_fail_and_poison,
++	TP_PROTO(struct bio *bio),
++	TP_ARGS(bio)
++);
++
++/* ec.c */
++
++TRACE_EVENT(stripe_create,
++	TP_PROTO(struct bch_fs *c, u64 idx, int ret),
++	TP_ARGS(c, idx, ret),
++
++	TP_STRUCT__entry(
++		__field(dev_t,		dev			)
++		__field(u64,		idx			)
++		__field(int,		ret			)
++	),
++
++	TP_fast_assign(
++		__entry->dev			= c->dev;
++		__entry->idx			= idx;
++		__entry->ret			= ret;
++	),
++
++	TP_printk("%d,%d idx %llu ret %i",
++		  MAJOR(__entry->dev), MINOR(__entry->dev),
++		  __entry->idx,
++		  __entry->ret)
++);
++
+ /* Journal */
+ 
+ DEFINE_EVENT(bch_fs, journal_full,
+@@ -797,53 +826,37 @@ TRACE_EVENT(bucket_invalidate,
+ 
+ /* Moving IO */
+ 
+-TRACE_EVENT(bucket_evacuate,
+-	TP_PROTO(struct bch_fs *c, struct bpos *bucket),
+-	TP_ARGS(c, bucket),
+-
+-	TP_STRUCT__entry(
+-		__field(dev_t,		dev			)
+-		__field(u32,		dev_idx			)
+-		__field(u64,		bucket			)
+-	),
+-
+-	TP_fast_assign(
+-		__entry->dev		= c->dev;
+-		__entry->dev_idx	= bucket->inode;
+-		__entry->bucket		= bucket->offset;
+-	),
+-
+-	TP_printk("%d:%d %u:%llu",
+-		  MAJOR(__entry->dev), MINOR(__entry->dev),
+-		  __entry->dev_idx, __entry->bucket)
++DEFINE_EVENT(fs_str, io_move,
++	TP_PROTO(struct bch_fs *c, const char *str),
++	TP_ARGS(c, str)
+ );
+ 
+-DEFINE_EVENT(fs_str, move_extent,
++DEFINE_EVENT(fs_str, io_move_read,
+ 	TP_PROTO(struct bch_fs *c, const char *str),
+ 	TP_ARGS(c, str)
+ );
+ 
+-DEFINE_EVENT(fs_str, move_extent_read,
++DEFINE_EVENT(fs_str, io_move_write,
+ 	TP_PROTO(struct bch_fs *c, const char *str),
+ 	TP_ARGS(c, str)
+ );
+ 
+-DEFINE_EVENT(fs_str, move_extent_write,
++DEFINE_EVENT(fs_str, io_move_finish,
+ 	TP_PROTO(struct bch_fs *c, const char *str),
+ 	TP_ARGS(c, str)
+ );
+ 
+-DEFINE_EVENT(fs_str, move_extent_finish,
++DEFINE_EVENT(fs_str, io_move_fail,
+ 	TP_PROTO(struct bch_fs *c, const char *str),
+ 	TP_ARGS(c, str)
+ );
+ 
+-DEFINE_EVENT(fs_str, move_extent_fail,
++DEFINE_EVENT(fs_str, io_move_write_fail,
+ 	TP_PROTO(struct bch_fs *c, const char *str),
+ 	TP_ARGS(c, str)
+ );
+ 
+-DEFINE_EVENT(fs_str, move_extent_start_fail,
++DEFINE_EVENT(fs_str, io_move_start_fail,
+ 	TP_PROTO(struct bch_fs *c, const char *str),
+ 	TP_ARGS(c, str)
+ );
+@@ -881,37 +894,6 @@ TRACE_EVENT(move_data,
+ 		  __entry->sectors_raced)
+ );
+ 
+-TRACE_EVENT(evacuate_bucket,
+-	TP_PROTO(struct bch_fs *c, struct bpos *bucket,
+-		 unsigned sectors, unsigned bucket_size,
+-		 int ret),
+-	TP_ARGS(c, bucket, sectors, bucket_size, ret),
+-
+-	TP_STRUCT__entry(
+-		__field(dev_t,		dev		)
+-		__field(u64,		member		)
+-		__field(u64,		bucket		)
+-		__field(u32,		sectors		)
+-		__field(u32,		bucket_size	)
+-		__field(int,		ret		)
+-	),
+-
+-	TP_fast_assign(
+-		__entry->dev			= c->dev;
+-		__entry->member			= bucket->inode;
+-		__entry->bucket			= bucket->offset;
+-		__entry->sectors		= sectors;
+-		__entry->bucket_size		= bucket_size;
+-		__entry->ret			= ret;
+-	),
+-
+-	TP_printk("%d,%d %llu:%llu sectors %u/%u ret %i",
+-		  MAJOR(__entry->dev), MINOR(__entry->dev),
+-		  __entry->member, __entry->bucket,
+-		  __entry->sectors, __entry->bucket_size,
+-		  __entry->ret)
+-);
+-
+ TRACE_EVENT(copygc,
+ 	TP_PROTO(struct bch_fs *c,
+ 		 u64 buckets,
+diff --git a/fs/bcachefs/util.c b/fs/bcachefs/util.c
+index da2cd11b3025..553de8d8e3e5 100644
+--- a/fs/bcachefs/util.c
++++ b/fs/bcachefs/util.c
+@@ -473,10 +473,10 @@ void bch2_time_stats_to_text(struct printbuf *out, struct bch2_time_stats *stats
+ 		u64 last_q = 0;
+ 
+ 		prt_printf(out, "quantiles (%s):\t", u->name);
+-		eytzinger0_for_each(i, NR_QUANTILES) {
+-			bool is_last = eytzinger0_next(i, NR_QUANTILES) == -1;
++		eytzinger0_for_each(j, NR_QUANTILES) {
++			bool is_last = eytzinger0_next(j, NR_QUANTILES) == -1;
+ 
+-			u64 q = max(quantiles->entries[i].m, last_q);
++			u64 q = max(quantiles->entries[j].m, last_q);
+ 			prt_printf(out, "%llu ", div64_u64(q, u->nsecs));
+ 			if (is_last)
+ 				prt_newline(out);
+@@ -704,12 +704,33 @@ void memcpy_from_bio(void *dst, struct bio *src, struct bvec_iter src_iter)
+ 	}
+ }
+ 
++#ifdef CONFIG_BCACHEFS_DEBUG
++void bch2_corrupt_bio(struct bio *bio)
++{
++	struct bvec_iter iter;
++	struct bio_vec bv;
++	unsigned offset = get_random_u32_below(bio->bi_iter.bi_size / sizeof(u64));
++
++	bio_for_each_segment(bv, bio, iter) {
++		unsigned u64s = bv.bv_len / sizeof(u64);
++
++		if (offset < u64s) {
++			u64 *segment = bvec_kmap_local(&bv);
++			segment[offset] = get_random_u64();
++			kunmap_local(segment);
++			return;
++		}
++		offset -= u64s;
++	}
++}
++#endif
++
+ #if 0
+ void eytzinger1_test(void)
+ {
+-	unsigned inorder, eytz, size;
++	unsigned inorder, size;
+ 
+-	pr_info("1 based eytzinger test:");
++	pr_info("1 based eytzinger test:\n");
+ 
+ 	for (size = 2;
+ 	     size < 65536;
+@@ -717,13 +738,7 @@ void eytzinger1_test(void)
+ 		unsigned extra = eytzinger1_extra(size);
+ 
+ 		if (!(size % 4096))
+-			pr_info("tree size %u", size);
+-
+-		BUG_ON(eytzinger1_prev(0, size) != eytzinger1_last(size));
+-		BUG_ON(eytzinger1_next(0, size) != eytzinger1_first(size));
+-
+-		BUG_ON(eytzinger1_prev(eytzinger1_first(size), size)	!= 0);
+-		BUG_ON(eytzinger1_next(eytzinger1_last(size), size)	!= 0);
++			pr_info("tree size %u\n", size);
+ 
+ 		inorder = 1;
+ 		eytzinger1_for_each(eytz, size) {
+@@ -734,15 +749,16 @@ void eytzinger1_test(void)
+ 
+ 			inorder++;
+ 		}
++		BUG_ON(inorder - 1 != size);
+ 	}
+ }
+ 
+ void eytzinger0_test(void)
+ {
+ 
+-	unsigned inorder, eytz, size;
++	unsigned inorder, size;
+ 
+-	pr_info("0 based eytzinger test:");
++	pr_info("0 based eytzinger test:\n");
+ 
+ 	for (size = 1;
+ 	     size < 65536;
+@@ -750,13 +766,7 @@ void eytzinger0_test(void)
+ 		unsigned extra = eytzinger0_extra(size);
+ 
+ 		if (!(size % 4096))
+-			pr_info("tree size %u", size);
+-
+-		BUG_ON(eytzinger0_prev(-1, size) != eytzinger0_last(size));
+-		BUG_ON(eytzinger0_next(-1, size) != eytzinger0_first(size));
+-
+-		BUG_ON(eytzinger0_prev(eytzinger0_first(size), size)	!= -1);
+-		BUG_ON(eytzinger0_next(eytzinger0_last(size), size)	!= -1);
++			pr_info("tree size %u\n", size);
+ 
+ 		inorder = 0;
+ 		eytzinger0_for_each(eytz, size) {
+@@ -767,54 +777,191 @@ void eytzinger0_test(void)
+ 
+ 			inorder++;
+ 		}
++		BUG_ON(inorder != size);
++
++		inorder = size - 1;
++		eytzinger0_for_each_prev(eytz, size) {
++			BUG_ON(eytz != eytzinger0_first(size) &&
++			       eytzinger0_next(eytzinger0_prev(eytz, size), size) != eytz);
++
++			inorder--;
++		}
++		BUG_ON(inorder != -1);
+ 	}
+ }
+ 
+-static inline int cmp_u16(const void *_l, const void *_r, size_t size)
++static inline int cmp_u16(const void *_l, const void *_r)
+ {
+ 	const u16 *l = _l, *r = _r;
+ 
+-	return (*l > *r) - (*r - *l);
++	return (*l > *r) - (*r > *l);
+ }
+ 
+-static void eytzinger0_find_test_val(u16 *test_array, unsigned nr, u16 search)
++static void eytzinger0_find_test_le(u16 *test_array, unsigned nr, u16 search)
+ {
+-	int i, c1 = -1, c2 = -1;
+-	ssize_t r;
++	int r, s;
++	bool bad;
+ 
+ 	r = eytzinger0_find_le(test_array, nr,
+ 			       sizeof(test_array[0]),
+ 			       cmp_u16, &search);
+-	if (r >= 0)
+-		c1 = test_array[r];
+-
+-	for (i = 0; i < nr; i++)
+-		if (test_array[i] <= search && test_array[i] > c2)
+-			c2 = test_array[i];
+-
+-	if (c1 != c2) {
+-		eytzinger0_for_each(i, nr)
+-			pr_info("[%3u] = %12u", i, test_array[i]);
+-		pr_info("find_le(%2u) -> [%2zi] = %2i should be %2i",
+-			i, r, c1, c2);
++	if (r >= 0) {
++		if (test_array[r] > search) {
++			bad = true;
++		} else {
++			s = eytzinger0_next(r, nr);
++			bad = s >= 0 && test_array[s] <= search;
++		}
++	} else {
++		s = eytzinger0_last(nr);
++		bad = s >= 0 && test_array[s] <= search;
++	}
++
++	if (bad) {
++		s = -1;
++		eytzinger0_for_each_prev(j, nr) {
++			if (test_array[j] <= search) {
++				s = j;
++				break;
++			}
++		}
++
++		eytzinger0_for_each(j, nr)
++			pr_info("[%3u] = %12u\n", j, test_array[j]);
++		pr_info("find_le(%12u) = %3i should be %3i\n",
++			search, r, s);
++		BUG();
+ 	}
+ }
+ 
++static void eytzinger0_find_test_gt(u16 *test_array, unsigned nr, u16 search)
++{
++	int r, s;
++	bool bad;
++
++	r = eytzinger0_find_gt(test_array, nr,
++			       sizeof(test_array[0]),
++			       cmp_u16, &search);
++	if (r >= 0) {
++		if (test_array[r] <= search) {
++			bad = true;
++		} else {
++			s = eytzinger0_prev(r, nr);
++			bad = s >= 0 && test_array[s] > search;
++		}
++	} else {
++		s = eytzinger0_first(nr);
++		bad = s >= 0 && test_array[s] > search;
++	}
++
++	if (bad) {
++		s = -1;
++		eytzinger0_for_each(j, nr) {
++			if (test_array[j] > search) {
++				s = j;
++				break;
++			}
++		}
++
++		eytzinger0_for_each(j, nr)
++			pr_info("[%3u] = %12u\n", j, test_array[j]);
++		pr_info("find_gt(%12u) = %3i should be %3i\n",
++			search, r, s);
++		BUG();
++	}
++}
++
++static void eytzinger0_find_test_ge(u16 *test_array, unsigned nr, u16 search)
++{
++	int r, s;
++	bool bad;
++
++	r = eytzinger0_find_ge(test_array, nr,
++			       sizeof(test_array[0]),
++			       cmp_u16, &search);
++	if (r >= 0) {
++		if (test_array[r] < search) {
++			bad = true;
++		} else {
++			s = eytzinger0_prev(r, nr);
++			bad = s >= 0 && test_array[s] >= search;
++		}
++	} else {
++		s = eytzinger0_first(nr);
++		bad = s >= 0 && test_array[s] >= search;
++	}
++
++	if (bad) {
++		s = -1;
++		eytzinger0_for_each(j, nr) {
++			if (test_array[j] >= search) {
++				s = j;
++				break;
++			}
++		}
++
++		eytzinger0_for_each(j, nr)
++			pr_info("[%3u] = %12u\n", j, test_array[j]);
++		pr_info("find_ge(%12u) = %3i should be %3i\n",
++			search, r, s);
++		BUG();
++	}
++}
++
++static void eytzinger0_find_test_eq(u16 *test_array, unsigned nr, u16 search)
++{
++	unsigned r;
++	int s;
++	bool bad;
++
++	r = eytzinger0_find(test_array, nr,
++			    sizeof(test_array[0]),
++			    cmp_u16, &search);
++
++	if (r < nr) {
++		bad = test_array[r] != search;
++	} else {
++		s = eytzinger0_find_le(test_array, nr,
++				       sizeof(test_array[0]),
++				       cmp_u16, &search);
++		bad = s >= 0 && test_array[s] == search;
++	}
++
++	if (bad) {
++		eytzinger0_for_each(j, nr)
++			pr_info("[%3u] = %12u\n", j, test_array[j]);
++		pr_info("find(%12u) = %3i is incorrect\n",
++			search, r);
++		BUG();
++	}
++}
++
++static void eytzinger0_find_test_val(u16 *test_array, unsigned nr, u16 search)
++{
++	eytzinger0_find_test_le(test_array, nr, search);
++	eytzinger0_find_test_gt(test_array, nr, search);
++	eytzinger0_find_test_ge(test_array, nr, search);
++	eytzinger0_find_test_eq(test_array, nr, search);
++}
++
+ void eytzinger0_find_test(void)
+ {
+ 	unsigned i, nr, allocated = 1 << 12;
+ 	u16 *test_array = kmalloc_array(allocated, sizeof(test_array[0]), GFP_KERNEL);
+ 
+ 	for (nr = 1; nr < allocated; nr++) {
+-		pr_info("testing %u elems", nr);
++		u16 prev = 0;
++
++		pr_info("testing %u elems\n", nr);
+ 
+ 		get_random_bytes(test_array, nr * sizeof(test_array[0]));
+ 		eytzinger0_sort(test_array, nr, sizeof(test_array[0]), cmp_u16, NULL);
+ 
+ 		/* verify array is sorted correctly: */
+-		eytzinger0_for_each(i, nr)
+-			BUG_ON(i != eytzinger0_last(nr) &&
+-			       test_array[i] > test_array[eytzinger0_next(i, nr)]);
++		eytzinger0_for_each(j, nr) {
++			BUG_ON(test_array[j] < prev);
++			prev = test_array[j];
++		}
+ 
+ 		for (i = 0; i < U16_MAX; i += 1 << 12)
+ 			eytzinger0_find_test_val(test_array, nr, i);
+diff --git a/fs/bcachefs/util.h b/fs/bcachefs/util.h
+index f4a4783219d9..7d921fc920a0 100644
+--- a/fs/bcachefs/util.h
++++ b/fs/bcachefs/util.h
+@@ -406,6 +406,18 @@ u64 bch2_get_random_u64_below(u64);
+ void memcpy_to_bio(struct bio *, struct bvec_iter, const void *);
+ void memcpy_from_bio(void *, struct bio *, struct bvec_iter);
+ 
++#ifdef CONFIG_BCACHEFS_DEBUG
++void bch2_corrupt_bio(struct bio *);
++
++static inline void bch2_maybe_corrupt_bio(struct bio *bio, unsigned ratio)
++{
++	if (ratio && !get_random_u32_below(ratio))
++		bch2_corrupt_bio(bio);
++}
++#else
++#define bch2_maybe_corrupt_bio(...)	do {} while (0)
++#endif
++
+ static inline void memcpy_u64s_small(void *dst, const void *src,
+ 				     unsigned u64s)
+ {
+@@ -419,7 +431,7 @@ static inline void memcpy_u64s_small(void *dst, const void *src,
+ static inline void __memcpy_u64s(void *dst, const void *src,
+ 				 unsigned u64s)
+ {
+-#ifdef CONFIG_X86_64
++#if defined(CONFIG_X86_64) && !defined(CONFIG_KMSAN)
+ 	long d0, d1, d2;
+ 
+ 	asm volatile("rep ; movsq"
+@@ -496,7 +508,7 @@ static inline void __memmove_u64s_up(void *_dst, const void *_src,
+ 	u64 *dst = (u64 *) _dst + u64s - 1;
+ 	u64 *src = (u64 *) _src + u64s - 1;
+ 
+-#ifdef CONFIG_X86_64
++#if defined(CONFIG_X86_64) && !defined(CONFIG_KMSAN)
+ 	long d0, d1, d2;
+ 
+ 	asm volatile("std ;\n"
+diff --git a/fs/bcachefs/xattr.c b/fs/bcachefs/xattr.c
+index aed7c6984173..f9667b944c0d 100644
+--- a/fs/bcachefs/xattr.c
++++ b/fs/bcachefs/xattr.c
+@@ -523,7 +523,7 @@ static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler,
+ 		if (ret < 0)
+ 			goto err_class_exit;
+ 
+-		ret = bch2_opt_check_may_set(c, opt_id, v);
++		ret = bch2_opt_check_may_set(c, NULL, opt_id, v);
+ 		if (ret < 0)
+ 			goto err_class_exit;
+ 
+-- 
+2.45.3
+
diff --git a/sys-kernel/vanilla-kernel/vanilla-kernel-6.14.ebuild b/sys-kernel/vanilla-kernel/vanilla-kernel-6.14.ebuild
new file mode 100644
index 0000000..5e83c42
--- /dev/null
+++ b/sys-kernel/vanilla-kernel/vanilla-kernel-6.14.ebuild
@@ -0,0 +1,126 @@
+# Copyright 2020-2025 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=8
+
+KERNEL_IUSE_MODULES_SIGN=1
+inherit kernel-build toolchain-funcs verify-sig
+
+MY_P=linux-${PV}
+# https://koji.fedoraproject.org/koji/packageinfo?packageID=8
+# forked to https://github.com/projg2/fedora-kernel-config-for-gentoo
+CONFIG_VER=6.13.6-gentoo
+GENTOO_CONFIG_VER=g15
+
+DESCRIPTION="Linux kernel built from vanilla upstream sources"
+HOMEPAGE="
+	https://wiki.gentoo.org/wiki/Project:Distribution_Kernel
+	https://www.kernel.org/
+"
+SRC_URI+="
+	https://cdn.kernel.org/pub/linux/kernel/v$(ver_cut 1).x/${MY_P}.tar.xz
+	https://github.com/projg2/gentoo-kernel-config/archive/${GENTOO_CONFIG_VER}.tar.gz
+		-> gentoo-kernel-config-${GENTOO_CONFIG_VER}.tar.gz
+	verify-sig? (
+		https://cdn.kernel.org/pub/linux/kernel/v$(ver_cut 1).x/${MY_P}.tar.sign
+	)
+	amd64? (
+		https://raw.githubusercontent.com/projg2/fedora-kernel-config-for-gentoo/${CONFIG_VER}/kernel-x86_64-fedora.config
+			-> kernel-x86_64-fedora.config.${CONFIG_VER}
+	)
+	arm64? (
+		https://raw.githubusercontent.com/projg2/fedora-kernel-config-for-gentoo/${CONFIG_VER}/kernel-aarch64-fedora.config
+			-> kernel-aarch64-fedora.config.${CONFIG_VER}
+	)
+	ppc64? (
+		https://raw.githubusercontent.com/projg2/fedora-kernel-config-for-gentoo/${CONFIG_VER}/kernel-ppc64le-fedora.config
+			-> kernel-ppc64le-fedora.config.${CONFIG_VER}
+	)
+	x86? (
+		https://raw.githubusercontent.com/projg2/fedora-kernel-config-for-gentoo/${CONFIG_VER}/kernel-i686-fedora.config
+			-> kernel-i686-fedora.config.${CONFIG_VER}
+	)
+"
+S=${WORKDIR}/${MY_P}
+
+LICENSE="GPL-2"
+KEYWORDS="~amd64 ~arm ~arm64 ~hppa ~loong ~ppc ~ppc64 ~x86"
+IUSE="debug hardened"
+REQUIRED_USE="arm? ( savedconfig )"
+
+BDEPEND="
+	debug? ( dev-util/pahole )
+	verify-sig? ( sec-keys/openpgp-keys-kernel )
+"
+PDEPEND="
+	>=virtual/dist-kernel-${PV}
+"
+
+VERIFY_SIG_OPENPGP_KEY_PATH=/usr/share/openpgp-keys/kernel.org.asc
+
+src_unpack() {
+	if use verify-sig; then
+		verify-sig_uncompress_verify_unpack \
+			"${DISTDIR}"/linux-${PV}.tar.{xz,sign}
+		unpack "gentoo-kernel-config-${GENTOO_CONFIG_VER}.tar.gz"
+	else
+		default
+	fi
+}
+
+src_prepare() {
+    # copy pkg maintainer supplied patches
+    if [ -d "${FILESDIR}/${MY_P}" ]; then
+            cp "${FILESDIR}/${MY_P}"/*.patch ${WORKDIR}/
+    fi
+
+    local PATCHES=(
+            # meh, genpatches have no directory
+            "${WORKDIR}"/*.patch
+    )
+        
+	default
+
+	local biendian=false
+
+	# prepare the default config
+	case ${ARCH} in
+		amd64)
+			cp "${FILESDIR}/${MY_P}.amd64.config" .config || die
+			;;
+		*)
+			die "Unsupported arch ${ARCH}"
+			;;
+	esac
+
+	local myversion="-dist"
+	use hardened && myversion+="-hardened"
+	echo "CONFIG_LOCALVERSION=\"${myversion}\"" > "${T}"/version.config || die
+	local dist_conf_path="${WORKDIR}/gentoo-kernel-config-${GENTOO_CONFIG_VER}"
+
+	local merge_configs=(
+		"${T}"/version.config
+		"${dist_conf_path}"/base.config
+	)
+	use debug || merge_configs+=(
+		"${dist_conf_path}"/no-debug.config
+	)
+	if use hardened; then
+		merge_configs+=( "${dist_conf_path}"/hardened-base.config )
+
+		tc-is-gcc && merge_configs+=( "${dist_conf_path}"/hardened-gcc-plugins.config )
+
+		if [[ -f "${dist_conf_path}/hardened-${ARCH}.config" ]]; then
+			merge_configs+=( "${dist_conf_path}/hardened-${ARCH}.config" )
+		fi
+	fi
+
+	# this covers ppc64 and aarch64_be only for now
+	if [[ ${biendian} == true && $(tc-endian) == big ]]; then
+		merge_configs+=( "${dist_conf_path}/big-endian.config" )
+	fi
+
+	use secureboot && merge_configs+=( "${dist_conf_path}/secureboot.config" )
+
+	kernel-build_merge_configs "${merge_configs[@]}"
+}
diff --git a/virtual/dist-kernel/dist-kernel-6.14.ebuild b/virtual/dist-kernel/dist-kernel-6.14.ebuild
new file mode 100644
index 0000000..0aaa472
--- /dev/null
+++ b/virtual/dist-kernel/dist-kernel-6.14.ebuild
@@ -0,0 +1,17 @@
+# Copyright 2021-2024 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=8
+
+DESCRIPTION="Virtual to depend on any Distribution Kernel"
+SLOT="0/${PVR}"
+KEYWORDS="amd64 ~arm arm64 ~hppa ~loong ~ppc ppc64 ~riscv ~sparc x86"
+
+RDEPEND="
+	|| (
+		~sys-kernel/gentoo-kernel-${PV}
+		~sys-kernel/gentoo-kernel-bin-${PV}
+		~sys-kernel/vanilla-kernel-${PV}
+		~sys-kernel/hardened-kernel-${PV}
+	)
+"