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