xref: /haiku/src/add-ons/kernel/network/stack/utility.cpp (revision 68ea01249e1e2088933cb12f9c28d4e5c5d1c9ef)
1 /*
2  * Copyright 2006-2013, 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 - UserBuffer
127 
128 
129 void*
130 UserBuffer::Push(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 status_t
158 UserBuffer::Pad(size_t length)
159 {
160 	if (fStatus != B_OK)
161 		return fStatus;
162 
163 	if (fAvailable < length)
164 		return fStatus = ENOBUFS;
165 
166 	fStatus = user_memset(fBuffer, 0, length);
167 	if (fStatus != B_OK)
168 		return fStatus;
169 
170 	fAvailable -= length;
171 	fBuffer += length;
172 
173 	return B_OK;
174 }
175 
176 
177 status_t
178 UserBuffer::PadToNext(size_t length)
179 {
180 	return Pad((BytesConsumed() + length - 1) / length - BytesConsumed());
181 }
182 
183 
184 // #pragma mark -
185 
186 
187 uint16
188 compute_checksum(uint8* _buffer, size_t length)
189 {
190 	uint16* buffer = (uint16*)_buffer;
191 	uint32 sum = 0;
192 
193 	// TODO: unfold loop for speed
194 	// TODO: write processor dependent version for speed
195 	while (length >= 2) {
196 		sum += *buffer++;
197 		length -= 2;
198 	}
199 
200 	if (length) {
201 		// give the last byte it's proper endian-aware treatment
202 #if B_HOST_IS_LENDIAN
203 		sum += *(uint8*)buffer;
204 #else
205 		uint8 ordered[2];
206 		ordered[0] = *(uint8*)buffer;
207 		ordered[1] = 0;
208 		sum += *(uint16*)ordered;
209 #endif
210 	}
211 
212 	while (sum >> 16) {
213 		sum = (sum & 0xffff) + (sum >> 16);
214 	}
215 
216 	return sum;
217 }
218 
219 
220 uint16
221 checksum(uint8* buffer, size_t length)
222 {
223 	return ~compute_checksum(buffer, length);
224 }
225 
226 
227 //	#pragma mark - Notifications
228 
229 
230 status_t
231 notify_socket(net_socket* socket, uint8 event, int32 value)
232 {
233 	return gNetSocketModule.notify(socket, event, value);
234 }
235 
236 
237 //	#pragma mark - FIFOs
238 
239 
240 Fifo::Fifo(const char* name, size_t maxBytes)
241 {
242 	base_fifo_init(this, name, maxBytes);
243 }
244 
245 
246 Fifo::~Fifo()
247 {
248 	Clear();
249 	delete_sem(notify);
250 }
251 
252 
253 status_t
254 Fifo::InitCheck() const
255 {
256 	return !(notify < B_OK);
257 }
258 
259 
260 status_t
261 Fifo::Enqueue(net_buffer* buffer)
262 {
263 	return base_fifo_enqueue_buffer(this, buffer);
264 }
265 
266 
267 status_t
268 Fifo::EnqueueAndNotify(net_buffer* _buffer, net_socket* socket, uint8 event)
269 {
270 	net_buffer *buffer = gNetBufferModule.clone(_buffer, false);
271 	if (buffer == NULL)
272 		return B_NO_MEMORY;
273 
274 	status_t status = Enqueue(buffer);
275 	if (status < B_OK)
276 		gNetBufferModule.free(buffer);
277 	else
278 		notify_socket(socket, event, current_bytes);
279 
280 	return status;
281 }
282 
283 
284 status_t
285 Fifo::Wait(mutex* lock, bigtime_t timeout)
286 {
287 	waiting++;
288 	mutex_unlock(lock);
289 	status_t status = acquire_sem_etc(notify, 1,
290 		B_CAN_INTERRUPT | B_ABSOLUTE_TIMEOUT, timeout);
291 	mutex_lock(lock);
292 	return status;
293 }
294 
295 
296 net_buffer*
297 Fifo::Dequeue(bool clone)
298 {
299 	net_buffer* buffer = (net_buffer*)list_get_first_item(&buffers);
300 
301 	// assert(buffer != NULL);
302 
303 	if (clone) {
304 		buffer = gNetBufferModule.clone(buffer, false);
305 		fifo_notify_one_reader(waiting, notify);
306 	}else {
307 		list_remove_item(&buffers, buffer);
308 		current_bytes -= buffer->size;
309 	}
310 
311 	return buffer;
312 }
313 
314 
315 status_t
316 Fifo::Clear()
317 {
318 	return base_fifo_clear(this);
319 }
320 
321 
322 void
323 Fifo::WakeAll()
324 {
325 #ifdef __HAIKU__
326 	release_sem_etc(notify, 0, B_RELEASE_ALL);
327 #else
328 	release_sem_etc(notify, 0, waiting);
329 #endif
330 }
331 
332 
333 status_t
334 init_fifo(net_fifo* fifo, const char* name, size_t maxBytes)
335 {
336 	mutex_init_etc(&fifo->lock, name, MUTEX_FLAG_CLONE_NAME);
337 
338 	status_t status = base_fifo_init(fifo, name, maxBytes);
339 	if (status < B_OK)
340 		mutex_destroy(&fifo->lock);
341 
342 	return status;
343 }
344 
345 
346 void
347 uninit_fifo(net_fifo* fifo)
348 {
349 	clear_fifo(fifo);
350 
351 	mutex_destroy(&fifo->lock);
352 	delete_sem(fifo->notify);
353 }
354 
355 
356 status_t
357 fifo_enqueue_buffer(net_fifo* fifo, net_buffer* buffer)
358 {
359 	MutexLocker locker(fifo->lock);
360 	return base_fifo_enqueue_buffer(fifo, buffer);
361 }
362 
363 
364 /*!	Gets the first buffer from the FIFO. If there is no buffer, it
365 	will wait depending on the \a flags and \a timeout.
366 	The following flags are supported (the rest is ignored):
367 		MSG_DONTWAIT - ignores the timeout and never wait for a buffer; if your
368 			socket is O_NONBLOCK, you should specify this flag. A \a timeout of
369 			zero is equivalent to this flag, though.
370 		MSG_PEEK - returns a clone of the buffer and keep the original
371 			in the FIFO.
372 */
373 ssize_t
374 fifo_dequeue_buffer(net_fifo* fifo, uint32 flags, bigtime_t timeout,
375 	net_buffer** _buffer)
376 {
377 	MutexLocker locker(fifo->lock);
378 	bool dontWait = (flags & MSG_DONTWAIT) != 0 || timeout == 0;
379 	status_t status;
380 
381 	while (true) {
382 		net_buffer* buffer = (net_buffer*)list_get_first_item(&fifo->buffers);
383 		if (buffer != NULL) {
384 			if ((flags & MSG_PEEK) != 0) {
385 				// we need to clone the buffer for inspection; we can't give a
386 				// handle to a buffer that we're still using
387 				buffer = gNetBufferModule.clone(buffer, false);
388 				if (buffer == NULL) {
389 					status = B_NO_MEMORY;
390 					break;
391 				}
392 			} else {
393 				list_remove_item(&fifo->buffers, buffer);
394 				fifo->current_bytes -= buffer->size;
395 			}
396 
397 			*_buffer = buffer;
398 			status = B_OK;
399 			break;
400 		}
401 
402 		if (!dontWait)
403 			fifo->waiting++;
404 
405 		locker.Unlock();
406 
407 		if (dontWait)
408 			return B_WOULD_BLOCK;
409 
410 		// we need to wait until a new buffer becomes available
411 		status = acquire_sem_etc(fifo->notify, 1,
412 			B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, timeout);
413 		if (status < B_OK)
414 			return status;
415 
416 		locker.Lock();
417 	}
418 
419 	// if another thread is waiting for data, since we didn't
420 	// eat the buffer, it will get it
421 	if (flags & MSG_PEEK)
422 		fifo_notify_one_reader(fifo->waiting, fifo->notify);
423 
424 	return status;
425 }
426 
427 
428 status_t
429 clear_fifo(net_fifo* fifo)
430 {
431 	MutexLocker locker(fifo->lock);
432 	return base_fifo_clear(fifo);
433 }
434 
435 
436 status_t
437 fifo_socket_enqueue_buffer(net_fifo* fifo, net_socket* socket, uint8 event,
438 	net_buffer* _buffer)
439 {
440 	net_buffer *buffer = gNetBufferModule.clone(_buffer, false);
441 	if (buffer == NULL)
442 		return B_NO_MEMORY;
443 
444 	MutexLocker locker(fifo->lock);
445 
446 	status_t status = base_fifo_enqueue_buffer(fifo, buffer);
447 	if (status < B_OK)
448 		gNetBufferModule.free(buffer);
449 	else
450 		notify_socket(socket, event, fifo->current_bytes);
451 
452 	return status;
453 }
454 
455 
456 //	#pragma mark - Timer
457 
458 
459 static status_t
460 timer_thread(void* /*data*/)
461 {
462 	status_t status = B_OK;
463 
464 	do {
465 		bigtime_t timeout = B_INFINITE_TIMEOUT;
466 
467 		if (status == B_TIMED_OUT || status == B_OK) {
468 			// scan timers for new timeout and/or execute a timer
469 			mutex_lock(&sTimerLock);
470 
471 			struct net_timer* timer = NULL;
472 			while (true) {
473 				timer = (net_timer*)list_get_next_item(&sTimers, timer);
474 				if (timer == NULL)
475 					break;
476 
477 				if (timer->due < system_time()) {
478 					// execute timer
479 					list_remove_item(&sTimers, timer);
480 					timer->due = -1;
481 					sCurrentTimer = timer;
482 
483 					mutex_unlock(&sTimerLock);
484 					timer->hook(timer, timer->data);
485 					mutex_lock(&sTimerLock);
486 
487 					sCurrentTimer = NULL;
488 					sWaitForTimerCondition.NotifyAll();
489 
490 					timer = NULL;
491 						// restart scanning as we unlocked the list
492 				} else {
493 					// calculate new timeout
494 					if (timer->due < timeout)
495 						timeout = timer->due;
496 				}
497 			}
498 
499 			sTimerTimeout = timeout;
500 			mutex_unlock(&sTimerLock);
501 		}
502 
503 		status = acquire_sem_etc(sTimerWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout);
504 			// the wait sem normally can't be acquired, so we
505 			// have to look at the status value the call returns:
506 			//
507 			// B_OK - a new timer has been added or canceled
508 			// B_TIMED_OUT - look for timers to be executed
509 			// B_BAD_SEM_ID - we are asked to quit
510 	} while (status != B_BAD_SEM_ID);
511 
512 	return B_OK;
513 }
514 
515 
516 /*!
517 	Initializes a timer before use. You can also use this function to change
518 	a timer later on, but make sure you have canceled it before using set_timer().
519 */
520 void
521 init_timer(net_timer* timer, net_timer_func hook, void* data)
522 {
523 	timer->hook = hook;
524 	timer->data = data;
525 	timer->due = 0;
526 	timer->flags = 0;
527 }
528 
529 
530 /*!	Sets or cancels a timer. When the \a delay is below zero, an eventually
531 	running timer is canceled, if not, it is scheduled to be executed after the
532 	specified \a delay.
533 	You need to have initialized the timer before calling this function.
534 
535 	In case you need to change a running timer, you have to cancel it first,
536 	before making any changes.
537 */
538 void
539 set_timer(net_timer* timer, bigtime_t delay)
540 {
541 	MutexLocker locker(sTimerLock);
542 
543 	TRACE("set_timer %p, hook %p, data %p\n", timer, timer->hook, timer->data);
544 
545 	if (timer->due > 0 && delay < 0) {
546 		// this timer is scheduled, cancel it
547 		list_remove_item(&sTimers, timer);
548 		timer->due = 0;
549 	}
550 
551 	if (delay >= 0) {
552 		// reschedule or add this timer
553 		if (timer->due <= 0)
554 			list_add_item(&sTimers, timer);
555 
556 		timer->due = system_time() + delay;
557 
558 		// notify timer about the change if necessary
559 		if (sTimerTimeout > timer->due)
560 			release_sem(sTimerWaitSem);
561 	}
562 }
563 
564 
565 bool
566 cancel_timer(struct net_timer* timer)
567 {
568 	MutexLocker locker(sTimerLock);
569 
570 	TRACE("cancel_timer %p, hook %p, data %p\n", timer, timer->hook,
571 		timer->data);
572 
573 	if (timer->due <= 0)
574 		return false;
575 
576 	// this timer is scheduled, cancel it
577 	list_remove_item(&sTimers, timer);
578 	timer->due = 0;
579 	return true;
580 }
581 
582 
583 status_t
584 wait_for_timer(struct net_timer* timer)
585 {
586 	if (find_thread(NULL) == sTimerThread) {
587 		// let's not wait for ourselves...
588 		return B_BAD_VALUE;
589 	}
590 
591 	while (true) {
592 		MutexLocker locker(sTimerLock);
593 
594 		if (timer->due <= 0 && sCurrentTimer != timer)
595 			return B_OK;
596 
597 		// we actually need to wait for this timer
598 		ConditionVariableEntry entry;
599 		sWaitForTimerCondition.Add(&entry);
600 
601 		locker.Unlock();
602 
603 		entry.Wait();
604 	}
605 
606 	return B_OK;
607 }
608 
609 
610 bool
611 is_timer_active(net_timer* timer)
612 {
613 	return timer->due > 0;
614 }
615 
616 
617 bool
618 is_timer_running(net_timer* timer)
619 {
620 	return timer == sCurrentTimer;
621 }
622 
623 
624 static int
625 dump_timer(int argc, char** argv)
626 {
627 	kprintf("timer       hook        data        due in\n");
628 
629 	struct net_timer* timer = NULL;
630 	while (true) {
631 		timer = (net_timer*)list_get_next_item(&sTimers, timer);
632 		if (timer == NULL)
633 			break;
634 
635 		kprintf("%p  %p  %p  %" B_PRId64 "\n", timer, timer->hook, timer->data,
636 			timer->due > 0 ? timer->due - system_time() : -1);
637 	}
638 
639 	return 0;
640 }
641 
642 
643 status_t
644 init_timers(void)
645 {
646 	list_init(&sTimers);
647 	sTimerTimeout = B_INFINITE_TIMEOUT;
648 
649 	status_t status = B_OK;
650 	mutex_init(&sTimerLock, "net timer");
651 
652 	sTimerWaitSem = create_sem(0, "net timer wait");
653 	if (sTimerWaitSem < B_OK) {
654 		status = sTimerWaitSem;
655 		goto err1;
656 	}
657 
658 	sTimerThread = spawn_kernel_thread(timer_thread, "net timer",
659 		B_NORMAL_PRIORITY, NULL);
660 	if (sTimerThread < B_OK) {
661 		status = sTimerThread;
662 		goto err2;
663 	}
664 
665 	sWaitForTimerCondition.Init(NULL, "wait for net timer");
666 
667 	add_debugger_command("net_timer", dump_timer,
668 		"Lists all active network timer");
669 
670 	return resume_thread(sTimerThread);
671 
672 err1:
673 	mutex_destroy(&sTimerLock);
674 err2:
675 	delete_sem(sTimerWaitSem);
676 	return status;
677 }
678 
679 
680 void
681 uninit_timers(void)
682 {
683 	delete_sem(sTimerWaitSem);
684 
685 	status_t status;
686 	wait_for_thread(sTimerThread, &status);
687 
688 	mutex_lock(&sTimerLock);
689 
690 	mutex_destroy(&sTimerLock);
691 
692 	remove_debugger_command("net_timer", dump_timer);
693 }
694 
695 
696 //	#pragma mark - Syscall restart
697 
698 
699 bool
700 is_syscall(void)
701 {
702 	return is_called_via_syscall();
703 }
704 
705 
706 bool
707 is_restarted_syscall(void)
708 {
709 	return syscall_restart_is_restarted();
710 }
711 
712 
713 void
714 store_syscall_restart_timeout(bigtime_t timeout)
715 {
716 	Thread* thread = thread_get_current_thread();
717 	if ((thread->flags & THREAD_FLAGS_SYSCALL) != 0)
718 		*(bigtime_t*)thread->syscall_restart.parameters = timeout;
719 }
720 
721 
722 bigtime_t
723 restore_syscall_restart_timeout(void)
724 {
725 	Thread* thread = thread_get_current_thread();
726 	return *(bigtime_t*)thread->syscall_restart.parameters;
727 }
728 
729