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