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