Re: [V4][PATCH 4/6] x86, nmi: add in logic to handle multipleevents and unknown NMIs

From: Robert Richter
Date: Wed Sep 14 2011 - 12:27:37 EST


On 13.09.11 16:58:27, Don Zickus wrote:
> @@ -87,6 +87,16 @@ static int notrace __kprobes nmi_handle(unsigned int type, struct pt_regs *regs)
>
> handled += a->handler(type, regs);
>
> + /*
> + * Optimization: only loop once if this is not a
> + * back-to-back NMI. The idea is nothing is dropped
> + * on the first NMI, only on the second of a back-to-back
> + * NMI. No need to waste cycles going through all the
> + * handlers.
> + */
> + if (!b2b && handled)
> + break;

Don, if I am not missing something, this actually does not work
because perfctr NMIs do not re-trigger. Suppose a handler running
before perfctr. It sets 'handled' and the chain is stopped here. To
run through the perfctr handler the NMI must retrigger which it
doesn't.

I tested the above with enclosed patch.

The patch handles the nmi and then stops the chain. You see by the PMI
count that the perfctr nmi is not handled:

Patch applied:

# echo $(($(grep PMI /proc/interrupts | sed -e 's/.*: *//;s/ *Non.*//;s/ */ + /g')))
0
# echo $(($(grep NMI /proc/interrupts | sed -e 's/.*: *//;s/ *Non.*//;s/ */ + /g')))
0
# perf record -e cpu-cycles bash -c 'perl -e "while(1) {}" & sleep 5 ; kill $!'
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.011 MB perf.data (~472 samples) ]
# echo $(($(grep PMI /proc/interrupts | sed -e 's/.*: *//;s/ *Non.*//;s/ */ + /g')))
128
# echo $(($(grep NMI /proc/interrupts | sed -e 's/.*: *//;s/ *Non.*//;s/ */ + /g')))
1387

W/o the patch (tip/perf/core: 51887c8):

# echo $(($(grep NMI /proc/interrupts | sed -e 's/.*: *//;s/ *Non.*//;s/ */ + /g')))
0
# echo $(($(grep PMI /proc/interrupts | sed -e 's/.*: *//;s/ *Non.*//;s/ */ + /g')))
0
# perf record -e cpu-cycles bash -c 'perl -e "while(1) {}" & sleep 5 ; kill $!'
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.194 MB perf.data (~8455 samples) ]
# echo $(($(grep PMI /proc/interrupts | sed -e 's/.*: *//;s/ *Non.*//;s/ */ + /g')))
4918
# echo $(($(grep NMI /proc/interrupts | sed -e 's/.*: *//;s/ *Non.*//;s/ */ + /g')))
4918

So we may not jump out the while loop.

-Robert

> +
> a = next_a;
> }
> rcu_read_unlock();