xref: /haiku/src/add-ons/kernel/network/stack/net_buffer.cpp (revision 2f470aec1c92ce6917b8a903e343795dc77af41f)
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