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