|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH V3 28/29] x86/vvtd: Add queued invalidation (QI) support
On Thu, Sep 21, 2017 at 11:02:09PM -0400, Lan Tianyu wrote:
> From: Chao Gao <chao.gao@xxxxxxxxx>
>
> Queued Invalidation Interface is an expanded invalidation interface with
> extended capabilities. Hardware implementations report support for queued
> invalidation interface through the Extended Capability Register. The queued
> invalidation interface uses an Invalidation Queue (IQ), which is a circular
> buffer in system memory. Software submits commands by writing Invalidation
> Descriptors to the IQ.
>
> In this patch, a new function viommu_process_iq() is used for emulating how
> hardware handles invalidation requests through QI.
>
> Signed-off-by: Chao Gao <chao.gao@xxxxxxxxx>
> Signed-off-by: Lan Tianyu <tianyu.lan@xxxxxxxxx>
> ---
> xen/drivers/passthrough/vtd/iommu.h | 19 ++-
> xen/drivers/passthrough/vtd/vvtd.c | 232
> ++++++++++++++++++++++++++++++++++++
> 2 files changed, 250 insertions(+), 1 deletion(-)
>
> diff --git a/xen/drivers/passthrough/vtd/iommu.h
> b/xen/drivers/passthrough/vtd/iommu.h
> index c69cd21..c2b83f1 100644
> --- a/xen/drivers/passthrough/vtd/iommu.h
> +++ b/xen/drivers/passthrough/vtd/iommu.h
> @@ -177,6 +177,21 @@
> #define DMA_IRTA_S(val) (val & 0xf)
> #define DMA_IRTA_SIZE(val) (1UL << (DMA_IRTA_S(val) + 1))
>
> +/* IQA_REG */
> +#define DMA_IQA_ADDR(val) (val & ~0xfffULL)
> +#define DMA_IQA_QS(val) (val & 0x7)
> +#define DMA_IQA_RSVD 0xff8ULL
> +
> +/* IECTL_REG */
> +#define DMA_IECTL_IM_SHIFT 31
> +#define DMA_IECTL_IM (1 << DMA_IECTL_IM_SHIFT)
Isn't this undefined behavior? It should be 1u.
You should consider using u for all the defines added.
> +#define DMA_IECTL_IP_SHIFT 30
> +#define DMA_IECTL_IP (1 << DMA_IECTL_IP_SHIFT)
> +
> +/* ICS_REG */
> +#define DMA_ICS_IWC_SHIFT 0
> +#define DMA_ICS_IWC (1 << DMA_ICS_IWC_SHIFT)
> +
> /* PMEN_REG */
> #define DMA_PMEN_EPM (((u32)1) << 31)
> #define DMA_PMEN_PRS (((u32)1) << 0)
> @@ -211,7 +226,8 @@
> #define DMA_FSTS_PPF (1U << DMA_FSTS_PPF_SHIFT)
> #define DMA_FSTS_AFO (1U << 2)
> #define DMA_FSTS_APF (1U << 3)
> -#define DMA_FSTS_IQE (1U << 4)
> +#define DMA_FSTS_IQE_SHIFT 4
> +#define DMA_FSTS_IQE (1U << DMA_FSTS_IQE_SHIFT)
> #define DMA_FSTS_ICE (1U << 5)
> #define DMA_FSTS_ITE (1U << 6)
> #define DMA_FSTS_PRO_SHIFT 7
> @@ -562,6 +578,7 @@ struct qinval_entry {
>
> /* Queue invalidation head/tail shift */
> #define QINVAL_INDEX_SHIFT 4
> +#define QINVAL_INDEX_MASK 0x7fff0ULL
>
> #define qinval_present(v) ((v).lo & 1)
> #define qinval_fault_disable(v) (((v).lo >> 1) & 1)
> diff --git a/xen/drivers/passthrough/vtd/vvtd.c
> b/xen/drivers/passthrough/vtd/vvtd.c
> index 55f7a46..668d0c9 100644
> --- a/xen/drivers/passthrough/vtd/vvtd.c
> +++ b/xen/drivers/passthrough/vtd/vvtd.c
> @@ -28,6 +28,7 @@
> #include <asm/current.h>
> #include <asm/event.h>
> #include <asm/hvm/domain.h>
> +#include <asm/hvm/support.h>
> #include <asm/io_apic.h>
> #include <asm/page.h>
> #include <asm/p2m.h>
> @@ -419,6 +420,177 @@ static int vvtd_record_fault(struct vvtd *vvtd,
> return X86EMUL_OKAY;
> }
>
> +/*
> + * Process a invalidation descriptor. Currently, only two types descriptors,
> + * Interrupt Entry Cache Invalidation Descritor and Invalidation Wait
> + * Descriptor are handled.
> + * @vvtd: the virtual vtd instance
> + * @i: the index of the invalidation descriptor to be processed
> + *
> + * If success return 0, or return non-zero when failure.
> + */
> +static int process_iqe(struct vvtd *vvtd, int i)
unsigned int.
> +{
> + uint64_t iqa;
> + struct qinval_entry *qinval_page;
> + int ret = 0;
> +
> + iqa = vvtd_get_reg_quad(vvtd, DMAR_IQA_REG);
> + qinval_page = map_guest_page(vvtd->domain,
> DMA_IQA_ADDR(iqa)>>PAGE_SHIFT);
PFN_DOWN instead of open coding the shift. Both can be initialized
at declaration. Also AFAICT iqa is only used once, so the local
variable is not needed.
> + if ( IS_ERR(qinval_page) )
> + {
> + gdprintk(XENLOG_ERR, "Can't map guest IRT (rc %ld)",
> + PTR_ERR(qinval_page));
> + return PTR_ERR(qinval_page);
> + }
> +
> + switch ( qinval_page[i].q.inv_wait_dsc.lo.type )
> + {
> + case TYPE_INVAL_WAIT:
> + if ( qinval_page[i].q.inv_wait_dsc.lo.sw )
> + {
> + uint32_t data = qinval_page[i].q.inv_wait_dsc.lo.sdata;
> + uint64_t addr = (qinval_page[i].q.inv_wait_dsc.hi.saddr << 2);
Unneeded parentheses.
> +
> + ret = hvm_copy_to_guest_phys(addr, &data, sizeof(data), current);
> + if ( ret )
> + vvtd_info("Failed to write status address");
Don't you need to return or do something here? (like raise some kind
of error?)
> + }
> +
> + /*
> + * The following code generates an invalidation completion event
> + * indicating the invalidation wait descriptor completion. Note that
> + * the following code fragment is not tested properly.
> + */
> + if ( qinval_page[i].q.inv_wait_dsc.lo.iflag )
> + {
> + uint32_t ie_data, ie_addr;
Missing newline, but...
> + if ( !vvtd_test_and_set_bit(vvtd, DMAR_ICS_REG,
> DMA_ICS_IWC_SHIFT) )
> + {
> + vvtd_set_bit(vvtd, DMAR_IECTL_REG, DMA_IECTL_IP_SHIFT);
> + if ( !vvtd_test_bit(vvtd, DMAR_IECTL_REG,
> DMA_IECTL_IM_SHIFT) )
> + {
> + ie_data = vvtd_get_reg(vvtd, DMAR_IEDATA_REG);
> + ie_addr = vvtd_get_reg(vvtd, DMAR_IEADDR_REG);
> + vvtd_generate_interrupt(vvtd, ie_addr, ie_data);
...you don't seem two need the two local variables. They are used only
once.
> + vvtd_clear_bit(vvtd, DMAR_IECTL_REG, DMA_IECTL_IP_SHIFT);
> + }
> + }
> + }
> + break;
> +
> + case TYPE_INVAL_IEC:
> + /*
> + * Currently, no cache is preserved in hypervisor. Only need to
> update
> + * pIRTEs which are modified in binding process.
> + */
> + break;
> +
> + default:
> + goto error;
There's no reason to use a label that's only used for the default
case. Simply place the code in the error label here.
> + }
> +
> + unmap_guest_page((void*)qinval_page);
> + return ret;
> +
> + error:
> + unmap_guest_page((void*)qinval_page);
> + gdprintk(XENLOG_ERR, "Internal error in Queue Invalidation.\n");
> + domain_crash(vvtd->domain);
Do you really need to crash the domain in such case?
> + return ret;
> +}
> +
> +/*
> + * Invalidate all the descriptors in Invalidation Queue.
> + */
> +static void vvtd_process_iq(struct vvtd *vvtd)
> +{
> + uint64_t iqh, iqt, iqa, max_entry, i;
> + int err = 0;
> +
> + /*
> + * No new descriptor is fetched from the Invalidation Queue until
> + * software clears the IQE field in the Fault Status Register
> + */
> + if ( vvtd_test_bit(vvtd, DMAR_FSTS_REG, DMA_FSTS_IQE_SHIFT) )
> + return;
> +
> + iqh = vvtd_get_reg_quad(vvtd, DMAR_IQH_REG);
> + iqt = vvtd_get_reg_quad(vvtd, DMAR_IQT_REG);
> + iqa = vvtd_get_reg_quad(vvtd, DMAR_IQA_REG);
> +
> + max_entry = 1 << (QINVAL_ENTRY_ORDER + DMA_IQA_QS(iqa));
> + iqh = MASK_EXTR(iqh, QINVAL_INDEX_MASK);
> + iqt = MASK_EXTR(iqt, QINVAL_INDEX_MASK);
This should be done above, when they are initialized.
> +
> + ASSERT(iqt < max_entry);
> + if ( iqh == iqt )
> + return;
> +
> + for ( i = iqh; i != iqt; i = (i + 1) % max_entry )
> + {
> + err = process_iqe(vvtd, i);
process_iqe takes an int parameter, and here you are feeding it a
uint64_t.
> + if ( err )
> + break;
> + }
> + vvtd_set_reg_quad(vvtd, DMAR_IQH_REG, i << QINVAL_INDEX_SHIFT);
> +
> + /*
> + * When IQE set, IQH references the desriptor associated with the error.
> + */
> + if ( err )
> + vvtd_report_non_recoverable_fault(vvtd, DMA_FSTS_IQE_SHIFT);
> +}
> +
> +static int vvtd_write_iqt(struct vvtd *vvtd, unsigned long val)
Not sure there's much point in making this function return int, when
all the return values are X86EMUL_OKAY.
Sionce val here is a register AFAICT, I would rather prefer that you
use an explicit type size (uint64_t or uint32_t as fits).
> +{
> + uint64_t max_entry, iqa = vvtd_get_reg_quad(vvtd, DMAR_IQA_REG);
> +
> + if ( val & ~QINVAL_INDEX_MASK )
> + {
> + vvtd_info("Attempt to set reserved bits in Invalidation Queue Tail");
> + return X86EMUL_OKAY;
> + }
> +
> + max_entry = 1 << (QINVAL_ENTRY_ORDER + DMA_IQA_QS(iqa));
1ull please.
> + if ( MASK_EXTR(val, QINVAL_INDEX_MASK) >= max_entry )
> + {
> + vvtd_info("IQT: Value %lx exceeded supported max index.", val);
> + return X86EMUL_OKAY;
> + }
> +
> + vvtd_set_reg_quad(vvtd, DMAR_IQT_REG, val);
> + vvtd_process_iq(vvtd);
> +
> + return X86EMUL_OKAY;
> +}
> +
> +static int vvtd_write_iqa(struct vvtd *vvtd, unsigned long val)
Same here, it seems like this function should return void, because the
current return value is meaningless, and same comment about 'val'
being uintXX_t.
> +{
> + uint32_t cap = vvtd_get_reg(vvtd, DMAR_CAP_REG);
> + unsigned int guest_max_addr_width = cap_mgaw(cap);
> +
> + if ( val & (~((1ULL << guest_max_addr_width) - 1) | DMA_IQA_RSVD) )
> + {
> + vvtd_info("Attempt to set reserved bits in Invalidation Queue
> Address");
> + return X86EMUL_OKAY;
> + }
> +
> + vvtd_set_reg_quad(vvtd, DMAR_IQA_REG, val);
> + return X86EMUL_OKAY;
> +}
> +
> +static int vvtd_write_ics(struct vvtd *vvtd, uint32_t val)
> +{
> + if ( val & DMA_ICS_IWC )
> + {
> + vvtd_clear_bit(vvtd, DMAR_ICS_REG, DMA_ICS_IWC_SHIFT);
> + /*When IWC field is cleared, the IP field needs to be cleared */
^ missing space.
> + vvtd_clear_bit(vvtd, DMAR_IECTL_REG, DMA_IECTL_IP_SHIFT);
> + }
> + return X86EMUL_OKAY;
This function wants to be void.
> +}
> +
> static int vvtd_write_frcd3(struct vvtd *vvtd, uint32_t val)
> {
> /* Writing a 1 means clear fault */
> @@ -430,6 +602,30 @@ static int vvtd_write_frcd3(struct vvtd *vvtd, uint32_t
> val)
> return X86EMUL_OKAY;
> }
>
> +static int vvtd_write_iectl(struct vvtd *vvtd, uint32_t val)
void please.
> +{
> + /*
> + * Only DMA_IECTL_IM bit is writable. Generate pending event when unmask.
> + */
Single line comments use /* ... */
> + if ( !(val & DMA_IECTL_IM) )
> + {
> + /* Clear IM and clear IP */
> + vvtd_clear_bit(vvtd, DMAR_IECTL_REG, DMA_IECTL_IM_SHIFT);
> + if ( vvtd_test_and_clear_bit(vvtd, DMAR_IECTL_REG,
> DMA_IECTL_IP_SHIFT) )
> + {
> + uint32_t ie_data, ie_addr;
> +
> + ie_data = vvtd_get_reg(vvtd, DMAR_IEDATA_REG);
> + ie_addr = vvtd_get_reg(vvtd, DMAR_IEADDR_REG);
> + vvtd_generate_interrupt(vvtd, ie_addr, ie_data);
No need for ie_data and ie_addr.
> + }
> + }
> + else
> + vvtd_set_bit(vvtd, DMAR_IECTL_REG, DMA_IECTL_IM_SHIFT);
> +
> + return X86EMUL_OKAY;
> +}
> +
> static int vvtd_write_fectl(struct vvtd *vvtd, uint32_t val)
> {
> /*
> @@ -476,6 +672,10 @@ static int vvtd_write_fsts(struct vvtd *vvtd, uint32_t
> val)
> if ( !((vvtd_get_reg(vvtd, DMAR_FSTS_REG) & DMA_FSTS_FAULTS)) )
> vvtd_clear_bit(vvtd, DMAR_FECTL_REG, DMA_FECTL_IP_SHIFT);
>
> + /* Continue to deal invalidation when IQE is clear */
> + if ( !vvtd_test_bit(vvtd, DMAR_FSTS_REG, DMA_FSTS_IQE_SHIFT) )
> + vvtd_process_iq(vvtd);
> +
> return X86EMUL_OKAY;
> }
>
> @@ -611,6 +811,32 @@ static int vvtd_write(struct vcpu *v, unsigned long addr,
> case DMAR_FECTL_REG:
> return vvtd_write_fectl(vvtd, val);
>
> + case DMAR_IECTL_REG:
> + return vvtd_write_iectl(vvtd, val);
> +
> + case DMAR_ICS_REG:
> + return vvtd_write_ics(vvtd, val);
> +
> + case DMAR_IQT_REG:
> + return vvtd_write_iqt(vvtd, (uint32_t)val);
> +
> + case DMAR_IQA_REG:
> + {
> + uint32_t iqa_hi;
> +
> + iqa_hi = vvtd_get_reg(vvtd, DMAR_IQA_REG_HI);
Initialization at declaration time, but since it's used only once, I
would rather prefer that you don't use a local variable at all.
> + return vvtd_write_iqa(vvtd,
> + (uint32_t)val | ((uint64_t)iqa_hi << 32));
> + }
> +
> + case DMAR_IQA_REG_HI:
> + {
> + uint32_t iqa_lo;
> +
> + iqa_lo = vvtd_get_reg(vvtd, DMAR_IQA_REG);
> + return vvtd_write_iqa(vvtd, (val << 32) | iqa_lo);
Same comment as above regarding iqa_lo.
Thanks, Roger.
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |