xref: /haiku/src/add-ons/kernel/network/stack/utility.cpp (revision 9d6d3fcf5fe8308cd020cecf89dede440346f8c4)
1 /*
2  * Copyright 2006, 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 "stack_private.h"
11 #include "utility.h"
12 
13 #include <net_buffer.h>
14 #include <util/AutoLock.h>
15 
16 #include <ByteOrder.h>
17 #include <KernelExport.h>
18 
19 
20 static struct list sTimers;
21 static benaphore sTimerLock;
22 static sem_id sTimerWaitSem;
23 static thread_id sTimerThread;
24 static bigtime_t sTimerTimeout;
25 
26 
27 uint16
28 compute_checksum(uint8 *_buffer, size_t length)
29 {
30 	uint16 *buffer = (uint16 *)_buffer;
31 	uint32 sum = 0;
32 
33 	// TODO: unfold loop for speed
34 	// TODO: write processor dependent version for speed
35 	while (length >= 2) {
36 		sum += *buffer++;
37 		length -= 2;
38 	}
39 
40 	if (length) {
41 		// give the last byte it's proper endian-aware treatment
42 #if B_HOST_IS_LENDIAN
43 		sum += *(uint8 *)buffer;
44 #else
45 		uint8 ordered[2];
46 		ordered[0] = *(uint8 *)buffer;
47 		ordered[1] = 0;
48 		sum += *(uint16 *)ordered;
49 #endif
50 	}
51 
52 	while (sum >> 16) {
53 		sum = (sum & 0xffff) + (sum >> 16);
54 	}
55 
56 	return sum;
57 }
58 
59 
60 uint16
61 checksum(uint8 *buffer, size_t length)
62 {
63 	return ~compute_checksum(buffer, length);
64 }
65 
66 
67 //	#pragma mark - Notifications
68 
69 
70 status_t
71 notify_socket(net_socket *socket, uint8 event, int32 value)
72 {
73 	return gNetSocketModule.notify(socket, event, value);
74 }
75 
76 
77 //	#pragma mark - FIFOs
78 
79 
80 status_t
81 init_fifo(net_fifo *fifo, const char *name, size_t maxBytes)
82 {
83 	status_t status = benaphore_init(&fifo->lock, name);
84 	if (status < B_OK)
85 		return status;
86 
87 	fifo->notify = create_sem(1, name);
88 	if (fifo->notify < B_OK) {
89 		benaphore_destroy(&fifo->lock);
90 		return fifo->notify;
91 	}
92 
93 	fifo->max_bytes = maxBytes;
94 	fifo->current_bytes = 0;
95 	fifo->waiting = 0;
96 
97 	list_init(&fifo->buffers);
98 
99 	return B_OK;
100 }
101 
102 
103 void
104 uninit_fifo(net_fifo *fifo)
105 {
106 	clear_fifo(fifo);
107 
108 	benaphore_destroy(&fifo->lock);
109 	delete_sem(fifo->notify);
110 }
111 
112 
113 status_t
114 fifo_enqueue_buffer(net_fifo *fifo, net_buffer *buffer)
115 {
116 	BenaphoreLocker locker(fifo->lock);
117 
118 	if (fifo->max_bytes > 0 && fifo->current_bytes + buffer->size > fifo->max_bytes)
119 		return ENOBUFS;
120 
121 	list_add_item(&fifo->buffers, buffer);
122 	fifo->current_bytes += buffer->size;
123 
124 	if (fifo->waiting > 0) {
125 		fifo->waiting--;
126 		release_sem_etc(fifo->notify, 1, B_DO_NOT_RESCHEDULE);
127 			// we still hold the benaphore lock, so it makes no sense
128 			// to reschedule after having released the sync semaphore
129 	}
130 
131 	return B_OK;
132 }
133 
134 
135 /*!
136 	Gets the first buffer from the FIFO. If there is no buffer, it
137 	will wait depending on the \a flags and \a timeout.
138 	The following flags are supported (the rest is ignored):
139 		MSG_DONTWAIT - ignores the timeout and never wait for a buffer; if your
140 			socket is O_NONBLOCK, you should specify this flag. A \a timeout of
141 			zero is equivalent to this flag, though.
142 		MSG_PEEK - returns a clone of the buffer and keep the original
143 			in the FIFO.
144 */
145 ssize_t
146 fifo_dequeue_buffer(net_fifo *fifo, uint32 flags, bigtime_t timeout,
147 	net_buffer **_buffer)
148 {
149 	benaphore_lock(&fifo->lock);
150 	bool dontWait = (flags & MSG_DONTWAIT) != 0 || timeout == 0;
151 	status_t status;
152 
153 	while (true) {
154 		net_buffer *buffer = (net_buffer *)list_get_first_item(&fifo->buffers);
155 		if (buffer != NULL) {
156 			if ((flags & MSG_PEEK) != 0) {
157 				// we need to clone the buffer for inspection; we can't give a
158 				// handle to a buffer that we're still using
159 				buffer = gNetBufferModule.clone(buffer, false);
160 				if (buffer == NULL) {
161 					status = B_NO_MEMORY;
162 					break;
163 				}
164 			} else
165 				list_remove_item(&fifo->buffers, buffer);
166 
167 			*_buffer = buffer;
168 			status = B_OK;
169 			break;
170 		}
171 
172 		if (!dontWait)
173 			fifo->waiting++;
174 
175 		// we need to wait until a new buffer becomes available
176 		benaphore_unlock(&fifo->lock);
177 
178 		if (dontWait)
179 			return B_WOULD_BLOCK;
180 
181 		status = acquire_sem_etc(fifo->notify, 1,
182 			B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, timeout);
183 		if (status < B_OK)
184 			return status;
185 
186 		// try again
187 		benaphore_lock(&fifo->lock);
188 	}
189 
190 	if ((flags & MSG_PEEK) != 0 && fifo->waiting > 0) {
191 		// another thread is waiting for data, since we didn't eat the
192 		// buffer, it gets it
193 		fifo->waiting--;
194 		release_sem_etc(fifo->notify, 1, B_DO_NOT_RESCHEDULE);
195 	}
196 
197 	benaphore_unlock(&fifo->lock);
198 	return status;
199 }
200 
201 
202 status_t
203 clear_fifo(net_fifo *fifo)
204 {
205 	BenaphoreLocker locker(fifo->lock);
206 
207 	while (true) {
208 		net_buffer *buffer = (net_buffer *)list_remove_head_item(&fifo->buffers);
209 		if (buffer == NULL)
210 			break;
211 
212 		gNetBufferModule.free(buffer);
213 	}
214 
215 	fifo->current_bytes = 0;
216 	return B_OK;
217 }
218 
219 
220 //	#pragma mark - Timer
221 
222 
223 static status_t
224 timer_thread(void * /*data*/)
225 {
226 	status_t status = B_OK;
227 
228 	do {
229 		bigtime_t timeout = B_INFINITE_TIMEOUT;
230 
231 		if (status == B_TIMED_OUT || status == B_OK) {
232 			// scan timers for new timeout and/or execute a timer
233 			if (benaphore_lock(&sTimerLock) < B_OK)
234 				return B_OK;
235 
236 			struct net_timer *timer = NULL;
237 			while (true) {
238 				timer = (net_timer *)list_get_next_item(&sTimers, timer);
239 				if (timer == NULL)
240 					break;
241 
242 				if (timer->due < system_time()) {
243 					// execute timer
244 					list_remove_item(&sTimers, timer);
245 					timer->due = -1;
246 
247 					benaphore_unlock(&sTimerLock);
248 					timer->hook(timer, timer->data);
249 					benaphore_lock(&sTimerLock);
250 
251 					timer = NULL;
252 						// restart scanning as we unlocked the list
253 				} else {
254 					// calculate new timeout
255 					if (timer->due < timeout)
256 						timeout = timer->due;
257 				}
258 			}
259 
260 			sTimerTimeout = timeout;
261 			benaphore_unlock(&sTimerLock);
262 		}
263 
264 		status = acquire_sem_etc(sTimerWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout);
265 			// the wait sem normally can't be acquired, so we
266 			// have to look at the status value the call returns:
267 			//
268 			// B_OK - a new timer has been added or canceled
269 			// B_TIMED_OUT - look for timers to be executed
270 			// B_BAD_SEM_ID - we are asked to quit
271 	} while (status != B_BAD_SEM_ID);
272 
273 	return B_OK;
274 }
275 
276 
277 /*!
278 	Initializes a timer before use. You can also use this function to change
279 	a timer later on, but make sure you have canceled it before using set_timer().
280 */
281 void
282 init_timer(net_timer *timer, net_timer_func hook, void *data)
283 {
284 	timer->hook = hook;
285 	timer->data = data;
286 	timer->due = 0;
287 }
288 
289 
290 /*!
291 	Sets or cancels a timer. When the \a delay is below zero, an eventually running
292 	timer is canceled, if not, it is scheduled to be executed after the specified
293 	\a delay.
294 	You need to have initialized the timer before calling this function.
295 	In case you need to change a running timer, you have to cancel it first, before
296 	making any changes.
297 */
298 void
299 set_timer(net_timer *timer, bigtime_t delay)
300 {
301 	BenaphoreLocker locker(sTimerLock);
302 
303 	if (timer->due > 0 && delay < 0) {
304 		// this timer is scheduled, cancel it
305 		list_remove_item(&sTimers, timer);
306 		timer->due = 0;
307 	}
308 
309 	if (delay >= 0) {
310 		// reschedule or add this timer
311 		if (timer->due <= 0)
312 			list_add_item(&sTimers, timer);
313 
314 		timer->due = system_time() + delay;
315 
316 		// notify timer about the change if necessary
317 		if (sTimerTimeout > timer->due)
318 			release_sem(sTimerWaitSem);
319 	}
320 }
321 
322 
323 bool
324 cancel_timer(struct net_timer *timer)
325 {
326 	BenaphoreLocker locker(sTimerLock);
327 
328 	if (timer->due <= 0)
329 		return false;
330 
331 	// this timer is scheduled, cancel it
332 	list_remove_item(&sTimers, timer);
333 	timer->due = 0;
334 	return true;
335 }
336 
337 
338 bool
339 is_timer_active(net_timer *timer)
340 {
341 	return timer->due > 0;
342 }
343 
344 
345 status_t
346 init_timers(void)
347 {
348 	list_init(&sTimers);
349 	sTimerTimeout = B_INFINITE_TIMEOUT;
350 
351 	status_t status = benaphore_init(&sTimerLock, "net timer");
352 	if (status < B_OK)
353 		return status;
354 
355 	sTimerWaitSem = create_sem(0, "net timer wait");
356 	if (sTimerWaitSem < B_OK) {
357 		status = sTimerWaitSem;
358 		goto err1;
359 	}
360 
361 	sTimerThread = spawn_kernel_thread(timer_thread, "net timer",
362 		B_NORMAL_PRIORITY, NULL);
363 	if (sTimerThread < B_OK) {
364 		status = sTimerThread;
365 		goto err2;
366 	}
367 
368 	return resume_thread(sTimerThread);
369 
370 err1:
371 	benaphore_destroy(&sTimerLock);
372 err2:
373 	delete_sem(sTimerWaitSem);
374 	return status;
375 }
376 
377 
378 void
379 uninit_timers(void)
380 {
381 	benaphore_destroy(&sTimerLock);
382 	delete_sem(sTimerWaitSem);
383 
384 	status_t status;
385 	wait_for_thread(sTimerThread, &status);
386 }
387 
388