NetBSD-5.0.2/sys/arch/xen/xen/xengnt.c

Compare this file to the similar file:
Show the results in this format:

/*      $NetBSD: xengnt.c,v 1.10 2008/10/25 17:12:29 jym Exp $      */

/*
 * Copyright (c) 2006 Manuel Bouyer.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Manuel Bouyer.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: xengnt.c,v 1.10 2008/10/25 17:12:29 jym Exp $");

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/extent.h>
#include <sys/kernel.h>
#include <uvm/uvm.h>

#include <xen/hypervisor.h>
#include <xen/xen.h>
#include <xen/granttables.h>

/* #define XENDEBUG */
#ifdef XENDEBUG
#define DPRINTF(x) printf x
#else
#define DPRINTF(x)
#endif

#define NR_GRANT_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(grant_entry_t))

int gnt_nr_grant_frames;
int gnt_max_grant_frames;

/* table of free grant entries */
grant_ref_t *gnt_entries;
int last_gnt_entry;

/* VM address of the grant table */
grant_entry_t *grant_table;

static grant_ref_t xengnt_get_entry(void);
#define XENGNT_NO_ENTRY 0xffffffff
static void xengnt_free_entry(grant_ref_t);
static void xengnt_resume(void);
static int xengnt_more_entries(void);

void
xengnt_init()
{
	struct gnttab_query_size query;
	int rc;
	int nr_grant_entries;
	int i;

	query.dom = DOMID_SELF;
	rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
	if ((rc < 0) || (query.status != GNTST_okay))
		gnt_max_grant_frames = 4; /* Legacy max number of frames */
	else
		gnt_max_grant_frames = query.max_nr_frames;
	gnt_nr_grant_frames = 0;

	nr_grant_entries =
	    gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE;

	grant_table = (void *)uvm_km_alloc(kernel_map,
	    gnt_max_grant_frames * PAGE_SIZE, 0, UVM_KMF_VAONLY);
	if (grant_table == NULL)
		panic("xengnt_init() no VM space");
	gnt_entries = malloc((nr_grant_entries + 1) * sizeof(grant_ref_t),
	    M_DEVBUF, M_NOWAIT);
	if (gnt_entries == NULL)
		panic("xengnt_init() no space for bitmask");
	for (i = 0; i <= nr_grant_entries; i++)
		gnt_entries[i] = XENGNT_NO_ENTRY;

	last_gnt_entry = 0;
	xengnt_resume();

}

static void
xengnt_resume()
{
	int previous_nr_grant_frames = gnt_nr_grant_frames;
	gnt_nr_grant_frames = 0;
	while (gnt_nr_grant_frames < previous_nr_grant_frames) {
		if (xengnt_more_entries() != 0)
			panic("xengnt_resume: can't restore grant frames");
	}
}

/*
 * Add another page to the grant table
 * Returns 0 on success, ENOMEM on failure
 */
static int
xengnt_more_entries()
{
	gnttab_setup_table_t setup;
	unsigned long *pages;
	int nframes_new = gnt_nr_grant_frames + 1;
	int i;

	if (gnt_nr_grant_frames == gnt_max_grant_frames)
		return ENOMEM;

	pages = malloc(nframes_new * sizeof(long), M_DEVBUF, M_NOWAIT);
	if (pages == NULL)
		return ENOMEM;

	setup.dom = DOMID_SELF;
	setup.nr_frames = nframes_new;
	setup.frame_list = pages;

	/*
	 * setup the grant table, made of nframes_new frames
	 * and return the list of their virtual addresses
	 * in 'pages'
	 */
	if (HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1) != 0)
		panic("%s: setup table failed", __func__);
	if (setup.status != GNTST_okay) {
		aprint_error("%s: setup table returned %d\n",
		    __func__, setup.status);
		free(pages, M_DEVBUF);
		return ENOMEM;
	}

	DPRINTF(("xengnt_more_entries: map 0x%lx -> %p\n",
	    pages[gnt_nr_grant_frames],
	    (char *)grant_table + gnt_nr_grant_frames * PAGE_SIZE));

	/*
	 * map between grant_table addresses and the machine addresses of
	 * the grant table frames
	 */
	pmap_kenter_ma(((vaddr_t)grant_table) + gnt_nr_grant_frames * PAGE_SIZE,
	    pages[gnt_nr_grant_frames] << PAGE_SHIFT, VM_PROT_WRITE);

	/*
	 * add the grant entries associated to the last grant table frame
	 * and mark them as free
	 */
	for (i = gnt_nr_grant_frames * NR_GRANT_ENTRIES_PER_PAGE;
	    i < nframes_new * NR_GRANT_ENTRIES_PER_PAGE;
	    i++) {
		KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
		gnt_entries[last_gnt_entry] = i;
		last_gnt_entry++;
	}
	gnt_nr_grant_frames = nframes_new;
	free(pages, M_DEVBUF);
	return 0;
}

/*
 * Returns a reference to the first free entry in grant table
 */
static grant_ref_t
xengnt_get_entry()
{
	grant_ref_t entry;
	int s = splvm();
	static struct timeval xengnt_nonmemtime;
	static const struct timeval xengnt_nonmemintvl = {5,0};

	if (last_gnt_entry == 0) {
		if (xengnt_more_entries()) {
			splx(s);
			if (ratecheck(&xengnt_nonmemtime, &xengnt_nonmemintvl))
				printf("xengnt_get_entry: out of grant "
				    "table entries\n");
			return XENGNT_NO_ENTRY;
		}
	}
	KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
	last_gnt_entry--;
	entry = gnt_entries[last_gnt_entry];
	gnt_entries[last_gnt_entry] = XENGNT_NO_ENTRY;
	splx(s);
	KASSERT(entry != XENGNT_NO_ENTRY);
	KASSERT(last_gnt_entry >= 0 && last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE);
	return entry;
}

/*
 * Mark the grant table entry as free
 */
static void
xengnt_free_entry(grant_ref_t entry)
{
	int s = splvm();
	KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
	KASSERT(last_gnt_entry >= 0 && last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE);
	gnt_entries[last_gnt_entry] = entry;
	last_gnt_entry++;
	splx(s);
}

int
xengnt_grant_access(domid_t dom, paddr_t ma, int ro, grant_ref_t *entryp)
{
	*entryp = xengnt_get_entry();
	if (__predict_false(*entryp == XENGNT_NO_ENTRY))
		return ENOMEM;

	grant_table[*entryp].frame = ma >> PAGE_SHIFT;
	grant_table[*entryp].domid  = dom;
	x86_lfence();
	grant_table[*entryp].flags =
	    GTF_permit_access | (ro ? GTF_readonly : 0);
	return 0;
}

void
xengnt_revoke_access(grant_ref_t entry)
{
	uint16_t flags, nflags;

	nflags = grant_table[entry].flags;

	do {
		if ((flags = nflags) & (GTF_reading|GTF_writing))
			panic("xengnt_revoke_access: still in use");
		nflags = xen_atomic_cmpxchg16(&grant_table[entry].flags,
		    flags, 0);
	} while (nflags != flags);
	xengnt_free_entry(entry);
}

int
xengnt_grant_transfer(domid_t dom, grant_ref_t *entryp)
{
	*entryp = xengnt_get_entry();
	if (__predict_false(*entryp == XENGNT_NO_ENTRY))
		return ENOMEM;

	grant_table[*entryp].frame = 0;
	grant_table[*entryp].domid  =dom;
	x86_lfence();
	grant_table[*entryp].flags = GTF_accept_transfer;
	return 0;
}

paddr_t
xengnt_revoke_transfer(grant_ref_t entry)
{
	paddr_t page;
	uint16_t flags;

	/* if the transfer has not started, free the entry and return 0 */
	while (!((flags = grant_table[entry].flags) & GTF_transfer_committed)) {
		if (xen_atomic_cmpxchg16(&grant_table[entry].flags,
		    flags, 0) == flags ) {
			xengnt_free_entry(entry);
			return 0;
		}
		HYPERVISOR_yield();
	}

	/* If transfer in progress, wait for completion */
	while (!((flags = grant_table[entry].flags) & GTF_transfer_completed))
		HYPERVISOR_yield();

	/* Read the frame number /after/ reading completion status. */
	__insn_barrier();
	page = grant_table[entry].frame;
	if (page == 0)
		printf("xengnt_revoke_transfer: guest sent pa 0\n");

	xengnt_free_entry(entry);
	return page;
}

int
xengnt_status(grant_ref_t entry)
{
	return (grant_table[entry].flags & (GTF_reading|GTF_writing));
}