1 /* 2 * Copyright 2005-2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 //! kernel-side implementation of the messaging service 8 9 10 #include <new> 11 12 #include <AutoDeleter.h> 13 #include <KernelExport.h> 14 #include <KMessage.h> 15 #include <messaging.h> 16 #include <MessagingServiceDefs.h> 17 18 #include "MessagingService.h" 19 20 //#define TRACE_MESSAGING_SERVICE 21 #ifdef TRACE_MESSAGING_SERVICE 22 # define PRINT(x) dprintf x 23 #else 24 # define PRINT(x) ; 25 #endif 26 27 28 using namespace std; 29 30 static MessagingService *sMessagingService = NULL; 31 32 static const int32 kMessagingAreaSize = B_PAGE_SIZE * 4; 33 34 35 // #pragma mark - MessagingArea 36 37 38 MessagingArea::MessagingArea() 39 { 40 } 41 42 43 MessagingArea::~MessagingArea() 44 { 45 if (fID >= 0) 46 delete_area(fID); 47 } 48 49 50 MessagingArea * 51 MessagingArea::Create(sem_id lockSem, sem_id counterSem) 52 { 53 // allocate the object on the heap 54 MessagingArea *area = new(nothrow) MessagingArea; 55 if (!area) 56 return NULL; 57 58 // create the area 59 area->fID = create_area("messaging", (void**)&area->fHeader, 60 B_ANY_KERNEL_ADDRESS, kMessagingAreaSize, B_FULL_LOCK, 61 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_USER_CLONEABLE_AREA); 62 if (area->fID < 0) { 63 delete area; 64 return NULL; 65 } 66 67 // finish the initialization of the object 68 area->fSize = kMessagingAreaSize; 69 area->fLockSem = lockSem; 70 area->fCounterSem = counterSem; 71 area->fNextArea = NULL; 72 area->InitHeader(); 73 74 return area; 75 } 76 77 78 void 79 MessagingArea::InitHeader() 80 { 81 fHeader->lock_counter = 1; // create locked 82 fHeader->size = fSize; 83 fHeader->kernel_area = fID; 84 fHeader->next_kernel_area = (fNextArea ? fNextArea->ID() : -1); 85 fHeader->command_count = 0; 86 fHeader->first_command = 0; 87 fHeader->last_command = 0; 88 } 89 90 91 bool 92 MessagingArea::CheckCommandSize(int32 dataSize) 93 { 94 int32 size = sizeof(messaging_command) + dataSize; 95 96 return (dataSize >= 0 97 && size <= kMessagingAreaSize - (int32)sizeof(messaging_area_header)); 98 } 99 100 101 bool 102 MessagingArea::Lock() 103 { 104 // benaphore-like locking 105 if (atomic_add(&fHeader->lock_counter, 1) == 0) 106 return true; 107 108 return (acquire_sem(fLockSem) == B_OK); 109 } 110 111 112 void 113 MessagingArea::Unlock() 114 { 115 if (atomic_add(&fHeader->lock_counter, -1) > 1) 116 release_sem(fLockSem); 117 } 118 119 120 area_id 121 MessagingArea::ID() const 122 { 123 return fID; 124 } 125 126 127 int32 128 MessagingArea::Size() const 129 { 130 return fSize; 131 } 132 133 134 bool 135 MessagingArea::IsEmpty() const 136 { 137 return fHeader->command_count == 0; 138 } 139 140 141 void * 142 MessagingArea::AllocateCommand(uint32 commandWhat, int32 dataSize, 143 bool &wasEmpty) 144 { 145 int32 size = sizeof(messaging_command) + dataSize; 146 147 if (dataSize < 0 || size > fSize - (int32)sizeof(messaging_area_header)) 148 return NULL; 149 150 // the area is used as a ring buffer 151 int32 startOffset = sizeof(messaging_area_header); 152 153 // the simple case first: the area is empty 154 int32 commandOffset; 155 wasEmpty = (fHeader->command_count == 0); 156 if (wasEmpty) { 157 commandOffset = startOffset; 158 159 // update the header 160 fHeader->command_count++; 161 fHeader->first_command = fHeader->last_command = commandOffset; 162 } else { 163 int32 firstCommandOffset = fHeader->first_command; 164 int32 lastCommandOffset = fHeader->last_command; 165 int32 firstCommandSize; 166 int32 lastCommandSize; 167 messaging_command *firstCommand = _CheckCommand(firstCommandOffset, 168 firstCommandSize); 169 messaging_command *lastCommand = _CheckCommand(lastCommandOffset, 170 lastCommandSize); 171 if (!firstCommand || !lastCommand) { 172 // something has been screwed up 173 return NULL; 174 } 175 176 // find space for the command 177 if (firstCommandOffset <= lastCommandOffset) { 178 // not wrapped 179 // try to allocate after the last command 180 if (size <= fSize - (lastCommandOffset + lastCommandSize)) { 181 commandOffset = (lastCommandOffset + lastCommandSize); 182 } else { 183 // is there enough space before the first command? 184 if (size > firstCommandOffset - startOffset) 185 return NULL; 186 commandOffset = startOffset; 187 } 188 } else { 189 // wrapped: we can only allocate between the last and the first 190 // command 191 commandOffset = lastCommandOffset + lastCommandSize; 192 if (size > firstCommandOffset - commandOffset) 193 return NULL; 194 } 195 196 // update the header and the last command 197 fHeader->command_count++; 198 lastCommand->next_command = fHeader->last_command = commandOffset; 199 } 200 201 // init the command 202 messaging_command *command 203 = (messaging_command*)((char*)fHeader + commandOffset); 204 command->next_command = 0; 205 command->command = commandWhat; 206 command->size = size; 207 208 return command->data; 209 } 210 211 212 void 213 MessagingArea::CommitCommand() 214 { 215 // TODO: If invoked while locked, we should supply B_DO_NOT_RESCHEDULE. 216 release_sem(fCounterSem); 217 } 218 219 220 void 221 MessagingArea::SetNextArea(MessagingArea *area) 222 { 223 fNextArea = area; 224 fHeader->next_kernel_area = (fNextArea ? fNextArea->ID() : -1); 225 } 226 227 228 MessagingArea * 229 MessagingArea::NextArea() const 230 { 231 return fNextArea; 232 } 233 234 235 messaging_command * 236 MessagingArea::_CheckCommand(int32 offset, int32 &size) 237 { 238 // check offset 239 if (offset < (int32)sizeof(messaging_area_header) 240 || offset + (int32)sizeof(messaging_command) > fSize 241 || (offset & 0x3)) { 242 return NULL; 243 } 244 245 // get and check size 246 messaging_command *command = (messaging_command*)((char*)fHeader + offset); 247 size = command->size; 248 if (size < (int32)sizeof(messaging_command)) 249 return NULL; 250 size = (size + 3) & ~0x3; // align 251 if (offset + size > fSize) 252 return NULL; 253 254 return command; 255 } 256 257 258 // #pragma mark - MessagingService 259 260 261 MessagingService::MessagingService() 262 : 263 fFirstArea(NULL), 264 fLastArea(NULL) 265 { 266 recursive_lock_init(&fLock, "messaging service"); 267 } 268 269 270 MessagingService::~MessagingService() 271 { 272 // Should actually never be called. Once created the service stays till the 273 // bitter end. 274 } 275 276 277 status_t 278 MessagingService::InitCheck() const 279 { 280 return B_OK; 281 } 282 283 284 bool 285 MessagingService::Lock() 286 { 287 return recursive_lock_lock(&fLock) == B_OK; 288 } 289 290 291 void 292 MessagingService::Unlock() 293 { 294 recursive_lock_unlock(&fLock); 295 } 296 297 298 status_t 299 MessagingService::RegisterService(sem_id lockSem, sem_id counterSem, 300 area_id &areaID) 301 { 302 // check, if a service is already registered 303 if (fFirstArea) 304 return B_BAD_VALUE; 305 306 status_t error = B_OK; 307 308 // check, if the semaphores are valid and belong to the calling team 309 thread_info threadInfo; 310 error = get_thread_info(find_thread(NULL), &threadInfo); 311 312 sem_info lockSemInfo; 313 if (error == B_OK) 314 error = get_sem_info(lockSem, &lockSemInfo); 315 316 sem_info counterSemInfo; 317 if (error == B_OK) 318 error = get_sem_info(counterSem, &counterSemInfo); 319 320 if (error != B_OK) 321 return error; 322 323 if (threadInfo.team != lockSemInfo.team 324 || threadInfo.team != counterSemInfo.team) { 325 return B_BAD_VALUE; 326 } 327 328 // create an area 329 fFirstArea = fLastArea = MessagingArea::Create(lockSem, counterSem); 330 if (!fFirstArea) 331 return B_NO_MEMORY; 332 333 areaID = fFirstArea->ID(); 334 fFirstArea->Unlock(); 335 336 // store the server team and the semaphores 337 fServerTeam = threadInfo.team; 338 fLockSem = lockSem; 339 fCounterSem = counterSem; 340 341 return B_OK; 342 } 343 344 345 status_t 346 MessagingService::UnregisterService() 347 { 348 // check, if the team calling this function is indeed the server team 349 thread_info threadInfo; 350 status_t error = get_thread_info(find_thread(NULL), &threadInfo); 351 if (error != B_OK) 352 return error; 353 354 if (threadInfo.team != fServerTeam) 355 return B_BAD_VALUE; 356 357 // delete all areas 358 while (fFirstArea) { 359 MessagingArea *area = fFirstArea; 360 fFirstArea = area->NextArea(); 361 delete area; 362 } 363 fLastArea = NULL; 364 365 // unset the other members 366 fLockSem = -1; 367 fCounterSem = -1; 368 fServerTeam = -1; 369 370 return B_OK; 371 } 372 373 374 status_t 375 MessagingService::SendMessage(const void *message, int32 messageSize, 376 const messaging_target *targets, int32 targetCount) 377 { 378 PRINT(("MessagingService::SendMessage(%p, %ld, %p, %ld)\n", message, 379 messageSize, targets, targetCount)); 380 if (!message || messageSize <= 0 || !targets || targetCount <= 0) 381 return B_BAD_VALUE; 382 383 int32 dataSize = sizeof(messaging_command_send_message) 384 + targetCount * sizeof(messaging_target) + messageSize; 385 386 // allocate space for the command 387 MessagingArea *area; 388 void *data; 389 bool wasEmpty; 390 status_t error = _AllocateCommand(MESSAGING_COMMAND_SEND_MESSAGE, dataSize, 391 area, data, wasEmpty); 392 if (error != B_OK) { 393 PRINT(("MessagingService::SendMessage(): Failed to allocate space for " 394 "send message command.\n")); 395 return error; 396 } 397 PRINT((" Allocated space for send message command: area: %p, data: %p, " 398 "wasEmpty: %d\n", area, data, wasEmpty)); 399 400 // prepare the command 401 messaging_command_send_message *command 402 = (messaging_command_send_message*)data; 403 command->message_size = messageSize; 404 command->target_count = targetCount; 405 memcpy(command->targets, targets, sizeof(messaging_target) * targetCount); 406 memcpy((char*)command + (dataSize - messageSize), message, messageSize); 407 408 // shoot 409 area->Unlock(); 410 if (wasEmpty) 411 area->CommitCommand(); 412 413 return B_OK; 414 } 415 416 417 status_t 418 MessagingService::_AllocateCommand(int32 commandWhat, int32 size, 419 MessagingArea *&area, void *&data, bool &wasEmpty) 420 { 421 if (!fFirstArea) 422 return B_NO_INIT; 423 424 if (!MessagingArea::CheckCommandSize(size)) 425 return B_BAD_VALUE; 426 427 // delete the discarded areas (save one) 428 ObjectDeleter<MessagingArea> discardedAreaDeleter; 429 MessagingArea *discardedArea = NULL; 430 431 while (fFirstArea != fLastArea) { 432 area = fFirstArea; 433 area->Lock(); 434 if (!area->IsEmpty()) { 435 area->Unlock(); 436 break; 437 } 438 439 PRINT(("MessagingService::_AllocateCommand(): Discarding area: %p\n", 440 area)); 441 442 fFirstArea = area->NextArea(); 443 area->SetNextArea(NULL); 444 discardedArea = area; 445 discardedAreaDeleter.SetTo(area); 446 } 447 448 // allocate space for the command in the last area 449 area = fLastArea; 450 area->Lock(); 451 data = area->AllocateCommand(commandWhat, size, wasEmpty); 452 453 if (!data) { 454 // not enough space in the last area: create a new area or reuse a 455 // discarded one 456 if (discardedArea) { 457 area = discardedAreaDeleter.Detach(); 458 area->InitHeader(); 459 PRINT(("MessagingService::_AllocateCommand(): Not enough space " 460 "left in current area. Recycling discarded one: %p\n", area)); 461 } else { 462 area = MessagingArea::Create(fLockSem, fCounterSem); 463 PRINT(("MessagingService::_AllocateCommand(): Not enough space " 464 "left in current area. Allocated new one: %p\n", area)); 465 } 466 if (!area) { 467 fLastArea->Unlock(); 468 return B_NO_MEMORY; 469 } 470 471 // add the new area 472 fLastArea->SetNextArea(area); 473 fLastArea->Unlock(); 474 fLastArea = area; 475 476 // allocate space for the command 477 data = area->AllocateCommand(commandWhat, size, wasEmpty); 478 479 if (!data) { 480 // that should never happen 481 area->Unlock(); 482 return B_NO_MEMORY; 483 } 484 } 485 486 return B_OK; 487 } 488 489 490 // #pragma mark - kernel private 491 492 493 status_t 494 send_message(const void *message, int32 messageSize, 495 const messaging_target *targets, int32 targetCount) 496 { 497 // check, if init_messaging_service() has been called yet 498 if (!sMessagingService) 499 return B_NO_INIT; 500 501 if (!sMessagingService->Lock()) 502 return B_BAD_VALUE; 503 504 status_t error = sMessagingService->SendMessage(message, messageSize, 505 targets, targetCount); 506 507 sMessagingService->Unlock(); 508 509 return error; 510 } 511 512 513 status_t 514 send_message(const KMessage *message, const messaging_target *targets, 515 int32 targetCount) 516 { 517 if (!message) 518 return B_BAD_VALUE; 519 520 return send_message(message->Buffer(), message->ContentSize(), targets, 521 targetCount); 522 } 523 524 525 status_t 526 init_messaging_service() 527 { 528 static char buffer[sizeof(MessagingService)]; 529 530 if (!sMessagingService) 531 sMessagingService = new(buffer) MessagingService; 532 533 status_t error = sMessagingService->InitCheck(); 534 535 // cleanup on error 536 if (error != B_OK) { 537 dprintf("ERROR: Failed to init messaging service: %s\n", 538 strerror(error)); 539 sMessagingService->~MessagingService(); 540 sMessagingService = NULL; 541 } 542 543 return error; 544 } 545 546 547 // #pragma mark - syscalls 548 549 550 /** \brief Called by the userland server to register itself as a messaging 551 service for the kernel. 552 \param lockingSem A semaphore used for locking the shared data. Semaphore 553 counter must be initialized to 0. 554 \param counterSem A semaphore released every time the kernel pushes a 555 command into an empty area. Semaphore counter must be initialized 556 to 0. 557 \return 558 - The ID of the kernel area used for communication, if everything went fine, 559 - an error code otherwise. 560 */ 561 area_id 562 _user_register_messaging_service(sem_id lockSem, sem_id counterSem) 563 { 564 // check, if init_messaging_service() has been called yet 565 if (!sMessagingService) 566 return B_NO_INIT; 567 568 if (!sMessagingService->Lock()) 569 return B_BAD_VALUE; 570 571 area_id areaID; 572 status_t error = sMessagingService->RegisterService(lockSem, counterSem, 573 areaID); 574 575 sMessagingService->Unlock(); 576 577 return (error != B_OK ? error : areaID); 578 } 579 580 581 status_t 582 _user_unregister_messaging_service() 583 { 584 // check, if init_messaging_service() has been called yet 585 if (!sMessagingService) 586 return B_NO_INIT; 587 588 if (!sMessagingService->Lock()) 589 return B_BAD_VALUE; 590 591 status_t error = sMessagingService->UnregisterService(); 592 593 sMessagingService->Unlock(); 594 595 return error; 596 } 597