Transitive grant support.

Signed-off-by: Steven Smith <steven.smith@citrix.com>
This commit is contained in:
Keir Fraser committed 2009-10-07 07:47:21 +01:00
1 parent b545941b66
commit 3672ce675c
3 files changed
+222 -43

No files matched your search

+196 -38
View File
@@ -309,11 +309,12 @@ static int _set_status_v2(domid_t domid,
/* If not already pinned, check the grant domid and type. */
if ( !act->pin &&
(((flags & mask) != GTF_permit_access) ||
( (((flags & mask) != GTF_permit_access) &&
((flags & mask) != GTF_transitive)) ||
(id != domid)) )
PIN_FAIL(done, GNTST_general_error,
"Bad flags (%x) or dom (%d). (expected dom %d)\n",
flags, id, domid);
"Bad flags (%x) or dom (%d). (expected dom %d, flags %x)\n",
flags, id, domid, mask);
if ( readonly )
{
@@ -338,7 +339,8 @@ static int _set_status_v2(domid_t domid,
if ( !act->pin )
{
if ( ((flags & mask) != GTF_permit_access) ||
if ( (((flags & mask) != GTF_permit_access) &&
((flags & mask) != GTF_transitive)) ||
(id != domid) ||
(!readonly && (flags & GTF_readonly)) )
{
@@ -1533,6 +1535,14 @@ __release_grant_for_copy(
struct active_grant_entry *act;
unsigned long r_frame;
uint16_t *status;
domid_t trans_domid;
grant_ref_t trans_gref;
int released_read;
int released_write;
struct domain *trans_dom;
released_read = 0;
released_write = 0;
spin_lock(&rd->grant_table->lock);
@@ -1541,9 +1551,19 @@ __release_grant_for_copy(
r_frame = act->frame;
if (rd->grant_table->gt_version == 1)
{
status = &sha->flags;
trans_domid = rd->domain_id;
/* Shut the compiler up. This'll never be used, because
trans_domid == rd->domain_id, but gcc doesn't know that. */
trans_gref = 0x1234567;
}
else
{
status = &status_entry(rd->grant_table, gref);
trans_domid = act->trans_dom;
trans_gref = act->trans_gref;
}
if ( readonly )
{
@@ -1555,13 +1575,51 @@ __release_grant_for_copy(
act->pin -= GNTPIN_hstw_inc;
if ( !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) )
{
released_write = 1;
gnttab_clear_flag(_GTF_writing, status);
}
}
if ( !act->pin )
{
gnttab_clear_flag(_GTF_reading, status);
released_read = 1;
}
spin_unlock(&rd->grant_table->lock);
if ( trans_domid != rd->domain_id )
{
if ( released_write || released_read )
{
trans_dom = rcu_lock_domain_by_id(trans_domid);
if ( trans_dom != NULL )
{
/* Recursive calls, but they're tail calls, so it's
okay. */
if ( released_write )
__release_grant_for_copy(trans_dom, trans_gref, 0);
else if ( released_read )
__release_grant_for_copy(trans_dom, trans_gref, 1);
}
}
}
}
/* The status for a grant indicates that we're taking more access than
the pin requires. Fix up the status to match the pin. Called
under the domain's grant table lock. */
/* Only safe on transitive grants. Even then, note that we don't
attempt to drop any pin on the referent grant. */
static void __fixup_status_for_pin(struct active_grant_entry *act,
uint16_t *status)
{
if ( !(act->pin & GNTPIN_hstw_mask) )
*status &= ~_GTF_writing;
if ( !(act->pin & GNTPIN_hstr_mask) )
*status &= ~_GTF_reading;
}
/* Grab a frame number from a grant entry and update the flags and pin
@@ -1570,16 +1628,28 @@ __release_grant_for_copy(
actually valid. */
static int
__acquire_grant_for_copy(
struct domain *rd, unsigned long gref, int readonly,
unsigned long *frame, unsigned *page_off, unsigned *length)
struct domain *rd, unsigned long gref, struct domain *ld, int readonly,
unsigned long *frame, unsigned *page_off, unsigned *length,
unsigned allow_transitive, struct domain **owning_domain)
{
grant_entry_v1_t *sha1;
grant_entry_v2_t *sha2;
grant_entry_header_t *shah;
struct active_grant_entry *act;
grant_status_t *status;
uint32_t old_pin;
domid_t trans_domid;
grant_ref_t trans_gref;
struct domain *rrd;
unsigned long grant_frame;
unsigned trans_page_off;
unsigned trans_length;
int is_sub_page;
struct domain *ignore;
s16 rc = GNTST_okay;
*owning_domain = NULL;
spin_lock(&rd->grant_table->lock);
if ( rd->grant_table->gt_version == 0 )
@@ -1607,46 +1677,128 @@ __acquire_grant_for_copy(
/* If already pinned, check the active domid and avoid refcnt overflow. */
if ( act->pin &&
((act->domid != current->domain->domain_id) ||
((act->domid != ld->domain_id) ||
(act->pin & 0x80808080U) != 0) )
PIN_FAIL(unlock_out, GNTST_general_error,
"Bad domain (%d != %d), or risk of counter overflow %08x\n",
act->domid, current->domain->domain_id, act->pin);
act->domid, ld->domain_id, act->pin);
old_pin = act->pin;
if ( !act->pin ||
(!readonly && !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) )
{
if ( (rc = _set_status(rd->grant_table->gt_version,
current->domain->domain_id,
readonly, 0, shah, act, status) ) != GNTST_okay )
ld->domain_id,
readonly, 0, shah, act,
status) ) != GNTST_okay )
goto unlock_out;
trans_domid = ld->domain_id;
trans_gref = 0;
if ( sha2 && (shah->flags & GTF_type_mask) == GTF_transitive )
{
if ( !allow_transitive )
PIN_FAIL(unlock_out, GNTST_general_error,
"transitive grant when transitivity not allowed\n");
trans_domid = sha2->transitive.trans_domid;
trans_gref = sha2->transitive.gref;
barrier(); /* Stop the compiler from re-loading
trans_domid from shared memory */
if ( trans_domid == rd->domain_id )
PIN_FAIL(unlock_out, GNTST_general_error,
"transitive grants cannot be self-referential\n");
/* We allow the trans_domid == ld->domain_id case, which
corresponds to a grant being issued by one domain, sent
to another one, and then transitively granted back to
the original domain. Allowing it is easy, and means
that you don't need to go out of your way to avoid it
in the guest. */
rrd = rcu_lock_domain_by_id(trans_domid);
if ( rrd == NULL )
PIN_FAIL(unlock_out, GNTST_general_error,
"transitive grant referenced bad domain %d\n",
trans_domid);
spin_unlock(&rd->grant_table->lock);
rc = __acquire_grant_for_copy(rrd, trans_gref, rd,
readonly, &grant_frame,
&trans_page_off, &trans_length,
0, &ignore);
spin_lock(&rd->grant_table->lock);
if ( rc != GNTST_okay ) {
__fixup_status_for_pin(act, status);
spin_unlock(&rd->grant_table->lock);
return rc;
}
/* We dropped the lock, so we have to check that nobody
else tried to pin (or, for that matter, unpin) the
reference in *this* domain. If they did, just give up
and try again. */
if ( act->pin != old_pin )
{
__fixup_status_for_pin(act, status);
spin_unlock(&rd->grant_table->lock);
return __acquire_grant_for_copy(rd, gref, ld, readonly,
frame, page_off, length,
allow_transitive,
owning_domain);
}
/* The actual remote remote grant may or may not be a
sub-page, but we always treat it as one because that
blocks mappings of transitive grants. */
is_sub_page = 1;
*owning_domain = rrd;
act->gfn = INVALID_GFN;
}
else if ( sha1 )
{
act->gfn = sha1->frame;
grant_frame = gmfn_to_mfn(rd, act->gfn);
is_sub_page = 0;
trans_page_off = 0;
trans_length = PAGE_SIZE;
*owning_domain = rd;
}
else if ( !(sha2->hdr.flags & GTF_sub_page) )
{
act->gfn = sha2->full_page.frame;
grant_frame = gmfn_to_mfn(rd, act->gfn);
is_sub_page = 0;
trans_page_off = 0;
trans_length = PAGE_SIZE;
*owning_domain = rd;
}
else
{
act->gfn = sha2->sub_page.frame;
grant_frame = gmfn_to_mfn(rd, act->gfn);
is_sub_page = 1;
trans_page_off = sha2->sub_page.page_off;
trans_length = sha2->sub_page.length;
*owning_domain = rd;
}
if ( !act->pin )
{
act->domid = current->domain->domain_id;
act->is_sub_page = 0;
act->start = 0;
act->length = PAGE_SIZE;
if ( sha1 )
{
act->gfn = sha1->frame;
}
else if ( shah->flags & GTF_sub_page )
{
act->start = sha2->sub_page.page_off;
act->length = sha2->sub_page.length;
act->is_sub_page = 1;
act->gfn = sha2->sub_page.frame;
}
else
{
act->gfn = sha2->full_page.frame;
}
act->frame = gmfn_to_mfn(rd, act->gfn);
act->domid = ld->domain_id;
act->is_sub_page = is_sub_page;
act->start = trans_page_off;
act->length = trans_length;
act->trans_dom = trans_domid;
act->trans_gref = trans_gref;
act->frame = grant_frame;
}
}
else
{
*owning_domain = rd;
}
act->pin += readonly ? GNTPIN_hstr_inc : GNTPIN_hstw_inc;
@@ -1664,6 +1816,7 @@ __gnttab_copy(
struct gnttab_copy *op)
{
struct domain *sd = NULL, *dd = NULL;
struct domain *source_domain = NULL, *dest_domain = NULL;
unsigned long s_frame, d_frame;
char *sp, *dp;
s16 rc = GNTST_okay;
@@ -1704,8 +1857,9 @@ __gnttab_copy(
if ( src_is_gref )
{
unsigned source_off, source_len;
rc = __acquire_grant_for_copy(sd, op->source.u.ref, 1, &s_frame,
&source_off, &source_len);
rc = __acquire_grant_for_copy(sd, op->source.u.ref, current->domain, 1,
&s_frame, &source_off, &source_len, 1,
&source_domain);
if ( rc != GNTST_okay )
goto error_out;
have_s_grant = 1;
@@ -1719,11 +1873,12 @@ __gnttab_copy(
else
{
s_frame = gmfn_to_mfn(sd, op->source.u.gmfn);
source_domain = sd;
}
if ( unlikely(!mfn_valid(s_frame)) )
PIN_FAIL(error_out, GNTST_general_error,
"source frame %lx invalid.\n", s_frame);
if ( !get_page(mfn_to_page(s_frame), sd) )
if ( !get_page(mfn_to_page(s_frame), source_domain) )
{
if ( !sd->is_dying )
gdprintk(XENLOG_WARNING, "Could not get src frame %lx\n", s_frame);
@@ -1735,8 +1890,9 @@ __gnttab_copy(
if ( dest_is_gref )
{
unsigned dest_off, dest_len;
rc = __acquire_grant_for_copy(dd, op->dest.u.ref, 0, &d_frame,
&dest_off, &dest_len);
rc = __acquire_grant_for_copy(dd, op->dest.u.ref, current->domain, 0,
&d_frame, &dest_off, &dest_len, 1,
&dest_domain);
if ( rc != GNTST_okay )
goto error_out;
have_d_grant = 1;
@@ -1750,11 +1906,13 @@ __gnttab_copy(
else
{
d_frame = gmfn_to_mfn(dd, op->dest.u.gmfn);
dest_domain = dd;
}
if ( unlikely(!mfn_valid(d_frame)) )
PIN_FAIL(error_out, GNTST_general_error,
"destination frame %lx invalid.\n", d_frame);
if ( !get_page_and_type(mfn_to_page(d_frame), dd, PGT_writable_page) )
if ( !get_page_and_type(mfn_to_page(d_frame), dest_domain,
PGT_writable_page) )
{
if ( !dd->is_dying )
gdprintk(XENLOG_WARNING, "Could not get dst frame %lx\n", d_frame);
+24 -5
View File
@@ -84,6 +84,11 @@
* Use SMP-safe bit-setting instruction.
*/
/*
* Reference to a grant entry in a specified domain's grant table.
*/
typedef uint32_t grant_ref_t;
/*
* A grant table comprises a packed array of grant entries in one or more
* page frames shared between Xen and a guest.
@@ -118,10 +123,13 @@ typedef struct grant_entry_v1 grant_entry_v1_t;
* GTF_permit_access: Allow @domid to map/access @frame.
* GTF_accept_transfer: Allow @domid to transfer ownership of one page frame
* to this guest. Xen writes the page number to @frame.
* GTF_transitive: Allow @domid to transitively access a subrange of
* @trans_grant in @trans_domid. No mappings are allowed.
*/
#define GTF_invalid (0U<<0)
#define GTF_permit_access (1U<<0)
#define GTF_accept_transfer (2U<<0)
#define GTF_transitive (3U<<0)
#define GTF_type_mask (3U<<0)
/*
@@ -218,6 +226,22 @@ union grant_entry_v2 {
uint64_t frame;
} sub_page;
/*
* If the grant is GTF_transitive, @domid is allowed to use the
* grant @gref in domain @trans_domid, as if it was the local
* domain. Obviously, the transitive access must be compatible
* with the original grant.
*
* The current version of Xen does not allow transitive grants
* to be mapped.
*/
struct {
grant_entry_header_t hdr;
domid_t trans_domid;
uint16_t pad0;
grant_ref_t gref;
} transitive;
uint32_t __spacer[4]; /* Pad to a power of two */
};
typedef union grant_entry_v2 grant_entry_v2_t;
@@ -230,11 +254,6 @@ typedef uint16_t grant_status_t;
* GRANT TABLE QUERIES AND USES
*/
/*
* Reference to a grant entry in a specified domain's grant table.
*/
typedef uint32_t grant_ref_t;
/*
* Handle to track a mapping created via a grant reference.
*/
+2
View File
@@ -32,6 +32,8 @@
struct active_grant_entry {
u32 pin; /* Reference count information. */
domid_t domid; /* Domain being granted access. */
domid_t trans_dom;
uint32_t trans_gref;
unsigned long frame; /* Frame being granted. */
unsigned long gfn; /* Guest's idea of the frame being granted. */
unsigned is_sub_page:1; /* True if this is a sub-page grant. */
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.