1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2021 Intel Corporation 4 */ 5 6 #include <linux/kmemleak.h> 7 #include <linux/module.h> 8 #include <linux/sizes.h> 9 10 #include <sys/pool.h> 11 12 #include <drm/drm_buddy.h> 13 14 static struct pool slab_blocks; 15 16 static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm, 17 struct drm_buddy_block *parent, 18 unsigned int order, 19 u64 offset) 20 { 21 struct drm_buddy_block *block; 22 23 BUG_ON(order > DRM_BUDDY_MAX_ORDER); 24 25 #ifdef __linux__ 26 block = kmem_cache_zalloc(slab_blocks, GFP_KERNEL); 27 #else 28 block = pool_get(&slab_blocks, PR_WAITOK | PR_ZERO); 29 #endif 30 if (!block) 31 return NULL; 32 33 block->header = offset; 34 block->header |= order; 35 block->parent = parent; 36 37 BUG_ON(block->header & DRM_BUDDY_HEADER_UNUSED); 38 return block; 39 } 40 41 static void drm_block_free(struct drm_buddy *mm, 42 struct drm_buddy_block *block) 43 { 44 #ifdef __linux__ 45 kmem_cache_free(slab_blocks, block); 46 #else 47 pool_put(&slab_blocks, block); 48 #endif 49 } 50 51 static void list_insert_sorted(struct drm_buddy *mm, 52 struct drm_buddy_block *block) 53 { 54 struct drm_buddy_block *node; 55 struct list_head *head; 56 57 head = &mm->free_list[drm_buddy_block_order(block)]; 58 if (list_empty(head)) { 59 list_add(&block->link, head); 60 return; 61 } 62 63 list_for_each_entry(node, head, link) 64 if (drm_buddy_block_offset(block) < drm_buddy_block_offset(node)) 65 break; 66 67 __list_add(&block->link, node->link.prev, &node->link); 68 } 69 70 static void mark_allocated(struct drm_buddy_block *block) 71 { 72 block->header &= ~DRM_BUDDY_HEADER_STATE; 73 block->header |= DRM_BUDDY_ALLOCATED; 74 75 list_del(&block->link); 76 } 77 78 static void mark_free(struct drm_buddy *mm, 79 struct drm_buddy_block *block) 80 { 81 block->header &= ~DRM_BUDDY_HEADER_STATE; 82 block->header |= DRM_BUDDY_FREE; 83 84 list_insert_sorted(mm, block); 85 } 86 87 static void mark_split(struct drm_buddy_block *block) 88 { 89 block->header &= ~DRM_BUDDY_HEADER_STATE; 90 block->header |= DRM_BUDDY_SPLIT; 91 92 list_del(&block->link); 93 } 94 95 /** 96 * drm_buddy_init - init memory manager 97 * 98 * @mm: DRM buddy manager to initialize 99 * @size: size in bytes to manage 100 * @chunk_size: minimum page size in bytes for our allocations 101 * 102 * Initializes the memory manager and its resources. 103 * 104 * Returns: 105 * 0 on success, error code on failure. 106 */ 107 int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size) 108 { 109 unsigned int i; 110 u64 offset; 111 112 if (size < chunk_size) 113 return -EINVAL; 114 115 if (chunk_size < PAGE_SIZE) 116 return -EINVAL; 117 118 if (!is_power_of_2(chunk_size)) 119 return -EINVAL; 120 121 size = round_down(size, chunk_size); 122 123 mm->size = size; 124 mm->avail = size; 125 mm->chunk_size = chunk_size; 126 mm->max_order = ilog2(size) - ilog2(chunk_size); 127 128 BUG_ON(mm->max_order > DRM_BUDDY_MAX_ORDER); 129 130 mm->free_list = kmalloc_array(mm->max_order + 1, 131 sizeof(struct list_head), 132 GFP_KERNEL); 133 if (!mm->free_list) 134 return -ENOMEM; 135 136 for (i = 0; i <= mm->max_order; ++i) 137 INIT_LIST_HEAD(&mm->free_list[i]); 138 139 mm->n_roots = hweight64(size); 140 141 mm->roots = kmalloc_array(mm->n_roots, 142 sizeof(struct drm_buddy_block *), 143 GFP_KERNEL); 144 if (!mm->roots) 145 goto out_free_list; 146 147 offset = 0; 148 i = 0; 149 150 /* 151 * Split into power-of-two blocks, in case we are given a size that is 152 * not itself a power-of-two. 153 */ 154 do { 155 struct drm_buddy_block *root; 156 unsigned int order; 157 u64 root_size; 158 159 order = ilog2(size) - ilog2(chunk_size); 160 root_size = chunk_size << order; 161 162 root = drm_block_alloc(mm, NULL, order, offset); 163 if (!root) 164 goto out_free_roots; 165 166 mark_free(mm, root); 167 168 BUG_ON(i > mm->max_order); 169 BUG_ON(drm_buddy_block_size(mm, root) < chunk_size); 170 171 mm->roots[i] = root; 172 173 offset += root_size; 174 size -= root_size; 175 i++; 176 } while (size); 177 178 return 0; 179 180 out_free_roots: 181 while (i--) 182 drm_block_free(mm, mm->roots[i]); 183 kfree(mm->roots); 184 out_free_list: 185 kfree(mm->free_list); 186 return -ENOMEM; 187 } 188 EXPORT_SYMBOL(drm_buddy_init); 189 190 /** 191 * drm_buddy_fini - tear down the memory manager 192 * 193 * @mm: DRM buddy manager to free 194 * 195 * Cleanup memory manager resources and the freelist 196 */ 197 void drm_buddy_fini(struct drm_buddy *mm) 198 { 199 int i; 200 201 for (i = 0; i < mm->n_roots; ++i) { 202 WARN_ON(!drm_buddy_block_is_free(mm->roots[i])); 203 drm_block_free(mm, mm->roots[i]); 204 } 205 206 WARN_ON(mm->avail != mm->size); 207 208 kfree(mm->roots); 209 kfree(mm->free_list); 210 } 211 EXPORT_SYMBOL(drm_buddy_fini); 212 213 static int split_block(struct drm_buddy *mm, 214 struct drm_buddy_block *block) 215 { 216 unsigned int block_order = drm_buddy_block_order(block) - 1; 217 u64 offset = drm_buddy_block_offset(block); 218 219 BUG_ON(!drm_buddy_block_is_free(block)); 220 BUG_ON(!drm_buddy_block_order(block)); 221 222 block->left = drm_block_alloc(mm, block, block_order, offset); 223 if (!block->left) 224 return -ENOMEM; 225 226 block->right = drm_block_alloc(mm, block, block_order, 227 offset + (mm->chunk_size << block_order)); 228 if (!block->right) { 229 drm_block_free(mm, block->left); 230 return -ENOMEM; 231 } 232 233 mark_free(mm, block->left); 234 mark_free(mm, block->right); 235 236 mark_split(block); 237 238 return 0; 239 } 240 241 static struct drm_buddy_block * 242 __get_buddy(struct drm_buddy_block *block) 243 { 244 struct drm_buddy_block *parent; 245 246 parent = block->parent; 247 if (!parent) 248 return NULL; 249 250 if (parent->left == block) 251 return parent->right; 252 253 return parent->left; 254 } 255 256 /** 257 * drm_get_buddy - get buddy address 258 * 259 * @block: DRM buddy block 260 * 261 * Returns the corresponding buddy block for @block, or NULL 262 * if this is a root block and can't be merged further. 263 * Requires some kind of locking to protect against 264 * any concurrent allocate and free operations. 265 */ 266 struct drm_buddy_block * 267 drm_get_buddy(struct drm_buddy_block *block) 268 { 269 return __get_buddy(block); 270 } 271 EXPORT_SYMBOL(drm_get_buddy); 272 273 static void __drm_buddy_free(struct drm_buddy *mm, 274 struct drm_buddy_block *block) 275 { 276 struct drm_buddy_block *parent; 277 278 while ((parent = block->parent)) { 279 struct drm_buddy_block *buddy; 280 281 buddy = __get_buddy(block); 282 283 if (!drm_buddy_block_is_free(buddy)) 284 break; 285 286 list_del(&buddy->link); 287 288 drm_block_free(mm, block); 289 drm_block_free(mm, buddy); 290 291 block = parent; 292 } 293 294 mark_free(mm, block); 295 } 296 297 /** 298 * drm_buddy_free_block - free a block 299 * 300 * @mm: DRM buddy manager 301 * @block: block to be freed 302 */ 303 void drm_buddy_free_block(struct drm_buddy *mm, 304 struct drm_buddy_block *block) 305 { 306 BUG_ON(!drm_buddy_block_is_allocated(block)); 307 mm->avail += drm_buddy_block_size(mm, block); 308 __drm_buddy_free(mm, block); 309 } 310 EXPORT_SYMBOL(drm_buddy_free_block); 311 312 /** 313 * drm_buddy_free_list - free blocks 314 * 315 * @mm: DRM buddy manager 316 * @objects: input list head to free blocks 317 */ 318 void drm_buddy_free_list(struct drm_buddy *mm, struct list_head *objects) 319 { 320 struct drm_buddy_block *block, *on; 321 322 list_for_each_entry_safe(block, on, objects, link) { 323 drm_buddy_free_block(mm, block); 324 cond_resched(); 325 } 326 INIT_LIST_HEAD(objects); 327 } 328 EXPORT_SYMBOL(drm_buddy_free_list); 329 330 static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2) 331 { 332 return s1 <= e2 && e1 >= s2; 333 } 334 335 static inline bool contains(u64 s1, u64 e1, u64 s2, u64 e2) 336 { 337 return s1 <= s2 && e1 >= e2; 338 } 339 340 static struct drm_buddy_block * 341 alloc_range_bias(struct drm_buddy *mm, 342 u64 start, u64 end, 343 unsigned int order) 344 { 345 u64 req_size = mm->chunk_size << order; 346 struct drm_buddy_block *block; 347 struct drm_buddy_block *buddy; 348 DRM_LIST_HEAD(dfs); 349 int err; 350 int i; 351 352 end = end - 1; 353 354 for (i = 0; i < mm->n_roots; ++i) 355 list_add_tail(&mm->roots[i]->tmp_link, &dfs); 356 357 do { 358 u64 block_start; 359 u64 block_end; 360 361 block = list_first_entry_or_null(&dfs, 362 struct drm_buddy_block, 363 tmp_link); 364 if (!block) 365 break; 366 367 list_del(&block->tmp_link); 368 369 if (drm_buddy_block_order(block) < order) 370 continue; 371 372 block_start = drm_buddy_block_offset(block); 373 block_end = block_start + drm_buddy_block_size(mm, block) - 1; 374 375 if (!overlaps(start, end, block_start, block_end)) 376 continue; 377 378 if (drm_buddy_block_is_allocated(block)) 379 continue; 380 381 if (block_start < start || block_end > end) { 382 u64 adjusted_start = max(block_start, start); 383 u64 adjusted_end = min(block_end, end); 384 385 if (round_down(adjusted_end + 1, req_size) <= 386 round_up(adjusted_start, req_size)) 387 continue; 388 } 389 390 if (contains(start, end, block_start, block_end) && 391 order == drm_buddy_block_order(block)) { 392 /* 393 * Find the free block within the range. 394 */ 395 if (drm_buddy_block_is_free(block)) 396 return block; 397 398 continue; 399 } 400 401 if (!drm_buddy_block_is_split(block)) { 402 err = split_block(mm, block); 403 if (unlikely(err)) 404 goto err_undo; 405 } 406 407 list_add(&block->right->tmp_link, &dfs); 408 list_add(&block->left->tmp_link, &dfs); 409 } while (1); 410 411 return ERR_PTR(-ENOSPC); 412 413 err_undo: 414 /* 415 * We really don't want to leave around a bunch of split blocks, since 416 * bigger is better, so make sure we merge everything back before we 417 * free the allocated blocks. 418 */ 419 buddy = __get_buddy(block); 420 if (buddy && 421 (drm_buddy_block_is_free(block) && 422 drm_buddy_block_is_free(buddy))) 423 __drm_buddy_free(mm, block); 424 return ERR_PTR(err); 425 } 426 427 static struct drm_buddy_block * 428 get_maxblock(struct drm_buddy *mm, unsigned int order) 429 { 430 struct drm_buddy_block *max_block = NULL, *node; 431 unsigned int i; 432 433 for (i = order; i <= mm->max_order; ++i) { 434 if (!list_empty(&mm->free_list[i])) { 435 node = list_last_entry(&mm->free_list[i], 436 struct drm_buddy_block, 437 link); 438 if (!max_block) { 439 max_block = node; 440 continue; 441 } 442 443 if (drm_buddy_block_offset(node) > 444 drm_buddy_block_offset(max_block)) { 445 max_block = node; 446 } 447 } 448 } 449 450 return max_block; 451 } 452 453 static struct drm_buddy_block * 454 alloc_from_freelist(struct drm_buddy *mm, 455 unsigned int order, 456 unsigned long flags) 457 { 458 struct drm_buddy_block *block = NULL; 459 unsigned int tmp; 460 int err; 461 462 if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) { 463 block = get_maxblock(mm, order); 464 if (block) 465 /* Store the obtained block order */ 466 tmp = drm_buddy_block_order(block); 467 } else { 468 for (tmp = order; tmp <= mm->max_order; ++tmp) { 469 if (!list_empty(&mm->free_list[tmp])) { 470 block = list_last_entry(&mm->free_list[tmp], 471 struct drm_buddy_block, 472 link); 473 if (block) 474 break; 475 } 476 } 477 } 478 479 if (!block) 480 return ERR_PTR(-ENOSPC); 481 482 BUG_ON(!drm_buddy_block_is_free(block)); 483 484 while (tmp != order) { 485 err = split_block(mm, block); 486 if (unlikely(err)) 487 goto err_undo; 488 489 block = block->right; 490 tmp--; 491 } 492 return block; 493 494 err_undo: 495 if (tmp != order) 496 __drm_buddy_free(mm, block); 497 return ERR_PTR(err); 498 } 499 500 static int __alloc_range(struct drm_buddy *mm, 501 struct list_head *dfs, 502 u64 start, u64 size, 503 struct list_head *blocks) 504 { 505 struct drm_buddy_block *block; 506 struct drm_buddy_block *buddy; 507 DRM_LIST_HEAD(allocated); 508 u64 end; 509 int err; 510 511 end = start + size - 1; 512 513 do { 514 u64 block_start; 515 u64 block_end; 516 517 block = list_first_entry_or_null(dfs, 518 struct drm_buddy_block, 519 tmp_link); 520 if (!block) 521 break; 522 523 list_del(&block->tmp_link); 524 525 block_start = drm_buddy_block_offset(block); 526 block_end = block_start + drm_buddy_block_size(mm, block) - 1; 527 528 if (!overlaps(start, end, block_start, block_end)) 529 continue; 530 531 if (drm_buddy_block_is_allocated(block)) { 532 err = -ENOSPC; 533 goto err_free; 534 } 535 536 if (contains(start, end, block_start, block_end)) { 537 if (!drm_buddy_block_is_free(block)) { 538 err = -ENOSPC; 539 goto err_free; 540 } 541 542 mark_allocated(block); 543 mm->avail -= drm_buddy_block_size(mm, block); 544 list_add_tail(&block->link, &allocated); 545 continue; 546 } 547 548 if (!drm_buddy_block_is_split(block)) { 549 err = split_block(mm, block); 550 if (unlikely(err)) 551 goto err_undo; 552 } 553 554 list_add(&block->right->tmp_link, dfs); 555 list_add(&block->left->tmp_link, dfs); 556 } while (1); 557 558 list_splice_tail(&allocated, blocks); 559 return 0; 560 561 err_undo: 562 /* 563 * We really don't want to leave around a bunch of split blocks, since 564 * bigger is better, so make sure we merge everything back before we 565 * free the allocated blocks. 566 */ 567 buddy = __get_buddy(block); 568 if (buddy && 569 (drm_buddy_block_is_free(block) && 570 drm_buddy_block_is_free(buddy))) 571 __drm_buddy_free(mm, block); 572 573 err_free: 574 drm_buddy_free_list(mm, &allocated); 575 return err; 576 } 577 578 static int __drm_buddy_alloc_range(struct drm_buddy *mm, 579 u64 start, 580 u64 size, 581 struct list_head *blocks) 582 { 583 DRM_LIST_HEAD(dfs); 584 int i; 585 586 for (i = 0; i < mm->n_roots; ++i) 587 list_add_tail(&mm->roots[i]->tmp_link, &dfs); 588 589 return __alloc_range(mm, &dfs, start, size, blocks); 590 } 591 592 /** 593 * drm_buddy_block_trim - free unused pages 594 * 595 * @mm: DRM buddy manager 596 * @new_size: original size requested 597 * @blocks: Input and output list of allocated blocks. 598 * MUST contain single block as input to be trimmed. 599 * On success will contain the newly allocated blocks 600 * making up the @new_size. Blocks always appear in 601 * ascending order 602 * 603 * For contiguous allocation, we round up the size to the nearest 604 * power of two value, drivers consume *actual* size, so remaining 605 * portions are unused and can be optionally freed with this function 606 * 607 * Returns: 608 * 0 on success, error code on failure. 609 */ 610 int drm_buddy_block_trim(struct drm_buddy *mm, 611 u64 new_size, 612 struct list_head *blocks) 613 { 614 struct drm_buddy_block *parent; 615 struct drm_buddy_block *block; 616 DRM_LIST_HEAD(dfs); 617 u64 new_start; 618 int err; 619 620 if (!list_is_singular(blocks)) 621 return -EINVAL; 622 623 block = list_first_entry(blocks, 624 struct drm_buddy_block, 625 link); 626 627 if (WARN_ON(!drm_buddy_block_is_allocated(block))) 628 return -EINVAL; 629 630 if (new_size > drm_buddy_block_size(mm, block)) 631 return -EINVAL; 632 633 if (!new_size || !IS_ALIGNED(new_size, mm->chunk_size)) 634 return -EINVAL; 635 636 if (new_size == drm_buddy_block_size(mm, block)) 637 return 0; 638 639 list_del(&block->link); 640 mark_free(mm, block); 641 mm->avail += drm_buddy_block_size(mm, block); 642 643 /* Prevent recursively freeing this node */ 644 parent = block->parent; 645 block->parent = NULL; 646 647 new_start = drm_buddy_block_offset(block); 648 list_add(&block->tmp_link, &dfs); 649 err = __alloc_range(mm, &dfs, new_start, new_size, blocks); 650 if (err) { 651 mark_allocated(block); 652 mm->avail -= drm_buddy_block_size(mm, block); 653 list_add(&block->link, blocks); 654 } 655 656 block->parent = parent; 657 return err; 658 } 659 EXPORT_SYMBOL(drm_buddy_block_trim); 660 661 /** 662 * drm_buddy_alloc_blocks - allocate power-of-two blocks 663 * 664 * @mm: DRM buddy manager to allocate from 665 * @start: start of the allowed range for this block 666 * @end: end of the allowed range for this block 667 * @size: size of the allocation 668 * @min_page_size: alignment of the allocation 669 * @blocks: output list head to add allocated blocks 670 * @flags: DRM_BUDDY_*_ALLOCATION flags 671 * 672 * alloc_range_bias() called on range limitations, which traverses 673 * the tree and returns the desired block. 674 * 675 * alloc_from_freelist() called when *no* range restrictions 676 * are enforced, which picks the block from the freelist. 677 * 678 * Returns: 679 * 0 on success, error code on failure. 680 */ 681 int drm_buddy_alloc_blocks(struct drm_buddy *mm, 682 u64 start, u64 end, u64 size, 683 u64 min_page_size, 684 struct list_head *blocks, 685 unsigned long flags) 686 { 687 struct drm_buddy_block *block = NULL; 688 unsigned int min_order, order; 689 unsigned long pages; 690 DRM_LIST_HEAD(allocated); 691 int err; 692 693 if (size < mm->chunk_size) 694 return -EINVAL; 695 696 if (min_page_size < mm->chunk_size) 697 return -EINVAL; 698 699 if (!is_power_of_2(min_page_size)) 700 return -EINVAL; 701 702 if (!IS_ALIGNED(start | end | size, mm->chunk_size)) 703 return -EINVAL; 704 705 if (end > mm->size) 706 return -EINVAL; 707 708 if (range_overflows(start, size, mm->size)) 709 return -EINVAL; 710 711 /* Actual range allocation */ 712 if (start + size == end) 713 return __drm_buddy_alloc_range(mm, start, size, blocks); 714 715 if (!IS_ALIGNED(size, min_page_size)) 716 return -EINVAL; 717 718 pages = size >> ilog2(mm->chunk_size); 719 order = fls(pages) - 1; 720 min_order = ilog2(min_page_size) - ilog2(mm->chunk_size); 721 722 do { 723 order = min(order, (unsigned int)fls(pages) - 1); 724 BUG_ON(order > mm->max_order); 725 BUG_ON(order < min_order); 726 727 do { 728 if (flags & DRM_BUDDY_RANGE_ALLOCATION) 729 /* Allocate traversing within the range */ 730 block = alloc_range_bias(mm, start, end, order); 731 else 732 /* Allocate from freelist */ 733 block = alloc_from_freelist(mm, order, flags); 734 735 if (!IS_ERR(block)) 736 break; 737 738 if (order-- == min_order) { 739 err = -ENOSPC; 740 goto err_free; 741 } 742 } while (1); 743 744 mark_allocated(block); 745 mm->avail -= drm_buddy_block_size(mm, block); 746 kmemleak_update_trace(block); 747 list_add_tail(&block->link, &allocated); 748 749 pages -= BIT(order); 750 751 if (!pages) 752 break; 753 } while (1); 754 755 list_splice_tail(&allocated, blocks); 756 return 0; 757 758 err_free: 759 drm_buddy_free_list(mm, &allocated); 760 return err; 761 } 762 EXPORT_SYMBOL(drm_buddy_alloc_blocks); 763 764 /** 765 * drm_buddy_block_print - print block information 766 * 767 * @mm: DRM buddy manager 768 * @block: DRM buddy block 769 * @p: DRM printer to use 770 */ 771 void drm_buddy_block_print(struct drm_buddy *mm, 772 struct drm_buddy_block *block, 773 struct drm_printer *p) 774 { 775 u64 start = drm_buddy_block_offset(block); 776 u64 size = drm_buddy_block_size(mm, block); 777 778 drm_printf(p, "%#018llx-%#018llx: %llu\n", start, start + size, size); 779 } 780 EXPORT_SYMBOL(drm_buddy_block_print); 781 782 /** 783 * drm_buddy_print - print allocator state 784 * 785 * @mm: DRM buddy manager 786 * @p: DRM printer to use 787 */ 788 void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p) 789 { 790 int order; 791 792 drm_printf(p, "chunk_size: %lluKiB, total: %lluMiB, free: %lluMiB\n", 793 mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20); 794 795 for (order = mm->max_order; order >= 0; order--) { 796 struct drm_buddy_block *block; 797 u64 count = 0, free; 798 799 list_for_each_entry(block, &mm->free_list[order], link) { 800 BUG_ON(!drm_buddy_block_is_free(block)); 801 count++; 802 } 803 804 drm_printf(p, "order-%2d ", order); 805 806 free = count * (mm->chunk_size << order); 807 if (free < SZ_1M) 808 drm_printf(p, "free: %8llu KiB", free >> 10); 809 else 810 drm_printf(p, "free: %8llu MiB", free >> 20); 811 812 drm_printf(p, ", blocks: %llu\n", count); 813 } 814 } 815 EXPORT_SYMBOL(drm_buddy_print); 816 817 void drm_buddy_module_exit(void) 818 { 819 #ifdef __linux__ 820 kmem_cache_destroy(slab_blocks); 821 #else 822 pool_destroy(&slab_blocks); 823 #endif 824 } 825 826 int __init drm_buddy_module_init(void) 827 { 828 #ifdef __linux__ 829 slab_blocks = KMEM_CACHE(drm_buddy_block, 0); 830 if (!slab_blocks) 831 return -ENOMEM; 832 #else 833 pool_init(&slab_blocks, sizeof(struct drm_buddy_block), 834 CACHELINESIZE, IPL_NONE, 0, "drmbb", NULL); 835 #endif 836 837 return 0; 838 } 839 840 module_init(drm_buddy_module_init); 841 module_exit(drm_buddy_module_exit); 842 843 MODULE_DESCRIPTION("DRM Buddy Allocator"); 844 MODULE_LICENSE("Dual MIT/GPL"); 845