0

To wait for an NMI interrupt, currently I do:

while true; do

  RES=$(cat /proc/interrupts | grep NMI)
  RES=$(echo "$RES" | sed 's/[^0-9]*//g')
  RES=$(echo "$RES" | sed 's/^0*//')

  if [ "$RES" != "" ]; then

    echo "DONE"
    exit 0
  
  fi

  sleep 1

done

Is there a nicer way to do this without polling or sleeping?

Maestro
  • 9,046
  • 15
  • 83
  • 116
  • Cross-posted, @Maestro, delete one. https://unix.stackexchange.com/questions/743062/how-to-wait-for-an-interrupt-without-polling – Gilles Quénot Apr 16 '23 at 15:13
  • https://meta.stackexchange.com/questions/64068/is-cross-posting-a-question-on-multiple-stack-exchange-sites-permitted-if-the-qu – Gilles Quénot Apr 16 '23 at 15:26
  • If you are parsing a file (such as `/proc/interrupts`) you could at least show a representative sample of its contents! Also, it seems a bit much to use 4 processes (`cat`, `grep`, 2x `sed`) when a single `awk` could do all that. – Mark Setchell Apr 16 '23 at 15:40
  • @MarkSetchell I just parse the count number for the NMI interrupt there and check if its non-zero, but I guessed it was not really relevant to the question. – Maestro Apr 16 '23 at 15:43

2 Answers2

1

Waiting for an NMI interrupt without polling can be achieved by using an event-driven approach. Instead of constantly checking the /proc/interrupts file, you can use a signal handler to catch the NMI signal and execute the required actions when the signal is received. Sad but, you cannot directly catch an NMI interrupt in a user-space script, as NMI interrupts are reserved for kernel-level code. However, you can write a kernel module to handle NMI interrupts and pass a signal to your user-space script when an NMI occurs.

Here is what you can do.. First: Write a kernel module to handle NMI interrupts. then when an NMI occurs, let the kernel module send a signal (e.g., SIGUSR1) to your user-space script. At last in your script, set up a signal handler to catch the signal sent by the kernel module and execute the required action. And check example below:

handle_signal() {
echo "NMI interrupt received"
echo "DONE"
exit 0
}

trap 'handle_signal' SIGUSR1
start_nmi_module
while true; do
    sleep 1
done

Answering the comment:

hmm, if you are unable to modify the kernel or add modules to the machines, then yes, your current method is likely the best option. But, you can make a few optimizations to reduce the load because of polling:

Example using awk:

while true; do
  RES=$(awk '/NMI/ {for (i=2; i<=NF; i++) if ($i ~ /^[0-9]+$/) {sum+=$i}} END {print sum}' /proc/interrupts)

  if [ "$RES" != "" ] && [ "$RES" -ne "0" ]; then
    echo "DONE"
    exit 0
  fi

  sleep 5
done

This script uses awk to read the /proc/interrupts file and sum the NMI interrupt counts for all the CPU. If the total count is not zero, the script exits with "DONE". The sleep interval has been increased to 5 seconds, because it will reduce the frequency and system load.

Answering second question:

So sad but, without kernel modifications or access to kernel modules, your options for handling NMI interrupts in real-time are limited. But, you can try to strike a balance between delay and load by changing the sleep interval according. Example, use a smaller sleep interval such as 0.1 or something like that or maybe 0.5 seconds. This would decrease the delay handling, the interrupt while not excessively increasing the system load.

while true; do


RES=$(awk '/NMI/ {for (i=2; i<=NF; i++) if ($i ~ /^[0-9]+$/) {sum+=$i}} END {print sum}' /proc/interrupts)

  if [ "$RES" != "" ] && [ "$RES" -ne "0" ]; then
    echo "DONE"
    exit 0
  fi

  sleep 0.1
done

Remember that this is still a polling and there will always be a trade between response time and load. Choosing carefully the sleep interval you can find the balance for your specific case.

  • 1
    I am not able to make any modifications to the kernel of the machines this script runs once. So I guess that means my current method is the only way? – Maestro Apr 16 '23 at 15:45
  • Thanks for your edit. The whole reason I wanted to get rid of sleep is to decrease the delay in handling the interrupt. So increasing it to 5 has the opposite effect. And decreasing it too much will waste cycles. – Maestro Apr 16 '23 at 16:42
  • @Maestro check again, hopefully this answers your question, good luck. – David NoHorizon Apr 16 '23 at 18:48
1

Stupid solution: As the kernel will log NMIs, too, redirect kernel messages to a separate file (or FIFO) via syslogd, and then grep for the NMI event in that file:

If there is no output, your process will block; only if there is some output, the grep will do something. Make sure the lines are not buffered before output to the file or FIFO.

U. Windl
  • 3,480
  • 26
  • 54