1/*
2 * Copyright 2006-2011, 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?
33class Fifo {
34public:
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
63static struct list sTimers;
64static mutex sTimerLock;
65static sem_id sTimerWaitSem;
66static ConditionVariable sWaitForTimerCondition;
67static net_timer* sCurrentTimer;
68static thread_id sTimerThread;
69static bigtime_t sTimerTimeout;
70
71
72static inline void
73fifo_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
82template<typename FifoType> static inline status_t
83base_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
95template<typename FifoType> static inline status_t
96base_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
110template<typename FifoType> static inline status_t
111base_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
129void*
130UserBuffer::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
157status_t
158UserBuffer::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
177status_t
178UserBuffer::PadToNext(size_t length)
179{
180	return Pad((BytesConsumed() + length - 1) / length - BytesConsumed());
181}
182
183
184// #pragma mark -
185
186
187uint16
188compute_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
220uint16
221checksum(uint8* buffer, size_t length)
222{
223	return ~compute_checksum(buffer, length);
224}
225
226
227//	#pragma mark - Notifications
228
229
230status_t
231notify_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
240Fifo::Fifo(const char* name, size_t maxBytes)
241{
242	base_fifo_init(this, name, maxBytes);
243}
244
245
246Fifo::~Fifo()
247{
248	Clear();
249	delete_sem(notify);
250}
251
252
253status_t
254Fifo::InitCheck() const
255{
256	return !(notify < B_OK);
257}
258
259
260status_t
261Fifo::Enqueue(net_buffer* buffer)
262{
263	return base_fifo_enqueue_buffer(this, buffer);
264}
265
266
267status_t
268Fifo::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
284status_t
285Fifo::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
296net_buffer*
297Fifo::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
315status_t
316Fifo::Clear()
317{
318	return base_fifo_clear(this);
319}
320
321
322void
323Fifo::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
333status_t
334init_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
346void
347uninit_fifo(net_fifo* fifo)
348{
349	clear_fifo(fifo);
350
351	mutex_destroy(&fifo->lock);
352	delete_sem(fifo->notify);
353}
354
355
356status_t
357fifo_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*/
373ssize_t
374fifo_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
428status_t
429clear_fifo(net_fifo* fifo)
430{
431	MutexLocker locker(fifo->lock);
432	return base_fifo_clear(fifo);
433}
434
435
436status_t
437fifo_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
459static status_t
460timer_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*/
520void
521init_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*/
538void
539set_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
565bool
566cancel_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
583status_t
584wait_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
610bool
611is_timer_active(net_timer* timer)
612{
613	return timer->due > 0;
614}
615
616
617bool
618is_timer_running(net_timer* timer)
619{
620	return timer == sCurrentTimer;
621}
622
623
624static int
625dump_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
643status_t
644init_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
672err1:
673	mutex_destroy(&sTimerLock);
674err2:
675	delete_sem(sTimerWaitSem);
676	return status;
677}
678
679
680void
681uninit_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
699bool
700is_syscall(void)
701{
702	Thread* thread = thread_get_current_thread();
703	return (thread->flags & THREAD_FLAGS_SYSCALL) != 0;
704}
705
706
707bool
708is_restarted_syscall(void)
709{
710	return syscall_restart_is_restarted();
711}
712
713
714void
715store_syscall_restart_timeout(bigtime_t timeout)
716{
717	Thread* thread = thread_get_current_thread();
718	if ((thread->flags & THREAD_FLAGS_SYSCALL) != 0)
719		*(bigtime_t*)thread->syscall_restart.parameters = timeout;
720}
721
722
723bigtime_t
724restore_syscall_restart_timeout(void)
725{
726	Thread* thread = thread_get_current_thread();
727	return *(bigtime_t*)thread->syscall_restart.parameters;
728}
729
730