[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v2 13/13] x86: extend the map and unmap 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> v2: - New in v2. --- xen/arch/x86/iommu_op.c | 68 ++++++++++++++------ xen/common/grant_table.c | 143 ++++++++++++++++++++++++++++++++++++++++++ xen/include/public/iommu_op.h | 31 +++++++-- xen/include/xen/grant_table.h | 7 +++ 4 files changed, 224 insertions(+), 25 deletions(-) diff --git a/xen/arch/x86/iommu_op.c b/xen/arch/x86/iommu_op.c index 7174838a6c..cca1c59457 100644 --- a/xen/arch/x86/iommu_op.c +++ b/xen/arch/x86/iommu_op.c @@ -22,6 +22,7 @@ #include <xen/event.h> #include <xen/guest_access.h> #include <xen/hypercall.h> +#include <xen/grant_table.h> struct get_reserved_ctxt { unsigned int max_entries; @@ -120,10 +121,12 @@ static int iommuop_map(struct xen_iommu_op_map *op) bool readonly = op->flags & XEN_IOMMUOP_map_readonly; bfn_t bfn = _bfn(op->bfn); struct page_info *page; + mfn_t mfn; unsigned int prot; int rc, ignore; - if ( op->pad || (op->flags & ~XEN_IOMMUOP_map_readonly) ) + if ( op->pad || (op->flags & ~(XEN_IOMMUOP_map_readonly | + XEN_IOMMUOP_map_gref)) ) return -EINVAL; if ( !iommu->iommu_op_ranges ) @@ -137,16 +140,29 @@ static int iommuop_map(struct xen_iommu_op_map *op) if ( !d ) return -ESRCH; - rc = get_paged_gfn(d, _gfn(op->gfn), readonly, NULL, &page); - if ( rc ) - goto unlock; + if ( op->flags & XEN_IOMMUOP_map_gref ) + { + rc = acquire_gref_for_iommu(d, op->u.gref, readonly, &mfn); + if ( rc ) + goto unlock; + + page = mfn_to_page(mfn); + } + else + { + rc = get_paged_gfn(d, _gfn(op->u.gfn), readonly, NULL, &page); + if ( rc ) + goto unlock; + + mfn = page_to_mfn(page); + } prot = IOMMUF_readable; if ( !readonly ) prot |= IOMMUF_writable; rc = -EIO; - if ( iommu_map_page(currd, bfn, page_to_mfn(page), prot) ) + if ( iommu_map_page(currd, bfn, mfn, prot) ) goto release; rc = rangeset_add_singleton(iommu->iommu_op_ranges, bfn_x(bfn)); @@ -160,7 +176,10 @@ static int iommuop_map(struct xen_iommu_op_map *op) ignore = iommu_unmap_page(currd, bfn); release: - put_page(page); + if ( op->flags & XEN_IOMMUOP_map_gref ) + release_gref_for_iommu(d, op->u.gref, readonly, mfn); + else + put_page(page); unlock: rcu_unlock_domain(d); @@ -174,11 +193,10 @@ static int iommuop_unmap(struct xen_iommu_op_unmap *op) bfn_t bfn = _bfn(op->bfn); mfn_t mfn; unsigned int prot; - struct page_info *page; + bool readonly; int rc; - if ( op->pad0 || op->pad1 ) - return -EINVAL; + if ( op->pad || (op->flags & ~XEN_IOMMUOP_unmap_gref) ) if ( !iommu->iommu_op_ranges ) return -EOPNOTSUPP; @@ -188,28 +206,40 @@ static int iommuop_unmap(struct xen_iommu_op_unmap *op) !mfn_valid(mfn) ) return -ENOENT; + readonly = !(prot & IOMMUF_writable); + d = rcu_lock_domain_by_any_id(op->domid); if ( !d ) return -ESRCH; - rc = get_paged_gfn(d, _gfn(op->gfn), !(prot & IOMMUF_writable), NULL, - &page); - if ( rc ) - goto unlock; + if ( op->flags & XEN_IOMMUOP_unmap_gref ) + { + rc = release_gref_for_iommu(d, op->u.gref, readonly, mfn); + if ( rc ) + goto unlock; + } + else + { + struct page_info *page; + + rc = get_paged_gfn(d, _gfn(op->u.gfn), readonly, NULL, &page); + if ( rc ) + goto unlock; - put_page(page); /* release extra reference just taken */ + put_page(page); /* release extra reference just taken */ - rc = -EINVAL; - if ( !mfn_eq(page_to_mfn(page), mfn) ) - goto unlock; + rc = -EINVAL; + if ( !mfn_eq(page_to_mfn(page), mfn) ) + goto unlock; - put_page(page); /* release reference taken in map */ + put_page(page); /* release reference taken in map */ + } rc = rangeset_remove_singleton(iommu->iommu_op_ranges, bfn_x(bfn)); if ( rc ) goto unlock; - if ( !iommu_unmap_page(currd, bfn) ) + if ( iommu_unmap_page(currd, bfn) ) rc = -EIO; unlock: diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c index 510f37f100..e5a0f2d06d 100644 --- a/xen/common/grant_table.c +++ b/xen/common/grant_table.c @@ -3911,6 +3911,149 @@ int gnttab_map_frame(struct domain *d, unsigned long idx, gfn_t gfn, 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); + struct page_info *page; + + rc = get_paged_gfn(d, gfn, readonly, NULL, &page); + if ( rc ) + 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/include/public/iommu_op.h b/xen/include/public/iommu_op.h index 737e2c8cfe..485782a522 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 unsigned long xen_bfn_t; @@ -79,12 +80,20 @@ struct xen_iommu_op_map { #define _XEN_IOMMUOP_map_readonly 0 #define XEN_IOMMUOP_map_readonly (1 << (_XEN_IOMMUOP_map_readonly)) +#define _XEN_IOMMUOP_map_gref 1 +#define XEN_IOMMUOP_map_gref (1 << (_XEN_IOMMUOP_map_gref)) uint32_t pad; /* IN - The IOMMU frame number which will hold the new mapping */ xen_bfn_t bfn; - /* 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; }; /* @@ -95,12 +104,22 @@ struct xen_iommu_op_map { struct xen_iommu_op_unmap { /* IN - The domid of the guest */ domid_t domid; - uint16_t pad0; - uint32_t pad1; + uint16_t flags; + +#define _XEN_IOMMUOP_unmap_gref 0 +#define XEN_IOMMUOP_unmap_gref (1 << (_XEN_IOMMUOP_unmap_gref)) + + uint32_t pad; /* IN - The IOMMU frame number which holds the mapping to be removed */ xen_bfn_t bfn; - /* 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; }; /* diff --git a/xen/include/xen/grant_table.h b/xen/include/xen/grant_table.h index b3a95fda58..22c80c2238 100644 --- a/xen/include/xen/grant_table.h +++ b/xen/include/xen/grant_table.h @@ -56,6 +56,13 @@ int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref, int gnttab_map_frame(struct domain *d, unsigned long idx, gfn_t gfn, 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); + unsigned int gnttab_dom0_frames(void); #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 |