From: Jan Kiszka <jan.kiszka@siemens.com>
To: Avi Kivity <avi@redhat.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>,
"kvm@vger.kernel.org" <kvm@vger.kernel.org>
Subject: Re: [RFC] KVM: Fix simultaneous NMIs
Date: Thu, 15 Sep 2011 19:25:04 +0200 [thread overview]
Message-ID: <4E7234F0.2080609@siemens.com> (raw)
In-Reply-To: <4E722FA8.2030006@redhat.com>
On 2011-09-15 19:02, Avi Kivity wrote:
> On 09/15/2011 07:01 PM, Jan Kiszka wrote:
>> On 2011-09-15 16:45, Avi Kivity wrote:
>>> If simultaneous NMIs happen, we're supposed to queue the second
>>> and next (collapsing them), but currently we sometimes collapse
>>> the second into the first.
>>
>> Can you describe the race in a few more details here ("sometimes" sounds
>> like "I don't know when" :) )?
>
> In this case it was "I'm in a hurry".
>
>>>
>>> void kvm_inject_nmi(struct kvm_vcpu *vcpu)
>>> {
>>> + atomic_inc(&vcpu->arch.nmi_pending);
>>> kvm_make_request(KVM_REQ_EVENT, vcpu);
>>> - vcpu->arch.nmi_pending = 1;
>>
>> Does the reordering matter?
>
> I think so. Suppose the vcpu enters just after kvm_make_request(); it
> sees KVM_REQ_EVENT and clears it, but doesn't see nmi_pending because it
> wasn't set set. Then comes a kick, the guest is reentered with
> nmi_pending set but KVM_REQ_EVENT clear and sails through the check and
> enters the guest. The NMI is delayed until the next KVM_REQ_EVENT.
That makes sense - and the old code looks more strange now.
>
>> Do we need barriers?
>
> Yes.
>
>>
>>> @@ -5570,9 +5570,9 @@ static void inject_pending_event(struct kvm_vcpu *vcpu)
>>> }
>>>
>>> /* try to inject new event if pending */
>>> - if (vcpu->arch.nmi_pending) {
>>> + if (atomic_read(&vcpu->arch.nmi_pending)) {
>>> if (kvm_x86_ops->nmi_allowed(vcpu)) {
>>> - vcpu->arch.nmi_pending = false;
>>> + atomic_dec(&vcpu->arch.nmi_pending);
>>
>> Here we lost NMIs in the past by overwriting nmi_pending while another
>> one was already queued, right?
>
> One place, yes. The other is kvm_inject_nmi() - if the first nmi didn't
> get picked up by the vcpu by the time the second nmi arrives, we lose
> the second nmi.
Thinking this through again, it's actually not yet clear to me what we
are modeling here: If two NMI events arrive almost perfectly in
parallel, does the real hardware guarantee that they will always cause
two NMI events in the CPU? Then this is required.
Otherwise I just lost understanding again why we were loosing NMIs here
and in kvm_inject_nmi (maybe elsewhere then?).
>
>>> if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win) {
>>> inject_pending_event(vcpu);
>>>
>>> /* enable NMI/IRQ window open exits if needed */
>>> - if (nmi_pending)
>>> + if (atomic_read(&vcpu->arch.nmi_pending)
>>> + && nmi_in_progress(vcpu))
>>
>> Is nmi_pending&& !nmi_in_progress possible at all?
>
> Yes, due to NMI-blocked-by-STI. A really touchy area.
And we don't need the window exit notification then? I don't understand
what nmi_in_progress is supposed to do here.
>
>> Is it rather a BUG
>> condition?
>
> No.
>
>> If not, what will happen next?
>
> The NMI window will open and we'll inject the NMI.
How will we know this? We do not request the exit, that's my worry.
> But I think we have
> a bug here - we should only kvm_collapse_nmis() if an NMI handler was
> indeed running, yet we do it unconditionally.
>
>>>
>>> +static inline void kvm_collapse_pending_nmis(struct kvm_vcpu *vcpu)
>>> +{
>>> + /* Collapse all NMIs queued while an NMI handler was running to one */
>>> + if (atomic_read(&vcpu->arch.nmi_pending))
>>> + atomic_set(&vcpu->arch.nmi_pending, 1);
>>
>> Is it OK that NMIs injected after the collapse will increment this to>
>> 1 again? Or is that impossible?
>>
>
> It's possible and okay. We're now completing execution of IRET. Doing
> atomic_set() after atomic_inc() means the NMI happened before IRET
> completed, and vice versa. Since these events are asynchronous, we're
> free to choose one or the other (a self-IPI-NMI just before the IRET
> must be swallowed, and a self-IPI-NMI just after the IRET would only be
> executed after the next time around the handler).
Need to think through this separately.
Jan
--
Siemens AG, Corporate Technology, CT T DE IT 1
Corporate Competence Center Embedded Linux
next prev parent reply other threads:[~2011-09-15 17:25 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-09-15 14:45 [RFC] KVM: Fix simultaneous NMIs Avi Kivity
2011-09-15 16:01 ` Jan Kiszka
2011-09-15 17:02 ` Avi Kivity
2011-09-15 17:25 ` Jan Kiszka [this message]
2011-09-15 17:48 ` Avi Kivity
2011-09-19 13:54 ` Marcelo Tosatti
2011-09-19 14:30 ` Avi Kivity
2011-09-19 14:54 ` Marcelo Tosatti
2011-09-19 15:09 ` Avi Kivity
2011-09-19 15:12 ` Avi Kivity
2011-09-19 15:22 ` Marcelo Tosatti
2011-09-19 15:37 ` Avi Kivity
2011-09-19 15:57 ` Marcelo Tosatti
2011-09-20 8:40 ` Avi Kivity
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4E7234F0.2080609@siemens.com \
--to=jan.kiszka@siemens.com \
--cc=avi@redhat.com \
--cc=kvm@vger.kernel.org \
--cc=mtosatti@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.