xref: /haiku/src/add-ons/kernel/network/protocols/tcp/BufferQueue.cpp (revision 2f470aec1c92ce6917b8a903e343795dc77af41f)
1 /*
2  * Copyright 2006-2007, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  */
8 
9 
10 #include "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 
23 BufferQueue::BufferQueue(size_t maxBytes)
24 	:
25 	fMaxBytes(maxBytes),
26 	fNumBytes(0),
27 	fContiguousBytes(0),
28 	fFirstSequence(0),
29 	fLastSequence(0),
30 	fPushPointer(0)
31 {
32 }
33 
34 
35 BufferQueue::~BufferQueue()
36 {
37 	// free up any buffers left in the queue
38 
39 	net_buffer *buffer;
40 	while ((buffer = fList.RemoveHead()) != NULL) {
41 		gBufferModule->free(buffer);
42 	}
43 }
44 
45 
46 void
47 BufferQueue::SetMaxBytes(size_t maxBytes)
48 {
49 	fMaxBytes = maxBytes;
50 }
51 
52 
53 void
54 BufferQueue::SetInitialSequence(tcp_sequence sequence)
55 {
56 	TRACE(("BufferQueue@%p::SetInitialSequence(%lu)\n", this, (uint32)sequence));
57 
58 	fFirstSequence = fLastSequence = sequence;
59 }
60 
61 
62 
63 void
64 BufferQueue::Add(net_buffer *buffer)
65 {
66 	Add(buffer, fLastSequence);
67 }
68 
69 
70 void
71 BufferQueue::Add(net_buffer *buffer, tcp_sequence sequence)
72 {
73 	TRACE(("BufferQueue@%p::Add(buffer %p, size %lu, sequence %lu)\n",
74 		this, buffer, buffer->size, (uint32)sequence));
75 	TRACE(("  in: first: %lu, last: %lu, num: %lu, cont: %lu\n", (uint32)fFirstSequence, (uint32)fLastSequence, fNumBytes, fContiguousBytes));
76 
77 	buffer->sequence = sequence;
78 
79 	if (fList.IsEmpty() || sequence >= fLastSequence) {
80 		// we usually just add the buffer to the end of the queue
81 		fList.Add(buffer);
82 
83 		if (sequence == fLastSequence && fLastSequence - fFirstSequence == fNumBytes) {
84 			// there is no hole in the buffer, we can make the whole buffer available
85 			fContiguousBytes += buffer->size;
86 		}
87 
88 		fLastSequence = sequence + buffer->size;
89 		fNumBytes += buffer->size;
90 		TRACE(("  out0: first: %lu, last: %lu, num: %lu, cont: %lu\n", (uint32)fFirstSequence, (uint32)fLastSequence, fNumBytes, fContiguousBytes));
91 		return;
92 	}
93 
94 	if (fLastSequence < sequence + buffer->size)
95 		fLastSequence = sequence + buffer->size;
96 
97 	if (fFirstSequence > sequence) {
98 		// this buffer contains data that is already long gone - trim it
99 		gBufferModule->remove_header(buffer, fFirstSequence - sequence);
100 		sequence = fFirstSequence;
101 	}
102 
103 	// find for the place where to insert the buffer into the queue
104 
105 	SegmentList::ReverseIterator iterator = fList.GetReverseIterator();
106 	net_buffer *previous = NULL;
107 	net_buffer *next = NULL;
108 	while ((previous = iterator.Next()) != NULL) {
109 		if (sequence >= previous->sequence) {
110 			// The new fragment can be inserted after this one
111 			break;
112 		}
113 
114 		next = previous;
115 	}
116 
117 	// check if we have duplicate data, and remove it if that is the case
118 	if (previous != NULL) {
119 		if (sequence == previous->sequence) {
120 			// we already have at least part of this data - ignore new data whenever
121 			// it makes sense (because some TCP implementations send bogus data when
122 			// probing the window)
123 			if (previous->size >= buffer->size) {
124 				gBufferModule->free(buffer);
125 				buffer = NULL;
126 			} else {
127 				fList.Remove(previous);
128 				gBufferModule->free(previous);
129 			}
130 		} else if (tcp_sequence(previous->sequence + previous->size) > sequence)
131 			gBufferModule->remove_header(buffer, previous->sequence + previous->size - sequence);
132 	}
133 
134 	if (buffer != NULL && next != NULL
135 		&& tcp_sequence(sequence + buffer->size) > next->sequence) {
136 		// we already have at least part of this data
137 		if (tcp_sequence(next->sequence + next->size) < sequence + buffer->size) {
138 			net_buffer *remove = next;
139 			next = (net_buffer *)next->link.next;
140 
141 			fList.Remove(remove);
142 			gBufferModule->free(remove);
143 		} else
144 			gBufferModule->remove_trailer(buffer, next->sequence - (sequence + buffer->size));
145 	}
146 
147 	if (buffer == NULL) {
148 		TRACE(("  out1: first: %lu, last: %lu, num: %lu, cont: %lu\n", (uint32)fFirstSequence, (uint32)fLastSequence, fNumBytes, fContiguousBytes));
149 		return;
150 	}
151 
152 	fList.Insert(next, buffer);
153 	fNumBytes += buffer->size;
154 
155 	// we might need to update the number of bytes available
156 
157 	if (fLastSequence - fFirstSequence == fNumBytes)
158 		fContiguousBytes = fNumBytes;
159 	else if (fFirstSequence + fContiguousBytes == sequence) {
160 		// the complicated case: the new segment may have connected almost all
161 		// buffers in the queue (but not all, or the above would be true)
162 
163 		do {
164 			fContiguousBytes += buffer->size;
165 
166 			buffer = (struct net_buffer *)buffer->link.next;
167 		} while (buffer != NULL && fFirstSequence + fContiguousBytes == buffer->sequence);
168 	}
169 
170 	TRACE(("  out2: first: %lu, last: %lu, num: %lu, cont: %lu\n", (uint32)fFirstSequence, (uint32)fLastSequence, fNumBytes, fContiguousBytes));
171 }
172 
173 
174 /*!
175 	Removes all data in the queue up to the \a sequence number as specified.
176 
177 	NOTE:
178 	  If there are missing segments in the buffers to be removed,
179 	  fContiguousBytes is not maintained correctly!
180 */
181 status_t
182 BufferQueue::RemoveUntil(tcp_sequence sequence)
183 {
184 	TRACE(("BufferQueue@%p::RemoveUntil(sequence %lu)\n", this, (uint32)sequence));
185 
186 	if (sequence < fFirstSequence)
187 		return B_OK;
188 
189 	SegmentList::Iterator iterator = fList.GetIterator();
190 	net_buffer *buffer = NULL;
191 	while ((buffer = iterator.Next()) != NULL && buffer->sequence < sequence) {
192 		if (sequence >= buffer->sequence + buffer->size) {
193 			// remove this buffer completely
194 			iterator.Remove();
195 			fNumBytes -= buffer->size;
196 
197 			fContiguousBytes -= buffer->size;
198 			gBufferModule->free(buffer);
199 		} else {
200 			// remove the header as far as needed
201 			size_t size = sequence - buffer->sequence;
202 			gBufferModule->remove_header(buffer, size);
203 
204 			buffer->sequence += size;
205 			fNumBytes -= size;
206 			fContiguousBytes -= size;
207 			break;
208 		}
209 	}
210 
211 	if (fList.IsEmpty())
212 		fFirstSequence = fLastSequence;
213 	else
214 		fFirstSequence = fList.Head()->sequence;
215 
216 	return B_OK;
217 }
218 
219 
220 /*!
221 	Clones the requested data in the buffer queue into the provided \a buffer.
222 */
223 status_t
224 BufferQueue::Get(net_buffer *buffer, tcp_sequence sequence, size_t bytes)
225 {
226 	TRACE(("BufferQueue@%p::Get(sequence %lu, bytes %lu)\n", this, (uint32)sequence, bytes));
227 
228 	if (bytes == 0)
229 		return B_OK;
230 
231 	if (sequence >= fLastSequence || sequence < fFirstSequence) {
232 		// we don't have the requested data
233 		return B_BAD_VALUE;
234 	}
235 	if (tcp_sequence(sequence + bytes) > fLastSequence)
236 		bytes = fLastSequence - sequence;
237 
238 	size_t bytesLeft = bytes;
239 
240 	// find first buffer matching the sequence
241 
242 	SegmentList::Iterator iterator = fList.GetIterator();
243 	net_buffer *source = NULL;
244 	while ((source = iterator.Next()) != NULL) {
245 		if (sequence < source->sequence + source->size)
246 			break;
247 	}
248 
249 	if (source == NULL)
250 		panic("we should have had that data...");
251 	if (source->sequence > sequence)
252 		panic("source %p, sequence = %lu (%lu)\n", source, source->sequence, (uint32)sequence);
253 
254 	// clone the data
255 
256 	uint32 offset = sequence - source->sequence;
257 
258 	while (source != NULL && bytesLeft > 0) {
259 		size_t size = min_c(source->size - offset, bytesLeft);
260 		status_t status = gBufferModule->append_cloned(buffer, source, offset, size);
261 		if (status < B_OK)
262 			return status;
263 
264 		bytesLeft -= size;
265 		offset = 0;
266 		source = iterator.Next();
267 	}
268 
269 	return B_OK;
270 }
271 
272 
273 /*!
274 	Creates a new buffer containing \a bytes bytes from the start of the
275 	buffer queue. If \a remove is \c true, the data is removed from the
276 	queue, if not, the data is cloned from the queue.
277 */
278 status_t
279 BufferQueue::Get(size_t bytes, bool remove, net_buffer **_buffer)
280 {
281 	if (bytes > Available())
282 		bytes = Available();
283 
284 	if (bytes == 0) {
285 		// we don't need to create a buffer when there is no data
286 		*_buffer = NULL;
287 		return B_OK;
288 	}
289 
290 	net_buffer *buffer = fList.First();
291 	size_t bytesLeft = bytes;
292 	ASSERT(buffer != NULL);
293 
294 	if (!remove || buffer->size > bytes) {
295 		// we need a new buffer
296 		buffer = gBufferModule->create(256);
297 		if (buffer == NULL)
298 			return B_NO_MEMORY;
299 	} else {
300 		// we can reuse this buffer
301 		bytesLeft -= buffer->size;
302 		fFirstSequence += buffer->size;
303 
304 		fList.Remove(buffer);
305 	}
306 
307 	// clone/copy the remaining data
308 
309 	SegmentList::Iterator iterator = fList.GetIterator();
310 	net_buffer *source = NULL;
311 	status_t status = B_OK;
312 	while (bytesLeft > 0 && (source = iterator.Next()) != NULL) {
313 		size_t size = min_c(source->size, bytesLeft);
314 		status_t status = gBufferModule->append_cloned(buffer, source, 0, size);
315 		if (status < B_OK)
316 			break;
317 
318 		bytesLeft -= size;
319 
320 		if (!remove)
321 			continue;
322 
323 		// remove either the whole buffer or only the part we cloned
324 
325 		fFirstSequence += size;
326 
327 		if (size == source->size) {
328 			iterator.Remove();
329 			gBufferModule->free(source);
330 		} else {
331 			gBufferModule->remove_header(source, size);
332 			source->sequence += size;
333 		}
334 	}
335 
336 	if (status == B_OK) {
337 		*_buffer = buffer;
338 		if (remove) {
339 			fNumBytes -= bytes;
340 			fContiguousBytes -= bytes;
341 		}
342 	} else
343 		gBufferModule->free(buffer);
344 
345 	return status;
346 }
347 
348 
349 size_t
350 BufferQueue::Available(tcp_sequence sequence) const
351 {
352 	if (sequence > (uint32)fFirstSequence + fContiguousBytes)
353 		return 0;
354 
355 	return fContiguousBytes + fFirstSequence - sequence;
356 }
357 
358 void
359 BufferQueue::SetPushPointer()
360 {
361 	if (fList.IsEmpty())
362 		fPushPointer = 0;
363 	else
364 		fPushPointer = fList.Tail()->sequence + fList.Tail()->size;
365 }
366