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