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