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
31static struct list sTimers;
32static mutex sTimerLock;
33static sem_id sTimerWaitSem;
34static ConditionVariable sWaitForTimerCondition;
35static net_timer* sCurrentTimer;
36static thread_id sTimerThread;
37static bigtime_t sTimerTimeout;
38
39
40// #pragma mark - UserBuffer
41
42
43void*
44UserBuffer::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
71status_t
72UserBuffer::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
91status_t
92UserBuffer::PadToNext(size_t length)
93{
94	return Pad((BytesConsumed() + length - 1) / length - BytesConsumed());
95}
96
97
98// #pragma mark -
99
100
101uint16
102compute_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
134uint16
135checksum(uint8* buffer, size_t length)
136{
137	return ~compute_checksum(buffer, length);
138}
139
140
141//	#pragma mark - Notifications
142
143
144status_t
145notify_socket(net_socket* socket, uint8 event, int32 value)
146{
147	return gNetSocketModule.notify(socket, event, value);
148}
149
150
151static inline void
152fifo_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
164status_t
165init_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
183void
184uninit_fifo(net_fifo* fifo)
185{
186	clear_fifo(fifo);
187
188	mutex_destroy(&fifo->lock);
189	delete_sem(fifo->notify);
190}
191
192
193static inline status_t
194base_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
208status_t
209fifo_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*/
225ssize_t
226fifo_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
283status_t
284clear_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
301status_t
302fifo_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
324static status_t
325timer_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*/
385void
386init_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*/
403void
404set_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
430bool
431cancel_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
448status_t
449wait_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
475bool
476is_timer_active(net_timer* timer)
477{
478	return timer->due > 0;
479}
480
481
482bool
483is_timer_running(net_timer* timer)
484{
485	return timer == sCurrentTimer;
486}
487
488
489static int
490dump_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
508status_t
509init_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
537err1:
538	mutex_destroy(&sTimerLock);
539err2:
540	delete_sem(sTimerWaitSem);
541	return status;
542}
543
544
545void
546uninit_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
564bool
565is_syscall(void)
566{
567	return is_called_via_syscall();
568}
569
570
571bool
572is_restarted_syscall(void)
573{
574	return syscall_restart_is_restarted();
575}
576
577
578void
579store_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
587bigtime_t
588restore_syscall_restart_timeout(void)
589{
590	Thread* thread = thread_get_current_thread();
591	return *(bigtime_t*)thread->syscall_restart.parameters;
592}
593
594