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