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