return &(to_vmx(vcpu)->pi_desc);
 }
 
+static int pi_try_set_control(struct pi_desc *pi_desc, u64 old, u64 new)
+{
+       /*
+        * PID.ON can be set at any time by a different vCPU or by hardware,
+        * e.g. a device.  PID.control must be written atomically, and the
+        * update must be retried with a fresh snapshot an ON change causes
+        * the cmpxchg to fail.
+        */
+       if (cmpxchg64(&pi_desc->control, old, new) != old)
+               return -EBUSY;
+
+       return 0;
+}
+
 void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)
 {
        struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
 
                new.ndst = dest;
                new.sn = 0;
-       } while (cmpxchg64(&pi_desc->control, old.control,
-                          new.control) != old.control);
+       } while (pi_try_set_control(pi_desc, old.control, new.control));
 
 after_clear_sn:
 
        if (!x2apic_mode)
                dest = (dest << 8) & 0xFF00;
 
+       WARN(pi_desc->nv != POSTED_INTR_WAKEUP_VECTOR,
+            "Wakeup handler not enabled while the vCPU was blocking");
+
        do {
                old.control = new.control = READ_ONCE(pi_desc->control);
-               WARN(old.nv != POSTED_INTR_WAKEUP_VECTOR,
-                    "Wakeup handler not enabled while the VCPU is blocked\n");
 
                new.ndst = dest;
 
                /* set 'NV' to 'notification vector' */
                new.nv = POSTED_INTR_VECTOR;
-       } while (cmpxchg64(&pi_desc->control, old.control,
-                          new.control) != old.control);
+       } while (pi_try_set_control(pi_desc, old.control, new.control));
 
        vcpu->pre_pcpu = -1;
 }
                      &per_cpu(blocked_vcpu_on_cpu, vcpu->cpu));
        spin_unlock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->cpu));
 
+       WARN(pi_desc->sn == 1,
+            "Posted Interrupt Suppress Notification set before blocking");
+
        do {
                old.control = new.control = READ_ONCE(pi_desc->control);
 
-               WARN((pi_desc->sn == 1),
-                    "Warning: SN field of posted-interrupts "
-                    "is set before blocking\n");
-
                /* set 'NV' to 'wakeup vector' */
                new.nv = POSTED_INTR_WAKEUP_VECTOR;
-       } while (cmpxchg64(&pi_desc->control, old.control,
-                          new.control) != old.control);
+       } while (pi_try_set_control(pi_desc, old.control, new.control));
 
        /* We should not block the vCPU if an interrupt is posted for it.  */
        if (pi_test_on(pi_desc))