1 /* $NetBSD: usb_mem.c,v 1.64 2013/12/22 18:29:25 mlelstv Exp $ */ 2 3 /* 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Lennart Augustsson (lennart@augustsson.net) at 9 * Carlstedt Research & Technology. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * USB DMA memory allocation. 35 * We need to allocate a lot of small (many 8 byte, some larger) 36 * memory blocks that can be used for DMA. Using the bus_dma 37 * routines directly would incur large overheads in space and time. 38 */ 39 40 #include <sys/cdefs.h> 41 __KERNEL_RCSID(0, "$NetBSD: usb_mem.c,v 1.64 2013/12/22 18:29:25 mlelstv Exp $"); 42 43 #include <sys/param.h> 44 #include <sys/systm.h> 45 #include <sys/kernel.h> 46 #include <sys/kmem.h> 47 #include <sys/queue.h> 48 #include <sys/device.h> /* for usbdivar.h */ 49 #include <sys/bus.h> 50 #include <sys/cpu.h> 51 #include <sys/once.h> 52 53 #include <sys/extent.h> 54 55 #ifdef DIAGNOSTIC 56 #include <sys/proc.h> 57 #endif 58 59 #include <dev/usb/usb.h> 60 #include <dev/usb/usbdi.h> 61 #include <dev/usb/usbdivar.h> /* just for usb_dma_t */ 62 #include <dev/usb/usb_mem.h> 63 64 #ifdef USB_DEBUG 65 #define DPRINTF(x) if (usbdebug) printf x 66 #define DPRINTFN(n,x) if (usbdebug>(n)) printf x 67 extern int usbdebug; 68 #else 69 #define DPRINTF(x) 70 #define DPRINTFN(n,x) 71 #endif 72 73 #define USB_MEM_SMALL roundup(64, CACHE_LINE_SIZE) 74 #define USB_MEM_CHUNKS 64 75 #define USB_MEM_BLOCK (USB_MEM_SMALL * USB_MEM_CHUNKS) 76 77 /* This struct is overlayed on free fragments. */ 78 struct usb_frag_dma { 79 usb_dma_block_t *block; 80 u_int offs; 81 LIST_ENTRY(usb_frag_dma) next; 82 }; 83 84 Static usbd_status usb_block_allocmem(bus_dma_tag_t, size_t, size_t, 85 usb_dma_block_t **, bool); 86 Static void usb_block_freemem(usb_dma_block_t *); 87 88 LIST_HEAD(usb_dma_block_qh, usb_dma_block); 89 Static struct usb_dma_block_qh usb_blk_freelist = 90 LIST_HEAD_INITIALIZER(usb_blk_freelist); 91 kmutex_t usb_blk_lock; 92 93 #ifdef DEBUG 94 Static struct usb_dma_block_qh usb_blk_fraglist = 95 LIST_HEAD_INITIALIZER(usb_blk_fraglist); 96 Static struct usb_dma_block_qh usb_blk_fulllist = 97 LIST_HEAD_INITIALIZER(usb_blk_fulllist); 98 #endif 99 Static u_int usb_blk_nfree = 0; 100 /* XXX should have different free list for different tags (for speed) */ 101 Static LIST_HEAD(, usb_frag_dma) usb_frag_freelist = 102 LIST_HEAD_INITIALIZER(usb_frag_freelist); 103 104 Static int usb_mem_init(void); 105 106 Static int 107 usb_mem_init(void) 108 { 109 110 mutex_init(&usb_blk_lock, MUTEX_DEFAULT, IPL_NONE); 111 return 0; 112 } 113 114 Static usbd_status 115 usb_block_allocmem(bus_dma_tag_t tag, size_t size, size_t align, 116 usb_dma_block_t **dmap, bool multiseg) 117 { 118 usb_dma_block_t *b; 119 int error; 120 121 DPRINTFN(5, ("usb_block_allocmem: size=%zu align=%zu\n", size, align)); 122 123 if (size == 0) { 124 #ifdef DIAGNOSTIC 125 printf("usb_block_allocmem: called with size==0\n"); 126 #endif 127 return USBD_INVAL; 128 } 129 130 #ifdef DIAGNOSTIC 131 if (cpu_softintr_p() || cpu_intr_p()) { 132 printf("usb_block_allocmem: in interrupt context, size=%lu\n", 133 (unsigned long) size); 134 } 135 #endif 136 137 KASSERT(mutex_owned(&usb_blk_lock)); 138 139 /* First check the free list. */ 140 LIST_FOREACH(b, &usb_blk_freelist, next) { 141 /* Don't allocate multiple segments to unwilling callers */ 142 if (b->nsegs != 1 && !multiseg) 143 continue; 144 if (b->tag == tag && b->size >= size && b->align >= align) { 145 LIST_REMOVE(b, next); 146 usb_blk_nfree--; 147 *dmap = b; 148 DPRINTFN(6,("usb_block_allocmem: free list size=%zu\n", 149 b->size)); 150 return (USBD_NORMAL_COMPLETION); 151 } 152 } 153 154 #ifdef DIAGNOSTIC 155 if (cpu_softintr_p() || cpu_intr_p()) { 156 printf("usb_block_allocmem: in interrupt context, failed\n"); 157 return (USBD_NOMEM); 158 } 159 #endif 160 161 DPRINTFN(6, ("usb_block_allocmem: no free\n")); 162 b = kmem_zalloc(sizeof *b, KM_SLEEP); 163 if (b == NULL) 164 return (USBD_NOMEM); 165 166 b->tag = tag; 167 b->size = size; 168 b->align = align; 169 170 if (!multiseg) 171 /* Caller wants one segment */ 172 b->nsegs = 1; 173 else 174 b->nsegs = (size + (PAGE_SIZE-1)) / PAGE_SIZE; 175 176 b->segs = kmem_alloc(b->nsegs * sizeof(*b->segs), KM_SLEEP); 177 if (b->segs == NULL) { 178 kmem_free(b, sizeof *b); 179 return USBD_NOMEM; 180 } 181 b->nsegs_alloc = b->nsegs; 182 183 error = bus_dmamem_alloc(tag, b->size, align, 0, 184 b->segs, b->nsegs, 185 &b->nsegs, BUS_DMA_NOWAIT); 186 if (error) 187 goto free0; 188 189 error = bus_dmamem_map(tag, b->segs, b->nsegs, b->size, 190 &b->kaddr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT); 191 if (error) 192 goto free1; 193 194 error = bus_dmamap_create(tag, b->size, b->nsegs, b->size, 195 0, BUS_DMA_NOWAIT, &b->map); 196 if (error) 197 goto unmap; 198 199 error = bus_dmamap_load(tag, b->map, b->kaddr, b->size, NULL, 200 BUS_DMA_NOWAIT); 201 if (error) 202 goto destroy; 203 204 *dmap = b; 205 #ifdef USB_FRAG_DMA_WORKAROUND 206 memset(b->kaddr, 0, b->size); 207 #endif 208 209 return (USBD_NORMAL_COMPLETION); 210 211 destroy: 212 bus_dmamap_destroy(tag, b->map); 213 unmap: 214 bus_dmamem_unmap(tag, b->kaddr, b->size); 215 free1: 216 bus_dmamem_free(tag, b->segs, b->nsegs); 217 free0: 218 kmem_free(b->segs, b->nsegs_alloc * sizeof(*b->segs)); 219 kmem_free(b, sizeof *b); 220 return (USBD_NOMEM); 221 } 222 223 #if 0 224 void 225 usb_block_real_freemem(usb_dma_block_t *b) 226 { 227 #ifdef DIAGNOSTIC 228 if (cpu_softintr_p() || cpu_intr_p()) { 229 printf("usb_block_real_freemem: in interrupt context\n"); 230 return; 231 } 232 #endif 233 bus_dmamap_unload(b->tag, b->map); 234 bus_dmamap_destroy(b->tag, b->map); 235 bus_dmamem_unmap(b->tag, b->kaddr, b->size); 236 bus_dmamem_free(b->tag, b->segs, b->nsegs); 237 kmem_free(b->segs, b->nsegs_alloc * sizeof(*b->segs)); 238 kmem_free(b, sizeof *b); 239 } 240 #endif 241 242 #ifdef DEBUG 243 static bool 244 usb_valid_block_p(usb_dma_block_t *b, struct usb_dma_block_qh *qh) 245 { 246 usb_dma_block_t *xb; 247 LIST_FOREACH(xb, qh, next) { 248 if (xb == b) 249 return true; 250 } 251 return false; 252 } 253 #endif 254 255 /* 256 * Do not free the memory unconditionally since we might be called 257 * from an interrupt context and that is BAD. 258 * XXX when should we really free? 259 */ 260 Static void 261 usb_block_freemem(usb_dma_block_t *b) 262 { 263 264 KASSERT(mutex_owned(&usb_blk_lock)); 265 266 DPRINTFN(6, ("usb_block_freemem: size=%zu\n", b->size)); 267 #ifdef DEBUG 268 LIST_REMOVE(b, next); 269 #endif 270 LIST_INSERT_HEAD(&usb_blk_freelist, b, next); 271 usb_blk_nfree++; 272 } 273 274 usbd_status 275 usb_allocmem(usbd_bus_handle bus, size_t size, size_t align, usb_dma_t *p) 276 { 277 return usb_allocmem_flags(bus, size, align, p, 0); 278 } 279 280 usbd_status 281 usb_allocmem_flags(usbd_bus_handle bus, size_t size, size_t align, usb_dma_t *p, 282 int flags) 283 { 284 bus_dma_tag_t tag = bus->dmatag; 285 usbd_status err; 286 struct usb_frag_dma *f; 287 usb_dma_block_t *b; 288 int i; 289 static ONCE_DECL(init_control); 290 bool frag; 291 292 RUN_ONCE(&init_control, usb_mem_init); 293 294 frag = (flags & USBMALLOC_MULTISEG); 295 296 /* If the request is large then just use a full block. */ 297 if (size > USB_MEM_SMALL || align > USB_MEM_SMALL) { 298 DPRINTFN(1, ("usb_allocmem: large alloc %d\n", (int)size)); 299 size = (size + USB_MEM_BLOCK - 1) & ~(USB_MEM_BLOCK - 1); 300 mutex_enter(&usb_blk_lock); 301 err = usb_block_allocmem(tag, size, align, &p->block, frag); 302 if (!err) { 303 #ifdef DEBUG 304 LIST_INSERT_HEAD(&usb_blk_fulllist, p->block, next); 305 #endif 306 p->block->flags = USB_DMA_FULLBLOCK; 307 p->offs = 0; 308 } 309 mutex_exit(&usb_blk_lock); 310 return (err); 311 } 312 313 mutex_enter(&usb_blk_lock); 314 /* Check for free fragments. */ 315 LIST_FOREACH(f, &usb_frag_freelist, next) { 316 KDASSERTMSG(usb_valid_block_p(f->block, &usb_blk_fraglist), 317 "%s: usb frag %p: unknown block pointer %p", 318 __func__, f, f->block); 319 if (f->block->tag == tag) 320 break; 321 } 322 if (f == NULL) { 323 DPRINTFN(1, ("usb_allocmem: adding fragments\n")); 324 err = usb_block_allocmem(tag, USB_MEM_BLOCK, USB_MEM_SMALL, &b, 325 false); 326 if (err) { 327 mutex_exit(&usb_blk_lock); 328 return (err); 329 } 330 #ifdef DEBUG 331 LIST_INSERT_HEAD(&usb_blk_fraglist, b, next); 332 #endif 333 b->flags = 0; 334 for (i = 0; i < USB_MEM_BLOCK; i += USB_MEM_SMALL) { 335 f = (struct usb_frag_dma *)((char *)b->kaddr + i); 336 f->block = b; 337 f->offs = i; 338 LIST_INSERT_HEAD(&usb_frag_freelist, f, next); 339 #ifdef USB_FRAG_DMA_WORKAROUND 340 i += 1 * USB_MEM_SMALL; 341 #endif 342 } 343 f = LIST_FIRST(&usb_frag_freelist); 344 } 345 p->block = f->block; 346 p->offs = f->offs; 347 #ifdef USB_FRAG_DMA_WORKAROUND 348 p->offs += USB_MEM_SMALL; 349 #endif 350 p->block->flags &= ~USB_DMA_RESERVE; 351 LIST_REMOVE(f, next); 352 mutex_exit(&usb_blk_lock); 353 DPRINTFN(5, ("usb_allocmem: use frag=%p size=%d\n", f, (int)size)); 354 355 return (USBD_NORMAL_COMPLETION); 356 } 357 358 void 359 usb_freemem(usbd_bus_handle bus, usb_dma_t *p) 360 { 361 struct usb_frag_dma *f; 362 363 mutex_enter(&usb_blk_lock); 364 if (p->block->flags & USB_DMA_FULLBLOCK) { 365 KDASSERTMSG(usb_valid_block_p(p->block, &usb_blk_fulllist), 366 "%s: dma %p: invalid block pointer %p", 367 __func__, p, p->block); 368 DPRINTFN(1, ("usb_freemem: large free\n")); 369 usb_block_freemem(p->block); 370 mutex_exit(&usb_blk_lock); 371 return; 372 } 373 KDASSERTMSG(usb_valid_block_p(p->block, &usb_blk_fraglist), 374 "%s: dma %p: invalid block pointer %p", 375 __func__, p, p->block); 376 //usb_syncmem(p, 0, USB_MEM_SMALL, BUS_DMASYNC_POSTREAD); 377 f = KERNADDR(p, 0); 378 #ifdef USB_FRAG_DMA_WORKAROUND 379 f = (void *)((uintptr_t)f - USB_MEM_SMALL); 380 #endif 381 f->block = p->block; 382 f->offs = p->offs; 383 #ifdef USB_FRAG_DMA_WORKAROUND 384 f->offs -= USB_MEM_SMALL; 385 #endif 386 LIST_INSERT_HEAD(&usb_frag_freelist, f, next); 387 mutex_exit(&usb_blk_lock); 388 DPRINTFN(5, ("usb_freemem: frag=%p\n", f)); 389 } 390 391 bus_addr_t 392 usb_dmaaddr(usb_dma_t *dma, unsigned int offset) 393 { 394 unsigned int i; 395 bus_size_t seg_offs; 396 397 offset += dma->offs; 398 399 KASSERT(offset < dma->block->size); 400 401 if (dma->block->nsegs == 1) { 402 KASSERT(dma->block->map->dm_segs[0].ds_len > offset); 403 return dma->block->map->dm_segs[0].ds_addr + offset; 404 } 405 406 /* Search for a bus_segment_t corresponding to this offset. With no 407 * record of the offset in the map to a particular dma_segment_t, we 408 * have to iterate from the start of the list each time. Could be 409 * improved */ 410 seg_offs = 0; 411 for (i = 0; i < dma->block->nsegs; i++) { 412 if (seg_offs + dma->block->map->dm_segs[i].ds_len > offset) 413 break; 414 415 seg_offs += dma->block->map->dm_segs[i].ds_len; 416 } 417 418 KASSERT(i != dma->block->nsegs); 419 offset -= seg_offs; 420 return dma->block->map->dm_segs[i].ds_addr + offset; 421 } 422 423 void 424 usb_syncmem(usb_dma_t *p, bus_addr_t offset, bus_size_t len, int ops) 425 { 426 bus_dmamap_sync(p->block->tag, p->block->map, p->offs + offset, 427 len, ops); 428 } 429 430 431 usbd_status 432 usb_reserve_allocm(struct usb_dma_reserve *rs, usb_dma_t *dma, u_int32_t size) 433 { 434 int error; 435 u_long start; 436 bus_addr_t baddr; 437 438 if (rs->vaddr == 0 || size > USB_MEM_RESERVE) 439 return USBD_NOMEM; 440 441 dma->block = kmem_zalloc(sizeof *dma->block, KM_SLEEP); 442 if (dma->block == NULL) { 443 aprint_error_dev(rs->dv, "%s: failed allocating dma block", 444 __func__); 445 goto out0; 446 } 447 448 dma->block->nsegs = 1; 449 dma->block->segs = kmem_alloc(dma->block->nsegs * 450 sizeof(*dma->block->segs), KM_SLEEP); 451 if (dma->block->segs == NULL) { 452 aprint_error_dev(rs->dv, "%s: failed allocating 1 dma segment", 453 __func__); 454 goto out1; 455 } 456 457 error = extent_alloc(rs->extent, size, PAGE_SIZE, 0, 458 EX_NOWAIT, &start); 459 460 if (error != 0) { 461 aprint_error_dev(rs->dv, "%s: extent_alloc size %u failed " 462 "(error %d)", __func__, size, error); 463 goto out2; 464 } 465 466 baddr = start; 467 dma->offs = baddr - rs->paddr; 468 dma->block->flags = USB_DMA_RESERVE; 469 dma->block->align = PAGE_SIZE; 470 dma->block->size = size; 471 dma->block->segs[0] = rs->map->dm_segs[0]; 472 dma->block->map = rs->map; 473 dma->block->kaddr = rs->vaddr; 474 dma->block->tag = rs->dtag; 475 476 return USBD_NORMAL_COMPLETION; 477 out2: 478 kmem_free(dma->block->segs, dma->block->nsegs * 479 sizeof(*dma->block->segs)); 480 out1: 481 kmem_free(dma->block, sizeof *dma->block); 482 out0: 483 return USBD_NOMEM; 484 } 485 486 void 487 usb_reserve_freem(struct usb_dma_reserve *rs, usb_dma_t *dma) 488 { 489 490 extent_free(rs->extent, 491 (u_long)(rs->paddr + dma->offs), dma->block->size, 0); 492 kmem_free(dma->block->segs, dma->block->nsegs * 493 sizeof(*dma->block->segs)); 494 kmem_free(dma->block, sizeof *dma->block); 495 } 496 497 int 498 usb_setup_reserve(device_t dv, struct usb_dma_reserve *rs, bus_dma_tag_t dtag, 499 size_t size) 500 { 501 int error, nseg; 502 bus_dma_segment_t seg; 503 504 rs->dtag = dtag; 505 rs->size = size; 506 rs->dv = dv; 507 508 error = bus_dmamem_alloc(dtag, USB_MEM_RESERVE, PAGE_SIZE, 0, 509 &seg, 1, &nseg, BUS_DMA_NOWAIT); 510 if (error != 0) 511 return error; 512 513 error = bus_dmamem_map(dtag, &seg, nseg, USB_MEM_RESERVE, 514 &rs->vaddr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT); 515 if (error != 0) 516 goto freeit; 517 518 error = bus_dmamap_create(dtag, USB_MEM_RESERVE, 1, 519 USB_MEM_RESERVE, 0, BUS_DMA_NOWAIT, &rs->map); 520 if (error != 0) 521 goto unmap; 522 523 error = bus_dmamap_load(dtag, rs->map, rs->vaddr, USB_MEM_RESERVE, 524 NULL, BUS_DMA_NOWAIT); 525 if (error != 0) 526 goto destroy; 527 528 rs->paddr = rs->map->dm_segs[0].ds_addr; 529 rs->extent = extent_create(device_xname(dv), (u_long)rs->paddr, 530 (u_long)(rs->paddr + USB_MEM_RESERVE - 1), 0, 0, 0); 531 if (rs->extent == NULL) { 532 rs->vaddr = 0; 533 return ENOMEM; 534 } 535 536 return 0; 537 538 destroy: 539 bus_dmamap_destroy(dtag, rs->map); 540 unmap: 541 bus_dmamem_unmap(dtag, rs->vaddr, size); 542 freeit: 543 bus_dmamem_free(dtag, &seg, nseg); 544 545 rs->vaddr = 0; 546 547 return error; 548 } 549