|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v2 3/4] iommu: elide flushing for higher order map/unmap operations
>>> On 03.12.18 at 18:40, <paul.durrant@xxxxxxxxxx> wrote:
> This patch removes any implicit flushing that occurs in the implementation
> of map and unmap operations and adds new iommu_map/unmap() wrapper
> functions. To maintain sematics of the iommu_legacy_map/unmap() wrapper
> functions, these are modified to call the new wrapper functions and then
> perform an explicit flush operation.
>
> Because VT-d currently performs two different types of flush dependent upon
> whether a PTE is being modified versus merely added (i.e. replacing a non-
> present PTE) 'iommu flush flags' are defined by this patch and the
> iommu_ops map_page() and unmap_page() methods are modified to OR the type
> of flush necessary for the PTE that has been populated or depopulated into
> an accumulated flags value. The accumulated value can then be passed into
> the explicit flush operation.
>
> The ARM SMMU implementations of map_page() and unmap_page() currently
> perform no implicit flushing and therefore the modified methods do not
> adjust the flush flags.
Which, however, likely is wrong. If we mean the flushing to be initiated
by the arch- and vendor-independent wrappers, then all map/unmap
backends should indicate the needed kind of flush. Granted this can be
done later, if things are otherwise correct on Arm right now.
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -971,8 +971,17 @@ static int __p2m_set_entry(struct p2m_domain *p2m,
>
> if ( need_iommu_pt_sync(p2m->domain) &&
> (lpae_is_valid(orig_pte) || lpae_is_valid(*entry)) )
> + {
> + unsigned int flush_flags = 0;
> +
> + if ( lpae_is_valid(orig_pte) )
> + flush_flags |= IOMMU_FLUSHF_modified;
> + if ( lpae_is_valid(*entry) )
> + flush_flags |= IOMMU_FLUSHF_added;
Shouldn't this be "else if" with the meaning assigned to both
types? From an abstract perspective I'd also expect that for
a single mapping no more than one of the flags can come
back set (through the iommu_ops interface).
> @@ -84,7 +86,7 @@ static bool set_iommu_pde_present(uint32_t *pde, unsigned
> long next_mfn,
>
> if ( maddr_old != maddr_next || iw != old_w || ir != old_r ||
> old_level != next_level )
> - need_flush = true;
> + flush_flags = IOMMU_FLUSHF_modified;
Why uniformly "modified"?
> @@ -645,11 +648,13 @@ static unsigned long flush_count(unsigned long dfn,
> unsigned int page_count,
> }
>
> int amd_iommu_flush_iotlb_pages(struct domain *d, dfn_t dfn,
> - unsigned int page_count)
> + unsigned int page_count,
> + unsigned int flush_flags)
> {
> unsigned long dfn_l = dfn_x(dfn);
>
> ASSERT(page_count && !dfn_eq(dfn, INVALID_DFN));
> + ASSERT(flush_flags & IOMMU_FLUSHF_modified);
Is this valid? What if a map operation solely re-established what
was already there? Aiui in that case set_iommu_pde_present()
would always return zero. Or take this (seeing that the generic
wrapper has a zero check for the flush flags):
> @@ -692,6 +697,7 @@ int amd_iommu_reserve_domain_unity_map(struct domain
> *domain,
> unsigned long npages, i;
> unsigned long gfn;
> unsigned int flags = !!ir;
> + unsigned int flush_flags = 0;
> int rt = 0;
>
> if ( iw )
> @@ -703,11 +709,21 @@ int amd_iommu_reserve_domain_unity_map(struct domain
> *domain,
> {
> unsigned long frame = gfn + i;
>
> - rt = amd_iommu_map_page(domain, _dfn(frame), _mfn(frame), flags);
> + rt = amd_iommu_map_page(domain, _dfn(frame), _mfn(frame), flags,
> + &flush_flags);
> if ( rt != 0 )
> - return rt;
> + break;
> }
> - return 0;
> +
> + /*
> + * The underlying implementation is void so the return value is
> + * meaningless and can hence be ignored.
> + */
> + while ( amd_iommu_flush_iotlb_pages(domain, _dfn(gfn), npages,
> + flush_flags) )
> + break;
Nothing here guarantees flush_flags to be non-zero.
> @@ -235,6 +236,9 @@ void __hwdom_init iommu_hwdom_init(struct domain *d)
> process_pending_softirqs();
> }
>
> + while ( !flush_flags && iommu_flush_all(d) )
> + break;
Is there a comment missing here explaining the seemingly odd
loop?
> @@ -381,6 +402,17 @@ int iommu_legacy_unmap(struct domain *d, dfn_t dfn,
> unsigned int page_order)
> return rc;
> }
>
> +int iommu_legacy_unmap(struct domain *d, dfn_t dfn, unsigned int page_order)
> +{
> + unsigned int flush_flags = 0;
> + int rc = iommu_unmap(d, dfn, page_order, &flush_flags);
> +
> + if ( !rc )
> + rc = iommu_flush(d, dfn, (1u << page_order), flush_flags);
No iommu_dont_flush_iotlb check needed here?
> --- a/xen/drivers/passthrough/vtd/iommu.c
> +++ b/xen/drivers/passthrough/vtd/iommu.c
> @@ -633,11 +633,14 @@ static int __must_check iommu_flush_iotlb(struct domain
> *d, dfn_t dfn,
>
> static int __must_check iommu_flush_iotlb_pages(struct domain *d,
> dfn_t dfn,
> - unsigned int page_count)
> + unsigned int page_count,
> + unsigned int flush_flags)
> {
> ASSERT(page_count && !dfn_eq(dfn, INVALID_DFN));
> + ASSERT(flush_flags);
>
> - return iommu_flush_iotlb(d, dfn, 1, page_count);
> + return iommu_flush_iotlb(d, dfn, flush_flags & IOMMU_FLUSHF_modified,
> + page_count);
Why the restriction to "modified"?
> @@ -1825,15 +1825,18 @@ static int __must_check intel_iommu_map_page(struct
> domain *d,
> spin_unlock(&hd->arch.mapping_lock);
> unmap_vtd_domain_page(page);
>
> - if ( !this_cpu(iommu_dont_flush_iotlb) )
> - rc = iommu_flush_iotlb(d, dfn, dma_pte_present(old), 1);
> + *flush_flags |= IOMMU_FLUSHF_added;
> + if ( dma_pte_present(old) )
> + *flush_flags |= IOMMU_FLUSHF_modified;
See my earlier comment as to only one of them to get set for an
individual mapping.
> @@ -62,14 +61,15 @@ int arch_iommu_populate_page_table(struct domain *d)
> {
> unsigned long mfn = mfn_x(page_to_mfn(page));
> unsigned long gfn = mfn_to_gmfn(d, mfn);
> + unsigned int flush_flags = 0;
>
> if ( gfn != gfn_x(INVALID_GFN) )
> {
> ASSERT(!(gfn >> DEFAULT_DOMAIN_ADDRESS_WIDTH));
> BUG_ON(SHARED_M2P(gfn));
> - rc = iommu_legacy_map(d, _dfn(gfn), _mfn(mfn),
> - PAGE_ORDER_4K, IOMMUF_readable |
> - IOMMUF_writable);
> + rc = iommu_map(d, _dfn(gfn), _mfn(mfn),
> + PAGE_ORDER_4K, IOMMUF_readable |
> + IOMMUF_writable, &flush_flags);
> }
> if ( rc )
> {
> @@ -103,7 +103,6 @@ int arch_iommu_populate_page_table(struct domain *d)
> }
>
> spin_unlock(&d->page_alloc_lock);
> - this_cpu(iommu_dont_flush_iotlb) = 0;
>
> if ( !rc )
> rc = iommu_flush_all(d);
Would be nice to have a comment here clarifying why flush_flags
doesn't get used.
> @@ -249,6 +251,9 @@ void __hwdom_init arch_iommu_hwdom_init(struct domain *d)
> if (!(i & 0xfffff))
> process_pending_softirqs();
> }
> +
> + if ( !flush_flags && iommu_flush_all(d) )
> + return;
> }
Again please attach a brief comment explaining the seemingly
strange construct.
> --- a/xen/include/xen/iommu.h
> +++ b/xen/include/xen/iommu.h
> @@ -93,6 +93,22 @@ void iommu_teardown(struct domain *d);
> #define _IOMMUF_writable 1
> #define IOMMUF_writable (1u<<_IOMMUF_writable)
>
> +enum
> +{
Brace on the same line as "enum" please, just like for struct/union. When
they're named this helps finding the place where a certain type gets
(fully) declared.
Jan
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |