1 /* 2 * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 */ 8 9 10 #include "utility.h" 11 12 #include <net_buffer.h> 13 #include <slab/Slab.h> 14 #include <util/list.h> 15 16 #include <ByteOrder.h> 17 #include <KernelExport.h> 18 #include <util/AutoLock.h> 19 20 #include <stdlib.h> 21 #include <string.h> 22 #include <sys/uio.h> 23 24 25 //#define TRACE_BUFFER 26 #ifdef TRACE_BUFFER 27 # define TRACE(x) dprintf x 28 #else 29 # define TRACE(x) ; 30 #endif 31 32 #define BUFFER_SIZE 2048 33 // maximum implementation derived buffer size is 65536 34 35 struct data_node { 36 struct list_link link; 37 struct data_header *header; 38 struct data_header *located; 39 size_t offset; // the net_buffer-wide offset of this node 40 uint8 *start; // points to the start of the data 41 uint16 used; // defines how much memory is used by this node 42 uint16 header_space; 43 uint16 tail_space; 44 }; 45 46 struct free_data { 47 struct free_data *next; 48 uint16 size; 49 }; 50 51 struct data_header { 52 int32 ref_count; 53 addr_t physical_address; 54 free_data *first_free; 55 uint8 *data_end; 56 size_t data_space; 57 data_node *first_node; 58 }; 59 60 #define MAX_FREE_BUFFER_SIZE (BUFFER_SIZE - sizeof(data_header)) 61 62 struct net_buffer_private : net_buffer { 63 struct list buffers; 64 data_node first_node; 65 }; 66 67 68 static object_cache *sNetBufferCache; 69 static object_cache *sDataNodeCache; 70 71 72 static status_t append_data(net_buffer *buffer, const void *data, size_t size); 73 static status_t trim_data(net_buffer *_buffer, size_t newSize); 74 static status_t remove_header(net_buffer *_buffer, size_t bytes); 75 static status_t remove_trailer(net_buffer *_buffer, size_t bytes); 76 77 78 #if 1 79 static void 80 dump_buffer(net_buffer *_buffer) 81 { 82 net_buffer_private *buffer = (net_buffer_private *)_buffer; 83 84 dprintf("buffer %p, size %ld\n", buffer, buffer->size); 85 data_node *node = NULL; 86 while ((node = (data_node *)list_get_next_item(&buffer->buffers, node)) != NULL) { 87 dprintf(" node %p, offset %lu, used %u, header %u, tail %u, header %p\n", 88 node, node->offset, node->used, node->header_space, node->tail_space, node->header); 89 //dump_block((char *)node->start, node->used, " "); 90 dump_block((char *)node->start, min_c(node->used, 32), " "); 91 } 92 } 93 #endif 94 95 96 static inline data_header * 97 allocate_data_header() 98 { 99 return (data_header *)object_cache_alloc(sDataNodeCache, CACHE_DONT_SLEEP); 100 } 101 102 103 static inline net_buffer_private * 104 allocate_net_buffer() 105 { 106 return (net_buffer_private *)object_cache_alloc(sNetBufferCache, 107 CACHE_DONT_SLEEP); 108 } 109 110 111 static inline void 112 free_data_header(data_header *header) 113 { 114 object_cache_free(sDataNodeCache, header); 115 } 116 117 118 static inline void 119 free_net_buffer(net_buffer_private *buffer) 120 { 121 object_cache_free(sNetBufferCache, buffer); 122 } 123 124 125 static data_header * 126 create_data_header(size_t headerSpace) 127 { 128 data_header *header = allocate_data_header(); 129 if (header == NULL) 130 return NULL; 131 132 header->ref_count = 1; 133 header->physical_address = 0; 134 // TODO: initialize this correctly 135 header->data_space = headerSpace; 136 header->data_end = (uint8 *)header + sizeof(struct data_header); 137 header->first_free = NULL; 138 header->first_node = NULL; 139 140 TRACE((" create new data header %p\n", header)); 141 return header; 142 } 143 144 145 static void 146 release_data_header(data_header *header) 147 { 148 if (atomic_add(&header->ref_count, -1) != 1) 149 return; 150 151 TRACE((" free header %p\n", header)); 152 free_data_header(header); 153 } 154 155 156 inline void 157 acquire_data_header(data_header *header) 158 { 159 atomic_add(&header->ref_count, 1); 160 } 161 162 163 static void 164 free_data_header_space(data_header *header, uint8 *data, size_t size) 165 { 166 if (size < sizeof(free_data)) 167 size = sizeof(free_data); 168 169 free_data *freeData = (free_data *)data; 170 freeData->next = header->first_free; 171 freeData->size = size; 172 173 header->first_free = freeData; 174 header->data_space += size; 175 // TODO: the first node's header space could grow again 176 } 177 178 179 /*! 180 Tries to allocate \a size bytes from the free space in the header. 181 */ 182 static uint8 * 183 alloc_data_header_space(data_header *header, size_t size) 184 { 185 if (size < sizeof(free_data)) 186 size = sizeof(free_data); 187 188 if (header->first_free != NULL && header->first_free->size >= size) { 189 // the first entry of the header space matches the allocation's needs 190 uint8 *data = (uint8 *)header->first_free; 191 header->first_free = header->first_free->next; 192 return data; 193 } 194 195 if (header->data_space < size) { 196 // there is no free space left, search free list 197 free_data *freeData = header->first_free; 198 free_data *last = NULL; 199 while (freeData != NULL) { 200 if (last != NULL && freeData->size >= size) { 201 // take this one 202 last->next = freeData->next; 203 return (uint8 *)freeData; 204 } 205 206 last = freeData; 207 freeData = freeData->next; 208 } 209 210 return NULL; 211 } 212 213 // allocate new space 214 215 uint8 *data = header->data_end; 216 header->data_end += size; 217 header->data_space -= size; 218 219 if (header->first_node != NULL) 220 header->first_node->header_space -= size; 221 222 return data; 223 } 224 225 226 /*! 227 Initializes the first data_node of a data_header. 228 The node must have been assigned to the header already. 229 */ 230 static void 231 init_first_data_node(data_node *node) 232 { 233 data_header *header = node->header; 234 235 node->offset = 0; 236 node->start = header->data_end + header->data_space; 237 node->used = 0; 238 node->header_space = header->data_space; 239 node->tail_space = MAX_FREE_BUFFER_SIZE - header->data_space; 240 241 header->first_node = node; 242 } 243 244 245 static data_node * 246 add_data_node(data_header *header, data_header *located = NULL) 247 { 248 if (located == NULL) 249 located = header; 250 251 data_node *node = (data_node *)alloc_data_header_space(located, 252 sizeof(data_node)); 253 if (node == NULL) 254 return NULL; 255 256 TRACE((" add data node %p to header %p\n", node, header)); 257 acquire_data_header(header); 258 if (located != header) 259 acquire_data_header(located); 260 261 memset(node, 0, sizeof(struct data_node)); 262 node->located = located; 263 node->header = header; 264 return node; 265 } 266 267 268 void 269 remove_data_node(data_node *node) 270 { 271 data_header *located = node->located; 272 273 TRACE((" remove data node %p from header %p (located %p)\n", 274 node, node->header, located)); 275 276 if (located != node->header) 277 release_data_header(node->header); 278 279 if (located == NULL) 280 return; 281 282 free_data_header_space(located, (uint8 *)node, sizeof(data_node)); 283 if (located->first_node == node) { 284 located->first_node = NULL; 285 located->data_space = 0; 286 } 287 288 release_data_header(located); 289 } 290 291 292 static inline data_node * 293 get_node_at_offset(net_buffer_private *buffer, size_t offset) 294 { 295 data_node *node = (data_node *)list_get_first_item(&buffer->buffers); 296 while (node->offset + node->used < offset) { 297 node = (data_node *)list_get_next_item(&buffer->buffers, node); 298 if (node == NULL) 299 return NULL; 300 } 301 302 return node; 303 } 304 305 306 // #pragma mark - 307 308 309 static net_buffer * 310 create_buffer(size_t headerSpace) 311 { 312 net_buffer_private *buffer = allocate_net_buffer(); 313 if (buffer == NULL) 314 return NULL; 315 316 TRACE(("create buffer %p\n", buffer)); 317 318 data_header *header = create_data_header(headerSpace); 319 if (header == NULL) { 320 free_net_buffer(buffer); 321 return NULL; 322 } 323 324 buffer->first_node.header = header; 325 buffer->first_node.located = NULL; 326 init_first_data_node(&buffer->first_node); 327 328 list_init(&buffer->buffers); 329 list_add_item(&buffer->buffers, &buffer->first_node); 330 331 buffer->source.ss_len = 0; 332 buffer->destination.ss_len = 0; 333 buffer->interface = NULL; 334 buffer->offset = 0; 335 buffer->flags = 0; 336 buffer->size = 0; 337 338 return buffer; 339 } 340 341 342 static void 343 free_buffer(net_buffer *_buffer) 344 { 345 net_buffer_private *buffer = (net_buffer_private *)_buffer; 346 347 TRACE(("free buffer %p\n", buffer)); 348 349 data_node *node; 350 while ((node = (data_node *)list_remove_head_item(&buffer->buffers)) != NULL) { 351 remove_data_node(node); 352 } 353 354 free_net_buffer(buffer); 355 } 356 357 358 /*! Creates a duplicate of the \a buffer. The new buffer does not share internal 359 storage; they are completely independent from each other. 360 */ 361 static net_buffer * 362 duplicate_buffer(net_buffer *_buffer) 363 { 364 net_buffer_private *buffer = (net_buffer_private *)_buffer; 365 366 net_buffer *duplicate = create_buffer(buffer->first_node.header_space); 367 if (duplicate == NULL) 368 return NULL; 369 370 // copy the data from the source buffer 371 372 data_node *node = (data_node *)list_get_first_item(&buffer->buffers); 373 while (true) { 374 if (append_data(duplicate, node->start, node->used) < B_OK) { 375 free_buffer(duplicate); 376 return NULL; 377 } 378 379 node = (data_node *)list_get_next_item(&buffer->buffers, node); 380 if (node == NULL) 381 break; 382 } 383 384 // copy meta data from source buffer 385 386 memcpy(&duplicate->source, &buffer->source, 387 min_c(buffer->source.ss_len, sizeof(sockaddr_storage))); 388 memcpy(&duplicate->destination, &buffer->destination, 389 min_c(buffer->destination.ss_len, sizeof(sockaddr_storage))); 390 391 duplicate->flags = buffer->flags; 392 duplicate->interface = buffer->interface; 393 duplicate->offset = buffer->offset; 394 duplicate->size = buffer->size; 395 duplicate->protocol = buffer->protocol; 396 397 return duplicate; 398 } 399 400 401 /*! Clones the buffer by grabbing another reference to the underlying data. 402 If that data changes, it will be changed in the clone as well. 403 404 If \a shareFreeSpace is \c true, the cloned buffer may claim the free 405 space in the original buffer as the original buffer can still do. If you 406 are using this, it's your responsibility that only one of the buffers 407 will do this. 408 */ 409 static net_buffer * 410 clone_buffer(net_buffer *_buffer, bool shareFreeSpace) 411 { 412 net_buffer_private *buffer = (net_buffer_private *)_buffer; 413 414 TRACE(("clone_buffer(buffer %p)\n", buffer)); 415 416 net_buffer_private *clone = allocate_net_buffer(); 417 if (clone == NULL) 418 return NULL; 419 420 data_node *sourceNode = (data_node *)list_get_first_item(&buffer->buffers); 421 if (sourceNode == NULL) { 422 free_net_buffer(clone); 423 return NULL; 424 } 425 426 list_init(&clone->buffers); 427 428 // grab reference to this buffer - all additional nodes will get 429 // theirs in add_data_node() 430 acquire_data_header(sourceNode->header); 431 data_node *node = &clone->first_node; 432 node->header = sourceNode->header; 433 node->located = NULL; 434 435 while (sourceNode != NULL) { 436 node->start = sourceNode->start; 437 node->used = sourceNode->used; 438 node->offset = sourceNode->offset; 439 440 if (shareFreeSpace) { 441 // both buffers could claim the free space - note that this option 442 // has to be used carefully 443 node->header_space = sourceNode->header_space; 444 node->tail_space = sourceNode->tail_space; 445 } else { 446 // the free space stays with the original buffer 447 node->header_space = 0; 448 node->tail_space = 0; 449 } 450 451 // add node to clone's list of buffers 452 list_add_item(&clone->buffers, node); 453 454 sourceNode = (data_node *)list_get_next_item(&buffer->buffers, sourceNode); 455 if (sourceNode == NULL) 456 break; 457 458 node = add_data_node(sourceNode->header); 459 if (node == NULL) { 460 // There was not enough space left for another node in this buffer 461 // TODO: handle this case! 462 panic("clone buffer hits size limit... (fix me)"); 463 free_net_buffer(clone); 464 return NULL; 465 } 466 } 467 468 // copy meta data from source buffer 469 470 memcpy(&clone->source, &buffer->source, 471 min_c(buffer->source.ss_len, sizeof(sockaddr_storage))); 472 memcpy(&clone->destination, &buffer->destination, 473 min_c(buffer->destination.ss_len, sizeof(sockaddr_storage))); 474 475 clone->flags = buffer->flags; 476 clone->interface = buffer->interface; 477 clone->offset = buffer->offset; 478 clone->size = buffer->size; 479 clone->protocol = buffer->protocol; 480 481 return clone; 482 } 483 484 485 /*! 486 Split the buffer at offset, the header data 487 is returned as new buffer. 488 TODO: optimize and avoid making a copy. 489 */ 490 static net_buffer * 491 split_buffer(net_buffer *from, uint32 offset) 492 { 493 net_buffer *buffer = duplicate_buffer(from); 494 if (buffer == NULL) 495 return NULL; 496 497 TRACE(("split_buffer(buffer %p -> %p, offset %ld)\n", from, buffer, offset)); 498 499 if (trim_data(buffer, offset) == B_OK) { 500 if (remove_header(from, offset) == B_OK) 501 return buffer; 502 } 503 504 free_buffer(buffer); 505 return NULL; 506 } 507 508 509 /*! 510 Merges the second buffer with the first. If \a after is \c true, the 511 second buffer's contents will be appended to the first ones, else they 512 will be prepended. 513 The second buffer will be freed if this function succeeds. 514 */ 515 static status_t 516 merge_buffer(net_buffer *_buffer, net_buffer *_with, bool after) 517 { 518 net_buffer_private *buffer = (net_buffer_private *)_buffer; 519 net_buffer_private *with = (net_buffer_private *)_with; 520 if (with == NULL) 521 return B_BAD_VALUE; 522 523 TRACE(("merge buffer %p with %p (%s)\n", buffer, with, after ? "after" : "before")); 524 //dump_buffer(buffer); 525 //dprintf("with:\n"); 526 //dump_buffer(with); 527 528 // TODO: this is currently very simplistic, I really need to finish the 529 // harder part of this implementation (data_node management per header) 530 531 data_node *before = NULL; 532 533 if (!after) { 534 // change offset of all nodes already in the buffer 535 data_node *node = NULL; 536 while (true) { 537 node = (data_node *)list_get_next_item(&buffer->buffers, node); 538 if (node == NULL) 539 break; 540 541 node->offset += with->size; 542 if (before == NULL) 543 before = node; 544 } 545 } 546 547 data_node *last = NULL; 548 549 while (true) { 550 data_node *node = (data_node *)list_get_next_item(&with->buffers, last); 551 if (node == NULL) 552 break; 553 554 if ((uint8 *)node > (uint8 *)node->header 555 && (uint8 *)node < (uint8 *)node->header + BUFFER_SIZE) { 556 // The node is already in the buffer, we can just move it 557 // over to the new owner 558 list_remove_item(&with->buffers, node); 559 } else { 560 // we need a new place for this node 561 data_node *newNode = add_data_node(node->header); 562 if (newNode == NULL) { 563 // try again on the buffers own header 564 newNode = add_data_node(node->header, buffer->first_node.header); 565 if (newNode == NULL) 566 // TODO: try to revert buffers to their initial state!! 567 return ENOBUFS; 568 } 569 570 last = node; 571 *newNode = *node; 572 node = newNode; 573 // the old node will get freed with its buffer 574 } 575 576 if (after) { 577 list_add_item(&buffer->buffers, node); 578 node->offset = buffer->size; 579 } else 580 list_insert_item_before(&buffer->buffers, before, node); 581 582 buffer->size += node->used; 583 } 584 585 // the data has been merged completely at this point 586 free_buffer(with); 587 588 //dprintf(" merge result:\n"); 589 //dump_buffer(buffer); 590 return B_OK; 591 } 592 593 594 /*! Writes into existing allocated memory. 595 \return B_BAD_VALUE if you write outside of the buffers current 596 bounds. 597 */ 598 static status_t 599 write_data(net_buffer *_buffer, size_t offset, const void *data, size_t size) 600 { 601 net_buffer_private *buffer = (net_buffer_private *)_buffer; 602 603 if (offset + size > buffer->size) 604 return B_BAD_VALUE; 605 if (size == 0) 606 return B_OK; 607 608 // find first node to write into 609 data_node *node = get_node_at_offset(buffer, offset); 610 if (node == NULL) 611 return B_BAD_VALUE; 612 613 offset -= node->offset; 614 615 while (true) { 616 size_t written = min_c(size, node->used - offset); 617 memcpy(node->start + offset, data, written); 618 619 size -= written; 620 if (size == 0) 621 break; 622 623 offset = 0; 624 data = (void *)((uint8 *)data + written); 625 626 node = (data_node *)list_get_next_item(&buffer->buffers, node); 627 if (node == NULL) 628 return B_BAD_VALUE; 629 } 630 631 return B_OK; 632 } 633 634 635 static status_t 636 read_data(net_buffer *_buffer, size_t offset, void *data, size_t size) 637 { 638 net_buffer_private *buffer = (net_buffer_private *)_buffer; 639 640 if (offset + size > buffer->size) 641 return B_BAD_VALUE; 642 if (size == 0) 643 return B_OK; 644 645 // find first node to read from 646 data_node *node = get_node_at_offset(buffer, offset); 647 if (node == NULL) 648 return B_BAD_VALUE; 649 650 offset -= node->offset; 651 652 while (true) { 653 size_t bytesRead = min_c(size, node->used - offset); 654 memcpy(data, node->start + offset, bytesRead); 655 656 size -= bytesRead; 657 if (size == 0) 658 break; 659 660 offset = 0; 661 data = (void *)((uint8 *)data + bytesRead); 662 663 node = (data_node *)list_get_next_item(&buffer->buffers, node); 664 if (node == NULL) 665 return B_BAD_VALUE; 666 } 667 668 return B_OK; 669 } 670 671 672 static status_t 673 prepend_size(net_buffer *_buffer, size_t size, void **_contiguousBuffer) 674 { 675 net_buffer_private *buffer = (net_buffer_private *)_buffer; 676 data_node *node = (data_node *)list_get_first_item(&buffer->buffers); 677 678 TRACE(("prepend_size(buffer %p, size %ld) [has %ld]\n", buffer, size, 679 node->header_space)); 680 //dump_buffer(buffer); 681 682 if (node->header_space < size) { 683 // we need to prepend new buffers 684 685 size_t bytesLeft = size; 686 do { 687 if (node->header_space == 0) { 688 size_t headerSpace = MAX_FREE_BUFFER_SIZE; 689 data_header *header = create_data_header(headerSpace); 690 if (header == NULL) { 691 // TODO: free up headers we already allocated! 692 return B_NO_MEMORY; 693 } 694 695 data_node *previous = node; 696 697 node = (data_node *)add_data_node(header); 698 init_first_data_node(node); 699 700 list_insert_item_before(&buffer->buffers, previous, node); 701 } 702 703 size_t willConsume = min_c(bytesLeft, node->header_space); 704 705 node->header_space -= willConsume; 706 node->start -= willConsume; 707 node->used += willConsume; 708 bytesLeft -= willConsume; 709 } while (bytesLeft > 0); 710 711 // correct data offset in all nodes 712 713 size_t offset = 0; 714 node = NULL; 715 while ((node = (data_node *)list_get_next_item(&buffer->buffers, 716 node)) != NULL) { 717 node->offset = offset; 718 offset += node->used; 719 } 720 721 if (_contiguousBuffer) 722 *_contiguousBuffer = NULL; 723 } else { 724 // the data fits into this buffer 725 node->header_space -= size; 726 node->start -= size; 727 node->used += size; 728 729 if (_contiguousBuffer) 730 *_contiguousBuffer = node->start; 731 732 // adjust offset of following nodes 733 while ((node = (data_node *)list_get_next_item(&buffer->buffers, node)) != NULL) { 734 node->offset += size; 735 } 736 } 737 738 buffer->size += size; 739 740 //dprintf(" prepend_size result:\n"); 741 //dump_buffer(buffer); 742 return B_OK; 743 } 744 745 746 static status_t 747 prepend_data(net_buffer *buffer, const void *data, size_t size) 748 { 749 void *contiguousBuffer; 750 status_t status = prepend_size(buffer, size, &contiguousBuffer); 751 if (status < B_OK) 752 return status; 753 754 if (contiguousBuffer) 755 memcpy(contiguousBuffer, data, size); 756 else 757 write_data(buffer, 0, data, size); 758 759 //dprintf(" prepend result:\n"); 760 //dump_buffer(buffer); 761 762 return B_OK; 763 } 764 765 766 static status_t 767 append_size(net_buffer *_buffer, size_t size, void **_contiguousBuffer) 768 { 769 net_buffer_private *buffer = (net_buffer_private *)_buffer; 770 data_node *node = (data_node *)list_get_last_item(&buffer->buffers); 771 772 TRACE(("append_size(buffer %p, size %ld)\n", buffer, size)); 773 //dump_buffer(buffer); 774 775 if (node->tail_space < size) { 776 // we need to append a new buffer 777 778 // compute how many buffers we're going to need 779 // TODO: this doesn't leave any tail space, if that should be desired... 780 uint32 tailSpace = node->tail_space; 781 uint32 minimalHeaderSpace = sizeof(data_header) + 3 * sizeof(data_node); 782 uint32 sizeNeeded = size - tailSpace; 783 uint32 count = (sizeNeeded + BUFFER_SIZE - minimalHeaderSpace - 1) 784 / (BUFFER_SIZE - minimalHeaderSpace); 785 uint32 headerSpace = BUFFER_SIZE - sizeNeeded / count - sizeof(data_header); 786 uint32 sizeUsed = MAX_FREE_BUFFER_SIZE - headerSpace; 787 uint32 sizeAdded = tailSpace; 788 789 // allocate space left in the node 790 node->tail_space -= tailSpace; 791 node->used += tailSpace; 792 buffer->size += tailSpace; 793 794 // allocate all buffers 795 796 for (uint32 i = 0; i < count; i++) { 797 if (i == count - 1) { 798 // last data_header - compensate rounding errors 799 sizeUsed = size - sizeAdded; 800 headerSpace = MAX_FREE_BUFFER_SIZE - sizeUsed; 801 } 802 803 data_header *header = create_data_header(headerSpace); 804 if (header == NULL) { 805 // TODO: free up headers we already allocated! 806 return B_NO_MEMORY; 807 } 808 809 node = (data_node *)add_data_node(header); 810 // this can't fail as we made sure there will be enough header space 811 812 init_first_data_node(node); 813 node->tail_space -= sizeUsed; 814 node->used = sizeUsed; 815 node->offset = buffer->size; 816 817 buffer->size += sizeUsed; 818 sizeAdded += sizeUsed; 819 820 list_add_item(&buffer->buffers, node); 821 } 822 823 if (_contiguousBuffer) 824 *_contiguousBuffer = NULL; 825 826 //dprintf(" append result 1:\n"); 827 //dump_buffer(buffer); 828 return B_OK; 829 } 830 831 // the data fits into this buffer 832 node->tail_space -= size; 833 834 if (_contiguousBuffer) 835 *_contiguousBuffer = node->start + node->used; 836 837 node->used += size; 838 buffer->size += size; 839 840 //dprintf(" append result 2:\n"); 841 //dump_buffer(buffer); 842 return B_OK; 843 } 844 845 846 static status_t 847 append_data(net_buffer *buffer, const void *data, size_t size) 848 { 849 size_t used = buffer->size; 850 851 void *contiguousBuffer; 852 status_t status = append_size(buffer, size, &contiguousBuffer); 853 if (status < B_OK) 854 return status; 855 856 if (contiguousBuffer) 857 memcpy(contiguousBuffer, data, size); 858 else 859 write_data(buffer, used, data, size); 860 861 return B_OK; 862 } 863 864 865 /*! 866 Removes bytes from the beginning of the buffer. 867 */ 868 static status_t 869 remove_header(net_buffer *_buffer, size_t bytes) 870 { 871 net_buffer_private *buffer = (net_buffer_private *)_buffer; 872 873 if (bytes > buffer->size) 874 return B_BAD_VALUE; 875 876 TRACE(("remove_header(buffer %p, %ld bytes)\n", buffer, bytes)); 877 //dump_buffer(buffer); 878 879 size_t left = bytes; 880 data_node *node = NULL; 881 882 while (left >= 0) { 883 node = (data_node *)list_get_first_item(&buffer->buffers); 884 if (node == NULL) { 885 if (left == 0) 886 break; 887 return B_ERROR; 888 } 889 890 if (node->used > left) 891 break; 892 893 // node will be removed completely 894 list_remove_item(&buffer->buffers, node); 895 left -= node->used; 896 remove_data_node(node); 897 node = NULL; 898 } 899 900 // cut remaining node, if any 901 902 if (node != NULL) { 903 size_t cut = min_c(node->used, left); 904 node->offset = 0; 905 node->start += cut; 906 node->header_space += cut; 907 node->used -= cut; 908 909 node = (data_node *)list_get_next_item(&buffer->buffers, node); 910 } 911 912 // adjust offset of following nodes 913 while (node != NULL) { 914 node->offset -= bytes; 915 node = (data_node *)list_get_next_item(&buffer->buffers, node); 916 } 917 918 buffer->size -= bytes; 919 920 //dprintf(" remove result:\n"); 921 //dump_buffer(buffer); 922 return B_OK; 923 } 924 925 926 /*! 927 Removes bytes from the end of the buffer. 928 */ 929 static status_t 930 remove_trailer(net_buffer *buffer, size_t bytes) 931 { 932 return trim_data(buffer, buffer->size - bytes); 933 } 934 935 936 /*! 937 Trims the buffer to the specified \a newSize by removing space from 938 the end of the buffer. 939 */ 940 static status_t 941 trim_data(net_buffer *_buffer, size_t newSize) 942 { 943 net_buffer_private *buffer = (net_buffer_private *)_buffer; 944 TRACE(("trim_data(buffer %p, newSize = %ld, buffer size = %ld)\n", 945 buffer, newSize, buffer->size)); 946 //dump_buffer(buffer); 947 948 if (newSize > buffer->size) 949 return B_BAD_VALUE; 950 if (newSize == buffer->size) 951 return B_OK; 952 953 data_node *node = get_node_at_offset(buffer, newSize); 954 if (node == NULL) { 955 // trim size greater than buffer size 956 return B_BAD_VALUE; 957 } 958 959 int32 diff = node->used + node->offset - newSize; 960 node->tail_space += diff; 961 node->used -= diff; 962 963 if (node->used > 0) 964 node = (data_node *)list_get_next_item(&buffer->buffers, node); 965 966 while (node != NULL) { 967 data_node *next = (data_node *)list_get_next_item(&buffer->buffers, node); 968 list_remove_item(&buffer->buffers, node); 969 remove_data_node(node); 970 971 node = next; 972 } 973 974 buffer->size = newSize; 975 976 //dprintf(" trim result:\n"); 977 //dump_buffer(buffer); 978 return B_OK; 979 } 980 981 982 /*! 983 Appends data coming from buffer \a source to the buffer \a buffer. It only 984 clones the data, though, that is the data is not copied, just referenced. 985 */ 986 status_t 987 append_cloned_data(net_buffer *_buffer, net_buffer *_source, uint32 offset, 988 size_t bytes) 989 { 990 if (bytes == 0) 991 return B_OK; 992 993 net_buffer_private *buffer = (net_buffer_private *)_buffer; 994 net_buffer_private *source = (net_buffer_private *)_source; 995 TRACE(("append_cloned_data(buffer %p, source %p, offset = %ld, bytes = %ld)\n", 996 buffer, source, offset, bytes)); 997 998 if (source->size < offset + bytes || source->size < offset) 999 return B_BAD_VALUE; 1000 1001 // find data_node to start with from the source buffer 1002 data_node *node = get_node_at_offset(source, offset); 1003 if (node == NULL) { 1004 // trim size greater than buffer size 1005 return B_BAD_VALUE; 1006 } 1007 1008 while (node != NULL && bytes > 0) { 1009 data_node *clone = add_data_node(node->header, buffer->first_node.header); 1010 if (clone == NULL) 1011 clone = add_data_node(node->header); 1012 if (clone == NULL) { 1013 // There is not enough space in the buffer for another node 1014 // TODO: handle this case! 1015 dump_buffer(buffer); 1016 dprintf("SOURCE:\n"); 1017 dump_buffer(source); 1018 panic("appending clone buffer in new header not implemented\n"); 1019 return ENOBUFS; 1020 } 1021 1022 if (offset) 1023 offset -= node->offset; 1024 1025 clone->offset = buffer->size; 1026 clone->start = node->start + offset; 1027 clone->used = min_c(bytes, node->used - offset); 1028 clone->header_space = 0; 1029 clone->tail_space = 0; 1030 1031 list_add_item(&buffer->buffers, clone); 1032 1033 offset = 0; 1034 bytes -= clone->used; 1035 buffer->size += clone->used; 1036 node = (data_node *)list_get_next_item(&source->buffers, node); 1037 } 1038 1039 if (bytes != 0) 1040 panic("add_cloned_data() failed, bytes != 0!\n"); 1041 1042 //dprintf(" append cloned result:\n"); 1043 //dump_buffer(buffer); 1044 return B_OK; 1045 } 1046 1047 1048 /*! 1049 Tries to directly access the requested space in the buffer. 1050 If the space is contiguous, the function will succeed and place a pointer 1051 to that space into \a _contiguousBuffer. 1052 1053 \return B_BAD_VALUE if the offset is outside of the buffer's bounds. 1054 \return B_ERROR in case the buffer is not contiguous at that location. 1055 */ 1056 static status_t 1057 direct_access(net_buffer *_buffer, uint32 offset, size_t size, 1058 void **_contiguousBuffer) 1059 { 1060 net_buffer_private *buffer = (net_buffer_private *)_buffer; 1061 1062 //TRACE(("direct_access(buffer %p, offset %ld, size %ld)\n", buffer, offset, size)); 1063 1064 if (offset + size > buffer->size) 1065 return B_BAD_VALUE; 1066 1067 // find node to access 1068 data_node *node = get_node_at_offset(buffer, offset); 1069 if (node == NULL) 1070 return B_BAD_VALUE; 1071 1072 offset -= node->offset; 1073 1074 if (size > node->used - offset) 1075 return B_ERROR; 1076 1077 *_contiguousBuffer = node->start + offset; 1078 return B_OK; 1079 } 1080 1081 1082 static int32 1083 checksum_data(net_buffer *_buffer, uint32 offset, size_t size, bool finalize) 1084 { 1085 net_buffer_private *buffer = (net_buffer_private *)_buffer; 1086 1087 if (offset + size > buffer->size || size == 0) 1088 return B_BAD_VALUE; 1089 1090 // find first node to read from 1091 data_node *node = get_node_at_offset(buffer, offset); 1092 if (node == NULL) 1093 return B_ERROR; 1094 1095 offset -= node->offset; 1096 1097 // Since the maximum buffer size is 65536 bytes, it's impossible 1098 // to overlap 32 bit - we don't need to handle this overlap in 1099 // the loop, we can safely do it afterwards 1100 uint32 sum = 0; 1101 1102 while (true) { 1103 size_t bytes = min_c(size, node->used - offset); 1104 if ((offset + node->offset) & 1) { 1105 // if we're at an uneven offset, we have to swap the checksum 1106 sum += __swap_int16(compute_checksum(node->start + offset, bytes)); 1107 } else 1108 sum += compute_checksum(node->start + offset, bytes); 1109 1110 size -= bytes; 1111 if (size == 0) 1112 break; 1113 1114 offset = 0; 1115 1116 node = (data_node *)list_get_next_item(&buffer->buffers, node); 1117 if (node == NULL) 1118 return B_ERROR; 1119 } 1120 1121 while (sum >> 16) { 1122 sum = (sum & 0xffff) + (sum >> 16); 1123 } 1124 1125 if (!finalize) 1126 return (uint16)sum; 1127 1128 return (uint16)~sum; 1129 } 1130 1131 1132 static uint32 1133 get_iovecs(net_buffer *_buffer, struct iovec *iovecs, uint32 vecCount) 1134 { 1135 net_buffer_private *buffer = (net_buffer_private *)_buffer; 1136 data_node *node = (data_node *)list_get_first_item(&buffer->buffers); 1137 uint32 count = 0; 1138 1139 while (node != NULL && count < vecCount) { 1140 if (node->used > 0) { 1141 iovecs[count].iov_base = node->start; 1142 iovecs[count].iov_len = node->used; 1143 count++; 1144 } 1145 1146 node = (data_node *)list_get_next_item(&buffer->buffers, node); 1147 } 1148 1149 return count; 1150 } 1151 1152 1153 static uint32 1154 count_iovecs(net_buffer *_buffer) 1155 { 1156 net_buffer_private *buffer = (net_buffer_private *)_buffer; 1157 data_node *node = (data_node *)list_get_first_item(&buffer->buffers); 1158 uint32 count = 0; 1159 1160 while (node != NULL) { 1161 if (node->used > 0) 1162 count++; 1163 1164 node = (data_node *)list_get_next_item(&buffer->buffers, node); 1165 } 1166 1167 return count; 1168 } 1169 1170 1171 status_t 1172 init_net_buffers() 1173 { 1174 // TODO improve our code a bit so we can add constructors 1175 // and keep around half-constructed buffers in the slab 1176 1177 sNetBufferCache = create_object_cache("net buffer cache", 1178 sizeof(net_buffer_private), 8, NULL, NULL, NULL); 1179 if (sNetBufferCache == NULL) 1180 return B_NO_MEMORY; 1181 1182 sDataNodeCache = create_object_cache("data node cache", BUFFER_SIZE, 0, 1183 NULL, NULL, NULL); 1184 if (sDataNodeCache == NULL) { 1185 delete_object_cache(sNetBufferCache); 1186 return B_NO_MEMORY; 1187 } 1188 1189 return B_OK; 1190 } 1191 1192 1193 status_t 1194 uninit_net_buffers() 1195 { 1196 delete_object_cache(sNetBufferCache); 1197 delete_object_cache(sDataNodeCache); 1198 1199 return B_OK; 1200 } 1201 1202 1203 static status_t 1204 std_ops(int32 op, ...) 1205 { 1206 switch (op) { 1207 case B_MODULE_INIT: 1208 case B_MODULE_UNINIT: 1209 return B_OK; 1210 1211 default: 1212 return B_ERROR; 1213 } 1214 } 1215 1216 1217 net_buffer_module_info gNetBufferModule = { 1218 { 1219 NET_BUFFER_MODULE_NAME, 1220 0, 1221 std_ops 1222 }, 1223 create_buffer, 1224 free_buffer, 1225 1226 duplicate_buffer, 1227 clone_buffer, 1228 split_buffer, 1229 merge_buffer, 1230 1231 prepend_size, 1232 prepend_data, 1233 append_size, 1234 append_data, 1235 NULL, // insert 1236 NULL, // remove 1237 remove_header, 1238 remove_trailer, 1239 trim_data, 1240 append_cloned_data, 1241 1242 NULL, // associate_data 1243 1244 direct_access, 1245 read_data, 1246 write_data, 1247 1248 checksum_data, 1249 1250 NULL, // get_memory_map 1251 get_iovecs, 1252 count_iovecs, 1253 1254 dump_buffer, // dump 1255 }; 1256 1257