From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mukesh Rathor Subject: [PATCH 18/18] PVH xen: Add and remove foreign pages Date: Fri, 24 May 2013 18:25:37 -0700 Message-ID: <1369445137-19755-19-git-send-email-mukesh.rathor@oracle.com> References: <1369445137-19755-1-git-send-email-mukesh.rathor@oracle.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1369445137-19755-1-git-send-email-mukesh.rathor@oracle.com> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org To: Xen-devel@lists.xensource.com List-Id: xen-devel@lists.xenproject.org In this patch, a new function, xenmem_add_foreign_to_pmap(), is added to map pages from foreign guest into current dom0 for domU creation. Also, allow XENMEM_remove_from_physmap to remove p2m_map_foreign pages. Note, in this path, we must release the refcount that was taken during the map phase. Changes in V2: - Move the XENMEM_remove_from_physmap changes here instead of prev patch - Move grant changes from this to one of the next patches. - In xenmem_add_foreign_to_pmap(), do locked get_gfn - Fail the mappings for qemu mapping pages for memory not there. Changes in V3: - remove mmio pages. - remove unrelated changes. - cleanup both add and remove. Changes in V5: - add a comment in public/memory.h Changes in V6: - replace ASSERTs with returning error code. Signed-off-by: Mukesh Rathor --- xen/arch/x86/mm.c | 77 +++++++++++++++++++++++++++++++++++++++++++ xen/common/memory.c | 44 ++++++++++++++++++++++--- xen/include/public/memory.h | 2 +- 3 files changed, 117 insertions(+), 6 deletions(-) diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index e992b4f..34137f9 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -4520,6 +4520,78 @@ static int handle_iomem_range(unsigned long s, unsigned long e, void *p) return 0; } +/* + * Add frames from foreign domain to current domain's physmap. Similar to + * XENMAPSPACE_gmfn but the frame is foreign being mapped into current, + * and is not removed from foreign domain. + * Usage: libxl on pvh dom0 creating a guest and doing privcmd_ioctl_mmap. + * Side Effect: the mfn for fgfn will be refcounted so it is not lost + * while mapped here. The refcnt is released in do_memory_op() + * via XENMEM_remove_from_physmap. + * Returns: 0 ==> success + */ +static int xenmem_add_foreign_to_pmap(domid_t foreign_domid, + unsigned long fgfn, unsigned long gpfn) +{ + p2m_type_t p2mt, p2mt_prev; + int rc = 0; + unsigned long prev_mfn, mfn = 0; + struct domain *fdom, *currd = current->domain; + struct page_info *page = NULL; + + if ( currd->domain_id == foreign_domid || foreign_domid == DOMID_SELF || + !is_pvh_domain(currd) ) + return -EINVAL; + + if ( !is_hardware_domain(currd) || !(fdom = get_pg_owner(foreign_domid)) ) + return -EPERM; + + /* following will take a refcnt on the mfn */ + page = get_page_from_gfn(fdom, fgfn, &p2mt, P2M_ALLOC); + if ( !page || !p2m_is_valid(p2mt) ) + { + if ( page ) + put_page(page); + put_pg_owner(fdom); + return -EINVAL; + } + mfn = page_to_mfn(page); + + /* Remove previously mapped page if it is present. */ + prev_mfn = mfn_x(get_gfn(currd, gpfn, &p2mt_prev)); + if ( mfn_valid(prev_mfn) ) + { + if ( is_xen_heap_mfn(prev_mfn) ) + /* Xen heap frames are simply unhooked from this phys slot */ + guest_physmap_remove_page(currd, gpfn, prev_mfn, 0); + else + /* Normal domain memory is freed, to avoid leaking memory. */ + guest_remove_page(currd, gpfn); + } + /* + * Create the new mapping. Can't use guest_physmap_add_page() because it + * will update the m2p table which will result in mfn -> gpfn of dom0 + * and not fgfn of domU. + */ + if ( set_foreign_p2m_entry(currd, gpfn, _mfn(mfn)) == 0 ) + { + gdprintk(XENLOG_WARNING, + "guest_physmap_add_page failed. gpfn:%lx mfn:%lx fgfn:%lx\n", + gpfn, mfn, fgfn); + put_page(page); + rc = -EINVAL; + } + + /* + * We must do this put_gfn after set_foreign_p2m_entry so another cpu + * doesn't populate the gpfn before us. + */ + put_gfn(currd, gpfn); + + put_pg_owner(fdom); + return rc; +} + static int xenmem_add_to_physmap_once( struct domain *d, const struct xen_add_to_physmap *xatp, @@ -4582,6 +4654,11 @@ static int xenmem_add_to_physmap_once( page = mfn_to_page(mfn); break; } + + case XENMAPSPACE_gmfn_foreign: + return xenmem_add_foreign_to_pmap(foreign_domid, xatp->idx, + xatp->gpfn); + default: break; } diff --git a/xen/common/memory.c b/xen/common/memory.c index 06a0d0a..47f0cb0 100644 --- a/xen/common/memory.c +++ b/xen/common/memory.c @@ -675,9 +675,11 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg) case XENMEM_remove_from_physmap: { + unsigned long mfn; struct xen_remove_from_physmap xrfp; struct page_info *page; - struct domain *d; + struct domain *d, *foreign_dom = NULL; + p2m_type_t p2mt, tp; if ( copy_from_guest(&xrfp, arg, 1) ) return -EFAULT; @@ -695,11 +697,43 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg) domain_lock(d); - page = get_page_from_gfn(d, xrfp.gpfn, NULL, P2M_ALLOC); - if ( page ) + /* + * if PVH, the gfn could be mapped to a mfn from foreign domain by the + * user space tool during domain creation. We need to check for that, + * free it up from the p2m, and release refcnt on it. In such a case, + * page would be NULL and the following call would not have refcnt'd + * the page. See also xenmem_add_foreign_to_pmap(). + */ + page = get_page_from_gfn(d, xrfp.gpfn, &p2mt, P2M_ALLOC); + + if ( page || p2m_is_foreign(p2mt) ) { - guest_physmap_remove_page(d, xrfp.gpfn, page_to_mfn(page), 0); - put_page(page); + if ( page ) + mfn = page_to_mfn(page); + else + { + mfn = mfn_x(get_gfn_query(d, xrfp.gpfn, &tp)); + foreign_dom = page_get_owner(mfn_to_page(mfn)); + + if ( !is_pvh_domain(d) || (d == foreign_dom) || + !p2m_is_foreign(tp) ) + { + rc = -EINVAL; + domain_unlock(d); + rcu_unlock_domain(d); + break; + } + } + + guest_physmap_remove_page(d, xrfp.gpfn, mfn, 0); + if ( page ) + put_page(page); + + if ( p2m_is_foreign(p2mt) ) + { + put_page(mfn_to_page(mfn)); + put_gfn(d, xrfp.gpfn); + } } else rc = -ENOENT; diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h index 7a26dee..e319b5d 100644 --- a/xen/include/public/memory.h +++ b/xen/include/public/memory.h @@ -208,7 +208,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_machphys_mapping_t); #define XENMAPSPACE_gmfn_range 3 /* GMFN range, XENMEM_add_to_physmap only. */ #define XENMAPSPACE_gmfn_foreign 4 /* GMFN from another dom, * XENMEM_add_to_physmap_range only. - */ + * (PVH x86 only) */ /* ` } */ /* -- 1.7.2.3