1 /* 2 * Copyright 2006-2009, 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 - 127 128 129 void* 130 UserBuffer::Copy(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 uint16 158 compute_checksum(uint8* _buffer, size_t length) 159 { 160 uint16* buffer = (uint16*)_buffer; 161 uint32 sum = 0; 162 163 // TODO: unfold loop for speed 164 // TODO: write processor dependent version for speed 165 while (length >= 2) { 166 sum += *buffer++; 167 length -= 2; 168 } 169 170 if (length) { 171 // give the last byte it's proper endian-aware treatment 172 #if B_HOST_IS_LENDIAN 173 sum += *(uint8*)buffer; 174 #else 175 uint8 ordered[2]; 176 ordered[0] = *(uint8*)buffer; 177 ordered[1] = 0; 178 sum += *(uint16*)ordered; 179 #endif 180 } 181 182 while (sum >> 16) { 183 sum = (sum & 0xffff) + (sum >> 16); 184 } 185 186 return sum; 187 } 188 189 190 uint16 191 checksum(uint8* buffer, size_t length) 192 { 193 return ~compute_checksum(buffer, length); 194 } 195 196 197 // #pragma mark - Notifications 198 199 200 status_t 201 notify_socket(net_socket* socket, uint8 event, int32 value) 202 { 203 return gNetSocketModule.notify(socket, event, value); 204 } 205 206 207 // #pragma mark - FIFOs 208 209 210 Fifo::Fifo(const char* name, size_t maxBytes) 211 { 212 base_fifo_init(this, name, maxBytes); 213 } 214 215 216 Fifo::~Fifo() 217 { 218 Clear(); 219 delete_sem(notify); 220 } 221 222 223 status_t 224 Fifo::InitCheck() const 225 { 226 return !(notify < B_OK); 227 } 228 229 230 status_t 231 Fifo::Enqueue(net_buffer* buffer) 232 { 233 return base_fifo_enqueue_buffer(this, buffer); 234 } 235 236 237 status_t 238 Fifo::EnqueueAndNotify(net_buffer* _buffer, net_socket* socket, uint8 event) 239 { 240 net_buffer *buffer = gNetBufferModule.clone(_buffer, false); 241 if (buffer == NULL) 242 return B_NO_MEMORY; 243 244 status_t status = Enqueue(buffer); 245 if (status < B_OK) 246 gNetBufferModule.free(buffer); 247 else 248 notify_socket(socket, event, current_bytes); 249 250 return status; 251 } 252 253 254 status_t 255 Fifo::Wait(mutex* lock, bigtime_t timeout) 256 { 257 waiting++; 258 mutex_unlock(lock); 259 status_t status = acquire_sem_etc(notify, 1, 260 B_CAN_INTERRUPT | B_ABSOLUTE_TIMEOUT, timeout); 261 mutex_lock(lock); 262 return status; 263 } 264 265 266 net_buffer* 267 Fifo::Dequeue(bool clone) 268 { 269 net_buffer* buffer = (net_buffer*)list_get_first_item(&buffers); 270 271 // assert(buffer != NULL); 272 273 if (clone) { 274 buffer = gNetBufferModule.clone(buffer, false); 275 fifo_notify_one_reader(waiting, notify); 276 }else { 277 list_remove_item(&buffers, buffer); 278 current_bytes -= buffer->size; 279 } 280 281 return buffer; 282 } 283 284 285 ssize_t 286 Fifo::Clear() 287 { 288 return base_fifo_clear(this); 289 } 290 291 292 void 293 Fifo::WakeAll() 294 { 295 #ifdef __HAIKU__ 296 release_sem_etc(notify, 0, B_RELEASE_ALL); 297 #else 298 release_sem_etc(notify, 0, waiting); 299 #endif 300 } 301 302 303 status_t 304 init_fifo(net_fifo* fifo, const char* name, size_t maxBytes) 305 { 306 mutex_init_etc(&fifo->lock, name, MUTEX_FLAG_CLONE_NAME); 307 308 status_t status = base_fifo_init(fifo, name, maxBytes); 309 if (status < B_OK) 310 mutex_destroy(&fifo->lock); 311 312 return status; 313 } 314 315 316 void 317 uninit_fifo(net_fifo* fifo) 318 { 319 clear_fifo(fifo); 320 321 mutex_destroy(&fifo->lock); 322 delete_sem(fifo->notify); 323 } 324 325 326 status_t 327 fifo_enqueue_buffer(net_fifo* fifo, net_buffer* buffer) 328 { 329 MutexLocker locker(fifo->lock); 330 return base_fifo_enqueue_buffer(fifo, buffer); 331 } 332 333 334 /*! Gets the first buffer from the FIFO. If there is no buffer, it 335 will wait depending on the \a flags and \a timeout. 336 The following flags are supported (the rest is ignored): 337 MSG_DONTWAIT - ignores the timeout and never wait for a buffer; if your 338 socket is O_NONBLOCK, you should specify this flag. A \a timeout of 339 zero is equivalent to this flag, though. 340 MSG_PEEK - returns a clone of the buffer and keep the original 341 in the FIFO. 342 */ 343 ssize_t 344 fifo_dequeue_buffer(net_fifo* fifo, uint32 flags, bigtime_t timeout, 345 net_buffer** _buffer) 346 { 347 MutexLocker locker(fifo->lock); 348 bool dontWait = (flags & MSG_DONTWAIT) != 0 || timeout == 0; 349 status_t status; 350 351 while (true) { 352 net_buffer* buffer = (net_buffer*)list_get_first_item(&fifo->buffers); 353 if (buffer != NULL) { 354 if ((flags & MSG_PEEK) != 0) { 355 // we need to clone the buffer for inspection; we can't give a 356 // handle to a buffer that we're still using 357 buffer = gNetBufferModule.clone(buffer, false); 358 if (buffer == NULL) { 359 status = B_NO_MEMORY; 360 break; 361 } 362 } else { 363 list_remove_item(&fifo->buffers, buffer); 364 fifo->current_bytes -= buffer->size; 365 } 366 367 *_buffer = buffer; 368 status = B_OK; 369 break; 370 } 371 372 if (!dontWait) 373 fifo->waiting++; 374 375 locker.Unlock(); 376 377 if (dontWait) 378 return B_WOULD_BLOCK; 379 380 // we need to wait until a new buffer becomes available 381 status = acquire_sem_etc(fifo->notify, 1, 382 B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, timeout); 383 if (status < B_OK) 384 return status; 385 386 locker.Lock(); 387 } 388 389 // if another thread is waiting for data, since we didn't 390 // eat the buffer, it will get it 391 if (flags & MSG_PEEK) 392 fifo_notify_one_reader(fifo->waiting, fifo->notify); 393 394 return status; 395 } 396 397 398 status_t 399 clear_fifo(net_fifo* fifo) 400 { 401 MutexLocker locker(fifo->lock); 402 return base_fifo_clear(fifo); 403 } 404 405 406 status_t 407 fifo_socket_enqueue_buffer(net_fifo* fifo, net_socket* socket, uint8 event, 408 net_buffer* _buffer) 409 { 410 net_buffer *buffer = gNetBufferModule.clone(_buffer, false); 411 if (buffer == NULL) 412 return B_NO_MEMORY; 413 414 MutexLocker locker(fifo->lock); 415 416 status_t status = base_fifo_enqueue_buffer(fifo, buffer); 417 if (status < B_OK) 418 gNetBufferModule.free(buffer); 419 else 420 notify_socket(socket, event, fifo->current_bytes); 421 422 return status; 423 } 424 425 426 // #pragma mark - Timer 427 428 429 static status_t 430 timer_thread(void* /*data*/) 431 { 432 status_t status = B_OK; 433 434 do { 435 bigtime_t timeout = B_INFINITE_TIMEOUT; 436 437 if (status == B_TIMED_OUT || status == B_OK) { 438 // scan timers for new timeout and/or execute a timer 439 mutex_lock(&sTimerLock); 440 441 struct net_timer* timer = NULL; 442 while (true) { 443 timer = (net_timer*)list_get_next_item(&sTimers, timer); 444 if (timer == NULL) 445 break; 446 447 if (timer->due < system_time()) { 448 // execute timer 449 list_remove_item(&sTimers, timer); 450 timer->due = -1; 451 sCurrentTimer = timer; 452 453 mutex_unlock(&sTimerLock); 454 timer->hook(timer, timer->data); 455 mutex_lock(&sTimerLock); 456 457 sCurrentTimer = NULL; 458 sWaitForTimerCondition.NotifyAll(); 459 460 timer = NULL; 461 // restart scanning as we unlocked the list 462 } else { 463 // calculate new timeout 464 if (timer->due < timeout) 465 timeout = timer->due; 466 } 467 } 468 469 sTimerTimeout = timeout; 470 mutex_unlock(&sTimerLock); 471 } 472 473 status = acquire_sem_etc(sTimerWaitSem, 1, B_ABSOLUTE_TIMEOUT, timeout); 474 // the wait sem normally can't be acquired, so we 475 // have to look at the status value the call returns: 476 // 477 // B_OK - a new timer has been added or canceled 478 // B_TIMED_OUT - look for timers to be executed 479 // B_BAD_SEM_ID - we are asked to quit 480 } while (status != B_BAD_SEM_ID); 481 482 return B_OK; 483 } 484 485 486 /*! 487 Initializes a timer before use. You can also use this function to change 488 a timer later on, but make sure you have canceled it before using set_timer(). 489 */ 490 void 491 init_timer(net_timer* timer, net_timer_func hook, void* data) 492 { 493 timer->hook = hook; 494 timer->data = data; 495 timer->due = 0; 496 timer->flags = 0; 497 } 498 499 500 /*! Sets or cancels a timer. When the \a delay is below zero, an eventually 501 running timer is canceled, if not, it is scheduled to be executed after the 502 specified \a delay. 503 You need to have initialized the timer before calling this function. 504 505 In case you need to change a running timer, you have to cancel it first, 506 before making any changes. 507 */ 508 void 509 set_timer(net_timer* timer, bigtime_t delay) 510 { 511 MutexLocker locker(sTimerLock); 512 513 TRACE("set_timer %p, hook %p, data %p\n", timer, timer->hook, timer->data); 514 515 if (timer->due > 0 && delay < 0) { 516 // this timer is scheduled, cancel it 517 list_remove_item(&sTimers, timer); 518 timer->due = 0; 519 } 520 521 if (delay >= 0) { 522 // reschedule or add this timer 523 if (timer->due <= 0) 524 list_add_item(&sTimers, timer); 525 526 timer->due = system_time() + delay; 527 528 // notify timer about the change if necessary 529 if (sTimerTimeout > timer->due) 530 release_sem(sTimerWaitSem); 531 } 532 } 533 534 535 bool 536 cancel_timer(struct net_timer* timer) 537 { 538 MutexLocker locker(sTimerLock); 539 540 TRACE("cancel_timer %p, hook %p, data %p\n", timer, timer->hook, 541 timer->data); 542 543 if (timer->due <= 0) 544 return false; 545 546 // this timer is scheduled, cancel it 547 list_remove_item(&sTimers, timer); 548 timer->due = 0; 549 return true; 550 } 551 552 553 status_t 554 wait_for_timer(struct net_timer* timer) 555 { 556 if (find_thread(NULL) == sTimerThread) { 557 // let's not wait for ourselves... 558 return B_BAD_VALUE; 559 } 560 561 while (true) { 562 MutexLocker locker(sTimerLock); 563 564 if (timer->due <= 0 && sCurrentTimer != timer) 565 return B_OK; 566 567 // we actually need to wait for this timer 568 ConditionVariableEntry entry; 569 sWaitForTimerCondition.Add(&entry); 570 571 locker.Unlock(); 572 573 entry.Wait(); 574 } 575 576 return B_OK; 577 } 578 579 580 bool 581 is_timer_active(net_timer* timer) 582 { 583 return timer->due > 0; 584 } 585 586 587 bool 588 is_timer_running(net_timer* timer) 589 { 590 return timer == sCurrentTimer; 591 } 592 593 594 static int 595 dump_timer(int argc, char** argv) 596 { 597 kprintf("timer hook data due in\n"); 598 599 struct net_timer* timer = NULL; 600 while (true) { 601 timer = (net_timer*)list_get_next_item(&sTimers, timer); 602 if (timer == NULL) 603 break; 604 605 kprintf("%p %p %p %Ld\n", timer, timer->hook, timer->data, 606 timer->due > 0 ? timer->due - system_time() : -1); 607 } 608 609 return 0; 610 } 611 612 613 status_t 614 init_timers(void) 615 { 616 list_init(&sTimers); 617 sTimerTimeout = B_INFINITE_TIMEOUT; 618 619 status_t status = B_OK; 620 mutex_init(&sTimerLock, "net timer"); 621 622 sTimerWaitSem = create_sem(0, "net timer wait"); 623 if (sTimerWaitSem < B_OK) { 624 status = sTimerWaitSem; 625 goto err1; 626 } 627 628 sTimerThread = spawn_kernel_thread(timer_thread, "net timer", 629 B_NORMAL_PRIORITY, NULL); 630 if (sTimerThread < B_OK) { 631 status = sTimerThread; 632 goto err2; 633 } 634 635 sWaitForTimerCondition.Init(NULL, "wait for net timer"); 636 637 add_debugger_command("net_timer", dump_timer, 638 "Lists all active network timer"); 639 640 return resume_thread(sTimerThread); 641 642 err1: 643 mutex_destroy(&sTimerLock); 644 err2: 645 delete_sem(sTimerWaitSem); 646 return status; 647 } 648 649 650 void 651 uninit_timers(void) 652 { 653 delete_sem(sTimerWaitSem); 654 mutex_lock(&sTimerLock); 655 656 mutex_destroy(&sTimerLock); 657 658 status_t status; 659 wait_for_thread(sTimerThread, &status); 660 661 remove_debugger_command("net_timer", dump_timer); 662 } 663 664 665 // #pragma mark - Syscall restart 666 667 668 bool 669 is_syscall(void) 670 { 671 struct thread* thread = thread_get_current_thread(); 672 return (thread->flags & THREAD_FLAGS_SYSCALL) != 0; 673 } 674 675 676 bool 677 is_restarted_syscall(void) 678 { 679 return syscall_restart_is_restarted(); 680 } 681 682 683 void 684 store_syscall_restart_timeout(bigtime_t timeout) 685 { 686 struct thread* thread = thread_get_current_thread(); 687 if ((thread->flags & THREAD_FLAGS_SYSCALL) != 0) 688 *(bigtime_t*)thread->syscall_restart.parameters = timeout; 689 } 690 691 692 bigtime_t 693 restore_syscall_restart_timeout(void) 694 { 695 struct thread* thread = thread_get_current_thread(); 696 return *(bigtime_t*)thread->syscall_restart.parameters; 697 } 698 699