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