kernel - Fix callout race and panic (2)
* Previous fix couldn't handle a callout_stop() vs callout_reset() race
on different cpus. When this race occurred, the callout_stop() would
get stuck waiting for the ARMED bit to clear, which it never would
because a new callout_reset() re-armed it.
* Refactor the callout code to clean it up. Remove the ARMED flag, instead
a callout is considered ARMED if the IPI_MASK counter bits are not zero
or PENDING is set. Use these rules to lock-in the owning cpu with an
atomic op
* Integrate all flags settings in IPIs with the atomic decrement of the
IPI_MASK count, rather than as separate operations.
* callout_stop() now just waits for the IPI sequence to end. callout_reset()
now re-tests whether the callout is still armed or not after callout_stop()
returns (since it needs to know for sure) before it tries to take over
the callout. This fixes the race.