1/* $NetBSD: xen_shm_machdep.c,v 1.9 2011/07/31 18:00:54 jym Exp $ */ 2 3/* 4 * Copyright (c) 2006 Manuel Bouyer. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 */ 27 28#include <sys/cdefs.h> 29__KERNEL_RCSID(0, "$NetBSD: xen_shm_machdep.c,v 1.9 2011/07/31 18:00:54 jym Exp $"); 30 31 32#include <sys/types.h> 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/queue.h> 36#include <sys/vmem.h> 37#include <sys/kernel.h> 38#include <uvm/uvm.h> 39 40#include <machine/pmap.h> 41#include <xen/hypervisor.h> 42#include <xen/xen.h> 43#include <xen/evtchn.h> 44#include <xen/xen_shm.h> 45 46/* 47 * Helper routines for the backend drivers. This implement the necessary 48 * functions to map a bunch of pages from foreign domains in our kernel VM 49 * space, do I/O to it, and unmap it. 50 * 51 * At boot time, we grab some kernel VM space that we'll use to map the foreign 52 * pages. We also maintain a virtual to machine mapping table to give back 53 * the appropriate address to bus_dma if requested. 54 * If no more VM space is available, we return an error. The caller can then 55 * register a callback which will be called when the required VM space is 56 * available. 57 */ 58 59/* pointers to our VM space */ 60static vaddr_t xen_shm_base_address; 61static u_long xen_shm_base_address_pg; 62static vaddr_t xen_shm_end_address; 63 64/* Grab enough VM space to map an entire vbd ring. */ 65/* Xen3 linux guests seems to eat more pages, gives enough for 10 vbd rings */ 66#define BLKIF_RING_SIZE __RING_SIZE((blkif_sring_t *)0, PAGE_SIZE) 67#define XENSHM_NPAGES (BLKIF_RING_SIZE * (BLKIF_MAX_SEGMENTS_PER_REQUEST + 1) * 10) 68 69static vsize_t xen_shm_size = (XENSHM_NPAGES * PAGE_SIZE); 70 71/* vm space management */ 72static vmem_t *xen_shm_arena; 73 74/* callbacks are registered in a FIFO list. */ 75 76static SIMPLEQ_HEAD(xen_shm_callback_head, xen_shm_callback_entry) 77 xen_shm_callbacks; 78struct xen_shm_callback_entry { 79 SIMPLEQ_ENTRY(xen_shm_callback_entry) xshmc_entries; 80 int (*xshmc_callback)(void *); /* our callback */ 81 void *xshmc_arg; /* cookie passed to the callback */ 82}; 83/* a pool of struct xen_shm_callback_entry */ 84static struct pool xen_shm_callback_pool; 85 86#ifdef DEBUG 87/* for ratecheck(9) */ 88static struct timeval xen_shm_errintvl = { 60, 0 }; /* a minute, each */ 89#endif 90 91void 92xen_shm_init(void) 93{ 94 SIMPLEQ_INIT(&xen_shm_callbacks); 95 pool_init(&xen_shm_callback_pool, sizeof(struct xen_shm_callback_entry), 96 0, 0, 0, "xshmc", NULL, IPL_VM); 97 /* ensure we'll always get items */ 98 if (pool_prime(&xen_shm_callback_pool, 99 PAGE_SIZE / sizeof(struct xen_shm_callback_entry)) != 0) { 100 panic("xen_shm_init can't prime pool"); 101 } 102 103 xen_shm_base_address = uvm_km_alloc(kernel_map, xen_shm_size, 0, 104 UVM_KMF_VAONLY); 105 xen_shm_end_address = xen_shm_base_address + xen_shm_size; 106 xen_shm_base_address_pg = xen_shm_base_address >> PAGE_SHIFT; 107 if (xen_shm_base_address == 0) { 108 panic("xen_shm_init no VM space"); 109 } 110 xen_shm_arena = vmem_create("xen_shm", 111 xen_shm_base_address_pg, 112 (xen_shm_end_address >> PAGE_SHIFT) - 1 - xen_shm_base_address_pg, 113 1, NULL, NULL, NULL, 1, VM_NOSLEEP, IPL_VM); 114 if (xen_shm_arena == NULL) { 115 panic("xen_shm_init no arena"); 116 } 117} 118 119int 120xen_shm_map(int nentries, int domid, grant_ref_t *grefp, vaddr_t *vap, 121 grant_handle_t *handlep, int flags) 122{ 123 int s, i; 124 vaddr_t new_va; 125 vmem_addr_t new_va_pg; 126 int err; 127 gnttab_map_grant_ref_t op[XENSHM_MAX_PAGES_PER_REQUEST]; 128 129#ifdef DIAGNOSTIC 130 if (nentries > XENSHM_MAX_PAGES_PER_REQUEST) { 131 printf("xen_shm_map: %d entries\n", nentries); 132 panic("xen_shm_map"); 133 } 134#endif 135 s = splvm(); /* splvm is the lowest level blocking disk and net IRQ */ 136 /* 137 * if a driver is waiting for ressources, don't try to allocate 138 * yet. This is to avoid a flood of small requests stalling large 139 * ones. 140 */ 141 if (__predict_false(SIMPLEQ_FIRST(&xen_shm_callbacks) != NULL) && 142 (flags & XSHM_CALLBACK) == 0) { 143#ifdef DEBUG 144 static struct timeval lasttime; 145#endif 146 splx(s); 147#ifdef DEBUG 148 if (ratecheck(&lasttime, &xen_shm_errintvl)) 149 printf("xen_shm_map: ENOMEM1\n"); 150#endif 151 return ENOMEM; 152 } 153 /* allocate the needed virtual space */ 154 if (vmem_alloc(xen_shm_arena, nentries, 155 VM_INSTANTFIT | VM_NOSLEEP, &new_va_pg) != 0) { 156#ifdef DEBUG 157 static struct timeval lasttime; 158#endif 159 splx(s); 160#ifdef DEBUG 161 if (ratecheck(&lasttime, &xen_shm_errintvl)) 162 printf("xen_shm_map: ENOMEM\n"); 163#endif 164 return ENOMEM; 165 } 166 splx(s); 167 168 new_va = new_va_pg << PAGE_SHIFT; 169 for (i = 0; i < nentries; i++) { 170 op[i].host_addr = new_va + i * PAGE_SIZE; 171 op[i].dom = domid; 172 op[i].ref = grefp[i]; 173 op[i].flags = GNTMAP_host_map | 174 ((flags & XSHM_RO) ? GNTMAP_readonly : 0); 175 } 176 err = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, op, nentries); 177 if (__predict_false(err)) 178 panic("xen_shm_map: HYPERVISOR_grant_table_op failed"); 179 for (i = 0; i < nentries; i++) { 180 if (__predict_false(op[i].status)) 181 return op[i].status; 182 handlep[i] = op[i].handle; 183 } 184 *vap = new_va; 185 return 0; 186} 187 188void 189xen_shm_unmap(vaddr_t va, int nentries, grant_handle_t *handlep) 190{ 191 gnttab_unmap_grant_ref_t op[XENSHM_MAX_PAGES_PER_REQUEST]; 192 int ret; 193 int i; 194 int s; 195 struct xen_shm_callback_entry *xshmc; 196 197#ifdef DIAGNOSTIC 198 if (nentries > XENSHM_MAX_PAGES_PER_REQUEST) { 199 printf("xen_shm_unmap: %d entries\n", nentries); 200 panic("xen_shm_unmap"); 201 } 202#endif 203 204 for (i = 0; i < nentries; i++) { 205 op[i].host_addr = va + i * PAGE_SIZE; 206 op[i].dev_bus_addr = 0; 207 op[i].handle = handlep[i]; 208 } 209 ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, 210 op, nentries); 211 if (__predict_false(ret)) 212 panic("xen_shm_unmap: unmap failed"); 213 va = va >> PAGE_SHIFT; 214 s = splvm(); /* splvm is the lowest level blocking disk and net IRQ */ 215 vmem_free(xen_shm_arena, va, nentries); 216 while (__predict_false((xshmc = SIMPLEQ_FIRST(&xen_shm_callbacks)) 217 != NULL)) { 218 SIMPLEQ_REMOVE_HEAD(&xen_shm_callbacks, xshmc_entries); 219 splx(s); 220 if (xshmc->xshmc_callback(xshmc->xshmc_arg) == 0) { 221 /* callback succeeded */ 222 s = splvm(); 223 pool_put(&xen_shm_callback_pool, xshmc); 224 } else { 225 /* callback failed, probably out of ressources */ 226 s = splvm(); 227 SIMPLEQ_INSERT_TAIL(&xen_shm_callbacks, xshmc, 228 xshmc_entries); 229 230 break; 231 } 232 } 233 splx(s); 234} 235 236int 237xen_shm_callback(int (*callback)(void *), void *arg) 238{ 239 struct xen_shm_callback_entry *xshmc; 240 int s; 241 242 s = splvm(); 243 xshmc = pool_get(&xen_shm_callback_pool, PR_NOWAIT); 244 if (xshmc == NULL) { 245 splx(s); 246 return ENOMEM; 247 } 248 xshmc->xshmc_arg = arg; 249 xshmc->xshmc_callback = callback; 250 SIMPLEQ_INSERT_TAIL(&xen_shm_callbacks, xshmc, xshmc_entries); 251 splx(s); 252 return 0; 253} 254