@@ -100,6 +100,23 @@ void vmx_pi_per_cpu_init(unsigned int cpu)
spin_lock_init(&per_cpu(vmx_pi_blocking, cpu).lock);
}
+void vmx_pi_get_ref(struct vcpu *v)
+{
+ ASSERT(atomic_read(&v->arch.hvm_vmx.pi_blocking.refcnt) >= 0);
+ atomic_inc(&v->arch.hvm_vmx.pi_blocking.refcnt);
+}
+
+void vmx_pi_put_ref(struct vcpu *v)
+{
+ atomic_dec(&v->arch.hvm_vmx.pi_blocking.refcnt);
+ ASSERT(atomic_read(&v->arch.hvm_vmx.pi_blocking.refcnt) >= 0);
+}
+
+bool vmx_pi_in_use(struct vcpu *v)
+{
+ return !!atomic_read(&v->arch.hvm_vmx.pi_blocking.refcnt);
+}
+
static void vmx_vcpu_block(struct vcpu *v)
{
unsigned long flags;
@@ -378,6 +395,9 @@ void vmx_pi_hooks_assign(struct domain *d)
d->arch.hvm_domain.pi_ops.vcpu_block = vmx_vcpu_block;
d->arch.hvm_domain.pi_ops.do_resume = vmx_pi_do_resume;
+ d->arch.hvm_domain.pi_ops.get_ref = vmx_pi_get_ref;
+ d->arch.hvm_domain.pi_ops.put_ref = vmx_pi_put_ref;
+ d->arch.hvm_domain.pi_ops.in_use = vmx_pi_in_use;
}
/* This function is called when pcidevs_lock is held */
@@ -416,6 +436,15 @@ void vmx_pi_hooks_deassign(struct domain *d)
d->arch.hvm_domain.pi_ops.vcpu_block = NULL;
d->arch.hvm_domain.pi_ops.switch_from = NULL;
d->arch.hvm_domain.pi_ops.do_resume = NULL;
+ d->arch.hvm_domain.pi_ops.get_ref = NULL;
+ d->arch.hvm_domain.pi_ops.put_ref = NULL;
+ d->arch.hvm_domain.pi_ops.in_use = NULL;
+ /*
+ * If device is still using by guest, but we forcibly deassign it,
+ * then the 'refcnt' is not zero here. Clear it for re-assignment.
+ */
+ for_each_vcpu ( d, v )
+ atomic_set(&v->arch.hvm_vmx.pi_blocking.refcnt, 0);
for_each_vcpu ( d, v )
vmx_pi_unblock_vcpu(v);
@@ -319,7 +319,7 @@ int pt_irq_create_bind(
{
uint8_t dest, dest_mode, delivery_mode;
int dest_vcpu_id;
- const struct vcpu *vcpu;
+ struct vcpu *vcpu;
if ( !(pirq_dpci->flags & HVM_IRQ_DPCI_MAPPED) )
{
@@ -596,6 +596,32 @@ static int remap_entry_to_msi_msg(
return 0;
}
+static void pi_get_ref(struct pi_desc *pi_desc)
+{
+ struct vcpu *v;
+
+ if ( !pi_desc )
+ return;
+
+ v = pi_desc_to_vcpu(pi_desc);
+ ASSERT(is_hvm_domain(v->domain));
+ if ( v->domain->arch.hvm_domain.pi_ops.get_ref )
+ v->domain->arch.hvm_domain.pi_ops.get_ref(v);
+}
+
+static void pi_put_ref(struct pi_desc *pi_desc)
+{
+ struct vcpu *v;
+
+ if ( !pi_desc )
+ return;
+
+ v = pi_desc_to_vcpu(pi_desc);
+ ASSERT(is_hvm_domain(v->domain));
+ if ( v->domain->arch.hvm_domain.pi_ops.put_ref )
+ v->domain->arch.hvm_domain.pi_ops.put_ref(v);
+}
+
static int msi_msg_to_remap_entry(
struct iommu *iommu, struct pci_dev *pdev,
struct msi_desc *msi_desc, struct msi_msg *msg)
@@ -619,6 +645,7 @@ static int msi_msg_to_remap_entry(
{
free_remap_entry(iommu, msi_desc->remap_index + i);
msi_desc[i].irte_initialized = false;
+ pi_put_ref(msi_desc[i].pi_desc);
}
spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags);
return 0;
@@ -962,11 +989,12 @@ void iommu_disable_x2apic_IR(void)
* This function is used to update the IRTE for posted-interrupt
* when guest changes MSI/MSI-X information.
*/
-int pi_update_irte(const struct pi_desc *pi_desc, const struct pirq *pirq,
+int pi_update_irte(struct pi_desc *pi_desc, const struct pirq *pirq,
const uint8_t gvec)
{
struct irq_desc *desc;
struct msi_desc *msi_desc;
+ struct pi_desc *old_pi_desc;
int rc;
desc = pirq_spin_lock_irq_desc(pirq, NULL);
@@ -979,13 +1007,22 @@ int pi_update_irte(const struct pi_desc *pi_desc, const struct pirq *pirq,
rc = -ENODEV;
goto unlock_out;
}
+ old_pi_desc = msi_desc->pi_desc;
+
+ pi_get_ref(pi_desc);
msi_desc->pi_desc = pi_desc;
msi_desc->gvec = gvec;
spin_unlock_irq(&desc->lock);
ASSERT(pcidevs_locked());
- return iommu_update_ire_from_msi(msi_desc, &msi_desc->msg);
+ rc = iommu_update_ire_from_msi(msi_desc, &msi_desc->msg);
+ if ( !rc )
+ pi_put_ref(old_pi_desc);
+ else
+ ASSERT_UNREACHABLE();
+
+ return rc;
unlock_out:
spin_unlock_irq(&desc->lock);
@@ -98,6 +98,12 @@ struct hvm_pi_ops {
/* Hook into the vmentry path. */
void (*do_resume)(struct vcpu *v);
+
+ /* Get/Put refcount of PI blocking of this vCPU */
+ void (*get_ref)(struct vcpu *v);
+ void (*put_ref)(struct vcpu *v);
+ /* Is the PI blocking is referred by IRTEs */
+ bool (*in_use)(struct vcpu *v);
};
struct hvm_domain {
@@ -85,6 +85,7 @@ struct pi_desc {
struct pi_blocking_vcpu {
struct list_head list;
spinlock_t *lock;
+ atomic_t refcnt; /* How many IRTEs refer to this vCPU? */
};
struct arch_vmx_struct {
@@ -160,6 +161,8 @@ struct arch_vmx_struct {
struct pi_blocking_vcpu pi_blocking;
};
+#define pi_desc_to_vcpu(a) container_of(a, struct vcpu, arch.hvm_vmx.pi_desc)
+
int vmx_create_vmcs(struct vcpu *v);
void vmx_destroy_vmcs(struct vcpu *v);
void vmx_vmcs_enter(struct vcpu *v);
@@ -92,7 +92,7 @@ bool_t iommu_supports_eim(void);
int iommu_enable_x2apic_IR(void);
void iommu_disable_x2apic_IR(void);
-int pi_update_irte(const struct pi_desc *pi_desc, const struct pirq *pirq,
+int pi_update_irte(struct pi_desc *pi_desc, const struct pirq *pirq,
const uint8_t gvec);
#endif /* !__ARCH_X86_IOMMU_H__ */
@@ -105,7 +105,7 @@ struct msi_desc {
bool irte_initialized;
uint8_t gvec; /* guest vector. valid when pi_desc isn't NULL */
- const struct pi_desc *pi_desc; /* pointer to posted descriptor */
+ struct pi_desc *pi_desc; /* pointer to posted descriptor */
struct list_head list;
This patch intruduces a 'refcnt' field in vmx_pi_blocking to track the reference count of 'pi_desc' of the vCPU. And change this field every time we re-program one IRTE. Signed-off-by: Chao Gao <chao.gao@intel.com> --- xen/arch/x86/hvm/vmx/vmx.c | 29 ++++++++++++++++++++++++ xen/drivers/passthrough/io.c | 2 +- xen/drivers/passthrough/vtd/intremap.c | 41 ++++++++++++++++++++++++++++++++-- xen/include/asm-x86/hvm/domain.h | 6 +++++ xen/include/asm-x86/hvm/vmx/vmcs.h | 3 +++ xen/include/asm-x86/iommu.h | 2 +- xen/include/asm-x86/msi.h | 2 +- 7 files changed, 80 insertions(+), 5 deletions(-)