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