# HG changeset patch
# User kfraser@xxxxxxxxxxxxxxxxxxxxx
# Node ID 247fc1245b217d33d299abfe88d10fa3ddb6a026
# Parent 4ee64035c0a3def8034ac3ed89b954eb985246a4
[XEN] Add a new type of grant copy operation.
Based on an original patch from Rolf Neugebauer at Intel.
Signed-off-by: Steven Smith <ssmith@xxxxxxxxxxxxx>
Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx>
---
xen/common/grant_table.c | 221 +++++++++++++++++++++++++++++++++++++++
xen/include/public/grant_table.h | 47 ++++++++
2 files changed, 267 insertions(+), 1 deletion(-)
diff -r 4ee64035c0a3 -r 247fc1245b21 xen/common/grant_table.c
--- a/xen/common/grant_table.c Thu Aug 10 11:36:27 2006 +0100
+++ b/xen/common/grant_table.c Thu Aug 10 11:54:15 2006 +0100
@@ -704,6 +704,218 @@ gnttab_transfer(
return 0;
}
+/* Undo __acquire_grant_for_copy. Again, this has no effect on page
+ type and reference counts. */
+static void
+__release_grant_for_copy(
+ struct domain *rd, unsigned long gref, int readonly)
+{
+ grant_entry_t *const sha = &rd->grant_table->shared[gref];
+ struct active_grant_entry *const act = &rd->grant_table->active[gref];
+ const unsigned long r_frame = act->frame;
+
+ if ( !readonly )
+ gnttab_log_dirty(rd, r_frame);
+
+ spin_lock(&rd->grant_table->lock);
+ if ( readonly )
+ act->pin -= GNTPIN_hstr_inc;
+ else
+ act->pin -= GNTPIN_hstw_inc;
+
+ if ( !(act->pin & GNTPIN_hstw_mask) && !readonly )
+ clear_bit(_GTF_writing, &sha->flags);
+
+ if ( !act->pin )
+ clear_bit(_GTF_reading, &sha->flags);
+ spin_unlock(&rd->grant_table->lock);
+}
+
+/* Grab a frame number from a grant entry and update the flags and pin
+ count as appropriate. Note that this does *not* update the page
+ type or reference counts. */
+static int
+__acquire_grant_for_copy(
+ struct domain *rd, unsigned long gref, int readonly,
+ unsigned long *frame)
+{
+ grant_entry_t *sha;
+ struct active_grant_entry *act;
+ s16 rc = GNTST_okay;
+ int retries = 0;
+ u16 sflags;
+ domid_t sdom;
+
+ if ( unlikely(gref >= NR_GRANT_ENTRIES) )
+ PIN_FAIL(error_out, GNTST_bad_gntref,
+ "Bad grant reference %ld\n", gref);
+
+ act = &rd->grant_table->active[gref];
+ sha = &rd->grant_table->shared[gref];
+
+ spin_lock(&rd->grant_table->lock);
+
+ if ( !act->pin ||
+ (!readonly && !(act->pin & GNTPIN_hstw_mask)) )
+ {
+ sflags = sha->flags;
+ sdom = sha->domid;
+
+ for ( ; ; )
+ {
+ u32 scombo;
+ u32 prev_scombo;
+ u32 new_scombo;
+
+ if ( unlikely((sflags & GTF_type_mask) != GTF_permit_access ||
+ sdom != current->domain->domain_id ) )
+ PIN_FAIL(unlock_out, GNTST_general_error,
+ "Bad flags (%x) or dom (%d). (NB. expected dom %d)\n",
+ sflags, sdom, current->domain->domain_id);
+ scombo = ((u32)sdom << 16) | (u32)sflags;
+ new_scombo = scombo | GTF_reading;
+ if ( !readonly )
+ {
+ new_scombo |= GTF_writing;
+ if ( unlikely(sflags & GTF_readonly) )
+ PIN_FAIL(unlock_out, GNTST_general_error,
+ "Attempt to write-pin a r/o grant entry.\n");
+ }
+ prev_scombo = cmpxchg((u32 *)&sha->flags, scombo, new_scombo);
+ if ( likely(prev_scombo == scombo) )
+ break;
+
+ if ( retries++ == 4 )
+ PIN_FAIL(unlock_out, GNTST_general_error,
+ "Shared grant entry is unstable.\n");
+ sflags = (u16)prev_scombo;
+ sdom = (u16)(prev_scombo >> 16);
+ }
+
+ if ( !act->pin )
+ {
+ act->domid = sdom;
+ act->frame = gmfn_to_mfn(rd, sha->frame);
+ }
+ }
+ else if ( (act->pin & 0x80808080U) != 0 )
+ PIN_FAIL(unlock_out, ENOSPC,
+ "Risk of counter overflow %08x\n", act->pin);
+
+ act->pin += readonly ? GNTPIN_hstr_inc : GNTPIN_hstw_inc;
+
+ *frame = act->frame;
+
+ unlock_out:
+ spin_unlock(&rd->grant_table->lock);
+ error_out:
+ return rc;
+}
+
+static void
+__gnttab_copy(
+ struct gnttab_copy *op)
+{
+ struct domain *sd = NULL, *dd = NULL;
+ unsigned long s_frame, d_frame;
+ void *sp, *dp;
+ s16 rc = GNTST_okay;
+ int have_d_grant = 0, have_s_grant = 0;
+
+ if ( ((op->source.offset + op->len) > PAGE_SIZE) ||
+ ((op->dest.offset + op->len) > PAGE_SIZE) )
+ PIN_FAIL(error_out, GNTST_bad_copy_arg, "copy beyond page area.\n");
+
+ if ( op->source.domid == DOMID_SELF )
+ {
+ sd = current->domain;
+ get_knownalive_domain(sd);
+ }
+ else if ( (sd = find_domain_by_id(op->source.domid)) == NULL )
+ {
+ PIN_FAIL(error_out, GNTST_bad_domain,
+ "couldn't find %d\n", op->source.domid);
+ }
+
+ if ( op->dest.domid == DOMID_SELF )
+ {
+ dd = current->domain;
+ get_knownalive_domain(dd);
+ }
+ else if ( (dd = find_domain_by_id(op->dest.domid)) == NULL )
+ {
+ PIN_FAIL(error_out, GNTST_bad_domain,
+ "couldn't find %d\n", op->dest.domid);
+ }
+
+ if ( op->flags & GNTCOPY_source_gref )
+ {
+ rc = __acquire_grant_for_copy(sd, op->source.u.ref, 1, &s_frame);
+ if ( rc != GNTST_okay )
+ goto error_out;
+ have_s_grant = 1;
+ }
+ else
+ {
+ s_frame = gmfn_to_mfn(sd, op->source.u.gmfn);
+ }
+ if ( !get_page(mfn_to_page(s_frame), sd) )
+ PIN_FAIL(error_out, GNTST_general_error,
+ "could not get source frame %lx.\n", s_frame);
+
+ if ( op->flags & GNTCOPY_dest_gref )
+ {
+ rc = __acquire_grant_for_copy(dd, op->dest.u.ref, 0, &d_frame);
+ if ( rc != GNTST_okay )
+ goto error_out;
+ have_d_grant = 1;
+ }
+ else
+ {
+ d_frame = gmfn_to_mfn(sd, op->dest.u.gmfn);
+ }
+ if ( !get_page_and_type(mfn_to_page(d_frame), dd, PGT_writable_page) )
+ PIN_FAIL(error_out, GNTST_general_error,
+ "could not get source frame %lx.\n", d_frame);
+
+ sp = map_domain_page(s_frame);
+ dp = map_domain_page(d_frame);
+
+ memcpy(dp + op->dest.offset, sp + op->source.offset, op->len);
+
+ unmap_domain_page(dp);
+ unmap_domain_page(sp);
+
+ error_out:
+ if ( have_s_grant )
+ __release_grant_for_copy(sd, op->source.u.ref, 1);
+ if ( have_d_grant )
+ __release_grant_for_copy(dd, op->dest.u.ref, 0);
+ if ( sd )
+ put_domain(sd);
+ if ( dd )
+ put_domain(dd);
+ op->status = rc;
+}
+
+static long
+gnttab_copy(
+ XEN_GUEST_HANDLE(gnttab_copy_t) uop, unsigned int count)
+{
+ int i;
+ struct gnttab_copy op;
+
+ for ( i = 0; i < count; i++ )
+ {
+ if ( unlikely(__copy_from_guest_offset(&op, uop, i, 1)) )
+ return -EFAULT;
+ __gnttab_copy(&op);
+ if ( unlikely(__copy_to_guest_offset(uop, i, &op, 1)) )
+ return -EFAULT;
+ }
+ return 0;
+}
+
long
do_grant_table_op(
unsigned int cmd, XEN_GUEST_HANDLE(void) uop, unsigned int count)
@@ -754,6 +966,15 @@ do_grant_table_op(
rc = gnttab_transfer(transfer, count);
break;
}
+ case GNTTABOP_copy:
+ {
+ XEN_GUEST_HANDLE(gnttab_copy_t) copy =
+ guest_handle_cast(uop, gnttab_copy_t);
+ if ( unlikely(!guest_handle_okay(copy, count)) )
+ goto out;
+ rc = gnttab_copy(copy, count);
+ break;
+ }
default:
rc = -ENOSYS;
break;
diff -r 4ee64035c0a3 -r 247fc1245b21 xen/include/public/grant_table.h
--- a/xen/include/public/grant_table.h Thu Aug 10 11:36:27 2006 +0100
+++ b/xen/include/public/grant_table.h Thu Aug 10 11:54:15 2006 +0100
@@ -248,6 +248,49 @@ struct gnttab_transfer {
};
typedef struct gnttab_transfer gnttab_transfer_t;
DEFINE_XEN_GUEST_HANDLE(gnttab_transfer_t);
+
+
+/*
+ * GNTTABOP_copy: Hypervisor based copy
+ * source and destinations can be eithers MFNs or, for foreign domains,
+ * grant references. the foreign domain has to grant read/write access
+ * in its grant table.
+ *
+ * The flags specify what type source and destinations are (either MFN
+ * or grant reference).
+ *
+ * Note that this can also be used to copy data between two domains
+ * via a third party if the source and destination domains had previously
+ * grant appropriate access to their pages to the third party.
+ *
+ * source_offset specifies an offset in the source frame, dest_offset
+ * the offset in the target frame and len specifies the number of
+ * bytes to be copied.
+ */
+
+#define _GNTCOPY_source_gref (0)
+#define GNTCOPY_source_gref (1<<_GNTCOPY_source_gref)
+#define _GNTCOPY_dest_gref (1)
+#define GNTCOPY_dest_gref (1<<_GNTCOPY_dest_gref)
+
+#define GNTTABOP_copy 5
+typedef struct gnttab_copy {
+ /* IN parameters. */
+ struct {
+ union {
+ grant_ref_t ref;
+ xen_pfn_t gmfn;
+ } u;
+ domid_t domid;
+ uint16_t offset;
+ } source, dest;
+ uint16_t len;
+ uint16_t flags; /* GNTCOPY_* */
+ /* OUT parameters. */
+ int16_t status;
+} gnttab_copy_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_copy_t);
+
/*
* Bitfield values for update_pin_status.flags.
@@ -290,6 +333,7 @@ DEFINE_XEN_GUEST_HANDLE(gnttab_transfer_
#define GNTST_no_device_space (-7) /* Out of space in I/O MMU. */
#define GNTST_permission_denied (-8) /* Not enough privilege for operation. */
#define GNTST_bad_page (-9) /* Specified page was invalid for op. */
+#define GNTST_bad_copy_arg (-10) /* copy arguments cross page boundary */
#define GNTTABOP_error_msgs { \
"okay", \
@@ -301,7 +345,8 @@ DEFINE_XEN_GUEST_HANDLE(gnttab_transfer_
"invalid device address", \
"no spare translation slot in the I/O MMU", \
"permission denied", \
- "bad page" \
+ "bad page", \
+ "copy arguments cross page boundary" \
}
#endif /* __XEN_PUBLIC_GRANT_TABLE_H__ */
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|