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