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 29using namespace std; 30 31static MessagingService *sMessagingService = NULL; 32 33static const int32 kMessagingAreaSize = B_PAGE_SIZE * 4; 34 35 36// #pragma mark - MessagingArea 37 38 39MessagingArea::MessagingArea() 40{ 41} 42 43 44MessagingArea::~MessagingArea() 45{ 46 if (fID >= 0) 47 delete_area(fID); 48} 49 50 51MessagingArea * 52MessagingArea::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 79void 80MessagingArea::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 92bool 93MessagingArea::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 102bool 103MessagingArea::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 113void 114MessagingArea::Unlock() 115{ 116 if (atomic_add(&fHeader->lock_counter, -1) > 1) 117 release_sem(fLockSem); 118} 119 120 121area_id 122MessagingArea::ID() const 123{ 124 return fID; 125} 126 127 128int32 129MessagingArea::Size() const 130{ 131 return fSize; 132} 133 134 135bool 136MessagingArea::IsEmpty() const 137{ 138 return fHeader->command_count == 0; 139} 140 141 142void * 143MessagingArea::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 213void 214MessagingArea::CommitCommand() 215{ 216 // TODO: If invoked while locked, we should supply B_DO_NOT_RESCHEDULE. 217 release_sem(fCounterSem); 218} 219 220 221void 222MessagingArea::SetNextArea(MessagingArea *area) 223{ 224 fNextArea = area; 225 fHeader->next_kernel_area = (fNextArea ? fNextArea->ID() : -1); 226} 227 228 229MessagingArea * 230MessagingArea::NextArea() const 231{ 232 return fNextArea; 233} 234 235 236messaging_command * 237MessagingArea::_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 263MessagingService::MessagingService() 264 : 265 fFirstArea(NULL), 266 fLastArea(NULL) 267{ 268 recursive_lock_init(&fLock, "messaging service"); 269} 270 271 272MessagingService::~MessagingService() 273{ 274 // Should actually never be called. Once created the service stays till the 275 // bitter end. 276} 277 278 279status_t 280MessagingService::InitCheck() const 281{ 282 return B_OK; 283} 284 285 286bool 287MessagingService::Lock() 288{ 289 return recursive_lock_lock(&fLock) == B_OK; 290} 291 292 293void 294MessagingService::Unlock() 295{ 296 recursive_lock_unlock(&fLock); 297} 298 299 300status_t 301MessagingService::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 347status_t 348MessagingService::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 376status_t 377MessagingService::SendMessage(const void *message, int32 messageSize, 378 const messaging_target *targets, int32 targetCount) 379{ 380PRINT(("MessagingService::SendMessage(%p, %ld, %p, %ld)\n", message, 381messageSize, 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 } 399PRINT((" 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 419status_t 420MessagingService::_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 495status_t 496send_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 515status_t 516send_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 527status_t 528init_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*/ 563area_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 583status_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