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