aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2009-10-07 07:46:36 +0100
committerKeir Fraser <keir.fraser@citrix.com>2009-10-07 07:46:36 +0100
commita98dc13703e091bb94e86a537d5c7a0ef9ffb62f (patch)
treebaddf36a6fe54f4d3ea1bc0238b69a3e5f0de4bc
parent1766cdc32b025ecea48d880370ddd78ece9bfc71 (diff)
downloadxen-a98dc13703e091bb94e86a537d5c7a0ef9ffb62f.tar.gz
xen-a98dc13703e091bb94e86a537d5c7a0ef9ffb62f.tar.bz2
xen-a98dc13703e091bb94e86a537d5c7a0ef9ffb62f.zip
Introduce a grant_entry_v2 structure.
Signed-off-by: Steven Smith <steven.smith@citrix.com>
-rw-r--r--tools/libxc/xc_linux.c32
-rw-r--r--tools/libxc/xc_offline_page.c45
-rw-r--r--tools/libxc/xenctrl.h4
-rw-r--r--xen/arch/x86/hvm/hvm.c31
-rw-r--r--xen/arch/x86/mm.c23
-rw-r--r--xen/common/compat/grant_table.c81
-rw-r--r--xen/common/grant_table.c713
-rw-r--r--xen/include/Makefile2
-rw-r--r--xen/include/asm-x86/grant_table.h19
-rw-r--r--xen/include/public/grant_table.h111
-rw-r--r--xen/include/public/memory.h2
-rw-r--r--xen/include/public/xen.h1
-rw-r--r--xen/include/xen/grant_table.h28
-rw-r--r--xen/include/xlat.lst5
14 files changed, 916 insertions, 181 deletions
diff --git a/tools/libxc/xc_linux.c b/tools/libxc/xc_linux.c
index fcddda3c1c..91dda1dec0 100644
--- a/tools/libxc/xc_linux.c
+++ b/tools/libxc/xc_linux.c
@@ -558,7 +558,21 @@ int xc_gnttab_op(int xc_handle, int cmd,
return ret;
}
-struct grant_entry_v1 *xc_gnttab_map_table(int xc_handle, int domid, int *gnt_num)
+int xc_gnttab_get_version(int xc_handle, int domid)
+{
+ struct gnttab_get_version query;
+ int rc;
+
+ query.dom = domid;
+ rc = xc_gnttab_op(xc_handle, GNTTABOP_get_version,
+ &query, sizeof(query), 1);
+ if (rc < 0)
+ return rc;
+ else
+ return query.version;
+}
+
+static void *_gnttab_map_table(int xc_handle, int domid, int *gnt_num)
{
int rc, i;
struct gnttab_query_size query;
@@ -638,6 +652,22 @@ err:
return gnt;
}
+struct grant_entry_v1 *xc_gnttab_map_table_v1(int xc_handle, int domid,
+ int *gnt_num)
+{
+ if (xc_gnttab_get_version(xc_handle, domid) == 2)
+ return NULL;
+ return _gnttab_map_table(xc_handle, domid, gnt_num);
+}
+
+struct grant_entry_v2 *xc_gnttab_map_table_v2(int xc_handle, int domid,
+ int *gnt_num)
+{
+ if (xc_gnttab_get_version(xc_handle, domid) != 2)
+ return NULL;
+ return _gnttab_map_table(xc_handle, domid, gnt_num);
+}
+
/*
* Local variables:
* mode: C
diff --git a/tools/libxc/xc_offline_page.c b/tools/libxc/xc_offline_page.c
index 57ff5f05c9..21d26bd783 100644
--- a/tools/libxc/xc_offline_page.c
+++ b/tools/libxc/xc_offline_page.c
@@ -132,8 +132,8 @@ int xc_query_page_offline_status(int xc, unsigned long start,
/*
* There should no update to the grant when domain paused
*/
-static int xc_is_page_granted(int xc_handle, xen_pfn_t gpfn,
- struct grant_entry_v1 *gnttab, int gnt_num)
+static int xc_is_page_granted_v1(int xc_handle, xen_pfn_t gpfn,
+ struct grant_entry_v1 *gnttab, int gnt_num)
{
int i = 0;
@@ -148,6 +148,22 @@ static int xc_is_page_granted(int xc_handle, xen_pfn_t gpfn,
return (i != gnt_num);
}
+static int xc_is_page_granted_v2(int xc_handle, xen_pfn_t gpfn,
+ struct grant_entry_v2 *gnttab, int gnt_num)
+{
+ int i = 0;
+
+ if (!gnttab)
+ return 0;
+
+ for (i = 0; i < gnt_num; i++)
+ if ( ((gnttab[i].hdr.flags & GTF_type_mask) != GTF_invalid) &&
+ (gnttab[i].frame == gpfn) )
+ break;
+
+ return (i != gnt_num);
+}
+
static xen_pfn_t pfn_to_mfn(xen_pfn_t pfn, xen_pfn_t *p2m, int gwidth)
{
return ((xen_pfn_t) ((gwidth==8)?
@@ -549,7 +565,8 @@ int xc_exchange_page(int xc_handle, int domid, xen_pfn_t mfn)
struct domain_mem_info minfo;
struct xc_mmu *mmu = NULL;
struct pte_backup old_ptes = {NULL, 0, 0};
- struct grant_entry_v1 *gnttab = NULL;
+ struct grant_entry_v1 *gnttab_v1 = NULL;
+ struct grant_entry_v2 *gnttab_v2 = NULL;
struct mmuext_op mops;
int gnt_num, unpined = 0;
void *old_p, *backup = NULL;
@@ -588,14 +605,20 @@ int xc_exchange_page(int xc_handle, int domid, xen_pfn_t mfn)
goto failed;
}
- gnttab = xc_gnttab_map_table(xc_handle, domid, &gnt_num);
- if (!gnttab)
+ gnttab_v2 = xc_gnttab_map_table_v2(xc_handle, domid, &gnt_num);
+ if (!gnttab_v2)
{
- ERROR("Failed to map grant table\n");
- goto failed;
+ gnttab_v1 = xc_gnttab_map_table_v1(xc_handle, domid, &gnt_num);
+ if (!gnttab_v1)
+ {
+ ERROR("Failed to map grant table\n");
+ goto failed;
+ }
}
- if (xc_is_page_granted(xc_handle, mfn, gnttab, gnt_num))
+ if (gnttab_v1
+ ? xc_is_page_granted_v1(xc_handle, mfn, gnttab_v1, gnt_num)
+ : xc_is_page_granted_v2(xc_handle, mfn, gnttab_v2, gnt_num))
{
ERROR("Page %lx is granted now\n", mfn);
goto failed;
@@ -755,8 +778,10 @@ failed:
if (backup)
free(backup);
- if (gnttab)
- munmap(gnttab, gnt_num / (PAGE_SIZE/sizeof(struct grant_entry_v1)));
+ if (gnttab_v1)
+ munmap(gnttab_v1, gnt_num / (PAGE_SIZE/sizeof(struct grant_entry_v1)));
+ if (gnttab_v2)
+ munmap(gnttab_v2, gnt_num / (PAGE_SIZE/sizeof(struct grant_entry_v2)));
close_mem_info(xc_handle, &minfo);
diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h
index 885c8dac2f..eebf33b143 100644
--- a/tools/libxc/xenctrl.h
+++ b/tools/libxc/xenctrl.h
@@ -943,7 +943,9 @@ int xc_gnttab_set_max_grants(int xcg_handle,
int xc_gnttab_op(int xc_handle, int cmd,
void * op, int op_size, int count);
-struct grant_entry_v1 *xc_gnttab_map_table(int xc_handle, int domid, int *gnt_num);
+int xc_gnttab_get_version(int xc_handle, int domid);
+struct grant_entry_v1 *xc_gnttab_map_table_v1(int xc_handle, int domid, int *gnt_num);
+struct grant_entry_v2 *xc_gnttab_map_table_v2(int xc_handle, int domid, int *gnt_num);
int xc_physdev_map_pirq(int xc_handle,
int domid,
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index aad2fa5fd7..c6b28d5346 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -2087,12 +2087,26 @@ enum hvm_intblk hvm_interrupt_blocked(struct vcpu *v, struct hvm_intack intack)
return hvm_intblk_none;
}
+static int grant_table_op_is_allowed(unsigned int cmd)
+{
+ switch (cmd) {
+ case GNTTABOP_query_size:
+ case GNTTABOP_setup_table:
+ case GNTTABOP_set_version:
+ case GNTTABOP_copy:
+ case GNTTABOP_map_grant_ref:
+ case GNTTABOP_unmap_grant_ref:
+ return 1;
+ default:
+ /* all other commands need auditing */
+ return 0;
+ }
+}
+
static long hvm_grant_table_op(
unsigned int cmd, XEN_GUEST_HANDLE(void) uop, unsigned int count)
{
- if ( (cmd != GNTTABOP_query_size) && (cmd != GNTTABOP_setup_table) &&
- (cmd != GNTTABOP_map_grant_ref) && (cmd != GNTTABOP_unmap_grant_ref) &&
- (cmd != GNTTABOP_copy))
+ if ( !grant_table_op_is_allowed(cmd) )
return -ENOSYS; /* all other commands need auditing */
return do_grant_table_op(cmd, uop, count);
}
@@ -2144,13 +2158,12 @@ static hvm_hypercall_t *hvm_hypercall32_table[NR_hypercalls] = {
#else /* defined(__x86_64__) */
-static long hvm_grant_table_op_compat32(
- unsigned int cmd, XEN_GUEST_HANDLE(void) uop, unsigned int count)
+static long hvm_grant_table_op_compat32(unsigned int cmd,
+ XEN_GUEST_HANDLE(void) uop,
+ unsigned int count)
{
- if ( (cmd != GNTTABOP_query_size) && (cmd != GNTTABOP_setup_table) &&
- (cmd != GNTTABOP_map_grant_ref) && (cmd != GNTTABOP_unmap_grant_ref) &&
- (cmd != GNTTABOP_copy))
- return -ENOSYS; /* all other commands need auditing */
+ if ( !grant_table_op_is_allowed(cmd) )
+ return -ENOSYS;
return compat_grant_table_op(cmd, uop, count);
}
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index f11341a082..c34b1a2df3 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -4000,12 +4000,25 @@ long arch_memory_op(int op, XEN_GUEST_HANDLE(void) arg)
case XENMAPSPACE_grant_table:
spin_lock(&d->grant_table->lock);
- if ( (xatp.idx >= nr_grant_frames(d->grant_table)) &&
- (xatp.idx < max_nr_grant_frames) )
- gnttab_grow_table(d, xatp.idx + 1);
+ if ( d->grant_table->gt_version == 0 )
+ d->grant_table->gt_version = 1;
- if ( xatp.idx < nr_grant_frames(d->grant_table) )
- mfn = virt_to_mfn(d->grant_table->shared[xatp.idx]);
+ if ( d->grant_table->gt_version == 2 &&
+ (xatp.idx & XENMAPIDX_grant_table_status) )
+ {
+ xatp.idx &= ~XENMAPIDX_grant_table_status;
+ if ( xatp.idx < nr_status_frames(d->grant_table) )
+ mfn = virt_to_mfn(d->grant_table->status[xatp.idx]);
+ }
+ else
+ {
+ if ( (xatp.idx >= nr_grant_frames(d->grant_table)) &&
+ (xatp.idx < max_nr_grant_frames) )
+ gnttab_grow_table(d, xatp.idx + 1);
+
+ if ( xatp.idx < nr_grant_frames(d->grant_table) )
+ mfn = virt_to_mfn(d->grant_table->shared_raw[xatp.idx]);
+ }
spin_unlock(&d->grant_table->lock);
break;
diff --git a/xen/common/compat/grant_table.c b/xen/common/compat/grant_table.c
index d3704fa503..ca60395f0a 100644
--- a/xen/common/compat/grant_table.c
+++ b/xen/common/compat/grant_table.c
@@ -9,6 +9,14 @@
CHECK_grant_entry_v1;
#undef xen_grant_entry_v1
+#define xen_grant_entry_header grant_entry_header
+CHECK_grant_entry_header;
+#undef xen_grant_entry_header
+
+#define xen_grant_entry_v2 grant_entry_v2
+CHECK_grant_entry_v2;
+#undef xen_grant_entry_v2
+
#define xen_gnttab_map_grant_ref gnttab_map_grant_ref
CHECK_gnttab_map_grant_ref;
#undef xen_gnttab_map_grant_ref
@@ -29,6 +37,16 @@ DEFINE_XEN_GUEST_HANDLE(gnttab_copy_compat_t);
CHECK_gnttab_dump_table;
#undef xen_gnttab_dump_table
+#define xen_gnttab_set_version gnttab_set_version
+CHECK_gnttab_set_version;
+#undef xen_gnttab_set_version
+
+DEFINE_XEN_GUEST_HANDLE(gnttab_get_status_frames_compat_t);
+
+#define xen_gnttab_get_version gnttab_get_version
+CHECK_gnttab_get_version;
+#undef xen_gnttab_get_version
+
int compat_grant_table_op(unsigned int cmd,
XEN_GUEST_HANDLE(void) cmp_uop,
unsigned int count)
@@ -76,6 +94,10 @@ int compat_grant_table_op(unsigned int cmd,
CASE(dump_table);
#endif
+#ifndef CHECK_gnttab_get_status_frames
+ CASE(get_status_frames);
+#endif
+
#undef CASE
default:
return do_grant_table_op(cmd, cmp_uop, count);
@@ -92,11 +114,13 @@ int compat_grant_table_op(unsigned int cmd,
struct gnttab_setup_table *setup;
struct gnttab_transfer *xfer;
struct gnttab_copy *copy;
+ struct gnttab_get_status_frames *get_status;
} nat;
union {
struct compat_gnttab_setup_table setup;
struct compat_gnttab_transfer xfer;
struct compat_gnttab_copy copy;
+ struct compat_gnttab_get_status_frames get_status;
} cmp;
set_xen_guest_handle(nat.uop, COMPAT_ARG_XLAT_VIRT_BASE);
@@ -233,6 +257,63 @@ int compat_grant_table_op(unsigned int cmd,
}
break;
+ case GNTTABOP_get_status_frames: {
+ unsigned int max_frame_list_size_in_pages =
+ (COMPAT_ARG_XLAT_SIZE - sizeof(*nat.get_status)) /
+ sizeof(*nat.get_status->frame_list.p);
+ if ( count != 1)
+ {
+ rc = -EINVAL;
+ break;
+ }
+ if ( unlikely(__copy_from_guest(&cmp.get_status, cmp_uop, 1) ||
+ !compat_handle_okay(cmp.get_status.frame_list,
+ cmp.get_status.nr_frames)) )
+ {
+ rc = -EFAULT;
+ break;
+ }
+ if ( max_frame_list_size_in_pages <
+ grant_to_status_frames(max_nr_grant_frames) )
+ {
+ gdprintk(XENLOG_WARNING,
+ "grant_to_status_frames(max_nr_grant_frames) is too large (%u,%u)\n",
+ grant_to_status_frames(max_nr_grant_frames),
+ max_frame_list_size_in_pages);
+ rc = -EINVAL;
+ break;
+ }
+
+#define XLAT_gnttab_get_status_frames_HNDL_frame_list(_d_, _s_) \
+ set_xen_guest_handle((_d_)->frame_list, (uint64_t *)(nat.get_status + 1))
+ XLAT_gnttab_get_status_frames(nat.get_status, &cmp.get_status);
+#undef XLAT_gnttab_get_status_frames_HNDL_frame_list
+
+ rc = gnttab_get_status_frames(
+ guest_handle_cast(nat.uop, gnttab_get_status_frames_t),
+ count);
+ if ( rc >= 0 )
+ {
+#define XLAT_gnttab_get_status_frames_HNDL_frame_list(_d_, _s_) \
+ do \
+ { \
+ if ( (_s_)->status == GNTST_okay ) \
+ { \
+ for ( i = 0; i < (_s_)->nr_frames; ++i ) \
+ { \
+ uint64_t frame = (_s_)->frame_list.p[i]; \
+ (void)__copy_to_compat_offset((_d_)->frame_list, i, &frame, 1); \
+ } \
+ } \
+ } while (0)
+ XLAT_gnttab_get_status_frames(&cmp.get_status, nat.get_status);
+#undef XLAT_gnttab_get_status_frames_HNDL_frame_list
+ if ( unlikely(__copy_to_guest(cmp_uop, &cmp.get_status, 1)) )
+ rc = -EFAULT;
+ }
+ break;
+ }
+
default:
domain_crash(current->domain);
break;
diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c
index 7ec32d99f4..c49692f3e3 100644
--- a/xen/common/grant_table.c
+++ b/xen/common/grant_table.c
@@ -105,9 +105,24 @@ static unsigned inline int max_nr_maptrack_frames(void)
}
-#define SHGNT_PER_PAGE (PAGE_SIZE / sizeof(grant_entry_v1_t))
-#define shared_entry(t, e) \
- ((t)->shared[(e)/SHGNT_PER_PAGE][(e)%SHGNT_PER_PAGE])
+#define SHGNT_PER_PAGE_V1 (PAGE_SIZE / sizeof(grant_entry_v1_t))
+#define shared_entry_v1(t, e) \
+ ((t)->shared_v1[(e)/SHGNT_PER_PAGE_V1][(e)%SHGNT_PER_PAGE_V1])
+#define SHGNT_PER_PAGE_V2 (PAGE_SIZE / sizeof(grant_entry_v2_t))
+#define shared_entry_v2(t, e) \
+ ((t)->shared_v2[(e)/SHGNT_PER_PAGE_V2][(e)%SHGNT_PER_PAGE_V2])
+#define STGNT_PER_PAGE (PAGE_SIZE / sizeof(grant_status_t))
+#define status_entry(t, e) \
+ ((t)->status[(e)/STGNT_PER_PAGE][(e)%STGNT_PER_PAGE])
+static grant_entry_header_t *
+shared_entry_header(struct grant_table *t, grant_ref_t ref)
+{
+ ASSERT(t->gt_version != 0);
+ if (t->gt_version == 1)
+ return (grant_entry_header_t*)&shared_entry_v1(t, ref);
+ else
+ return &shared_entry_v2(t, ref).hdr;
+}
#define ACGNT_PER_PAGE (PAGE_SIZE / sizeof(struct active_grant_entry))
#define active_entry(t, e) \
((t)->active[(e)/ACGNT_PER_PAGE][(e)%ACGNT_PER_PAGE])
@@ -183,6 +198,174 @@ get_maptrack_handle(
return handle;
}
+/* Number of grant table entries. Caller must hold d's grant table lock. */
+static unsigned int nr_grant_entries(struct grant_table *gt)
+{
+ ASSERT(gt->gt_version != 0);
+ if (gt->gt_version == 1)
+ return (nr_grant_frames(gt) << PAGE_SHIFT) / sizeof(grant_entry_v1_t);
+ else
+ return (nr_grant_frames(gt) << PAGE_SHIFT) / sizeof(grant_entry_v2_t);
+}
+
+static int _set_status_v1(domid_t domid,
+ int readonly,
+ grant_entry_header_t *shah,
+ struct active_grant_entry *act)
+{
+ int rc = GNTST_okay;
+ union grant_combo scombo, prev_scombo, new_scombo;
+ uint16_t mask = GTF_type_mask;
+
+ /*
+ * We bound the number of times we retry CMPXCHG on memory locations that
+ * we share with a guest OS. The reason is that the guest can modify that
+ * location at a higher rate than we can read-modify-CMPXCHG, so the guest
+ * could cause us to livelock. There are a few cases where it is valid for
+ * the guest to race our updates (e.g., to change the GTF_readonly flag),
+ * so we allow a few retries before failing.
+ */
+ int retries = 0;
+
+ scombo.word = *(u32 *)shah;
+
+ /*
+ * This loop attempts to set the access (reading/writing) flags
+ * in the grant table entry. It tries a cmpxchg on the field
+ * up to five times, and then fails under the assumption that
+ * the guest is misbehaving.
+ */
+ for ( ; ; )
+ {
+ /* If not already pinned, check the grant domid and type. */
+ if ( !act->pin &&
+ (((scombo.shorts.flags & mask) !=
+ GTF_permit_access) ||
+ (scombo.shorts.domid != domid)) )
+ PIN_FAIL(done, GNTST_general_error,
+ "Bad flags (%x) or dom (%d). (expected dom %d)\n",
+ scombo.shorts.flags, scombo.shorts.domid,
+ domid);
+
+ new_scombo = scombo;
+ new_scombo.shorts.flags |= GTF_reading;
+
+ if ( !readonly )
+ {
+ new_scombo.shorts.flags |= GTF_writing;
+ if ( unlikely(scombo.shorts.flags & GTF_readonly) )
+ PIN_FAIL(done, GNTST_general_error,
+ "Attempt to write-pin a r/o grant entry.\n");
+ }
+
+ prev_scombo.word = cmpxchg((u32 *)shah,
+ scombo.word, new_scombo.word);
+ if ( likely(prev_scombo.word == scombo.word) )
+ break;
+
+ if ( retries++ == 4 )
+ PIN_FAIL(done, GNTST_general_error,
+ "Shared grant entry is unstable.\n");
+
+ scombo = prev_scombo;
+ }
+
+done:
+ return rc;
+}
+
+static int _set_status_v2(domid_t domid,
+ int readonly,
+ grant_entry_header_t *shah,
+ struct active_grant_entry *act,
+ grant_status_t *status)
+{
+ int rc = GNTST_okay;
+ union grant_combo scombo;
+ uint16_t flags = shah->flags;
+ domid_t id = shah->domid;
+ uint16_t mask = GTF_type_mask;
+
+ /* we read flags and domid in a single memory access.
+ this avoids the need for another memory barrier to
+ ensure access to these fields are not reordered */
+ scombo.word = *(u32 *)shah;
+ barrier(); /* but we still need to stop the compiler from turning
+ it back into two reads */
+ flags = scombo.shorts.flags;
+ id = scombo.shorts.domid;
+
+ /* If not already pinned, check the grant domid and type. */
+ if ( !act->pin &&
+ (((flags & mask) != GTF_permit_access) ||
+ (id != domid)) )
+ PIN_FAIL(done, GNTST_general_error,
+ "Bad flags (%x) or dom (%d). (expected dom %d)\n",
+ flags, id, domid);
+
+ if ( readonly )
+ {
+ *status |= GTF_reading;
+ }
+ else
+ {
+ if ( unlikely(flags & GTF_readonly) )
+ PIN_FAIL(done, GNTST_general_error,
+ "Attempt to write-pin a r/o grant entry.\n");
+ *status |= GTF_reading | GTF_writing;
+ }
+
+ /* Make sure guest sees status update before checking if flags are
+ still valid */
+ mb();
+
+ scombo.word = *(u32 *)shah;
+ barrier();
+ flags = scombo.shorts.flags;
+ id = scombo.shorts.domid;
+
+ if ( !act->pin )
+ {
+ if ( ((flags & mask) != GTF_permit_access) ||
+ (id != domid) ||
+ (!readonly && (flags & GTF_readonly)) )
+ {
+ gnttab_clear_flag(_GTF_reading | _GTF_writing, status);
+ PIN_FAIL(done, GNTST_general_error,
+ "Unstable flags (%x) or dom (%d). (expected dom %d) "
+ "(r/w: %d)\n",
+ flags, id, domid, !readonly);
+ }
+ }
+ else
+ {
+ if ( unlikely(flags & GTF_readonly) )
+ {
+ gnttab_clear_flag(_GTF_writing, status);
+ PIN_FAIL(done, GNTST_general_error,
+ "Unstable grant readonly flag\n");
+ }
+ }
+
+done:
+ return rc;
+}
+
+
+static int _set_status(unsigned gt_version,
+ domid_t domid,
+ int readonly,
+ grant_entry_header_t *shah,
+ struct active_grant_entry *act,
+ grant_status_t *status)
+{
+
+ if (gt_version == 1)
+ return _set_status_v1(domid, readonly, shah, act);
+ else
+ return _set_status_v2(domid, readonly, shah, act, status);
+}
+
/*
* Returns 0 if TLB flush / invalidate required by caller.
* va will indicate the address to be invalidated.
@@ -205,18 +388,10 @@ __gnttab_map_grant_ref(
unsigned int cache_flags;
struct active_grant_entry *act;
struct grant_mapping *mt;
- grant_entry_v1_t *sha;
- union grant_combo scombo, prev_scombo, new_scombo;
-
- /*
- * We bound the number of times we retry CMPXCHG on memory locations that
- * we share with a guest OS. The reason is that the guest can modify that
- * location at a higher rate than we can read-modify-CMPXCHG, so the guest
- * could cause us to livelock. There are a few cases where it is valid for
- * the guest to race our updates (e.g., to change the GTF_readonly flag),
- * so we allow a few retries before failing.
- */
- int retries = 0;
+ grant_entry_v1_t *sha1;
+ grant_entry_v2_t *sha2;
+ grant_entry_header_t *shah;
+ uint16_t *status;
led = current;
ld = led->domain;
@@ -262,12 +437,25 @@ __gnttab_map_grant_ref(
spin_lock(&rd->grant_table->lock);
+ if ( rd->grant_table->gt_version == 0 )
+ PIN_FAIL(unlock_out, GNTST_general_error,
+ "remote grant table not yet set up");
+
/* Bounds check on the grant ref */
if ( unlikely(op->ref >= nr_grant_entries(rd->grant_table)))
PIN_FAIL(unlock_out, GNTST_bad_gntref, "Bad ref (%d).\n", op->ref);
act = &active_entry(rd->grant_table, op->ref);
- sha = &shared_entry(rd->grant_table, op->ref);
+ shah = shared_entry_header(rd->grant_table, op->ref);
+ if (rd->grant_table->gt_version == 1) {
+ sha1 = &shared_entry_v1(rd->grant_table, op->ref);
+ sha2 = NULL;
+ status = &shah->flags;
+ } else {
+ sha2 = &shared_entry_v2(rd->grant_table, op->ref);
+ sha1 = NULL;
+ status = &status_entry(rd->grant_table, op->ref);
+ }
/* If already pinned, check the active domid and avoid refcnt overflow. */
if ( act->pin &&
@@ -281,54 +469,19 @@ __gnttab_map_grant_ref(
(!(op->flags & GNTMAP_readonly) &&
!(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask))) )
{
- scombo.word = *(u32 *)&sha->flags;
-
- /*
- * This loop attempts to set the access (reading/writing) flags
- * in the grant table entry. It tries a cmpxchg on the field
- * up to five times, and then fails under the assumption that
- * the guest is misbehaving.
- */
- for ( ; ; )
- {
- /* If not already pinned, check the grant domid and type. */
- if ( !act->pin &&
- (((scombo.shorts.flags & GTF_type_mask) !=
- GTF_permit_access) ||
- (scombo.shorts.domid != ld->domain_id)) )
- PIN_FAIL(unlock_out, GNTST_general_error,
- "Bad flags (%x) or dom (%d). (expected dom %d)\n",
- scombo.shorts.flags, scombo.shorts.domid,
- ld->domain_id);
-
- new_scombo = scombo;
- new_scombo.shorts.flags |= GTF_reading;
-
- if ( !(op->flags & GNTMAP_readonly) )
- {
- new_scombo.shorts.flags |= GTF_writing;
- if ( unlikely(scombo.shorts.flags & GTF_readonly) )
- PIN_FAIL(unlock_out, GNTST_general_error,
- "Attempt to write-pin a r/o grant entry.\n");
- }
-
- prev_scombo.word = cmpxchg((u32 *)&sha->flags,
- scombo.word, new_scombo.word);
- if ( likely(prev_scombo.word == scombo.word) )
- break;
-
- if ( retries++ == 4 )
- PIN_FAIL(unlock_out, GNTST_general_error,
- "Shared grant entry is unstable.\n");
-
- scombo = prev_scombo;
- }
+ if ( (rc = _set_status(rd->grant_table->gt_version,
+ ld->domain_id, op->flags & GNTMAP_readonly,
+ shah, act, status) ) != GNTST_okay )
+ goto unlock_out;
if ( !act->pin )
{
- act->domid = scombo.shorts.domid;
- act->gfn = sha->frame;
- act->frame = gmfn_to_mfn(rd, sha->frame);
+ act->domid = ld->domain_id;
+ if ( sha1 )
+ act->gfn = sha1->frame;
+ else
+ act->gfn = sha2->full_page.frame;
+ act->frame = gmfn_to_mfn(rd, act->gfn);
}
}
@@ -343,7 +496,7 @@ __gnttab_map_grant_ref(
frame = act->frame;
act_pin = act->pin;
- cache_flags = (sha->flags & (GTF_PAT | GTF_PWT | GTF_PCD) );
+ cache_flags = (shah->flags & (GTF_PAT | GTF_PWT | GTF_PCD) );
spin_unlock(&rd->grant_table->lock);
@@ -457,7 +610,7 @@ __gnttab_map_grant_ref(
spin_lock(&rd->grant_table->lock);
act = &active_entry(rd->grant_table, op->ref);
- sha = &shared_entry(rd->grant_table, op->ref);
+ shah = shared_entry_header(rd->grant_table, op->ref);
if ( op->flags & GNTMAP_device_map )
act->pin -= (op->flags & GNTMAP_readonly) ?
@@ -468,10 +621,10 @@ __gnttab_map_grant_ref(
if ( !(op->flags & GNTMAP_readonly) &&
!(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask)) )
- gnttab_clear_flag(_GTF_writing, &sha->flags);
+ gnttab_clear_flag(_GTF_writing, status);
if ( !act->pin )
- gnttab_clear_flag(_GTF_reading, &sha->flags);
+ gnttab_clear_flag(_GTF_reading, status);
unlock_out:
spin_unlock(&rd->grant_table->lock);
@@ -508,7 +661,6 @@ __gnttab_unmap_common(
domid_t dom;
struct domain *ld, *rd;
struct active_grant_entry *act;
- grant_entry_v1_t *sha;
s16 rc = 0;
u32 old_pin;
@@ -556,7 +708,6 @@ __gnttab_unmap_common(
spin_lock(&rd->grant_table->lock);
act = &active_entry(rd->grant_table, op->map->ref);
- sha = &shared_entry(rd->grant_table, op->map->ref);
old_pin = act->pin;
if ( op->frame == 0 )
@@ -622,8 +773,9 @@ __gnttab_unmap_common_complete(struct gnttab_unmap_common *op)
{
struct domain *ld, *rd;
struct active_grant_entry *act;
- grant_entry_v1_t *sha;
+ grant_entry_header_t *sha;
struct page_info *pg;
+ uint16_t *status;
rd = op->rd;
@@ -642,8 +794,16 @@ __gnttab_unmap_common_complete(struct gnttab_unmap_common *op)
rcu_lock_domain(rd);
spin_lock(&rd->grant_table->lock);
+ if ( rd->grant_table->gt_version == 0 )
+ goto unmap_out;
+
act = &active_entry(rd->grant_table, op->map->ref);
- sha = &shared_entry(rd->grant_table, op->map->ref);
+ sha = shared_entry_header(rd->grant_table, op->map->ref);
+
+ if ( rd->grant_table->gt_version == 1 )
+ status = &sha->flags;
+ else
+ status = &status_entry(rd->grant_table, op->map->ref);
if ( unlikely(op->frame != act->frame) )
{
@@ -694,10 +854,10 @@ __gnttab_unmap_common_complete(struct gnttab_unmap_common *op)
if ( ((act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0) &&
!(op->flags & GNTMAP_readonly) )
- gnttab_clear_flag(_GTF_writing, &sha->flags);
+ gnttab_clear_flag(_GTF_writing, status);
if ( act->pin == 0 )
- gnttab_clear_flag(_GTF_reading, &sha->flags);
+ gnttab_clear_flag(_GTF_reading, status);
unmap_out:
spin_unlock(&rd->grant_table->lock);
@@ -829,6 +989,50 @@ fault:
return -EFAULT;
}
+static int
+gnttab_populate_status_frames(struct domain *d, struct grant_table *gt)
+{
+ unsigned i;
+ unsigned req_status_frames;
+
+ req_status_frames = grant_to_status_frames(gt->nr_grant_frames);
+ for ( i = nr_status_frames(gt); i < req_status_frames; i++ )
+ {
+ if ( (gt->status[i] = alloc_xenheap_page()) == NULL )
+ goto status_alloc_failed;
+ clear_page(gt->status[i]);
+ }
+ /* Share the new status frames with the recipient domain */
+ for ( i = nr_status_frames(gt); i < req_status_frames; i++ )
+ gnttab_create_status_page(d, gt, i);
+
+ gt->nr_status_frames = req_status_frames;
+
+ return 0;
+
+status_alloc_failed:
+ for ( i = nr_status_frames(gt); i < req_status_frames; i++ )
+ {
+ free_xenheap_page(gt->status[i]);
+ gt->status[i] = NULL;
+ }
+ return -ENOMEM;
+}
+
+static void
+gnttab_unpopulate_status_frames(struct domain *d, struct grant_table *gt)
+{
+ int i;
+
+ for ( i = 0; i < nr_status_frames(gt); i++ )
+ {
+ page_set_owner(virt_to_page(gt->status[i]), dom_xen);
+ free_xenheap_page(gt->status[i]);
+ gt->status[i] = NULL;
+ }
+ gt->nr_status_frames = 0;
+}
+
int
gnttab_grow_table(struct domain *d, unsigned int req_nr_frames)
{
@@ -855,9 +1059,9 @@ gnttab_grow_table(struct domain *d, unsigned int req_nr_frames)
/* Shared */
for ( i = nr_grant_frames(gt); i < req_nr_frames; i++ )
{
- if ( (gt->shared[i] = alloc_xenheap_page()) == NULL )
+ if ( (gt->shared_raw[i] = alloc_xenheap_page()) == NULL )
goto shared_alloc_failed;
- clear_page(gt->shared[i]);
+ clear_page(gt->shared_raw[i]);
}
/* Share the new shared frames with the recipient domain */
@@ -866,13 +1070,20 @@ gnttab_grow_table(struct domain *d, unsigned int req_nr_frames)
gt->nr_grant_frames = req_nr_frames;
+ /* Status pages - version 2 */
+ if (gt->gt_version > 1)
+ {
+ if ( gnttab_populate_status_frames(d, gt) )
+ goto shared_alloc_failed;
+ }
+
return 1;
shared_alloc_failed:
for ( i = nr_grant_frames(gt); i < req_nr_frames; i++ )
{
- free_xenheap_page(gt->shared[i]);
- gt->shared[i] = NULL;
+ free_xenheap_page(gt->shared_raw[i]);
+ gt->shared_raw[i] = NULL;
}
active_alloc_failed:
for ( i = nr_active_grant_frames(gt);
@@ -942,7 +1153,13 @@ gnttab_setup_table(
spin_lock(&d->grant_table->lock);
- if ( (op.nr_frames > nr_grant_frames(d->grant_table)) &&
+ if ( d->grant_table->gt_version == 0 )
+ d->grant_table->gt_version = 1;
+
+ if ( (op.nr_frames > nr_grant_frames(d->grant_table) ||
+ ( (d->grant_table->gt_version > 1 ) &&
+ (grant_to_status_frames(op.nr_frames) >
+ nr_status_frames(d->grant_table)) ) ) &&
!gnttab_grow_table(d, op.nr_frames) )
{
gdprintk(XENLOG_INFO,
@@ -1046,7 +1263,7 @@ gnttab_prepare_for_transfer(
struct domain *rd, struct domain *ld, grant_ref_t ref)
{
struct grant_table *rgt;
- struct grant_entry_v1 *sha;
+ grant_entry_header_t *sha;
union grant_combo scombo, prev_scombo, new_scombo;
int retries = 0;
@@ -1058,6 +1275,14 @@ gnttab_prepare_for_transfer(
spin_lock(&rgt->lock);
+ if ( rgt->gt_version == 0 )
+ {
+ gdprintk(XENLOG_INFO,
+ "Grant table not ready for transfer to domain(%d).\n",
+ rd->domain_id);
+ goto fail;
+ }
+
if ( unlikely(ref >= nr_grant_entries(rd->grant_table)) )
{
gdprintk(XENLOG_INFO,
@@ -1066,7 +1291,7 @@ gnttab_prepare_for_transfer(
goto fail;
}
- sha = &shared_entry(rgt, ref);
+ sha = shared_entry_header(rgt, ref);
scombo.word = *(u32 *)&sha->flags;
@@ -1115,7 +1340,6 @@ gnttab_transfer(
struct domain *e;
struct page_info *page;
int i;
- grant_entry_v1_t *sha;
struct gnttab_transfer gop;
unsigned long mfn;
unsigned int max_bitsize;
@@ -1248,11 +1472,21 @@ gnttab_transfer(
/* Tell the guest about its new page frame. */
spin_lock(&e->grant_table->lock);
- sha = &shared_entry(e->grant_table, gop.ref);
- guest_physmap_add_page(e, sha->frame, mfn, 0);
- sha->frame = mfn;
+ if ( e->grant_table->gt_version == 1 )
+ {
+ grant_entry_v1_t *sha = &shared_entry_v1(e->grant_table, gop.ref);
+ guest_physmap_add_page(e, sha->frame, mfn, 0);
+ sha->frame = mfn;
+ }
+ else
+ {
+ grant_entry_v2_t *sha = &shared_entry_v2(e->grant_table, gop.ref);
+ guest_physmap_add_page(e, sha->full_page.frame, mfn, 0);
+ sha->full_page.frame = mfn;
+ }
wmb();
- sha->flags |= GTF_transfer_completed;
+ shared_entry_header(e->grant_table, gop.ref)->flags |=
+ GTF_transfer_completed;
spin_unlock(&e->grant_table->lock);
@@ -1278,16 +1512,22 @@ static void
__release_grant_for_copy(
struct domain *rd, unsigned long gref, int readonly)
{
- grant_entry_v1_t *sha;
+ grant_entry_header_t *sha;
struct active_grant_entry *act;
unsigned long r_frame;
+ uint16_t *status;
spin_lock(&rd->grant_table->lock);
act = &active_entry(rd->grant_table, gref);
- sha = &shared_entry(rd->grant_table, gref);
+ sha = shared_entry_header(rd->grant_table, gref);
r_frame = act->frame;
+ if (rd->grant_table->gt_version == 1)
+ status = &sha->flags;
+ else
+ status = &status_entry(rd->grant_table, gref);
+
if ( readonly )
{
act->pin -= GNTPIN_hstr_inc;
@@ -1298,11 +1538,11 @@ __release_grant_for_copy(
act->pin -= GNTPIN_hstw_inc;
if ( !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) )
- gnttab_clear_flag(_GTF_writing, &sha->flags);
+ gnttab_clear_flag(_GTF_writing, status);
}
if ( !act->pin )
- gnttab_clear_flag(_GTF_reading, &sha->flags);
+ gnttab_clear_flag(_GTF_reading, status);
spin_unlock(&rd->grant_table->lock);
}
@@ -1316,21 +1556,38 @@ __acquire_grant_for_copy(
struct domain *rd, unsigned long gref, int readonly,
unsigned long *frame)
{
- grant_entry_v1_t *sha;
+ grant_entry_v1_t *sha1;
+ grant_entry_v2_t *sha2;
+ grant_entry_header_t *shah;
struct active_grant_entry *act;
+ grant_status_t *status;
s16 rc = GNTST_okay;
- int retries = 0;
- union grant_combo scombo, prev_scombo, new_scombo;
spin_lock(&rd->grant_table->lock);
+ if ( rd->grant_table->gt_version == 0 )
+ PIN_FAIL(unlock_out, GNTST_general_error,
+ "remote grant table not ready\n");
+
if ( unlikely(gref >= nr_grant_entries(rd->grant_table)) )
PIN_FAIL(unlock_out, GNTST_bad_gntref,
"Bad grant reference %ld\n", gref);
act = &active_entry(rd->grant_table, gref);
- sha = &shared_entry(rd->grant_table, gref);
-
+ shah = shared_entry_header(rd->grant_table, gref);
+ if ( rd->grant_table->gt_version == 1 )
+ {
+ sha1 = &shared_entry_v1(rd->grant_table, gref);
+ sha2 = NULL;
+ status = &shah->flags;
+ }
+ else
+ {
+ sha1 = NULL;
+ sha2 = &shared_entry_v2(rd->grant_table, gref);
+ status = &status_entry(rd->grant_table, gref);
+ }
+
/* If already pinned, check the active domid and avoid refcnt overflow. */
if ( act->pin &&
((act->domid != current->domain->domain_id) ||
@@ -1342,48 +1599,18 @@ __acquire_grant_for_copy(
if ( !act->pin ||
(!readonly && !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) )
{
- scombo.word = *(u32 *)&sha->flags;
-
- for ( ; ; )
- {
- /* If not already pinned, check the grant domid and type. */
- if ( !act->pin &&
- (((scombo.shorts.flags & GTF_type_mask) !=
- GTF_permit_access) ||
- (scombo.shorts.domid != current->domain->domain_id)) )
- PIN_FAIL(unlock_out, GNTST_general_error,
- "Bad flags (%x) or dom (%d). (expected dom %d)\n",
- scombo.shorts.flags, scombo.shorts.domid,
- current->domain->domain_id);
-
- new_scombo = scombo;
- new_scombo.shorts.flags |= GTF_reading;
-
- if ( !readonly )
- {
- new_scombo.shorts.flags |= GTF_writing;
- if ( unlikely(scombo.shorts.flags & GTF_readonly) )
- PIN_FAIL(unlock_out, GNTST_general_error,
- "Attempt to write-pin a r/o grant entry.\n");
- }
-
- prev_scombo.word = cmpxchg((u32 *)&sha->flags,
- scombo.word, new_scombo.word);
- if ( likely(prev_scombo.word == scombo.word) )
- break;
-
- if ( retries++ == 4 )
- PIN_FAIL(unlock_out, GNTST_general_error,
- "Shared grant entry is unstable.\n");
-
- scombo = prev_scombo;
- }
-
+ if ( (rc = _set_status(rd->grant_table->gt_version,
+ current->domain->domain_id,
+ readonly, shah, act, status) ) != GNTST_okay )
+ goto unlock_out;
if ( !act->pin )
{
- act->domid = scombo.shorts.domid;
- act->gfn = sha->frame;
- act->frame = gmfn_to_mfn(rd, sha->frame);
+ act->domid = current->domain->domain_id;
+ if ( sha1 )
+ act->gfn = sha1->frame;
+ else
+ act->gfn = sha2->full_page.frame;
+ act->frame = gmfn_to_mfn(rd, act->gfn);
}
}
@@ -1528,6 +1755,165 @@ gnttab_copy(
return 0;
}
+static long
+gnttab_set_version(XEN_GUEST_HANDLE(gnttab_set_version_t uop))
+{
+ gnttab_set_version_t op;
+ struct domain *d = current->domain;
+ struct grant_table *gt = d->grant_table;
+ struct active_grant_entry *act;
+ long res = 0;
+ int i;
+
+ if (copy_from_guest(&op, uop, 1))
+ return -EFAULT;
+
+ if (op.version != 1 && op.version != 2)
+ return -EINVAL;
+
+ spin_lock(&gt->lock);
+ /* Make sure that the grant table isn't currently in use when we
+ change the version number. */
+ /* (You need to change the version number for e.g. kexec.) */
+ if ( gt->gt_version != 0 )
+ {
+ for ( i = 0; i < nr_grant_entries(gt); i++ )
+ {
+ act = &active_entry(gt, i);
+ if ( act->pin != 0 )
+ {
+ gdprintk(XENLOG_WARNING,
+ "tried to change grant table version from %d to %d, but some grant entries still in use\n",
+ gt->gt_version,
+ op.version);
+ res = -EBUSY;
+ goto out;
+ }
+ }
+ }
+
+ /* XXX: If we're going to version 2, we could maybe shrink the
+ active grant table here. */
+
+ if ( op.version == 2 && gt->gt_version < 2 )
+ {
+ res = gnttab_populate_status_frames(d, gt);
+ if ( res < 0)
+ goto out;
+ }
+
+ if ( op.version < 2 && gt->gt_version == 2 )
+ gnttab_unpopulate_status_frames(d, gt);
+
+ if ( op.version != gt->gt_version )
+ {
+ /* Make sure there's no crud left over in the table from the
+ old version. */
+ for ( i = 0; i < nr_grant_frames(gt); i++ )
+ memset(gt->shared_raw[i], 0, PAGE_SIZE);
+ }
+
+ gt->gt_version = op.version;
+
+out:
+ spin_unlock(&gt->lock);
+
+ return res;
+}
+
+static long
+gnttab_get_status_frames(XEN_GUEST_HANDLE(gnttab_get_status_frames_t) uop,
+ int count)
+{
+ gnttab_get_status_frames_t op;
+ struct domain *d;
+ struct grant_table *gt;
+ uint64_t gmfn;
+ int i;
+ int rc;
+
+ if ( count != 1 )
+ return -EINVAL;
+
+ if ( unlikely(copy_from_guest(&op, uop, 1) != 0) )
+ {
+ gdprintk(XENLOG_INFO,
+ "Fault while reading gnttab_get_status_frames_t.\n");
+ return -EFAULT;
+ }
+
+ rc = rcu_lock_target_domain_by_id(op.dom, &d);
+ if ( rc < 0 )
+ {
+ if ( rc == -ESRCH )
+ op.status = GNTST_bad_domain;
+ else if ( rc == -EPERM )
+ op.status = GNTST_permission_denied;
+ else
+ op.status = GNTST_general_error;
+ goto out1;
+ }
+
+ gt = d->grant_table;
+
+ if ( unlikely(op.nr_frames > nr_status_frames(gt)) ) {
+ gdprintk(XENLOG_INFO, "Guest requested addresses for %d grant status "
+ "frames, but only %d are available.\n",
+ op.nr_frames, nr_status_frames(gt));
+ op.status = GNTST_general_error;
+ goto out2;
+ }
+
+ op.status = GNTST_okay;
+
+ spin_lock(&gt->lock);
+
+ for ( i = 0; i < op.nr_frames; i++ )
+ {
+ gmfn = gnttab_status_gmfn(d, d->grant_table, i);
+ if (copy_to_guest_offset(op.frame_list,
+ i,
+ &gmfn,
+ 1))
+ op.status = GNTST_bad_virt_addr;
+ }
+
+ spin_unlock(&gt->lock);
+out2:
+ rcu_unlock_domain(d);
+out1:
+ if ( unlikely(copy_to_guest(uop, &op, 1)) )
+ return -EFAULT;
+
+ return 0;
+}
+
+static long
+gnttab_get_version(XEN_GUEST_HANDLE(gnttab_get_version_t uop))
+{
+ gnttab_get_version_t op;
+ struct domain *d;
+
+ if ( copy_from_guest(&op, uop, 1) )
+ return -EFAULT;
+ d = rcu_lock_domain_by_id(op.dom);
+ if ( d == NULL )
+ return -ESRCH;
+ if ( !IS_PRIV_FOR(current->domain, d) )
+ {
+ rcu_unlock_domain(d);
+ return -EPERM;
+ }
+ spin_lock(&d->grant_table->lock);
+ op.version = d->grant_table->gt_version;
+ spin_unlock(&d->grant_table->lock);
+
+ if ( copy_to_guest(uop, &op, 1) )
+ return -EFAULT;
+ else
+ return 0;
+}
+
long
do_grant_table_op(
unsigned int cmd, XEN_GUEST_HANDLE(void) uop, unsigned int count)
@@ -1630,6 +2016,22 @@ do_grant_table_op(
ASSERT(rc <= 0);
break;
}
+ case GNTTABOP_set_version:
+ {
+ rc = gnttab_set_version(guest_handle_cast(uop, gnttab_set_version_t));
+ break;
+ }
+ case GNTTABOP_get_status_frames:
+ {
+ rc = gnttab_get_status_frames(
+ guest_handle_cast(uop, gnttab_get_status_frames_t), count);
+ break;
+ }
+ case GNTTABOP_get_version:
+ {
+ rc = gnttab_get_version(guest_handle_cast(uop, gnttab_get_version_t));
+ break;
+ }
default:
rc = -ENOSYS;
break;
@@ -1666,9 +2068,6 @@ grant_table_create(
struct grant_table *t;
int i;
- /* If this sizeof assertion fails, fix the function: shared_index */
- ASSERT(sizeof(grant_entry_v1_t) == 8);
-
if ( (t = xmalloc(struct grant_table)) == NULL )
goto no_mem_0;
@@ -1703,28 +2102,36 @@ grant_table_create(
t->maptrack[0][i].ref = i+1;
/* Shared grant table. */
- if ( (t->shared = xmalloc_array(struct grant_entry_v1 *,
- max_nr_grant_frames)) == NULL )
+ if ( (t->shared_raw = xmalloc_array(void *, max_nr_grant_frames)) == NULL )
goto no_mem_3;
- memset(t->shared, 0, max_nr_grant_frames * sizeof(t->shared[0]));
+ memset(t->shared_raw, 0, max_nr_grant_frames * sizeof(t->shared_raw[0]));
for ( i = 0; i < INITIAL_NR_GRANT_FRAMES; i++ )
{
- if ( (t->shared[i] = alloc_xenheap_page()) == NULL )
+ if ( (t->shared_raw[i] = alloc_xenheap_page()) == NULL )
goto no_mem_4;
- clear_page(t->shared[i]);
+ clear_page(t->shared_raw[i]);
}
for ( i = 0; i < INITIAL_NR_GRANT_FRAMES; i++ )
gnttab_create_shared_page(d, t, i);
+ /* Status pages for grant table - for version 2 */
+ t->status = xmalloc_array(grant_status_t *,
+ grant_to_status_frames(max_nr_grant_frames));
+ if ( t->status == NULL )
+ goto no_mem_4;
+ memset(t->status, 0,
+ grant_to_status_frames(max_nr_grant_frames) * sizeof(t->status[0]));
+ t->nr_status_frames = 0;
+
/* Okay, install the structure. */
d->grant_table = t;
return 0;
no_mem_4:
for ( i = 0; i < INITIAL_NR_GRANT_FRAMES; i++ )
- free_xenheap_page(t->shared[i]);
- xfree(t->shared);
+ free_xenheap_page(t->shared_raw[i]);
+ xfree(t->shared_raw);
no_mem_3:
free_xenheap_page(t->maptrack[0]);
xfree(t->maptrack);
@@ -1749,7 +2156,8 @@ gnttab_release_mappings(
grant_handle_t handle;
struct domain *rd;
struct active_grant_entry *act;
- struct grant_entry_v1*sha;
+ grant_entry_header_t *sha;
+ uint16_t *status;
struct page_info *pg;
BUG_ON(!d->is_dying);
@@ -1777,7 +2185,12 @@ gnttab_release_mappings(
spin_lock(&rd->grant_table->lock);
act = &active_entry(rd->grant_table, ref);
- sha = &shared_entry(rd->grant_table, ref);
+ sha = shared_entry_header(rd->grant_table, ref);
+ if (rd->grant_table->gt_version == 1)
+ status = &sha->flags;
+ else
+ status = &status_entry(rd->grant_table, ref);
+
pg = mfn_to_page(act->frame);
if ( map->flags & GNTMAP_readonly )
@@ -1823,11 +2236,11 @@ gnttab_release_mappings(
}
if ( (act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0 )
- gnttab_clear_flag(_GTF_writing, &sha->flags);
+ gnttab_clear_flag(_GTF_writing, status);
}
if ( act->pin == 0 )
- gnttab_clear_flag(_GTF_reading, &sha->flags);
+ gnttab_clear_flag(_GTF_reading, status);
spin_unlock(&rd->grant_table->lock);
@@ -1849,8 +2262,8 @@ grant_table_destroy(
return;
for ( i = 0; i < nr_grant_frames(t); i++ )
- free_xenheap_page(t->shared[i]);
- xfree(t->shared);
+ free_xenheap_page(t->shared_raw[i]);
+ xfree(t->shared_raw);
for ( i = 0; i < nr_maptrack_frames(t); i++ )
free_xenheap_page(t->maptrack[i]);
@@ -1860,6 +2273,10 @@ grant_table_destroy(
free_xenheap_page(t->active[i]);
xfree(t->active);
+ for ( i = 0; i < nr_status_frames(t); i++ )
+ free_xenheap_page(t->status[i]);
+ xfree(t->status);
+
xfree(t);
d->grant_table = NULL;
}
diff --git a/xen/include/Makefile b/xen/include/Makefile
index 8f9975ce31..633030ba03 100644
--- a/xen/include/Makefile
+++ b/xen/include/Makefile
@@ -58,7 +58,7 @@ compat/%.h: compat/%.i Makefile $(BASEDIR)/tools/compat-build-header.py
mv -f $@.new $@
compat/%.i: compat/%.c Makefile
- $(CPP) $(filter-out -M% .%.d,$(CFLAGS)) $(cppflags-y) -o $@ $<
+ $(CPP) -include public/xen-compat.h $(filter-out -M% .%.d,$(CFLAGS)) $(cppflags-y) -o $@ $<
compat/%.c: public/%.h xlat.lst Makefile $(BASEDIR)/tools/compat-build-source.py
mkdir -p $(@D)
diff --git a/xen/include/asm-x86/grant_table.h b/xen/include/asm-x86/grant_table.h
index 4c72d7ccfc..07e96b661b 100644
--- a/xen/include/asm-x86/grant_table.h
+++ b/xen/include/asm-x86/grant_table.h
@@ -21,16 +21,31 @@ int replace_grant_host_mapping(
#define gnttab_create_shared_page(d, t, i) \
do { \
share_xen_page_with_guest( \
- virt_to_page((char *)(t)->shared[i]), \
+ virt_to_page((char *)(t)->shared_raw[i]), \
(d), XENSHARE_writable); \
} while ( 0 )
+#define gnttab_create_status_page(d, t, i) \
+ do { \
+ share_xen_page_with_guest( \
+ virt_to_page((char *)(t)->status[i]), \
+ (d), XENSHARE_writable); \
+ } while ( 0 )
+
+
#define gnttab_shared_mfn(d, t, i) \
- ((virt_to_maddr((t)->shared[i]) >> PAGE_SHIFT))
+ ((virt_to_maddr((t)->shared_raw[i]) >> PAGE_SHIFT))
#define gnttab_shared_gmfn(d, t, i) \
(mfn_to_gmfn(d, gnttab_shared_mfn(d, t, i)))
+
+#define gnttab_status_mfn(d, t, i) \
+ ((virt_to_maddr((t)->status[i]) >> PAGE_SHIFT))
+
+#define gnttab_status_gmfn(d, t, i) \
+ (mfn_to_gmfn(d, gnttab_status_mfn(d, t, i)))
+
#define gnttab_mark_dirty(d, f) paging_mark_dirty((d), (f))
static inline void gnttab_clear_flag(unsigned long nr, uint16_t *addr)
diff --git a/xen/include/public/grant_table.h b/xen/include/public/grant_table.h
index 7e9b2da57b..f4890585b4 100644
--- a/xen/include/public/grant_table.h
+++ b/xen/include/public/grant_table.h
@@ -90,6 +90,11 @@
* [XEN]: This field is written by Xen and read by the sharing guest.
* [GST]: This field is written by the guest and read by Xen.
*/
+
+/*
+ * Version 1 of the grant table entry structure is maintained purely
+ * for backwards compatibility. New guests should use version 2.
+ */
#if __XEN_INTERFACE_VERSION__ < 0x0003020a
#define grant_entry_v1 grant_entry
#define grant_entry_v1_t grant_entry_t
@@ -154,6 +159,55 @@ typedef struct grant_entry_v1 grant_entry_v1_t;
#define _GTF_transfer_completed (3)
#define GTF_transfer_completed (1U<<_GTF_transfer_completed)
+/*
+ * Version 2 grant table entries. These fulfil the same role as
+ * version 1 entries, but can represent more complicated operations.
+ * Any given domain will have either a version 1 or a version 2 table,
+ * and every entry in the table will be the same version.
+ *
+ * The interface by which domains use grant references does not depend
+ * on the grant table version in use by the other domain.
+ */
+#if __XEN_INTERFACE_VERSION__ >= 0x0003020a
+/*
+ * Version 1 and version 2 grant entries share a common prefix. The
+ * fields of the prefix are documented as part of struct
+ * grant_entry_v1.
+ */
+struct grant_entry_header {
+ uint16_t flags;
+ domid_t domid;
+};
+typedef struct grant_entry_header grant_entry_header_t;
+
+/*
+ * Version 2 of the grant entry structure.
+ */
+union grant_entry_v2 {
+ grant_entry_header_t hdr;
+
+ /*
+ * This member is used for V1-style full page grants, where either:
+ *
+ * -- hdr.type is GTF_accept_transfer, or
+ * -- hdr.type is GTF_permit_access and GTF_sub_page is not set.
+ *
+ * In that case, the frame field has the same semantics as the
+ * field of the same name in the V1 entry structure.
+ */
+ struct {
+ grant_entry_header_t hdr;
+ uint32_t pad0;
+ uint64_t frame;
+ } full_page;
+
+ uint32_t __spacer[4]; /* Pad to a power of two */
+};
+typedef union grant_entry_v2 grant_entry_v2_t;
+
+typedef uint16_t grant_status_t;
+
+#endif /* __XEN_INTERFACE_VERSION__ */
/***********************************
* GRANT TABLE QUERIES AND USES
@@ -363,6 +417,63 @@ struct gnttab_unmap_and_replace {
typedef struct gnttab_unmap_and_replace gnttab_unmap_and_replace_t;
DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_and_replace_t);
+#if __XEN_INTERFACE_VERSION__ >= 0x0003020a
+/*
+ * GNTTABOP_set_version: Request a particular version of the grant
+ * table shared table structure. This operation can only be performed
+ * once in any given domain. It must be performed before any grants
+ * are activated; otherwise, the domain will be stuck with version 1.
+ * The only defined versions are 1 and 2.
+ */
+#define GNTTABOP_set_version 8
+struct gnttab_set_version {
+ /* IN parameters */
+ uint32_t version;
+};
+typedef struct gnttab_set_version gnttab_set_version_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_set_version_t);
+
+
+/*
+ * GNTTABOP_get_status_frames: Get the list of frames used to store grant
+ * status for <dom>. In grant format version 2, the status is separated
+ * from the other shared grant fields to allow more efficient synchronization
+ * using barriers instead of atomic cmpexch operations.
+ * <nr_frames> specify the size of vector <frame_list>.
+ * The frame addresses are returned in the <frame_list>.
+ * Only <nr_frames> addresses are returned, even if the table is larger.
+ * NOTES:
+ * 1. <dom> may be specified as DOMID_SELF.
+ * 2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF.
+ */
+#define GNTTABOP_get_status_frames 9
+struct gnttab_get_status_frames {
+ /* IN parameters. */
+ uint32_t nr_frames;
+ domid_t dom;
+ /* OUT parameters. */
+ int16_t status; /* GNTST_* */
+ XEN_GUEST_HANDLE(uint64_t) frame_list;
+};
+typedef struct gnttab_get_status_frames gnttab_get_status_frames_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_get_status_frames_t);
+
+/*
+ * GNTTABOP_get_version: Get the grant table version which is in
+ * effect for domain <dom>.
+ */
+#define GNTTABOP_get_version 10
+struct gnttab_get_version {
+ /* IN parameters */
+ domid_t dom;
+ uint16_t pad;
+ /* OUT parameters */
+ uint32_t version;
+};
+typedef struct gnttab_get_version gnttab_get_version_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_get_version_t);
+
+#endif /* __XEN_INTERFACE_VERSION__ */
/*
* Bitfield values for gnttab_map_grant_ref.flags.
diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h
index 2af19de6f8..be7af487d6 100644
--- a/xen/include/public/memory.h
+++ b/xen/include/public/memory.h
@@ -211,6 +211,8 @@ struct xen_add_to_physmap {
#define XENMAPSPACE_gmfn 2 /* GMFN */
unsigned int space;
+#define XENMAPIDX_grant_table_status 0x80000000
+
/* Index into source mapping space. */
xen_ulong_t idx;
diff --git a/xen/include/public/xen.h b/xen/include/public/xen.h
index 1159055436..0cd04728d9 100644
--- a/xen/include/public/xen.h
+++ b/xen/include/public/xen.h
@@ -47,6 +47,7 @@ DEFINE_XEN_GUEST_HANDLE(long);
__DEFINE_XEN_GUEST_HANDLE(ulong, unsigned long);
DEFINE_XEN_GUEST_HANDLE(void);
+DEFINE_XEN_GUEST_HANDLE(uint64_t);
DEFINE_XEN_GUEST_HANDLE(xen_pfn_t);
#endif
diff --git a/xen/include/xen/grant_table.h b/xen/include/xen/grant_table.h
index c1ea9e0652..5eb44874d5 100644
--- a/xen/include/xen/grant_table.h
+++ b/xen/include/xen/grant_table.h
@@ -80,7 +80,15 @@ struct grant_table {
/* Table size. Number of frames shared with guest */
unsigned int nr_grant_frames;
/* Shared grant table (see include/public/grant_table.h). */
- struct grant_entry_v1 **shared;
+ union {
+ void **shared_raw;
+ struct grant_entry_v1 **shared_v1;
+ union grant_entry_v2 **shared_v2;
+ };
+ /* Number of grant status frames shared with guest (for version 2) */
+ unsigned int nr_status_frames;
+ /* State grant table (see include/public/grant_table.h). */
+ grant_status_t **status;
/* Active grant table. */
struct active_grant_entry **active;
/* Mapping tracking table. */
@@ -89,6 +97,9 @@ struct grant_table {
unsigned int maptrack_limit;
/* Lock protecting updates to active and shared grant tables. */
spinlock_t lock;
+ /* The defined versions are 1 and 2. Set to 0 if we don't know
+ what version to use yet. */
+ unsigned gt_version;
};
/* Create/destroy per-domain grant table context. */
@@ -114,10 +125,19 @@ static inline unsigned int nr_grant_frames(struct grant_table *gt)
return gt->nr_grant_frames;
}
-/* Number of grant table entries. Caller must hold d's grant table lock. */
-static inline unsigned int nr_grant_entries(struct grant_table *gt)
+/* Number of status grant table frames. Caller must hold d's gr. table lock.*/
+static inline unsigned int nr_status_frames(struct grant_table *gt)
{
- return (nr_grant_frames(gt) << PAGE_SHIFT) / sizeof(grant_entry_v1_t);
+ return gt->nr_status_frames;
+}
+
+#define GRANT_STATUS_PER_PAGE (PAGE_SIZE / sizeof(grant_status_t))
+#define GRANT_PER_PAGE (PAGE_SIZE / sizeof(grant_entry_v2_t))
+/* Number of grant table status entries. Caller must hold d's gr. table lock.*/
+static inline unsigned int grant_to_status_frames(int grant_frames)
+{
+ return (grant_frames * GRANT_PER_PAGE + GRANT_STATUS_PER_PAGE - 1) /
+ GRANT_STATUS_PER_PAGE;
}
static inline unsigned int
diff --git a/xen/include/xlat.lst b/xen/include/xlat.lst
index 585e2c3e6d..daf466f13b 100644
--- a/xen/include/xlat.lst
+++ b/xen/include/xlat.lst
@@ -44,7 +44,12 @@
! gnttab_transfer grant_table.h
? gnttab_unmap_grant_ref grant_table.h
? gnttab_unmap_and_replace grant_table.h
+? gnttab_set_version grant_table.h
+? gnttab_get_version grant_table.h
+! gnttab_get_status_frames grant_table.h
? grant_entry_v1 grant_table.h
+? grant_entry_header grant_table.h
+? grant_entry_v2 grant_table.h
? kexec_exec kexec.h
! kexec_image kexec.h
! kexec_range kexec.h