[PATCH v2 00/12] x86: WARN() hackery

Peter Zijlstra posted 12 patches 1 month, 1 week ago
[PATCH v2 00/12] x86: WARN() hackery
Posted by Peter Zijlstra 1 month, 1 week ago
Hi!

I was going through my patch cabinet the other day and found these patches.

Mark was going to see if he could make ARM64 play ball to see if the generic
bits really are that. But clearly he's forgotten about things just as much as I
had.

We should probably just merge these, as they improbe the code-gen for WARN()
significantly on x86 and then I'll help Mark out for ARM64 when he gets around
to it and needs a hand.

Freshly rebased and somewhat tested.

As a quick reminder, this makes WARN(), which has a vararg printf like
arguments work with the #UD based WARN infra by hiding the format in the
bug_table (its a constant after all) and replacing this first argument with the
address of the bug-table entry while making the actual function that's called a
UD1 instruction.

The exception handler then create a va_list from pt_regs and vprintk() does the
rest.

Also here:

  git://git.kernel.org/pub/scm/linux/kernel/git/peterz/queue.git x86/bugs


v1: https://lkml.kernel.org/r/20250607094224.104791182@infradead.org
Re: [PATCH v2 00/12] x86: WARN() hackery
Posted by Linus Torvalds 1 month, 1 week ago
On Mon, 10 Nov 2025 at 03:58, Peter Zijlstra <peterz@infradead.org> wrote:
>
> We should probably just merge these, as they improve the code-gen for WARN()
> significantly on x86

Yes..  And I'd actually like to see some example code generation in
the commit messages just to make that "improve the code-gen" more
explicit.

I know I saw some originally (or at least we discussed it), but it's
been long enough that my bird-brain has long since forgotten..

It looks like this series is on top of the BUGVERBOSE_DETAILED updates
(that I like but don't have in my tree yet - I think it's all
scheduled for 6.19). So I just scanned the patches visually and they
all look fine, I was just left wanting a more concrete "look, we used
to do this, now we generate _this_ instead".

                Linus
Re: [PATCH v2 00/12] x86: WARN() hackery
Posted by Peter Zijlstra 1 month, 1 week ago
On Mon, Nov 10, 2025 at 08:18:02AM -0800, Linus Torvalds wrote:
> On Mon, 10 Nov 2025 at 03:58, Peter Zijlstra <peterz@infradead.org> wrote:
> >
> > We should probably just merge these, as they improve the code-gen for WARN()
> > significantly on x86
> 
> Yes..  And I'd actually like to see some example code generation in
> the commit messages just to make that "improve the code-gen" more
> explicit.

Damn, I actually thought to include some and then I totally forgot.

So lets see:

        if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
                      "corrupted preempt_count: %s/%d/0x%x\n",
                      current->comm, current->pid, preempt_count()))

#define WARN_ONCE(condition, format...)                         \
        DO_ONCE_LITE_IF(condition, WARN, 1, format)

#define WARN(condition, format...) ({                                   \
        int __ret_warn_on = !!(condition);                              \
        if (unlikely(__ret_warn_on))                                    \
                __WARN_printf(TAINT_WARN, format);                      \
        unlikely(__ret_warn_on);                                        \
})

#define __WARN_printf(taint, arg...) do {                               \
                instrumentation_begin();                                \
                __warn_printk(arg);                                     \
                __WARN_FLAGS("", BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint));\
                instrumentation_end();                                  \
        } while (0)

Turns into this majestic pile:


# ../kernel/sched/core.c:5093:  if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
        cmpl    $2, %ecx        #, _7
        jne     .L1472  #,

...

.L1472:
# ../kernel/sched/core.c:5093:  if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
        cmpb    $0, __already_done.11(%rip)     #, __already_done
        je      .L1513  #,

...

.L1513:
# ../kernel/sched/core.c:5093:  if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
        movb    $1, __already_done.11(%rip)     #, __already_done
# ../kernel/sched/core.c:5093:  if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,


# 0 "" 2
# ../kernel/sched/core.c:5093:  if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
#NO_APP
        movl    1424(%r14), %edx        # _15->pid, _15->pid
        leaq    1912(%r14), %rsi        #, _17
        movq    $.LC43, %rdi    #,
        call    __warn_printk   #
# ../kernel/sched/core.c:5093:  if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,

# 0 "" 2
# 5093 "../kernel/sched/core.c" 1
        1:       .byte 0x0f, 0x0b ;
.pushsection __bug_table,"aw"
912:
        .pushsection .discard.annotate_data,"M", @progbits, 8
        .long 912b - .
        .long 1
        .popsection
        2:      .long 1b - .    # bug_entry::bug_addr
        .long .LC1 - .  # bug_entry::file       #
        .word 5093      # bug_entry::line       #
        .word 2313      # bug_entry::flags      #
        .org 2b + 12    #
.popsection
.pushsection .discard.annotate_insn,"M", @progbits, 8
        .long 1b - .
        .long 8		# ANNOTYPE_REACHABLE
        .popsection


While afterwards it looks like so:


# ../kernel/sched/core.c:5093:  if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
        cmpl    $2, %ecx        #, _7
        jne     .L1442  #,

...

.L1442:
# ../kernel/sched/core.c:5093:  if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
#APP
# 5093 "../kernel/sched/core.c" 1
        lea (2f)(%rip), %rdi    # bug
1:
.pushsection __bug_table,"aw"
        912:
        .pushsection .discard.annotate_data,"M", @progbits, 8
        .long 912b - .
        .long 1
        .popsection
        2:
        .long 1b - .    # bug_entry::bug_addr
        .long .LC43 - . # bug_entry::format     #
        .long .LC1 - .  # bug_entry::file       #
        .word 5093      # bug_entry::line       #
        .word 2323      # bug_entry::flags      #
        .org 2b + 16    #
.popsection
        
# 0 "" 2
#NO_APP  
        movl    1424(%r14), %edx        # pretmp_19->pid, pretmp_19->pid
# ../kernel/sched/core.c:5093:  if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
        leaq    1912(%r14), %rsi        #, _13
# ../kernel/sched/core.c:5093:  if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
        call    __SCT__WARN_trap        #

Where at runtime we patch that "call __SCT__WARN_trap" to read like:

	ud1 (%ecx), %rdi


The whole DO_ONCE_LITE_IF/__already_done thing is gone (replaced with
BUGFLAG_ONCE) and the external __warn_prink() call is also gone, now
inside the exception handler through the pt_regs->va_list magic.

I'll see if I can clean that up somewhat and stick in the changelog.