xref: /haiku/src/add-ons/kernel/network/protocols/tcp/BufferQueue.cpp (revision 1a76488fc88584bf66b9751d7fb9b6527ac20d87)
1 /*
2  * Copyright 2006-2010, 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 "BufferQueue.h"
11 
12 #include <KernelExport.h>
13 #include <arpa/inet.h>
14 
15 
16 //#define TRACE_BUFFER_QUEUE
17 #ifdef TRACE_BUFFER_QUEUE
18 #	define TRACE(x) dprintf x
19 #else
20 #	define TRACE(x)
21 #endif
22 
23 #if DEBUG_TCP_BUFFER_QUEUE
24 #	define VERIFY() Verify();
25 #else
26 #	define VERIFY() ;
27 #endif
28 
29 
30 BufferQueue::BufferQueue(size_t maxBytes)
31 	:
32 	fMaxBytes(maxBytes),
33 	fNumBytes(0),
34 	fContiguousBytes(0),
35 	fFirstSequence(0),
36 	fLastSequence(0),
37 	fPushPointer(0)
38 {
39 }
40 
41 
42 BufferQueue::~BufferQueue()
43 {
44 	// free up any buffers left in the queue
45 
46 	net_buffer *buffer;
47 	while ((buffer = fList.RemoveHead()) != NULL) {
48 		gBufferModule->free(buffer);
49 	}
50 }
51 
52 
53 void
54 BufferQueue::SetMaxBytes(size_t maxBytes)
55 {
56 	fMaxBytes = maxBytes;
57 }
58 
59 
60 void
61 BufferQueue::SetInitialSequence(tcp_sequence sequence)
62 {
63 	TRACE(("BufferQueue@%p::SetInitialSequence(%" B_PRIu32 ")\n", this,
64 		sequence.Number()));
65 
66 	fFirstSequence = fLastSequence = sequence;
67 }
68 
69 
70 
71 void
72 BufferQueue::Add(net_buffer *buffer)
73 {
74 	Add(buffer, fLastSequence);
75 }
76 
77 
78 void
79 BufferQueue::Add(net_buffer *buffer, tcp_sequence sequence)
80 {
81 	TRACE(("BufferQueue@%p::Add(buffer %p, size %" B_PRIu32 ", sequence %"
82 		B_PRIu32 ")\n", this, buffer, buffer->size, sequence.Number()));
83 	TRACE(("  in: first: %" B_PRIu32 ", last: %" B_PRIu32 ", num: %lu, cont: "
84 		"%lu\n", fFirstSequence.Number(), fLastSequence.Number(), fNumBytes,
85 		fContiguousBytes));
86 	VERIFY();
87 
88 	if (tcp_sequence(sequence + buffer->size) <= fFirstSequence
89 		|| buffer->size == 0) {
90 		// This buffer does not contain any data of interest
91 		gBufferModule->free(buffer);
92 		return;
93 	}
94 	if (sequence < fFirstSequence) {
95 		// this buffer contains data that is already long gone - trim it
96 		gBufferModule->remove_header(buffer,
97 			(fFirstSequence - sequence).Number());
98 		sequence = fFirstSequence;
99 	}
100 
101 	if (fList.IsEmpty() || sequence >= fLastSequence) {
102 		// we usually just add the buffer to the end of the queue
103 		fList.Add(buffer);
104 		buffer->sequence = sequence.Number();
105 
106 		if (sequence == fLastSequence
107 			&& fLastSequence - fFirstSequence == fNumBytes) {
108 			// there is no hole in the buffer, we can make the whole buffer
109 			// available
110 			fContiguousBytes += buffer->size;
111 		}
112 
113 		fLastSequence = sequence + buffer->size;
114 		fNumBytes += buffer->size;
115 
116 		TRACE(("  out0: first: %" B_PRIu32 ", last: %" B_PRIu32 ", num: %"
117 			B_PRIuSIZE ", cont: %" B_PRIuSIZE "\n",	fFirstSequence.Number(),
118 			fLastSequence.Number(), fNumBytes, fContiguousBytes));
119 		VERIFY();
120 		return;
121 	}
122 
123 	if (fLastSequence < sequence + buffer->size)
124 		fLastSequence = sequence + buffer->size;
125 
126 	// find the place where to insert the buffer into the queue
127 
128 	SegmentList::ReverseIterator iterator = fList.GetReverseIterator();
129 	net_buffer *previous = NULL;
130 	net_buffer *next = NULL;
131 	while ((previous = iterator.Next()) != NULL) {
132 		if (sequence >= previous->sequence) {
133 			// The new fragment can be inserted after this one
134 			break;
135 		}
136 
137 		next = previous;
138 	}
139 
140 	// check if we have duplicate data, and remove it if that is the case
141 	if (previous != NULL) {
142 		if (sequence == previous->sequence) {
143 			// we already have at least part of this data - ignore new data
144 			// whenever it makes sense (because some TCP implementations send
145 			// bogus data when probing the window)
146 			if (previous->size >= buffer->size) {
147 				gBufferModule->free(buffer);
148 				buffer = NULL;
149 			} else {
150 				fList.Remove(previous);
151 				fNumBytes -= previous->size;
152 				gBufferModule->free(previous);
153 			}
154 		} else if (tcp_sequence(previous->sequence + previous->size)
155 				>= sequence + buffer->size) {
156 			// We already know this data
157 			gBufferModule->free(buffer);
158 			buffer = NULL;
159 		} else if (tcp_sequence(previous->sequence + previous->size)
160 				> sequence) {
161 			// We already have the first part of this buffer
162 			gBufferModule->remove_header(buffer,
163 				(previous->sequence + previous->size - sequence).Number());
164 			sequence = previous->sequence + previous->size;
165 		}
166 	}
167 
168 	// "next" always starts at or after the buffer sequence
169 	ASSERT(next == NULL || buffer == NULL || next->sequence >= sequence);
170 
171 	while (buffer != NULL && next != NULL
172 		&& tcp_sequence(sequence + buffer->size) > next->sequence) {
173 		// we already have at least part of this data
174 		if (tcp_sequence(next->sequence + next->size)
175 				<= sequence + buffer->size) {
176 			net_buffer *remove = next;
177 			next = (net_buffer *)next->link.next;
178 
179 			fList.Remove(remove);
180 			fNumBytes -= remove->size;
181 			gBufferModule->free(remove);
182 		} else if (tcp_sequence(next->sequence) > sequence) {
183 			// We have the end of this buffer already
184 			gBufferModule->remove_trailer(buffer,
185 				(sequence + buffer->size - next->sequence).Number());
186 		} else {
187 			// We already have this data
188 			gBufferModule->free(buffer);
189 			buffer = NULL;
190 		}
191 	}
192 
193 	if (buffer == NULL) {
194 		TRACE(("  out1: first: %" B_PRIu32 ", last: %" B_PRIu32 ", num: %"
195 			B_PRIuSIZE ", cont: %" B_PRIuSIZE "\n", fFirstSequence.Number(),
196 			fLastSequence.Number(), fNumBytes, fContiguousBytes));
197 		VERIFY();
198 		return;
199 	}
200 
201 	fList.Insert(next, buffer);
202 	buffer->sequence = sequence.Number();
203 	fNumBytes += buffer->size;
204 
205 	// we might need to update the number of bytes available
206 
207 	if (fLastSequence - fFirstSequence == fNumBytes)
208 		fContiguousBytes = fNumBytes;
209 	else if (fFirstSequence + fContiguousBytes == sequence) {
210 		// the complicated case: the new segment may have connected almost all
211 		// buffers in the queue (but not all, or the above would be true)
212 
213 		do {
214 			fContiguousBytes += buffer->size;
215 
216 			buffer = (struct net_buffer *)buffer->link.next;
217 		} while (buffer != NULL
218 			&& fFirstSequence + fContiguousBytes == buffer->sequence);
219 	}
220 
221 	TRACE(("  out2: first: %" B_PRIu32 ", last: %" B_PRIu32 ", num: %lu, cont: "
222 		"%lu\n", fFirstSequence.Number(), fLastSequence.Number(), fNumBytes,
223 		fContiguousBytes));
224 	VERIFY();
225 }
226 
227 
228 /*!	Removes all data in the queue up to the \a sequence number as specified.
229 
230 	NOTE: If there are missing segments in the buffers to be removed,
231 	fContiguousBytes is not maintained correctly!
232 */
233 status_t
234 BufferQueue::RemoveUntil(tcp_sequence sequence)
235 {
236 	TRACE(("BufferQueue@%p::RemoveUntil(sequence %" B_PRIu32 ")\n", this,
237 		sequence.Number()));
238 	VERIFY();
239 
240 	if (sequence < fFirstSequence)
241 		return B_OK;
242 
243 	SegmentList::Iterator iterator = fList.GetIterator();
244 	tcp_sequence lastRemoved = fFirstSequence;
245 	net_buffer *buffer = NULL;
246 	while ((buffer = iterator.Next()) != NULL && buffer->sequence < sequence) {
247 		ASSERT(lastRemoved == buffer->sequence);
248 			// This assures that the queue has no holes, and fContiguousBytes
249 			// is maintained correctly.
250 
251 		if (sequence >= buffer->sequence + buffer->size) {
252 			// remove this buffer completely
253 			iterator.Remove();
254 			fNumBytes -= buffer->size;
255 
256 			fContiguousBytes -= buffer->size;
257 			lastRemoved = buffer->sequence + buffer->size;
258 			gBufferModule->free(buffer);
259 		} else {
260 			// remove the header as far as needed
261 			size_t size = (sequence - buffer->sequence).Number();
262 			gBufferModule->remove_header(buffer, size);
263 
264 			buffer->sequence += size;
265 			fNumBytes -= size;
266 			fContiguousBytes -= size;
267 			break;
268 		}
269 	}
270 
271 	if (fList.IsEmpty())
272 		fFirstSequence = fLastSequence;
273 	else
274 		fFirstSequence = fList.Head()->sequence;
275 
276 	VERIFY();
277 	return B_OK;
278 }
279 
280 
281 /*!	Clones the requested data in the buffer queue into the provided \a buffer.
282 */
283 status_t
284 BufferQueue::Get(net_buffer *buffer, tcp_sequence sequence, size_t bytes)
285 {
286 	TRACE(("BufferQueue@%p::Get(sequence %" B_PRIu32 ", bytes %lu)\n", this,
287 		sequence.Number(), bytes));
288 	VERIFY();
289 
290 	if (bytes == 0)
291 		return B_OK;
292 
293 	if (sequence >= fLastSequence || sequence < fFirstSequence) {
294 		// we don't have the requested data
295 		return B_BAD_VALUE;
296 	}
297 	if (tcp_sequence(sequence + bytes) > fLastSequence)
298 		bytes = (fLastSequence - sequence).Number();
299 
300 	size_t bytesLeft = bytes;
301 
302 	// find first buffer matching the sequence
303 
304 	SegmentList::Iterator iterator = fList.GetIterator();
305 	net_buffer *source = NULL;
306 	while ((source = iterator.Next()) != NULL) {
307 		if (sequence < source->sequence + source->size)
308 			break;
309 	}
310 
311 	if (source == NULL)
312 		panic("we should have had that data...");
313 	if (tcp_sequence(source->sequence) > sequence) {
314 		panic("source %p, sequence = %" B_PRIu32 " (%" B_PRIu32 ")\n", source,
315 			source->sequence, sequence.Number());
316 	}
317 
318 	// clone the data
319 
320 	uint32 offset = (sequence - source->sequence).Number();
321 
322 	while (source != NULL && bytesLeft > 0) {
323 		size_t size = min_c(source->size - offset, bytesLeft);
324 		status_t status = gBufferModule->append_cloned(buffer, source, offset,
325 			size);
326 		if (status < B_OK)
327 			return status;
328 
329 		bytesLeft -= size;
330 		offset = 0;
331 		source = iterator.Next();
332 	}
333 
334 	VERIFY();
335 	return B_OK;
336 }
337 
338 
339 /*!	Creates a new buffer containing \a bytes bytes from the start of the
340 	buffer queue. If \a remove is \c true, the data is removed from the
341 	queue, if not, the data is cloned from the queue.
342 */
343 status_t
344 BufferQueue::Get(size_t bytes, bool remove, net_buffer **_buffer)
345 {
346 	if (bytes > Available())
347 		bytes = Available();
348 
349 	if (bytes == 0) {
350 		// we don't need to create a buffer when there is no data
351 		*_buffer = NULL;
352 		return B_OK;
353 	}
354 
355 	net_buffer *buffer = fList.First();
356 	size_t bytesLeft = bytes;
357 	ASSERT(buffer != NULL);
358 
359 	if (!remove || buffer->size > bytes) {
360 		// we need a new buffer
361 		buffer = gBufferModule->create(256);
362 		if (buffer == NULL)
363 			return B_NO_MEMORY;
364 	} else {
365 		// we can reuse this buffer
366 		bytesLeft -= buffer->size;
367 		fFirstSequence += buffer->size;
368 
369 		fList.Remove(buffer);
370 	}
371 
372 	// clone/copy the remaining data
373 
374 	SegmentList::Iterator iterator = fList.GetIterator();
375 	net_buffer *source = NULL;
376 	status_t status = B_OK;
377 	while (bytesLeft > 0 && (source = iterator.Next()) != NULL) {
378 		size_t size = min_c(source->size, bytesLeft);
379 		status = gBufferModule->append_cloned(buffer, source, 0, size);
380 		if (status < B_OK)
381 			break;
382 
383 		bytesLeft -= size;
384 
385 		if (!remove)
386 			continue;
387 
388 		// remove either the whole buffer or only the part we cloned
389 
390 		fFirstSequence += size;
391 
392 		if (size == source->size) {
393 			iterator.Remove();
394 			gBufferModule->free(source);
395 		} else {
396 			gBufferModule->remove_header(source, size);
397 			source->sequence += size;
398 		}
399 	}
400 
401 	if (remove && buffer->size) {
402 		fNumBytes -= buffer->size;
403 		fContiguousBytes -= buffer->size;
404 	}
405 
406 	// We always return what we got, or else we would lose data
407 	if (status < B_OK && buffer->size == 0) {
408 		// We could not remove any bytes from the buffer, so
409 		// let this call fail.
410 		gBufferModule->free(buffer);
411 		VERIFY();
412 		return status;
413 	}
414 
415 	*_buffer = buffer;
416 	VERIFY();
417 	return B_OK;
418 }
419 
420 
421 size_t
422 BufferQueue::Available(tcp_sequence sequence) const
423 {
424 	if (sequence > (fFirstSequence + fContiguousBytes).Number())
425 		return 0;
426 
427 	return (fContiguousBytes + fFirstSequence - sequence).Number();
428 }
429 
430 
431 void
432 BufferQueue::SetPushPointer()
433 {
434 	if (fList.IsEmpty())
435 		fPushPointer = 0;
436 	else
437 		fPushPointer = fList.Tail()->sequence + fList.Tail()->size;
438 }
439 
440 
441 int
442 BufferQueue::PopulateSackInfo(tcp_sequence sequence, int maxSackCount,
443 	tcp_sack* sacks)
444 {
445 	SegmentList::ReverseIterator iterator = fList.GetReverseIterator();
446 	net_buffer* buffer = iterator.Next();
447 
448 	int sackCount = 0;
449 	TRACE(("BufferQueue::PopulateSackInfo() %" B_PRIu32 "\n",
450 		sequence.Number()));
451 	while (buffer != NULL && buffer->sequence > sequence) {
452 		if (buffer->sequence + buffer->size < sacks[sackCount].left_edge) {
453 			if (sackCount + 1 == maxSackCount)
454 				break;
455 			++sackCount;
456 			sacks[sackCount].left_edge = buffer->sequence;
457 			sacks[sackCount].right_edge = buffer->sequence + buffer->size;
458 		} else {
459 			sacks[sackCount].left_edge = buffer->sequence;
460 			if (sacks[sackCount].right_edge == 0)
461 				sacks[sackCount].right_edge = buffer->sequence + buffer->size;
462 		}
463 
464 		buffer = iterator.Next();
465 	}
466 
467 	if (sacks[0].left_edge != 0) {
468 		for (int i = 0; i <= sackCount; ++i) {
469 			sacks[i].left_edge = htonl(sacks[i].left_edge);
470 			sacks[i].right_edge = htonl(sacks[i].right_edge);
471 		}
472 		++sackCount;
473 	}
474 
475 	return sackCount;
476 }
477 
478 #if DEBUG_TCP_BUFFER_QUEUE
479 
480 /*!	Perform a sanity check of the whole queue.
481 */
482 void
483 BufferQueue::Verify() const
484 {
485 	ASSERT(Available() == 0 || fList.First() != NULL);
486 
487 	if (fList.First() == NULL) {
488 		ASSERT(fNumBytes == 0);
489 		return;
490 	}
491 
492 	SegmentList::ConstIterator iterator = fList.GetIterator();
493 	size_t numBytes = 0;
494 	size_t contiguousBytes = 0;
495 	bool contiguous = true;
496 	tcp_sequence last = fFirstSequence;
497 
498 	while (net_buffer* buffer = iterator.Next()) {
499 		if (contiguous && buffer->sequence == last)
500 			contiguousBytes += buffer->size;
501 		else
502 			contiguous = false;
503 
504 		ASSERT(last <= buffer->sequence);
505 		ASSERT(buffer->size > 0);
506 
507 		numBytes += buffer->size;
508 		last = buffer->sequence + buffer->size;
509 	}
510 
511 	ASSERT(last == fLastSequence);
512 	ASSERT(contiguousBytes == fContiguousBytes);
513 	ASSERT(numBytes == fNumBytes);
514 }
515 
516 
517 void
518 BufferQueue::Dump() const
519 {
520 	SegmentList::ConstIterator iterator = fList.GetIterator();
521 	int32 number = 0;
522 	while (net_buffer* buffer = iterator.Next()) {
523 		kprintf("      %" B_PRId32 ". buffer %p, sequence %" B_PRIu32
524 			", size %" B_PRIu32 "\n", ++number, buffer, buffer->sequence,
525 			buffer->size);
526 	}
527 }
528 
529 #endif	// DEBUG_TCP_BUFFER_QUEUE
530