diff options
author | mrj <none@none> | 2007-12-21 14:13:23 -0800 |
---|---|---|
committer | mrj <none@none> | 2007-12-21 14:13:23 -0800 |
commit | 551bc2a66868b5cb5be6b70ab9f55515e77a39a9 (patch) | |
tree | a01e761c9864ea9483c468ced858a0f67edcbf93 /usr/src/uts/common/xen/os/gnttab.c | |
parent | 71a79fe7afa36dcf0de6902c2c6ef432980534d3 (diff) | |
download | illumos-joyent-551bc2a66868b5cb5be6b70ab9f55515e77a39a9.tar.gz |
PSARC 2007/664 Paravirtualized Drivers for Fully Virtualized xVM Domains
6525093 xnb/xnf should use hypervisor based copy for xnb->xnf data path
6608917 members of struct xnf and xnb need unique names
6609324 deadlock trying to own the HAT migrate lock
6609805 still missing XPV_DISALLOW_MIGRATE/XPV_ALLOW_MIGRATE bracketing in hat_i86.c
6616384 xnb's grant ref unmapping is inefficient
6619947 Solaris should provide a PV network driver for xVM HVM environments
6632774 panic setting up xen console
--HG--
rename : usr/src/uts/i86xpv/os/gnttab.c => usr/src/uts/common/xen/os/gnttab.c
rename : usr/src/uts/i86xpv/os/hypercall.c => usr/src/uts/common/xen/os/hypercall.c
rename : usr/src/uts/i86xpv/sys/gnttab.h => usr/src/uts/common/xen/sys/gnttab.h
rename : usr/src/uts/i86xpv/ml/hypersubr.s => usr/src/uts/intel/ia32/ml/hypersubr.s
rename : usr/src/uts/i86xpv/sys/hypervisor.h => usr/src/uts/intel/sys/hypervisor.h
rename : usr/src/uts/i86xpv/sys/xen_errno.h => usr/src/uts/intel/sys/xen_errno.h
Diffstat (limited to 'usr/src/uts/common/xen/os/gnttab.c')
-rw-r--r-- | usr/src/uts/common/xen/os/gnttab.c | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/usr/src/uts/common/xen/os/gnttab.c b/usr/src/uts/common/xen/os/gnttab.c new file mode 100644 index 0000000000..238c45768e --- /dev/null +++ b/usr/src/uts/common/xen/os/gnttab.c @@ -0,0 +1,561 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * gnttab.c + * + * Granting foreign access to our memory reservation. + * + * Copyright (c) 2005, Christopher Clark + * Copyright (c) 2004-2005, K A Fraser + * + * This file may be distributed separately from the Linux kernel, or + * incorporated into other software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/archsystm.h> +#ifdef XPV_HVM_DRIVER +#include <sys/xpv_support.h> +#include <sys/mman.h> +#include <vm/hat.h> +#endif +#include <sys/hypervisor.h> +#include <sys/gnttab.h> +#include <sys/sysmacros.h> +#include <sys/machsystm.h> +#include <sys/systm.h> +#include <sys/mutex.h> +#include <sys/atomic.h> +#include <sys/spl.h> +#include <sys/condvar.h> +#include <sys/cpuvar.h> +#include <sys/taskq.h> +#include <sys/panic.h> +#include <sys/cmn_err.h> +#include <sys/promif.h> +#include <sys/cpu.h> +#include <sys/vmem.h> +#include <vm/hat_i86.h> +#include <sys/bootconf.h> +#include <sys/bootsvcs.h> +#ifndef XPV_HVM_DRIVER +#include <sys/bootinfo.h> +#include <sys/multiboot.h> +#include <vm/kboot_mmu.h> +#endif +#include <sys/bootvfs.h> +#include <sys/bootprops.h> +#include <vm/seg_kmem.h> + +#define cmpxchg(t, c, n) atomic_cas_16((t), (c), (n)) + +/* External tools reserve first few grant table entries. */ +#define NR_RESERVED_ENTRIES 8 + +#define NR_GRANT_ENTRIES (NR_GRANT_FRAMES * \ + MMU_PAGESIZE / sizeof (grant_entry_t)) +#define GNTTAB_LIST_END (NR_GRANT_ENTRIES + 1) +#define VALID_GRANT_REF(r) ((r) < NR_GRANT_ENTRIES) + +static grant_ref_t gnttab_list[NR_GRANT_ENTRIES]; +static int gnttab_free_count; +static grant_ref_t gnttab_free_head; +static kmutex_t gnttab_list_lock; + +static grant_entry_t *shared; +#define GT_PGADDR(i) ((uintptr_t)shared + ((i) << PAGESHIFT)) + +static struct gnttab_free_callback *gnttab_free_callback_list = NULL; + +static int +get_free_entries(int count) +{ + int ref; + grant_ref_t head; + + mutex_enter(&gnttab_list_lock); + if (gnttab_free_count < count) { + mutex_exit(&gnttab_list_lock); + return (-1); + } + ref = head = gnttab_free_head; + gnttab_free_count -= count; + while (count-- > 1) + head = gnttab_list[head]; + gnttab_free_head = gnttab_list[head]; + gnttab_list[head] = GNTTAB_LIST_END; + mutex_exit(&gnttab_list_lock); + return (ref); +} + +#define get_free_entry() get_free_entries(1) + +static void +do_free_callbacks(void) +{ + struct gnttab_free_callback *callback, *next; + + callback = gnttab_free_callback_list; + gnttab_free_callback_list = NULL; + + while (callback != NULL) { + next = callback->next; + if (gnttab_free_count >= callback->count) { + callback->next = NULL; + callback->fn(callback->arg); + } else { + callback->next = gnttab_free_callback_list; + gnttab_free_callback_list = callback; + } + callback = next; + } +} + +static void +check_free_callbacks(void) +{ + if (gnttab_free_callback_list) + do_free_callbacks(); +} + +static void +put_free_entry(grant_ref_t ref) +{ + ASSERT(VALID_GRANT_REF(ref)); + + mutex_enter(&gnttab_list_lock); + gnttab_list[ref] = gnttab_free_head; + gnttab_free_head = ref; + gnttab_free_count++; + check_free_callbacks(); + mutex_exit(&gnttab_list_lock); +} + +/* + * Public grant-issuing interface functions + */ + +int +gnttab_grant_foreign_access(domid_t domid, gnttab_frame_t frame, int readonly) +{ + int ref; + + if ((ref = get_free_entry()) == -1) + return (-1); + + ASSERT(VALID_GRANT_REF(ref)); + + shared[ref].frame = frame; + shared[ref].domid = domid; + membar_producer(); + shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0); + + return (ref); +} + +void +gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid, + gnttab_frame_t frame, int readonly) +{ + ASSERT(VALID_GRANT_REF(ref)); + + shared[ref].frame = frame; + shared[ref].domid = domid; + membar_producer(); + shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0); +} + + +int +gnttab_query_foreign_access(grant_ref_t ref) +{ + uint16_t nflags; + + ASSERT(VALID_GRANT_REF(ref)); + + nflags = shared[ref].flags; + + return (nflags & (GTF_reading|GTF_writing)); +} + +/* ARGSUSED */ +int +gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly) +{ + uint16_t flags, nflags; + + ASSERT(VALID_GRANT_REF(ref)); + + nflags = shared[ref].flags; + do { + if ((flags = nflags) & (GTF_reading|GTF_writing)) { + cmn_err(CE_WARN, "g.e. still in use!"); + return (0); + } + } while ((nflags = cmpxchg(&shared[ref].flags, flags, 0)) != flags); + + return (1); +} + +void +gnttab_end_foreign_access(grant_ref_t ref, int readonly, gnttab_frame_t page) +{ + ASSERT(VALID_GRANT_REF(ref)); + + if (gnttab_end_foreign_access_ref(ref, readonly)) { + put_free_entry(ref); + /* + * XXPV - we don't support freeing a page here + */ + if (page != 0) { + cmn_err(CE_WARN, + "gnttab_end_foreign_access_ref: using unsupported free_page interface"); + /* free_page(page); */ + } + } else { + /* + * XXX This needs to be fixed so that the ref and page are + * placed on a list to be freed up later. + */ + cmn_err(CE_WARN, "leaking g.e. and page still in use!"); + } +} + +int +gnttab_grant_foreign_transfer(domid_t domid) +{ + int ref; + + if ((ref = get_free_entry()) == -1) + return (-1); + + ASSERT(VALID_GRANT_REF(ref)); + + shared[ref].frame = 0; + shared[ref].domid = domid; + membar_producer(); + shared[ref].flags = GTF_accept_transfer; + + return (ref); +} + +void +gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid) +{ + ASSERT(VALID_GRANT_REF(ref)); + + shared[ref].frame = 0; + shared[ref].domid = domid; + membar_producer(); + shared[ref].flags = GTF_accept_transfer; +} + +gnttab_frame_t +gnttab_end_foreign_transfer_ref(grant_ref_t ref) +{ + gnttab_frame_t frame; + uint16_t flags; + + ASSERT(VALID_GRANT_REF(ref)); + + /* + * If a transfer is not even yet started, try to reclaim the grant + * reference and return failure (== 0). + */ + while (!((flags = shared[ref].flags) & GTF_transfer_committed)) { + if (cmpxchg(&shared[ref].flags, flags, 0) == flags) + return (0); + (void) HYPERVISOR_yield(); + } + + /* If a transfer is in progress then wait until it is completed. */ + while (!(flags & GTF_transfer_completed)) { + flags = shared[ref].flags; + (void) HYPERVISOR_yield(); + } + + /* Read the frame number /after/ reading completion status. */ + membar_consumer(); + frame = shared[ref].frame; + ASSERT(frame != 0); + + return (frame); +} + +gnttab_frame_t +gnttab_end_foreign_transfer(grant_ref_t ref) +{ + gnttab_frame_t frame; + + ASSERT(VALID_GRANT_REF(ref)); + + frame = gnttab_end_foreign_transfer_ref(ref); + put_free_entry(ref); + return (frame); +} + +void +gnttab_free_grant_reference(grant_ref_t ref) +{ + ASSERT(VALID_GRANT_REF(ref)); + + put_free_entry(ref); +} + +void +gnttab_free_grant_references(grant_ref_t head) +{ + grant_ref_t ref; + int count = 1; + + if (head == GNTTAB_LIST_END) + return; + mutex_enter(&gnttab_list_lock); + ref = head; + while (gnttab_list[ref] != GNTTAB_LIST_END) { + ref = gnttab_list[ref]; + count++; + } + gnttab_list[ref] = gnttab_free_head; + gnttab_free_head = head; + gnttab_free_count += count; + check_free_callbacks(); + mutex_exit(&gnttab_list_lock); +} + +int +gnttab_alloc_grant_references(uint16_t count, grant_ref_t *head) +{ + int h = get_free_entries(count); + + if (h == -1) + return (-1); + + *head = h; + + return (0); +} + +int +gnttab_claim_grant_reference(grant_ref_t *private_head) +{ + grant_ref_t g = *private_head; + + if (g == GNTTAB_LIST_END) + return (-1); + *private_head = gnttab_list[g]; + return (g); +} + +void +gnttab_release_grant_reference(grant_ref_t *private_head, grant_ref_t release) +{ + ASSERT(VALID_GRANT_REF(release)); + + gnttab_list[release] = *private_head; + *private_head = release; +} + +void +gnttab_request_free_callback(struct gnttab_free_callback *callback, + void (*fn)(void *), void *arg, uint16_t count) +{ + mutex_enter(&gnttab_list_lock); + if (callback->next) + goto out; + callback->fn = fn; + callback->arg = arg; + callback->count = count; + callback->next = gnttab_free_callback_list; + gnttab_free_callback_list = callback; + check_free_callbacks(); +out: + mutex_exit(&gnttab_list_lock); +} + +#ifdef XPV_HVM_DRIVER + +static void +gnttab_map(void) +{ + struct xen_add_to_physmap xatp; + caddr_t va; + pfn_t pfn; + int i; + + va = (caddr_t)shared; + for (i = 0; i < NR_GRANT_FRAMES; i++) { + pfn = hat_getpfnum(kas.a_hat, va); + + xatp.domid = DOMID_SELF; + xatp.idx = i; + xatp.space = XENMAPSPACE_grant_table; + xatp.gpfn = pfn; + hat_unload(kas.a_hat, va, MMU_PAGESIZE, HAT_UNLOAD); + if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp) != 0) + panic("Couldn't map grant table"); + + hat_devload(kas.a_hat, va, MMU_PAGESIZE, pfn, + PROT_READ | PROT_WRITE, + HAT_LOAD | HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST); + + va += MMU_PAGESIZE; + } +} + +void +gnttab_init(void) +{ + int i; + + shared = (grant_entry_t *)xen_alloc_pages(NR_GRANT_FRAMES); + + gnttab_map(); + + for (i = NR_RESERVED_ENTRIES; i < NR_GRANT_ENTRIES; i++) + gnttab_list[i] = i + 1; + gnttab_free_count = NR_GRANT_ENTRIES - NR_RESERVED_ENTRIES; + gnttab_free_head = NR_RESERVED_ENTRIES; + + mutex_init(&gnttab_list_lock, NULL, MUTEX_DEFAULT, NULL); +} + +void +gnttab_resume(void) +{ + gnttab_map(); +} + +#else /* XPV_HVM_DRIVER */ + +void +gnttab_init(void) +{ + gnttab_setup_table_t set; + gnttab_frame_t frames[NR_GRANT_FRAMES]; + int i; + + set.dom = DOMID_SELF; + set.nr_frames = NR_GRANT_FRAMES; + /*LINTED: constant in conditional context*/ + set_xen_guest_handle(set.frame_list, frames); + + /* + * Take 4 pages of grant table space from the hypervisor and map it + */ + if ((HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &set, 1) != 0) || + (set.status != 0)) { + cmn_err(CE_PANIC, "Grant Table setup failed"); + } + + shared = vmem_xalloc(heap_arena, NR_GRANT_FRAMES * MMU_PAGESIZE, + MMU_PAGESIZE, 0, 0, 0, 0, VM_SLEEP); + + for (i = 0; i < NR_GRANT_FRAMES; i++) + kbm_map_ma(FRAME_TO_MA(frames[i]), GT_PGADDR(i), 0); + + for (i = NR_RESERVED_ENTRIES; i < NR_GRANT_ENTRIES; i++) + gnttab_list[i] = i + 1; + gnttab_free_count = NR_GRANT_ENTRIES - NR_RESERVED_ENTRIES; + gnttab_free_head = NR_RESERVED_ENTRIES; + + mutex_init(&gnttab_list_lock, NULL, MUTEX_DEFAULT, NULL); +} + +void +gnttab_resume(void) +{ + gnttab_setup_table_t set; + gnttab_frame_t frames[NR_GRANT_FRAMES]; + int i; + + set.dom = DOMID_SELF; + set.nr_frames = NR_GRANT_FRAMES; + /*LINTED: constant in conditional context*/ + set_xen_guest_handle(set.frame_list, frames); + + /* + * Take NR_GRANT_FRAMES pages of grant table space from the + * hypervisor and map it + */ + if ((HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &set, 1) != 0) || + (set.status != 0)) { + cmn_err(CE_PANIC, "Grant Table setup failed"); + } + + for (i = 0; i < NR_GRANT_FRAMES; i++) { + (void) HYPERVISOR_update_va_mapping(GT_PGADDR(i), + FRAME_TO_MA(frames[i]) | PT_VALID | PT_WRITABLE, + UVMF_INVLPG | UVMF_ALL); + } +} + +#endif /* XPV_HVM_DRIVER */ + +void +gnttab_suspend(void) +{ + int i; + + /* + * clear grant table mappings before suspending + */ + for (i = 0; i < NR_GRANT_FRAMES; i++) { + (void) HYPERVISOR_update_va_mapping(GT_PGADDR(i), + 0, UVMF_INVLPG); + } +} + +/* + * Local variables: + * c-file-style: "solaris" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ |