diff options
Diffstat (limited to 'arch/x86/kvm/ioapic.c')
| -rw-r--r-- | arch/x86/kvm/ioapic.c | 34 | 
1 files changed, 32 insertions, 2 deletions
diff --git a/arch/x86/kvm/ioapic.c b/arch/x86/kvm/ioapic.c index d859ae8890d0..9fd2dd89a1c5 100644 --- a/arch/x86/kvm/ioapic.c +++ b/arch/x86/kvm/ioapic.c @@ -271,8 +271,9 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)  {  	unsigned index;  	bool mask_before, mask_after; -	int old_remote_irr, old_delivery_status;  	union kvm_ioapic_redirect_entry *e; +	unsigned long vcpu_bitmap; +	int old_remote_irr, old_delivery_status, old_dest_id, old_dest_mode;  	switch (ioapic->ioregsel) {  	case IOAPIC_REG_VERSION: @@ -296,6 +297,8 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)  		/* Preserve read-only fields */  		old_remote_irr = e->fields.remote_irr;  		old_delivery_status = e->fields.delivery_status; +		old_dest_id = e->fields.dest_id; +		old_dest_mode = e->fields.dest_mode;  		if (ioapic->ioregsel & 1) {  			e->bits &= 0xffffffff;  			e->bits |= (u64) val << 32; @@ -321,7 +324,34 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)  		if (e->fields.trig_mode == IOAPIC_LEVEL_TRIG  		    && ioapic->irr & (1 << index))  			ioapic_service(ioapic, index, false); -		kvm_make_scan_ioapic_request(ioapic->kvm); +		if (e->fields.delivery_mode == APIC_DM_FIXED) { +			struct kvm_lapic_irq irq; + +			irq.shorthand = 0; +			irq.vector = e->fields.vector; +			irq.delivery_mode = e->fields.delivery_mode << 8; +			irq.dest_id = e->fields.dest_id; +			irq.dest_mode = e->fields.dest_mode; +			bitmap_zero(&vcpu_bitmap, 16); +			kvm_bitmap_or_dest_vcpus(ioapic->kvm, &irq, +						 &vcpu_bitmap); +			if (old_dest_mode != e->fields.dest_mode || +			    old_dest_id != e->fields.dest_id) { +				/* +				 * Update vcpu_bitmap with vcpus specified in +				 * the previous request as well. This is done to +				 * keep ioapic_handled_vectors synchronized. +				 */ +				irq.dest_id = old_dest_id; +				irq.dest_mode = old_dest_mode; +				kvm_bitmap_or_dest_vcpus(ioapic->kvm, &irq, +							 &vcpu_bitmap); +			} +			kvm_make_scan_ioapic_request_mask(ioapic->kvm, +							  &vcpu_bitmap); +		} else { +			kvm_make_scan_ioapic_request(ioapic->kvm); +		}  		break;  	}  }  |