Re: [linus:master] [atomic] 80047d84ee: WARNING:at_include/linux/instrumented.h:#__split_huge_pmd_locked

From: Arnd Bergmann

Date: Tue Nov 03 2026 - 07:57:39 EST


On Tue, Nov 3, 2026, at 01:58, kernel test robot wrote:
> Hello,
>
> kernel test robot noticed
> "WARNING:at_include/linux/instrumented.h:#__split_huge_pmd_locked" on:
>
> commit: 80047d84eed25e9c92cfb9169980a0dfec110246 ("atomic: add
> alignment check to instrumented atomic operations")
> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git master
>
> [test failed on linux-next/master 7649ba73f9e7e1e04d0b46213e39c79ae09dd55c]
>
> in testcase: trinity
> with following parameters:
>
> runtime: 300s
> group: group-02
> nr_groups: 5
>
>
> config: i386-randconfig-1005-20261101
> compiler: gcc-14
> test machine: qemu-system-i386 -enable-kvm -cpu
> Icelake-Server-noTSX,enforce,lm=off -smp 4 -m 4G
>
> (please refer to attached dmesg/kmsg for entire log/backtrace)
>
>
> If you fix the issue in a separate patch/commit (i.e. not just a new
> version of the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <yi1.lai@xxxxxxxxx>
> | Closes:
> https://lore.kernel.org/oe-lkp/202611022259.def3e086-lkp@xxxxxxxxx
>
>
> [ 18.704659][ T3738] ------------[ cut here ]------------
> [ 18.706815][ T3738] WARNING: include/linux/instrumented.h:62 at
> __split_huge_pmd_locked+0x6d2/0xac0, CPU#0: trinity-c1/3738
> [ 18.717796][ T3738] Modules linked in:
> [ 18.719286][ T3738] CPU: 0 UID: 65534 PID: 3738 Comm: trinity-c1 Not
> tainted 6.19.0-rc6-00100-g80047d84eed2 #1 PREEMPT(lazy)
> [ 18.723193][ T3738] Hardware name: QEMU Standard PC (i440FX + PIIX,
> 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
> [ 18.726644][ T3738] EIP: __split_huge_pmd_locked
> (x86/include/asm/pgtable.h:228 (discriminator 1) huge_memory.c:3128
> (discriminator 1))
> [ 18.728717][ T3738] Code: dc 85 d2 75 0c c6 45 c2 00 85 c0 0f 84 ee
> fe ff ff f6 45 bc 03 0f 85 47 03 00 00 8b 45 bc 80 08 10 c6 45 c2 01 e9
> d5 fe ff ff <0f> 0b 0f c7 4d ec 0f 84 34 fd ff ff eb f2 e8 db 58 40 01
> 85 c0 0f
> All code
> ========
> 0: dc 85 d2 75 0c c6 faddl -0x39f38a2e(%rbp)
> 6: 45 c2 00 85 rex.RB ret $0x8500
> a: c0 0f 84 rorb $0x84,(%rdi)
> d: ee out %al,(%dx)/out
> e: fe (bad)
> f: ff (bad)
> 10: ff f6 push %rsi
> 12: 45 bc 03 0f 85 47 rex.RB mov $0x47850f03,%r12d
> 18: 03 00 add (%rax),%eax
> 1a: 00 8b 45 bc 80 08 add %cl,0x880bc45(%rbx)
> 20: 10 c6 adc %al,%dh
> 22: 45 c2 01 e9 rex.RB ret $0xe901
> 26: d5 fe ff ff {rex2 0xfe} ud0 %r23,%r31
> 2a:* 0f 0b ud2 <-- trapping instruction
> 2c: 0f c7 4d ec cmpxchg8b -0x14(%rbp)
> 30: 0f 84 34 fd ff ff je 0xfffffffffffffd6a
> 36: eb f2 jmp 0x2a
> 38: e8 db 58 40 01 call 0x1405918
> 3d: 85 c0 test %eax,%eax
> 3f: 0f .byte 0xf

This is apparently how we got here:

static __always_inline void instrument_atomic_check_alignment(const volatile void *v, size_t size)
{
#ifndef __DISABLE_EXPORTS
WARN_ON_ONCE(IS_ENABLED(CONFIG_DEBUG_ATOMIC) && ((unsigned long)v & (size - 1)));
#endif
}

static __always_inline void instrument_atomic_read_write(const volatile void *v, size_t size)
{
kasan_check_write(v, size);
kcsan_check_atomic_read_write(v, size);
instrument_atomic_check_alignment(v, size);
}

#define try_cmpxchg64(ptr, oldp, ...) \
({ \
typeof(ptr) __ai_ptr = (ptr); \
typeof(oldp) __ai_oldp = (oldp); \
kcsan_mb(); \
instrument_atomic_read_write(__ai_ptr, sizeof(*__ai_ptr)); \
instrument_read_write(__ai_oldp, sizeof(*__ai_oldp)); \
raw_try_cmpxchg64(__ai_ptr, __ai_oldp, __VA_ARGS__); \
})

#define pxx_xchg64(_pxx, _ptr, _val) ({ \
_pxx##val_t *_p = (_pxx##val_t *)_ptr; \
_pxx##val_t _o = *_p; \
do { } while (!try_cmpxchg64(_p, &_o, (_val))); \
native_make_##_pxx(_o); \
})

static inline void native_set_pmd(pmd_t *pmdp, pmd_t pmd)
{
pxx_xchg64(pmd, pmdp, native_pmd_val(pmd));
}

#define set_pmd(pmdp, pmd) native_set_pmd(pmdp, pmd)

static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd,
unsigned long haddr, bool freeze)
{
pmd_t old_pmd, _pmd;
...
/*
* Withdraw the table only after we mark the pmd entry invalid.
* This's critical for some architectures (Power).
*/
pgtable = pgtable_trans_huge_withdraw(mm, pmd);
pmd_populate(mm, &_pmd, pgtable);
...
}

So the cmpxchg8b() happens on local "_pmd" variable, which happens
to be unaligned on the stack in this configuration and could potentially
span a page boundary.

I don't know why if this has to use an atomic pmd_populate() here,
but if this is required, the patch below would ensure that the
cmpxchg8b operates on an aligned word.

Arnd

diff --git a/arch/x86/include/asm/pgtable-3level_types.h b/arch/x86/include/asm/pgtable-3level_types.h
index 580b09bf6a45..526986ba6b17 100644
--- a/arch/x86/include/asm/pgtable-3level_types.h
+++ b/arch/x86/include/asm/pgtable-3level_types.h
@@ -5,11 +5,11 @@
#ifndef __ASSEMBLER__
#include <linux/types.h>

-typedef u64 pteval_t;
-typedef u64 pmdval_t;
-typedef u64 pudval_t;
-typedef u64 p4dval_t;
-typedef u64 pgdval_t;
+typedef u64 pteval_t __aligned(8);
+typedef u64 pmdval_t __aligned(8);
+typedef u64 pudval_t __aligned(8);
+typedef u64 p4dval_t __aligned(8);
+typedef u64 pgdval_t __aligned(8);
typedef u64 pgprotval_t;

typedef union {


Arnd