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