1 /* 2 * Copyright (c) 2004-2010, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * Stefano Ceccherini 7 * Axel Dörfler, axeld@pinc-software.de 8 * Ingo Weinhold, ingo_weinhold@gmx.de 9 */ 10 11 12 #include <ksystem_info.h> 13 #include <system_info.h> 14 #include <system_revision.h> 15 #include <arch/system_info.h> 16 17 #include <string.h> 18 19 #include <OS.h> 20 #include <KernelExport.h> 21 22 #include <AutoDeleter.h> 23 24 #include <block_cache.h> 25 #include <cpu.h> 26 #include <debug.h> 27 #include <kernel.h> 28 #include <lock.h> 29 #include <Notifications.h> 30 #include <messaging.h> 31 #include <port.h> 32 #include <real_time_clock.h> 33 #include <sem.h> 34 #include <smp.h> 35 #include <team.h> 36 #include <thread.h> 37 #include <util/AutoLock.h> 38 #include <vm/vm.h> 39 #include <vm/vm_page.h> 40 41 42 const static int64 kKernelVersion = 0x1; 43 const static char *kKernelName = "kernel_" HAIKU_ARCH; 44 45 46 static int 47 dump_info(int argc, char **argv) 48 { 49 kprintf("kernel build: %s %s (gcc%d %s)\n", __DATE__, __TIME__, __GNUC__, 50 __VERSION__); 51 kprintf("revision: %s\n\n", get_haiku_revision()); 52 53 kprintf("cpu count: %" B_PRId32 ", active times:\n", smp_get_num_cpus()); 54 55 for (int32 i = 0; i < smp_get_num_cpus(); i++) 56 kprintf(" [%" B_PRId32 "] %" B_PRId64 "\n", i + 1, gCPU[i].active_time); 57 58 // ToDo: Add page_faults 59 kprintf("pages:\t\t%" B_PRIuPHYSADDR " (%" B_PRIuPHYSADDR " max)\n", 60 vm_page_num_pages() - vm_page_num_free_pages(), vm_page_num_pages()); 61 62 kprintf("sems:\t\t%" B_PRId32 " (%" B_PRId32 " max)\n", sem_used_sems(), 63 sem_max_sems()); 64 kprintf("ports:\t\t%" B_PRId32 " (%" B_PRId32 " max)\n", port_used_ports(), 65 port_max_ports()); 66 kprintf("threads:\t%" B_PRId32 " (%" B_PRId32 " max)\n", 67 thread_used_threads(), thread_max_threads()); 68 kprintf("teams:\t\t%" B_PRId32 " (%" B_PRId32 " max)\n", team_used_teams(), 69 team_max_teams()); 70 71 return 0; 72 } 73 74 75 // #pragma mark - user notifications 76 77 78 class SystemNotificationService : private NotificationListener { 79 public: 80 SystemNotificationService() 81 { 82 mutex_init(&fLock, "system notification service"); 83 } 84 85 status_t Init() 86 { 87 status_t error = fTeamListeners.Init(); 88 if (error != B_OK) 89 return error; 90 91 error = NotificationManager::Manager().AddListener("teams", 92 TEAM_ADDED | TEAM_REMOVED | TEAM_EXEC, *this); 93 if (error != B_OK) 94 return error; 95 96 error = NotificationManager::Manager().AddListener("threads", 97 THREAD_ADDED | THREAD_REMOVED | TEAM_EXEC, *this); 98 if (error != B_OK) 99 return error; 100 101 return B_OK; 102 } 103 104 status_t StartListening(int32 object, uint32 flags, port_id port, 105 int32 token) 106 { 107 // check the parameters 108 if ((object < 0 && object != -1) || port < 0) 109 return B_BAD_VALUE; 110 111 if ((flags & B_WATCH_SYSTEM_ALL) == 0 112 || (flags & ~(uint32)B_WATCH_SYSTEM_ALL) != 0) { 113 return B_BAD_VALUE; 114 } 115 116 MutexLocker locker(fLock); 117 118 // maybe the listener already exists 119 ListenerList* listenerList; 120 Listener* listener = _FindListener(object, port, token, listenerList); 121 if (listener != NULL) { 122 // just add the new flags 123 listener->flags |= flags; 124 return B_OK; 125 } 126 127 // create a new listener 128 listener = new(std::nothrow) Listener; 129 if (listener == NULL) 130 return B_NO_MEMORY; 131 ObjectDeleter<Listener> listenerDeleter(listener); 132 133 listener->port = port; 134 listener->token = token; 135 listener->flags = flags; 136 137 // if there's no list yet, create a new list 138 if (listenerList == NULL) { 139 listenerList = new(std::nothrow) ListenerList; 140 if (listenerList == NULL) 141 return B_NO_MEMORY; 142 143 listenerList->object = object; 144 145 fTeamListeners.Insert(listenerList); 146 } 147 148 listener->list = listenerList; 149 listenerList->listeners.Add(listener); 150 listenerDeleter.Detach(); 151 152 team_associate_data(listener); 153 154 return B_OK; 155 } 156 157 status_t StopListening(int32 object, uint32 flags, port_id port, 158 int32 token) 159 { 160 MutexLocker locker(fLock); 161 162 // find the listener 163 ListenerList* listenerList; 164 Listener* listener = _FindListener(object, port, token, listenerList); 165 if (listener == NULL) 166 return B_ENTRY_NOT_FOUND; 167 168 // clear the given flags 169 listener->flags &= ~flags; 170 171 if (listener->flags != 0) 172 return B_OK; 173 174 team_dissociate_data(listener); 175 _RemoveListener(listener); 176 177 return B_OK; 178 } 179 180 private: 181 struct ListenerList; 182 183 struct Listener : AssociatedData { 184 DoublyLinkedListLink<Listener> listLink; 185 ListenerList* list; 186 port_id port; 187 int32 token; 188 uint32 flags; 189 190 virtual void OwnerDeleted(AssociatedDataOwner* owner); 191 }; 192 193 friend struct Listener; 194 195 struct ListenerList { 196 typedef DoublyLinkedList<Listener, 197 DoublyLinkedListMemberGetLink<Listener, &Listener::listLink> > List; 198 199 ListenerList* hashNext; 200 List listeners; 201 int32 object; 202 }; 203 204 struct ListenerHashDefinition { 205 typedef int32 KeyType; 206 typedef ListenerList ValueType; 207 208 size_t HashKey(int32 key) const 209 { 210 return key; 211 } 212 213 size_t Hash(const ListenerList* value) const 214 { 215 return HashKey(value->object); 216 } 217 218 bool Compare(int32 key, const ListenerList* value) const 219 { 220 return value->object == key; 221 } 222 223 ListenerList*& GetLink(ListenerList* value) const 224 { 225 return value->hashNext; 226 } 227 }; 228 229 typedef BOpenHashTable<ListenerHashDefinition> ListenerHash; 230 231 private: 232 virtual void EventOccurred(NotificationService& service, 233 const KMessage* event) 234 { 235 MutexLocker locker(fLock); 236 237 int32 eventCode; 238 int32 teamID; 239 if (event->FindInt32("event", &eventCode) != B_OK 240 || event->FindInt32("team", &teamID) != B_OK) { 241 return; 242 } 243 244 int32 object; 245 uint32 opcode; 246 uint32 flags; 247 248 // translate the event 249 if (event->What() == TEAM_MONITOR) { 250 switch (eventCode) { 251 case TEAM_ADDED: 252 opcode = B_TEAM_CREATED; 253 flags = B_WATCH_SYSTEM_TEAM_CREATION; 254 break; 255 case TEAM_REMOVED: 256 opcode = B_TEAM_DELETED; 257 flags = B_WATCH_SYSTEM_TEAM_DELETION; 258 break; 259 case TEAM_EXEC: 260 opcode = B_TEAM_EXEC; 261 flags = B_WATCH_SYSTEM_TEAM_CREATION 262 | B_WATCH_SYSTEM_TEAM_DELETION; 263 break; 264 default: 265 return; 266 } 267 268 object = teamID; 269 } else if (event->What() == THREAD_MONITOR) { 270 if (event->FindInt32("thread", &object) != B_OK) 271 return; 272 273 switch (eventCode) { 274 case THREAD_ADDED: 275 opcode = B_THREAD_CREATED; 276 flags = B_WATCH_SYSTEM_THREAD_CREATION; 277 break; 278 case THREAD_REMOVED: 279 opcode = B_THREAD_DELETED; 280 flags = B_WATCH_SYSTEM_THREAD_DELETION; 281 break; 282 case THREAD_NAME_CHANGED: 283 opcode = B_THREAD_NAME_CHANGED; 284 flags = B_WATCH_SYSTEM_THREAD_PROPERTIES; 285 break; 286 default: 287 return; 288 } 289 } else 290 return; 291 292 // find matching listeners 293 messaging_target targets[kMaxMessagingTargetCount]; 294 int32 targetCount = 0; 295 296 _AddTargets(fTeamListeners.Lookup(teamID), flags, targets, 297 targetCount, object, opcode); 298 _AddTargets(fTeamListeners.Lookup(-1), flags, targets, targetCount, 299 object, opcode); 300 301 // send the message 302 if (targetCount > 0) 303 _SendMessage(targets, targetCount, object, opcode); 304 } 305 306 void _AddTargets(ListenerList* listenerList, uint32 flags, 307 messaging_target* targets, int32& targetCount, int32 object, 308 uint32 opcode) 309 { 310 if (listenerList == NULL) 311 return; 312 313 for (ListenerList::List::Iterator it 314 = listenerList->listeners.GetIterator(); 315 Listener* listener = it.Next();) { 316 if ((listener->flags & flags) == 0) 317 continue; 318 319 // array is full -- need to flush it first 320 if (targetCount == kMaxMessagingTargetCount) { 321 _SendMessage(targets, targetCount, object, opcode); 322 targetCount = 0; 323 } 324 325 // add the listener 326 targets[targetCount].port = listener->port; 327 targets[targetCount++].token = listener->token; 328 } 329 } 330 331 void _SendMessage(messaging_target* targets, int32 targetCount, 332 int32 object, uint32 opcode) 333 { 334 // prepare the message 335 char buffer[128]; 336 KMessage message; 337 message.SetTo(buffer, sizeof(buffer), B_SYSTEM_OBJECT_UPDATE); 338 message.AddInt32("opcode", opcode); 339 if (opcode < B_THREAD_CREATED) 340 message.AddInt32("team", object); 341 else 342 message.AddInt32("thread", object); 343 344 // send it 345 send_message(message.Buffer(), message.ContentSize(), targets, 346 targetCount); 347 } 348 349 Listener* _FindListener(int32 object, port_id port, int32 token, 350 ListenerList*& _listenerList) 351 { 352 _listenerList = fTeamListeners.Lookup(object); 353 if (_listenerList == NULL) 354 return NULL; 355 356 for (ListenerList::List::Iterator it 357 = _listenerList->listeners.GetIterator(); 358 Listener* listener = it.Next();) { 359 if (listener->port == port && listener->token == token) 360 return listener; 361 } 362 363 return NULL; 364 } 365 366 void _RemoveObsoleteListener(Listener* listener) 367 { 368 MutexLocker locker(fLock); 369 _RemoveListener(listener); 370 } 371 372 void _RemoveListener(Listener* listener) 373 { 374 // no flags anymore -- remove the listener 375 ListenerList* listenerList = listener->list; 376 listenerList->listeners.Remove(listener); 377 listener->ReleaseReference(); 378 379 if (listenerList->listeners.IsEmpty()) { 380 // no listeners in the list anymore -- remove the list from the hash 381 // table 382 fTeamListeners.Remove(listenerList); 383 delete listenerList; 384 } 385 } 386 387 private: 388 static const int32 kMaxMessagingTargetCount = 8; 389 390 mutex fLock; 391 ListenerHash fTeamListeners; 392 }; 393 394 static SystemNotificationService sSystemNotificationService; 395 396 397 void 398 SystemNotificationService::Listener::OwnerDeleted(AssociatedDataOwner* owner) 399 { 400 sSystemNotificationService._RemoveObsoleteListener(this); 401 } 402 403 404 // #pragma mark - 405 406 407 status_t 408 _get_system_info(system_info *info, size_t size) 409 { 410 if (size != sizeof(system_info)) 411 return B_BAD_VALUE; 412 413 memset(info, 0, sizeof(system_info)); 414 415 info->boot_time = rtc_boot_time(); 416 info->cpu_count = smp_get_num_cpus(); 417 418 for (int32 i = 0; i < info->cpu_count; i++) 419 info->cpu_infos[i].active_time = cpu_get_active_time(i); 420 421 vm_page_get_stats(info); 422 // TODO: Add page_faults 423 424 info->used_threads = thread_used_threads(); 425 info->max_threads = thread_max_threads(); 426 info->used_teams = team_used_teams(); 427 info->max_teams = team_max_teams(); 428 info->used_ports = port_used_ports(); 429 info->max_ports = port_max_ports(); 430 info->used_sems = sem_used_sems(); 431 info->max_sems = sem_max_sems(); 432 433 info->kernel_version = kKernelVersion; 434 strlcpy(info->kernel_name, kKernelName, B_FILE_NAME_LENGTH); 435 strlcpy(info->kernel_build_date, __DATE__, B_OS_NAME_LENGTH); 436 strlcpy(info->kernel_build_time, __TIME__, B_OS_NAME_LENGTH); 437 info->abi = B_HAIKU_ABI; 438 439 // all other stuff is architecture specific 440 return arch_get_system_info(info, size); 441 } 442 443 444 status_t 445 system_info_init(struct kernel_args *args) 446 { 447 add_debugger_command("info", &dump_info, "System info"); 448 449 return arch_system_info_init(args); 450 } 451 452 453 status_t 454 system_notifications_init() 455 { 456 new (&sSystemNotificationService) SystemNotificationService; 457 458 status_t error = sSystemNotificationService.Init(); 459 if (error != B_OK) { 460 panic("system_info_init(): Failed to init system notification service"); 461 return error; 462 } 463 464 return B_OK; 465 } 466 467 468 // #pragma mark - 469 470 471 status_t 472 _user_get_system_info(system_info *userInfo, size_t size) 473 { 474 // The BeBook says get_system_info() always returns B_OK, 475 // but that ain't true with invalid addresses 476 if (userInfo == NULL || !IS_USER_ADDRESS(userInfo)) 477 return B_BAD_ADDRESS; 478 479 system_info info; 480 status_t status = _get_system_info(&info, size); 481 if (status == B_OK) { 482 if (user_memcpy(userInfo, &info, sizeof(system_info)) < B_OK) 483 return B_BAD_ADDRESS; 484 485 return B_OK; 486 } 487 488 return status; 489 } 490 491 492 status_t 493 _user_get_system_info_etc(int32 id, void* userInfo, size_t size) 494 { 495 if (userInfo == NULL || !IS_USER_ADDRESS(userInfo)) 496 return B_BAD_ADDRESS; 497 498 switch (id) { 499 case B_MEMORY_INFO: 500 { 501 if (size < sizeof(system_memory_info)) 502 return B_BAD_VALUE; 503 504 system_memory_info info; 505 vm_get_info(&info); 506 507 info.block_cache_memory = block_cache_used_memory(); 508 509 return user_memcpy(userInfo, &info, sizeof(system_memory_info)); 510 } 511 512 default: 513 return B_BAD_VALUE; 514 } 515 } 516 517 518 status_t 519 _user_start_watching_system(int32 object, uint32 flags, port_id port, 520 int32 token) 521 { 522 return sSystemNotificationService.StartListening(object, flags, port, 523 token); 524 } 525 526 527 status_t 528 _user_stop_watching_system(int32 object, uint32 flags, port_id port, 529 int32 token) 530 { 531 return sSystemNotificationService.StopListening(object, flags, port, token); 532 } 533