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