xref: /haiku/src/add-ons/kernel/network/stack/utility.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
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 "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 static inline void
28 fifo_notify_one_reader(int32 &waiting, sem_id sem)
29 {
30 	if (waiting > 0) {
31 		waiting--;
32 		release_sem_etc(sem, 1, B_DO_NOT_RESCHEDULE);
33 	}
34 }
35 
36 
37 template<typename FifoType> static inline status_t
38 base_fifo_init(FifoType *fifo, const char *name, size_t maxBytes)
39 {
40 	fifo->notify = create_sem(0, name);
41 	fifo->max_bytes = maxBytes;
42 	fifo->current_bytes = 0;
43 	fifo->waiting = 0;
44 	list_init(&fifo->buffers);
45 
46 	return fifo->notify;
47 }
48 
49 
50 template<typename FifoType> static inline status_t
51 base_fifo_enqueue_buffer(FifoType *fifo, net_buffer *buffer)
52 {
53 	if (fifo->max_bytes > 0 && fifo->current_bytes + buffer->size > fifo->max_bytes)
54 		return ENOBUFS;
55 
56 	list_add_item(&fifo->buffers, buffer);
57 	fifo->current_bytes += buffer->size;
58 	fifo_notify_one_reader(fifo->waiting, fifo->notify);
59 
60 	return B_OK;
61 }
62 
63 
64 template<typename FifoType> static inline status_t
65 base_fifo_clear(FifoType *fifo)
66 {
67 	while (true) {
68 		net_buffer *buffer = (net_buffer *)list_remove_head_item(&fifo->buffers);
69 		if (buffer == NULL)
70 			break;
71 
72 		gNetBufferModule.free(buffer);
73 	}
74 
75 	fifo->current_bytes = 0;
76 	return B_OK;
77 }
78 
79 
80 //	#pragma mark -
81 
82 
83 void *
84 UserBuffer::Copy(void *source, size_t length)
85 {
86 	if (fStatus != B_OK)
87 		return NULL;
88 
89 	if (fAvailable < length) {
90 		fStatus = ENOBUFS;
91 		return NULL;
92 	}
93 
94 #ifdef _KERNEL_MODE
95 	fStatus = user_memcpy(fBuffer, source, length);
96 	if (fStatus < B_OK)
97 		return NULL;
98 #else
99 	memcpy(fBuffer, source, length);
100 #endif
101 
102 	void *current = fBuffer;
103 
104 	fAvailable -= length;
105 	fBuffer += length;
106 
107 	return current;
108 }
109 
110 
111 uint16
112 compute_checksum(uint8 *_buffer, size_t length)
113 {
114 	uint16 *buffer = (uint16 *)_buffer;
115 	uint32 sum = 0;
116 
117 	// TODO: unfold loop for speed
118 	// TODO: write processor dependent version for speed
119 	while (length >= 2) {
120 		sum += *buffer++;
121 		length -= 2;
122 	}
123 
124 	if (length) {
125 		// give the last byte it's proper endian-aware treatment
126 #if B_HOST_IS_LENDIAN
127 		sum += *(uint8 *)buffer;
128 #else
129 		uint8 ordered[2];
130 		ordered[0] = *(uint8 *)buffer;
131 		ordered[1] = 0;
132 		sum += *(uint16 *)ordered;
133 #endif
134 	}
135 
136 	while (sum >> 16) {
137 		sum = (sum & 0xffff) + (sum >> 16);
138 	}
139 
140 	return sum;
141 }
142 
143 
144 uint16
145 checksum(uint8 *buffer, size_t length)
146 {
147 	return ~compute_checksum(buffer, length);
148 }
149 
150 
151 //	#pragma mark - Notifications
152 
153 
154 status_t
155 notify_socket(net_socket *socket, uint8 event, int32 value)
156 {
157 	return gNetSocketModule.notify(socket, event, value);
158 }
159 
160 
161 //	#pragma mark - FIFOs
162 
163 
164 Fifo::Fifo(const char *name, size_t maxBytes)
165 {
166 	base_fifo_init(this, name, maxBytes);
167 }
168 
169 
170 Fifo::~Fifo()
171 {
172 	Clear();
173 	delete_sem(notify);
174 }
175 
176 
177 status_t
178 Fifo::InitCheck() const
179 {
180 	return !(notify < B_OK);
181 }
182 
183 
184 status_t
185 Fifo::Enqueue(net_buffer *buffer)
186 {
187 	return base_fifo_enqueue_buffer(this, buffer);
188 }
189 
190 
191 status_t
192 Fifo::EnqueueAndNotify(net_buffer *_buffer, net_socket *socket, uint8 event)
193 {
194 	net_buffer *buffer = gNetBufferModule.clone(_buffer, false);
195 	if (buffer == NULL)
196 		return B_NO_MEMORY;
197 
198 	status_t status = Enqueue(buffer);
199 	if (status < B_OK)
200 		gNetBufferModule.free(buffer);
201 	else
202 		notify_socket(socket, event, current_bytes);
203 
204 	return status;
205 }
206 
207 
208 status_t
209 Fifo::Wait(benaphore *lock, bigtime_t timeout)
210 {
211 	waiting++;
212 	benaphore_unlock(lock);
213 	status_t status = acquire_sem_etc(notify, 1,
214 		B_CAN_INTERRUPT | B_ABSOLUTE_TIMEOUT, timeout);
215 	benaphore_lock(lock);
216 	return status;
217 }
218 
219 
220 net_buffer *
221 Fifo::Dequeue(bool clone)
222 {
223 	net_buffer *buffer = (net_buffer *)list_get_first_item(&buffers);
224 
225 	// assert(buffer != NULL);
226 
227 	if (clone) {
228 		buffer = gNetBufferModule.clone(buffer, false);
229 		fifo_notify_one_reader(waiting, notify);
230 	}else {
231 		list_remove_item(&buffers, buffer);
232 		current_bytes -= buffer->size;
233 	}
234 
235 	return buffer;
236 }
237 
238 
239 ssize_t
240 Fifo::Clear()
241 {
242 	return base_fifo_clear(this);
243 }
244 
245 
246 void
247 Fifo::WakeAll()
248 {
249 #ifdef __HAIKU__
250 	release_sem_etc(notify, 0, B_RELEASE_ALL);
251 #else
252 	release_sem_etc(notify, 0, waiting);
253 #endif
254 }
255 
256 
257 status_t
258 init_fifo(net_fifo *fifo, const char *name, size_t maxBytes)
259 {
260 	status_t status = benaphore_init(&fifo->lock, name);
261 	if (status < B_OK)
262 		return status;
263 
264 	status = base_fifo_init(fifo, name, maxBytes);
265 	if (status < B_OK)
266 		benaphore_destroy(&fifo->lock);
267 
268 	return status;
269 }
270 
271 
272 void
273 uninit_fifo(net_fifo *fifo)
274 {
275 	clear_fifo(fifo);
276 
277 	benaphore_destroy(&fifo->lock);
278 	delete_sem(fifo->notify);
279 }
280 
281 
282 status_t
283 fifo_enqueue_buffer(net_fifo *fifo, net_buffer *buffer)
284 {
285 	BenaphoreLocker locker(fifo->lock);
286 	return base_fifo_enqueue_buffer(fifo, buffer);
287 }
288 
289 
290 /*!
291 	Gets the first buffer from the FIFO. If there is no buffer, it
292 	will wait depending on the \a flags and \a timeout.
293 	The following flags are supported (the rest is ignored):
294 		MSG_DONTWAIT - ignores the timeout and never wait for a buffer; if your
295 			socket is O_NONBLOCK, you should specify this flag. A \a timeout of
296 			zero is equivalent to this flag, though.
297 		MSG_PEEK - returns a clone of the buffer and keep the original
298 			in the FIFO.
299 */
300 ssize_t
301 fifo_dequeue_buffer(net_fifo *fifo, uint32 flags, bigtime_t timeout,
302 	net_buffer **_buffer)
303 {
304 	BenaphoreLocker locker(fifo->lock);
305 	bool dontWait = (flags & MSG_DONTWAIT) != 0 || timeout == 0;
306 	status_t status;
307 
308 	while (true) {
309 		net_buffer *buffer = (net_buffer *)list_get_first_item(&fifo->buffers);
310 		if (buffer != NULL) {
311 			if ((flags & MSG_PEEK) != 0) {
312 				// we need to clone the buffer for inspection; we can't give a
313 				// handle to a buffer that we're still using
314 				buffer = gNetBufferModule.clone(buffer, false);
315 				if (buffer == NULL) {
316 					status = B_NO_MEMORY;
317 					break;
318 				}
319 			} else {
320 				list_remove_item(&fifo->buffers, buffer);
321 				fifo->current_bytes -= buffer->size;
322 			}
323 
324 			*_buffer = buffer;
325 			status = B_OK;
326 			break;
327 		}
328 
329 		if (!dontWait)
330 			fifo->waiting++;
331 
332 		locker.Unlock();
333 
334 		if (dontWait)
335 			return B_WOULD_BLOCK;
336 
337 		// we need to wait until a new buffer becomes available
338 		status = acquire_sem_etc(fifo->notify, 1,
339 			B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, timeout);
340 		if (status < B_OK)
341 			return status;
342 
343 		locker.Lock();
344 	}
345 
346 	// if another thread is waiting for data, since we didn't
347 	// eat the buffer, it will get it
348 	if (flags & MSG_PEEK)
349 		fifo_notify_one_reader(fifo->waiting, fifo->notify);
350 
351 	return status;
352 }
353 
354 
355 status_t
356 clear_fifo(net_fifo *fifo)
357 {
358 	BenaphoreLocker locker(fifo->lock);
359 	return base_fifo_clear(fifo);
360 }
361 
362 
363 status_t
364 fifo_socket_enqueue_buffer(net_fifo *fifo, net_socket *socket, uint8 event,
365 	net_buffer *_buffer)
366 {
367 	net_buffer *buffer = gNetBufferModule.clone(_buffer, false);
368 	if (buffer == NULL)
369 		return B_NO_MEMORY;
370 
371 	BenaphoreLocker locker(fifo->lock);
372 
373 	status_t status = base_fifo_enqueue_buffer(fifo, buffer);
374 	if (status < B_OK)
375 		gNetBufferModule.free(buffer);
376 	else
377 		notify_socket(socket, event, fifo->current_bytes);
378 
379 	return status;
380 }
381 
382 
383 //	#pragma mark - Timer
384 
385 
386 static status_t
387 timer_thread(void * /*data*/)
388 {
389 	status_t status = B_OK;
390 
391 	do {
392 		bigtime_t timeout = B_INFINITE_TIMEOUT;
393 
394 		if (status == B_TIMED_OUT || status == B_OK) {
395 			// scan timers for new timeout and/or execute a timer
396 			if (benaphore_lock(&sTimerLock) < B_OK)
397 				return B_OK;
398 
399 			struct net_timer *timer = NULL;
400 			while (true) {
401 				timer = (net_timer *)list_get_next_item(&sTimers, timer);
402 				if (timer == NULL)
403 					break;
404 
405 				if (timer->due < system_time()) {
406 					// execute timer
407 					list_remove_item(&sTimers, timer);
408 					timer->due = -1;
409 
410 					benaphore_unlock(&sTimerLock);
411 					timer->hook(timer, timer->data);
412 					benaphore_lock(&sTimerLock);
413 
414 					timer = NULL;
415 						// restart scanning as we unlocked the list
416 				} else {
417 					// calculate new timeout
418 					if (timer->due < timeout)
419 						timeout = timer->due;
420 				}
421 			}
422 
423 			sTimerTimeout = timeout;
424 			benaphore_unlock(&sTimerLock);
425 		}
426 
427 		status = acquire_sem_etc(sTimerWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout);
428 			// the wait sem normally can't be acquired, so we
429 			// have to look at the status value the call returns:
430 			//
431 			// B_OK - a new timer has been added or canceled
432 			// B_TIMED_OUT - look for timers to be executed
433 			// B_BAD_SEM_ID - we are asked to quit
434 	} while (status != B_BAD_SEM_ID);
435 
436 	return B_OK;
437 }
438 
439 
440 /*!
441 	Initializes a timer before use. You can also use this function to change
442 	a timer later on, but make sure you have canceled it before using set_timer().
443 */
444 void
445 init_timer(net_timer *timer, net_timer_func hook, void *data)
446 {
447 	timer->hook = hook;
448 	timer->data = data;
449 	timer->due = 0;
450 }
451 
452 
453 /*!
454 	Sets or cancels a timer. When the \a delay is below zero, an eventually running
455 	timer is canceled, if not, it is scheduled to be executed after the specified
456 	\a delay.
457 	You need to have initialized the timer before calling this function.
458 	In case you need to change a running timer, you have to cancel it first, before
459 	making any changes.
460 */
461 void
462 set_timer(net_timer *timer, bigtime_t delay)
463 {
464 	BenaphoreLocker locker(sTimerLock);
465 
466 	if (timer->due > 0 && delay < 0) {
467 		// this timer is scheduled, cancel it
468 		list_remove_item(&sTimers, timer);
469 		timer->due = 0;
470 	}
471 
472 	if (delay >= 0) {
473 		// reschedule or add this timer
474 		if (timer->due <= 0)
475 			list_add_item(&sTimers, timer);
476 
477 		timer->due = system_time() + delay;
478 
479 		// notify timer about the change if necessary
480 		if (sTimerTimeout > timer->due)
481 			release_sem(sTimerWaitSem);
482 	}
483 }
484 
485 
486 bool
487 cancel_timer(struct net_timer *timer)
488 {
489 	BenaphoreLocker locker(sTimerLock);
490 
491 	if (timer->due <= 0)
492 		return false;
493 
494 	// this timer is scheduled, cancel it
495 	list_remove_item(&sTimers, timer);
496 	timer->due = 0;
497 	return true;
498 }
499 
500 
501 bool
502 is_timer_active(net_timer *timer)
503 {
504 	return timer->due > 0;
505 }
506 
507 
508 static int
509 dump_timer(int argc, char **argv)
510 {
511 	kprintf("timer       hook        data        due in\n");
512 
513 	struct net_timer *timer = NULL;
514 	while (true) {
515 		timer = (net_timer *)list_get_next_item(&sTimers, timer);
516 		if (timer == NULL)
517 			break;
518 
519 		kprintf("%p  %p  %p  %Ld\n", timer, timer->hook, timer->data,
520 			timer->due > 0 ? timer->due - system_time() : -1);
521 	}
522 
523 	return 0;
524 }
525 
526 
527 status_t
528 init_timers(void)
529 {
530 	list_init(&sTimers);
531 	sTimerTimeout = B_INFINITE_TIMEOUT;
532 
533 	status_t status = benaphore_init(&sTimerLock, "net timer");
534 	if (status < B_OK)
535 		return status;
536 
537 	sTimerWaitSem = create_sem(0, "net timer wait");
538 	if (sTimerWaitSem < B_OK) {
539 		status = sTimerWaitSem;
540 		goto err1;
541 	}
542 
543 	sTimerThread = spawn_kernel_thread(timer_thread, "net timer",
544 		B_NORMAL_PRIORITY, NULL);
545 	if (sTimerThread < B_OK) {
546 		status = sTimerThread;
547 		goto err2;
548 	}
549 
550 	add_debugger_command("net_timer", dump_timer,
551 		"Lists all active network timer");
552 
553 	return resume_thread(sTimerThread);
554 
555 err1:
556 	benaphore_destroy(&sTimerLock);
557 err2:
558 	delete_sem(sTimerWaitSem);
559 	return status;
560 }
561 
562 
563 void
564 uninit_timers(void)
565 {
566 	benaphore_destroy(&sTimerLock);
567 	delete_sem(sTimerWaitSem);
568 
569 	status_t status;
570 	wait_for_thread(sTimerThread, &status);
571 }
572 
573