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