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