From f8b41f8219423baa0c158bbff84b22578a7286ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Thu, 24 Aug 2006 23:41:54 +0000 Subject: [PATCH] Moved functions around, so that the static private functions are at the top. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18623 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/system/kernel/team.c | 719 +++++++++++++++++++-------------------- 1 file changed, 355 insertions(+), 364 deletions(-) diff --git a/src/system/kernel/team.c b/src/system/kernel/team.c index d2770c3bcc..bd576cc3bd 100644 --- a/src/system/kernel/team.c +++ b/src/system/kernel/team.c @@ -74,20 +74,8 @@ static int32 sUsedTeams = 1; spinlock team_spinlock = 0; -static void insert_group_into_session(struct process_session *session, struct process_group *group); -static void insert_team_into_group(struct process_group *group, struct team *team); -static struct process_session *create_process_session(pid_t id); -static struct process_group *create_process_group(pid_t id); -static struct team *create_team_struct(const char *name, bool kernel); -static void delete_team_struct(struct team *p); -static int team_struct_compare(void *_p, const void *_key); -static uint32 team_struct_hash(void *_p, const void *_key, uint32 range); -static int process_group_compare(void *_p, const void *_key); -static uint32 process_group_hash(void *_p, const void *_key, uint32 range); -static void free_strings_array(char **strings, int32 count); -static status_t user_copy_strings_array(char * const *strings, int32 count, char ***_strings); -static void _dump_team_info(struct team *p); -static int dump_team_info(int argc, char **argv); + +// #pragma mark - Private functions static void @@ -170,67 +158,6 @@ dump_teams(int argc, char **argv) } -status_t -team_init(kernel_args *args) -{ - struct process_session *session; - struct process_group *group; - - // create the team hash table - sTeamHash = hash_init(16, offsetof(struct team, next), - &team_struct_compare, &team_struct_hash); - - sGroupHash = hash_init(16, offsetof(struct process_group, next), - &process_group_compare, &process_group_hash); - - // create initial session and process groups - - session = create_process_session(1); - if (session == NULL) - panic("Could not create initial session.\n"); - - group = create_process_group(1); - if (group == NULL) - panic("Could not create initial process group.\n"); - - insert_group_into_session(session, group); - - // create the kernel team - sKernelTeam = create_team_struct("kernel_team", true); - if (sKernelTeam == NULL) - panic("could not create kernel team!\n"); - strcpy(sKernelTeam->args, sKernelTeam->name); - sKernelTeam->state = TEAM_STATE_NORMAL; - - insert_team_into_group(group, sKernelTeam); - - sKernelTeam->io_context = vfs_new_io_context(NULL); - if (sKernelTeam->io_context == NULL) - panic("could not create io_context for kernel team!\n"); - - // stick it in the team hash - hash_insert(sTeamHash, sKernelTeam); - - add_debugger_command("team", &dump_team_info, "list info about a particular team"); - add_debugger_command("teams", &dump_teams, "list all teams"); - return 0; -} - - -int32 -team_max_teams(void) -{ - return sMaxTeams; -} - - -int32 -team_used_teams(void) -{ - return sUsedTeams; -} - - /** Frees an array of strings in kernel space. * * \param strings strings array @@ -366,66 +293,6 @@ copy_strings_array(char * const *strings, int32 count, char ***_strings, } -static int -process_group_compare(void *_group, const void *_key) -{ - struct process_group *group = _group; - const struct team_key *key = _key; - - if (group->id == key->id) - return 0; - - return 1; -} - - -static uint32 -process_group_hash(void *_group, const void *_key, uint32 range) -{ - struct process_group *group = _group; - const struct team_key *key = _key; - - if (group != NULL) - return group->id % range; - - return (uint32)key->id % range; -} - - -/** Quick check to see if we have a valid team ID. - */ - -bool -team_is_valid(team_id id) -{ - struct team *team; - cpu_status state; - - if (id <= 0) - return false; - - state = disable_interrupts(); - GRAB_TEAM_LOCK(); - - team = team_get_team_struct_locked(id); - - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - return team != NULL; -} - - -struct team * -team_get_team_struct_locked(team_id id) -{ - struct team_key key; - key.id = id; - - return hash_lookup(sTeamHash, &key); -} - - static int team_struct_compare(void *_p, const void *_key) { @@ -452,6 +319,32 @@ team_struct_hash(void *_p, const void *_key, uint32 range) } +static int +process_group_compare(void *_group, const void *_key) +{ + struct process_group *group = _group; + const struct team_key *key = _key; + + if (group->id == key->id) + return 0; + + return 1; +} + + +static uint32 +process_group_hash(void *_group, const void *_key, uint32 range) +{ + struct process_group *group = _group; + const struct team_key *key = _key; + + if (group != NULL) + return group->id % range; + + return (uint32)key->id % range; +} + + static void insert_team_into_parent(struct team *parent, struct team *team) { @@ -564,25 +457,6 @@ remove_group_from_session(struct process_group *group) } -void -team_delete_process_group(struct process_group *group) -{ - if (group == NULL) - return; - - TRACE(("team_delete_process_group(id = %ld)\n", group->id)); - - delete_sem(group->dead_child_sem); - - // remove_group_from_session() keeps this pointer around - // only if the session can be freed as well - if (group->session) { - TRACE(("team_delete_process_group(): frees session %ld\n", group->session->id)); - free(group->session); - } - - free(group); -} /** Removes the team from the group. If that group becomes therefore @@ -673,63 +547,6 @@ create_process_session(pid_t id) } -struct team * -team_get_kernel_team(void) -{ - return sKernelTeam; -} - - -team_id -team_get_kernel_team_id(void) -{ - if (!sKernelTeam) - return 0; - - return sKernelTeam->id; -} - - -team_id -team_get_current_team_id(void) -{ - return thread_get_current_thread()->team->id; -} - - -status_t -team_get_address_space(team_id id, vm_address_space **_addressSpace) -{ - cpu_status state; - struct team *team; - status_t status; - - // ToDo: we need to do something about B_SYSTEM_TEAM vs. its real ID (1) - if (id == 1) { - // we're the kernel team, so we don't have to go through all - // the hassle (locking and hash lookup) - *_addressSpace = vm_get_kernel_address_space(); - return B_OK; - } - - state = disable_interrupts(); - GRAB_TEAM_LOCK(); - - team = team_get_team_struct_locked(id); - if (team != NULL) { - atomic_add(&team->address_space->ref_count, 1); - *_addressSpace = team->address_space; - status = B_OK; - } else - status = B_BAD_VALUE; - - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - return status; -} - - static struct team * create_team_struct(const char *name, bool kernel) { @@ -795,155 +612,6 @@ delete_team_struct(struct team *team) } -/** Removes the specified team from the global team hash, and from its parent. - * It also moves all of its children up to the parent. - * You must hold the team lock when you call this function. - * If \a _freeGroup is set to a value other than \c NULL, it must be freed - * from the calling function. - */ - -void -team_remove_team(struct team *team, struct process_group **_freeGroup) -{ - struct team *parent = team->parent; - - // remember how long this team lasted - parent->dead_children.kernel_time += team->dead_threads_kernel_time - + team->dead_children.kernel_time; - parent->dead_children.user_time += team->dead_threads_user_time - + team->dead_children.user_time; - - hash_remove(sTeamHash, team); - sUsedTeams--; - - team->state = TEAM_STATE_DEATH; - - // reparent each of the team's children - reparent_children(team); - - // remove us from our process group - remove_team_from_group(team, _freeGroup); - - // remove us from our parent - remove_team_from_parent(parent, team); -} - - -void -team_delete_team(struct team *team) -{ - team_id teamID = team->id; - port_id debuggerPort = -1; - cpu_status state; - - if (team->num_threads > 0) { - // there are other threads still in this team, - // cycle through and signal kill on each of the threads - // ToDo: this can be optimized. There's got to be a better solution. - struct thread *temp_thread; - char death_sem_name[B_OS_NAME_LENGTH]; - sem_id deathSem; - int32 threadCount; - - sprintf(death_sem_name, "team %ld death sem", teamID); - deathSem = create_sem(0, death_sem_name); - if (deathSem < 0) - panic("team_delete_team: cannot init death sem for team %ld\n", teamID); - - state = disable_interrupts(); - GRAB_TEAM_LOCK(); - - team->death_sem = deathSem; - threadCount = team->num_threads; - - // If the team was being debugged, that will stop with the termination - // of the nub thread. The team structure has already been removed from - // the team hash table at this point, so noone can install a debugger - // anymore. We fetch the debugger's port to send it a message at the - // bitter end. - GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info); - - if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) - debuggerPort = team->debug_info.debugger_port; - - RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); - - // we can safely walk the list because of the lock. no new threads can be created - // because of the TEAM_STATE_DEATH flag on the team - temp_thread = team->thread_list; - while (temp_thread) { - struct thread *next = temp_thread->team_next; - - send_signal_etc(temp_thread->id, SIGKILLTHR, B_DO_NOT_RESCHEDULE); - temp_thread = next; - } - - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - // wait until all threads in team are dead. - acquire_sem_etc(team->death_sem, threadCount, 0, 0); - delete_sem(team->death_sem); - } - - // If someone is waiting for this team to be loaded, but it dies - // unexpectedly before being done, we need to notify the waiting - // thread now. - - state = disable_interrupts(); - GRAB_TEAM_LOCK(); - - if (team->loading_info) { - // there's indeed someone waiting - struct team_loading_info *loadingInfo = team->loading_info; - team->loading_info = NULL; - - loadingInfo->result = B_ERROR; - loadingInfo->done = true; - - GRAB_THREAD_LOCK(); - - // wake up the waiting thread - if (loadingInfo->thread->state == B_THREAD_SUSPENDED) { - loadingInfo->thread->state = B_THREAD_READY; - loadingInfo->thread->next_state = B_THREAD_READY; - scheduler_enqueue_in_run_queue(loadingInfo->thread); - } - - RELEASE_THREAD_LOCK(); - } - - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - // notify team watchers - - { - // we're not reachable from anyone anymore at this point, so we - // can safely access the list without any locking - struct team_watcher *watcher; - while ((watcher = list_remove_head_item(&team->watcher_list)) != NULL) { - watcher->hook(teamID, watcher->data); - free(watcher); - } - } - - // free team resources - - vm_delete_address_space(team->address_space); - delete_owned_ports(teamID); - sem_delete_owned_sems(teamID); - remove_images(team); - vfs_free_io_context(team->io_context); - - // ToDo: should our death_entries be moved one level up? - delete_team_struct(team); - - // notify the debugger, that the team is gone - user_debug_team_deleted(teamID, debuggerPort); -} - - static uint32 get_arguments_data_size(char **args, int32 argc) { @@ -1804,6 +1472,331 @@ err: } +// #pragma mark - Private kernel API + + +status_t +team_init(kernel_args *args) +{ + struct process_session *session; + struct process_group *group; + + // create the team hash table + sTeamHash = hash_init(16, offsetof(struct team, next), + &team_struct_compare, &team_struct_hash); + + sGroupHash = hash_init(16, offsetof(struct process_group, next), + &process_group_compare, &process_group_hash); + + // create initial session and process groups + + session = create_process_session(1); + if (session == NULL) + panic("Could not create initial session.\n"); + + group = create_process_group(1); + if (group == NULL) + panic("Could not create initial process group.\n"); + + insert_group_into_session(session, group); + + // create the kernel team + sKernelTeam = create_team_struct("kernel_team", true); + if (sKernelTeam == NULL) + panic("could not create kernel team!\n"); + strcpy(sKernelTeam->args, sKernelTeam->name); + sKernelTeam->state = TEAM_STATE_NORMAL; + + insert_team_into_group(group, sKernelTeam); + + sKernelTeam->io_context = vfs_new_io_context(NULL); + if (sKernelTeam->io_context == NULL) + panic("could not create io_context for kernel team!\n"); + + // stick it in the team hash + hash_insert(sTeamHash, sKernelTeam); + + add_debugger_command("team", &dump_team_info, "list info about a particular team"); + add_debugger_command("teams", &dump_teams, "list all teams"); + return 0; +} + + +int32 +team_max_teams(void) +{ + return sMaxTeams; +} + + +int32 +team_used_teams(void) +{ + return sUsedTeams; +} + + +/** Quick check to see if we have a valid team ID. + */ + +bool +team_is_valid(team_id id) +{ + struct team *team; + cpu_status state; + + if (id <= 0) + return false; + + state = disable_interrupts(); + GRAB_TEAM_LOCK(); + + team = team_get_team_struct_locked(id); + + RELEASE_TEAM_LOCK(); + restore_interrupts(state); + + return team != NULL; +} + + +struct team * +team_get_team_struct_locked(team_id id) +{ + struct team_key key; + key.id = id; + + return hash_lookup(sTeamHash, &key); +} + + +void +team_delete_process_group(struct process_group *group) +{ + if (group == NULL) + return; + + TRACE(("team_delete_process_group(id = %ld)\n", group->id)); + + delete_sem(group->dead_child_sem); + + // remove_group_from_session() keeps this pointer around + // only if the session can be freed as well + if (group->session) { + TRACE(("team_delete_process_group(): frees session %ld\n", group->session->id)); + free(group->session); + } + + free(group); +} + + +/** Removes the specified team from the global team hash, and from its parent. + * It also moves all of its children up to the parent. + * You must hold the team lock when you call this function. + * If \a _freeGroup is set to a value other than \c NULL, it must be freed + * from the calling function. + */ + +void +team_remove_team(struct team *team, struct process_group **_freeGroup) +{ + struct team *parent = team->parent; + + // remember how long this team lasted + parent->dead_children.kernel_time += team->dead_threads_kernel_time + + team->dead_children.kernel_time; + parent->dead_children.user_time += team->dead_threads_user_time + + team->dead_children.user_time; + + hash_remove(sTeamHash, team); + sUsedTeams--; + + team->state = TEAM_STATE_DEATH; + + // reparent each of the team's children + reparent_children(team); + + // remove us from our process group + remove_team_from_group(team, _freeGroup); + + // remove us from our parent + remove_team_from_parent(parent, team); +} + + +void +team_delete_team(struct team *team) +{ + team_id teamID = team->id; + port_id debuggerPort = -1; + cpu_status state; + + if (team->num_threads > 0) { + // there are other threads still in this team, + // cycle through and signal kill on each of the threads + // ToDo: this can be optimized. There's got to be a better solution. + struct thread *temp_thread; + char death_sem_name[B_OS_NAME_LENGTH]; + sem_id deathSem; + int32 threadCount; + + sprintf(death_sem_name, "team %ld death sem", teamID); + deathSem = create_sem(0, death_sem_name); + if (deathSem < 0) + panic("team_delete_team: cannot init death sem for team %ld\n", teamID); + + state = disable_interrupts(); + GRAB_TEAM_LOCK(); + + team->death_sem = deathSem; + threadCount = team->num_threads; + + // If the team was being debugged, that will stop with the termination + // of the nub thread. The team structure has already been removed from + // the team hash table at this point, so noone can install a debugger + // anymore. We fetch the debugger's port to send it a message at the + // bitter end. + GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info); + + if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) + debuggerPort = team->debug_info.debugger_port; + + RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); + + // we can safely walk the list because of the lock. no new threads can be created + // because of the TEAM_STATE_DEATH flag on the team + temp_thread = team->thread_list; + while (temp_thread) { + struct thread *next = temp_thread->team_next; + + send_signal_etc(temp_thread->id, SIGKILLTHR, B_DO_NOT_RESCHEDULE); + temp_thread = next; + } + + RELEASE_TEAM_LOCK(); + restore_interrupts(state); + + // wait until all threads in team are dead. + acquire_sem_etc(team->death_sem, threadCount, 0, 0); + delete_sem(team->death_sem); + } + + // If someone is waiting for this team to be loaded, but it dies + // unexpectedly before being done, we need to notify the waiting + // thread now. + + state = disable_interrupts(); + GRAB_TEAM_LOCK(); + + if (team->loading_info) { + // there's indeed someone waiting + struct team_loading_info *loadingInfo = team->loading_info; + team->loading_info = NULL; + + loadingInfo->result = B_ERROR; + loadingInfo->done = true; + + GRAB_THREAD_LOCK(); + + // wake up the waiting thread + if (loadingInfo->thread->state == B_THREAD_SUSPENDED) { + loadingInfo->thread->state = B_THREAD_READY; + loadingInfo->thread->next_state = B_THREAD_READY; + scheduler_enqueue_in_run_queue(loadingInfo->thread); + } + + RELEASE_THREAD_LOCK(); + } + + RELEASE_TEAM_LOCK(); + restore_interrupts(state); + + // notify team watchers + + { + // we're not reachable from anyone anymore at this point, so we + // can safely access the list without any locking + struct team_watcher *watcher; + while ((watcher = list_remove_head_item(&team->watcher_list)) != NULL) { + watcher->hook(teamID, watcher->data); + free(watcher); + } + } + + // free team resources + + vm_delete_address_space(team->address_space); + delete_owned_ports(teamID); + sem_delete_owned_sems(teamID); + remove_images(team); + vfs_free_io_context(team->io_context); + + // ToDo: should our death_entries be moved one level up? + delete_team_struct(team); + + // notify the debugger, that the team is gone + user_debug_team_deleted(teamID, debuggerPort); +} + + +struct team * +team_get_kernel_team(void) +{ + return sKernelTeam; +} + + +team_id +team_get_kernel_team_id(void) +{ + if (!sKernelTeam) + return 0; + + return sKernelTeam->id; +} + + +team_id +team_get_current_team_id(void) +{ + return thread_get_current_thread()->team->id; +} + + +status_t +team_get_address_space(team_id id, vm_address_space **_addressSpace) +{ + cpu_status state; + struct team *team; + status_t status; + + // ToDo: we need to do something about B_SYSTEM_TEAM vs. its real ID (1) + if (id == 1) { + // we're the kernel team, so we don't have to go through all + // the hassle (locking and hash lookup) + *_addressSpace = vm_get_kernel_address_space(); + return B_OK; + } + + state = disable_interrupts(); + GRAB_TEAM_LOCK(); + + team = team_get_team_struct_locked(id); + if (team != NULL) { + atomic_add(&team->address_space->ref_count, 1); + *_addressSpace = team->address_space; + status = B_OK; + } else + status = B_BAD_VALUE; + + RELEASE_TEAM_LOCK(); + restore_interrupts(state); + + return status; +} + + /** Adds a hook to the team that is called as soon as this * team goes away. * This call might get public in the future. @@ -1885,8 +1878,7 @@ stop_watching_team(team_id teamID, void (*hook)(team_id, void *), void *data) } -// #pragma mark - -// public team API +// #pragma mark - Public kernel API thread_id @@ -2208,8 +2200,7 @@ getsid(pid_t process) } -// #pragma mark - -// User syscalls +// #pragma mark - User syscalls status_t