[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v7 7/7] x86: extend the map and unmap xen_iommu_ops to support grant references
This patch allows a domain to add or remove foreign frames from its IOMMU mappings by grant reference as well as GFN. This is necessary, for example, to support a PV network backend that needs to construct a packet buffer that can be directly accessed by a NIC. Signed-off-by: Paul Durrant <paul.durrant@xxxxxxxxxx> --- Cc: Jan Beulich <jbeulich@xxxxxxxx> Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> Cc: George Dunlap <George.Dunlap@xxxxxxxxxxxxx> Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx> Cc: Julien Grall <julien.grall@xxxxxxx> Cc: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx> Cc: Stefano Stabellini <sstabellini@xxxxxxxxxx> Cc: Tim Deegan <tim@xxxxxxx> Cc: Wei Liu <wei.liu2@xxxxxxxxxx> v7: - Re-base. v6: - Re-base. v2: - New in v2. --- xen/common/grant_table.c | 151 ++++++++++++++++++++++++++++++++++++++++++ xen/common/iommu_op.c | 100 +++++++++++++++++++--------- xen/include/public/iommu_op.h | 29 ++++++-- xen/include/xen/grant_table.h | 5 ++ 4 files changed, 248 insertions(+), 37 deletions(-) diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c index 878e668bf5..f7c9333f60 100644 --- a/xen/common/grant_table.c +++ b/xen/common/grant_table.c @@ -3927,6 +3927,157 @@ int gnttab_get_status_frame(struct domain *d, unsigned long idx, return rc; } +int +acquire_gref_for_iommu(struct domain *d, grant_ref_t gref, + bool readonly, mfn_t *mfn) +{ + struct domain *currd = current->domain; + struct grant_table *gt = d->grant_table; + grant_entry_header_t *shah; + struct active_grant_entry *act; + uint16_t *status; + int rc; + + grant_read_lock(gt); + + rc = -ENOENT; + if ( gref > nr_grant_entries(gt) ) + goto unlock; + + act = active_entry_acquire(gt, gref); + shah = shared_entry_header(gt, gref); + status = ( gt->gt_version == 2 ) ? + &status_entry(gt, gref) : + &shah->flags; + + rc = -EACCES; + if ( (shah->flags & GTF_type_mask) != GTF_permit_access || + (shah->flags & GTF_sub_page) ) + goto release; + + rc = -ERANGE; + if ( act->pin && ((act->domid != currd->domain_id) || + (act->pin & 0x80808080U) != 0) ) + goto release; + + rc = -EINVAL; + if ( !act->pin || + (!readonly && !(act->pin & GNTPIN_devw_mask)) ) { + if ( _set_status(gt->gt_version, currd->domain_id, readonly, + 0, shah, act, status) != GNTST_okay ) + goto release; + } + + if ( !act->pin ) + { + gfn_t gfn = gt->gt_version == 1 ? + _gfn(shared_entry_v1(gt, gref).frame) : + _gfn(shared_entry_v2(gt, gref).full_page.frame); + p2m_type_t p2mt; + struct page_info *page; + + rc = check_get_page_from_gfn(d, gfn, readonly, &p2mt, &page); + if ( rc ) + goto clear; + + rc = -EINVAL; + if ( p2m_is_foreign(p2mt) ) + { + put_page(page); + goto clear; + } + + act_set_gfn(act, gfn); + act->mfn = page_to_mfn(page); + act->domid = currd->domain_id; + act->start = 0; + act->length = PAGE_SIZE; + act->is_sub_page = false; + act->trans_domain = d; + act->trans_gref = gref; + } + else + { + ASSERT(mfn_valid(act->mfn)); + if ( !get_page(mfn_to_page(act->mfn), d) ) + goto clear; + } + + rc = 0; + act->pin += readonly ? GNTPIN_devr_inc : GNTPIN_devw_inc; + *mfn = act->mfn; + goto release; + + clear: + if ( !readonly && !(act->pin & GNTPIN_devw_mask) ) + gnttab_clear_flag(_GTF_writing, status); + + if ( !act->pin ) + gnttab_clear_flag(_GTF_reading, status); + + release: + active_entry_release(act); + + unlock: + grant_read_unlock(gt); + + return rc; +} + +int +release_gref_for_iommu(struct domain *d, grant_ref_t gref, + bool readonly, mfn_t mfn) +{ + struct domain *currd = current->domain; + struct grant_table *gt = d->grant_table; + grant_entry_header_t *shah; + struct active_grant_entry *act; + uint16_t *status; + int rc; + + grant_read_lock(gt); + + rc = -ENOENT; + if ( gref > nr_grant_entries(gt) ) + goto unlock; + + act = active_entry_acquire(gt, gref); + shah = shared_entry_header(gt, gref); + status = ( gt->gt_version == 2 ) ? + &status_entry(gt, gref) : + &shah->flags; + + rc = -EINVAL; + if ( !act->pin || (act->domid != currd->domain_id) || + !mfn_eq(act->mfn, mfn) ) + goto release; + + rc = 0; + if ( readonly ) + act->pin -= GNTPIN_devr_inc; + else + { + gnttab_mark_dirty(d, mfn); + + act->pin -= GNTPIN_devw_inc; + if ( !(act->pin & GNTPIN_devw_mask) ) + gnttab_clear_flag(_GTF_writing, status); + } + + if ( !act->pin ) + gnttab_clear_flag(_GTF_reading, status); + + put_page(mfn_to_page(mfn)); + + release: + active_entry_release(act); + + unlock: + grant_read_unlock(gt); + + return rc; +} + static void gnttab_usage_print(struct domain *rd) { int first = 1; diff --git a/xen/common/iommu_op.c b/xen/common/iommu_op.c index 0876414df5..5e8d8c2e7a 100644 --- a/xen/common/iommu_op.c +++ b/xen/common/iommu_op.c @@ -21,6 +21,7 @@ #include <xen/event.h> #include <xen/guest_access.h> +#include <xen/grant_table.h> #include <xen/hypercall.h> #include <xen/nospec.h> @@ -134,14 +135,13 @@ static int iommuop_map(struct xen_iommu_op_map *op) struct domain_iommu *iommu = dom_iommu(currd); bool readonly = op->flags & XEN_IOMMUOP_map_readonly; dfn_t dfn = _dfn(op->dfn); - p2m_type_t p2mt; - struct page_info *page; - mfn_t ignore; + mfn_t mfn, ignore; unsigned int flags; int rc; if ( op->pad || (op->flags & ~(XEN_IOMMUOP_map_all | - XEN_IOMMUOP_map_readonly)) ) + XEN_IOMMUOP_map_readonly | + XEN_IOMMUOP_map_gref)) ) return -EINVAL; if ( !iommu->domain_control ) @@ -159,16 +159,31 @@ static int iommuop_map(struct xen_iommu_op_map *op) if ( !d ) return -ESRCH; - rc = check_get_page_from_gfn(d, _gfn(op->gfn), readonly, &p2mt, &page); - if ( rc ) - goto unlock_domain; - - rc = -EINVAL; - if ( p2mt != p2m_ram_rw || - (!readonly && !get_page_type(page, PGT_writable_page)) ) + if ( op->flags & XEN_IOMMUOP_map_gref ) { - put_page(page); - goto unlock_domain; + rc = acquire_gref_for_iommu(d, op->u.gref, readonly, &mfn); + if ( rc ) + goto unlock_domain; + } + else + { + p2m_type_t p2mt; + struct page_info *page; + + rc = check_get_page_from_gfn(d, _gfn(op->u.gfn), readonly, &p2mt, + &page); + if ( rc ) + goto unlock_domain; + + rc = -EINVAL; + if ( p2mt != p2m_ram_rw || + (!readonly && !get_page_type(page, PGT_writable_page)) ) + { + put_page(page); + goto unlock_domain; + } + + mfn = page_to_mfn(page); } spin_lock(&iommu->lock); @@ -186,16 +201,23 @@ static int iommuop_map(struct xen_iommu_op_map *op) if ( !readonly ) flags |= IOMMUF_writable; - rc = iommu_map_page_nocrash(currd, dfn, page_to_mfn(page), flags); + rc = iommu_map_page_nocrash(currd, dfn, mfn, flags); unlock_iommu: spin_unlock(&iommu->lock); if ( rc ) /* retain references if mapping is successful */ { - if ( !readonly ) - put_page_type(page); - put_page(page); + if ( op->flags & XEN_IOMMUOP_map_gref ) + release_gref_for_iommu(d, op->u.gref, readonly, mfn); + else + { + struct page_info *page = mfn_to_page(mfn); + + if ( !readonly ) + put_page_type(page); + put_page(page); + } } unlock_domain: @@ -211,12 +233,11 @@ static int iommuop_unmap(struct xen_iommu_op_unmap *op) mfn_t mfn; unsigned int flags; bool readonly; - p2m_type_t p2mt; - struct page_info *page; int rc; if ( op->pad || - (op->flags & ~XEN_IOMMUOP_unmap_all) ) + (op->flags & ~(XEN_IOMMUOP_unmap_all | + XEN_IOMMUOP_unmap_gref)) ) return -EINVAL; if ( !iommu->domain_control ) @@ -243,23 +264,36 @@ static int iommuop_unmap(struct xen_iommu_op_unmap *op) readonly = !(flags & IOMMUF_writable); - /* Make sure the mapped frame matches */ - rc = check_get_page_from_gfn(d, _gfn(op->gfn), readonly, &p2mt, &page); - if ( rc ) - goto unlock; + if ( op->flags & XEN_IOMMUOP_map_gref ) + { + rc = release_gref_for_iommu(d, op->u.gref, readonly, mfn); + if ( rc ) + goto unlock; + } + else + { + p2m_type_t p2mt; + struct page_info *page; - rc = !mfn_eq(mfn, page_to_mfn(page)) ? -EINVAL : 0; + /* Make sure the mapped frame matches */ + rc = check_get_page_from_gfn(d, _gfn(op->u.gfn), readonly, &p2mt, + &page); + if ( rc ) + goto unlock; - /* Release reference taken above */ - put_page(page); + rc = !mfn_eq(mfn, page_to_mfn(page)) ? -EINVAL : 0; - if ( rc ) - goto unlock; + /* Release reference taken above */ + put_page(page); - /* Release references taken in map */ - if ( !readonly ) - put_page_type(page); - put_page(page); + if ( rc ) + goto unlock; + + /* Release references taken in map */ + if ( !readonly ) + put_page_type(page); + put_page(page); + } /* * This really should not fail. If it does, there is an implicit diff --git a/xen/include/public/iommu_op.h b/xen/include/public/iommu_op.h index fcca47e8d2..b49d3ee2b6 100644 --- a/xen/include/public/iommu_op.h +++ b/xen/include/public/iommu_op.h @@ -24,6 +24,7 @@ #define XEN_PUBLIC_IOMMU_OP_H #include "xen.h" +#include "grant_table.h" typedef uint64_t xen_dfn_t; @@ -107,6 +108,10 @@ struct xen_iommu_op_map { #define _XEN_IOMMUOP_map_readonly 1 #define XEN_IOMMUOP_map_readonly (1 << (_XEN_IOMMUOP_map_readonly)) + /* Is the memory specified by gfn or grant reference? */ +#define _XEN_IOMMUOP_map_gref 2 +#define XEN_IOMMUOP_map_gref (1 << (_XEN_IOMMUOP_map_gref)) + uint32_t pad; /* * IN - Segment/Bus/Device/Function of the initiator. @@ -116,8 +121,14 @@ struct xen_iommu_op_map { uint64_t sbdf; /* IN - The IOMMU frame number which will hold the new mapping */ xen_dfn_t dfn; - /* IN - The guest frame number of the page to be mapped */ - xen_pfn_t gfn; + /* + * IN - The guest frame number or grant reference of the page to + * be mapped. + */ + union { + xen_pfn_t gfn; + grant_ref_t gref; + } u; }; /* @@ -143,6 +154,10 @@ struct xen_iommu_op_unmap { #define _XEN_IOMMUOP_unmap_all 0 #define XEN_IOMMUOP_unmap_all (1 << (_XEN_IOMMUOP_unmap_all)) + /* Is the memory specified by gfn or grant reference? */ +#define _XEN_IOMMUOP_unmap_gref 1 +#define XEN_IOMMUOP_unmap_gref (1 << (_XEN_IOMMUOP_unmap_gref)) + uint32_t pad; /* * IN - Segment/Bus/Device/Function of the initiator. @@ -152,8 +167,14 @@ struct xen_iommu_op_unmap { uint64_t sbdf; /* IN - The IOMMU frame number which holds the mapping to be removed */ xen_dfn_t dfn; - /* IN - The guest frame number of the page that is mapped */ - xen_pfn_t gfn; + /* + * IN - The guest frame number or grant reference of the page that + * is mapped. + */ + union { + xen_pfn_t gfn; + grant_ref_t gref; + } u; }; struct xen_iommu_op { diff --git a/xen/include/xen/grant_table.h b/xen/include/xen/grant_table.h index 12e8a4b80b..4a26f5dbe4 100644 --- a/xen/include/xen/grant_table.h +++ b/xen/include/xen/grant_table.h @@ -61,4 +61,9 @@ int gnttab_get_shared_frame(struct domain *d, unsigned long idx, int gnttab_get_status_frame(struct domain *d, unsigned long idx, mfn_t *mfn); +int acquire_gref_for_iommu(struct domain *d, grant_ref_t gref, + bool readonly, mfn_t *mfn); +int release_gref_for_iommu(struct domain *d, grant_ref_t gref, + bool readonly, mfn_t mfn); + #endif /* __XEN_GRANT_TABLE_H__ */ -- 2.11.0 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |