/* Copyright (C) 2005 David Decotigny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include /* for SOS_PAGE_SIZE */ #include #include "process.h" #define SOS_PROCESS_MAX_OPENED_FILES 64 #define SOS_PROCESS_MAX_NAMELEN 32 /** * Definition of a process in SOS. A process is basically the * collection of all the resources requested by a user program. The * threads are one of these resources, the mm_context is one other * example of such resources. */ struct sos_process { char name[SOS_PROCESS_MAX_NAMELEN]; /** Process identifier */ sos_pid_t pid; /** First important resource: the address space */ struct sos_umem_vmm_as *address_space; /** Second important resource: the CPU execution contexts, aka the threads executing in the context of this process. */ struct sos_thread *thread_list; sos_count_t nb_threads; /** Reference counter, including threads */ sos_count_t ref_cnt; /** The array of opened file descriptors */ struct sos_fs_opened_file * fds[SOS_PROCESS_MAX_OPENED_FILES]; /** Where the root of the process is (for chroot support). May be NULL */ struct sos_fs_opened_file * root; /** Where the current working dir of the process is */ struct sos_fs_opened_file * cwd; /** To chain the processes in the global list of active processes and PIDs */ struct sos_hash_linkage hlink_pidtable; }; /** The list of processes in the system */ static struct sos_hash_table * process_hash; /** The bitmap for PID allocation */ static void * bitmap_pid; /** Number of bits in the PID bitmap */ #define SOS_PROCESS_PID_BITMAP_NBITS \ (8 * SOS_PROCESS_PID_BITMAP_NPAGES * SOS_PAGE_SIZE) /** The PID where we start looking for a free slot */ static sos_pid_t next_base_pid; /** The cache for the sos_process structures */ struct sos_kslab_cache *cache_struct_process; /** * Helper function for debugging purposes */ void sos_process_dumplist(void) { struct sos_thread * cur_thr = sos_thread_get_current(); int nb_procs = 0; /* Internal function used to dump infos about a single process */ sos_bool_t process_iterator(void* custom_data, struct sos_process * proc) { struct sos_thread * thr; int nb_thrs; nb_procs ++; sos_bochs_printf(" proc %d @%p: '%s', %d threads, %d refs\n", proc->pid, proc, proc->name?proc->name:"(none)", proc->nb_threads, proc->ref_cnt); list_foreach_forward_named(proc->thread_list, thr, nb_thrs, prev_in_process, next_in_process) { if (thr == cur_thr) sos_bochs_printf(" thr@%p: [CURRENT]\n", thr); else sos_bochs_printf(" thr@%p: %cstate=%d eip=%p\n", thr, sos_cpu_context_is_in_user_mode(thr->cpu_state)?'u':'k', thr->state, (void*)sos_cpu_context_get_PC(thr->cpu_state)); } return TRUE; } sos_bochs_printf("\n"); sos_hash_walk(process_hash, (sos_hash_map_func_t*)process_iterator, NULL); sos_bochs_printf(" ======= %d processes =======\n", nb_procs); sos_bochs_printf("\n"); } sos_ret_t sos_process_subsystem_setup() { /* Create the cache for the process structures */ cache_struct_process = sos_kmem_cache_create("struct_process", sizeof(struct sos_process), 3, 0, SOS_KSLAB_CREATE_MAP | SOS_KSLAB_CREATE_ZERO); if (NULL == cache_struct_process) return -SOS_ENOMEM; /* Allocate pages for the PID bitmap */ bitmap_pid = (void*) sos_kmem_vmm_alloc(SOS_PROCESS_PID_BITMAP_NPAGES, SOS_KMEM_VMM_MAP); if (NULL == bitmap_pid) { sos_kmem_cache_destroy(cache_struct_process); return -SOS_ENOMEM; } /* The next process might (and actually will !) have PID 1 */ next_base_pid = 1; /* Create the hash for the process list */ process_hash = sos_hash_create("pidtable", struct sos_process, NULL, NULL, 37, pid, hlink_pidtable); if (NULL == process_hash) { sos_kmem_cache_destroy(cache_struct_process); sos_kmem_vmm_free((sos_vaddr_t)bitmap_pid); return -SOS_ENOMEM; } return SOS_OK; } /** Helper function to allocate a PID entry and bind it to the given process */ static sos_ret_t register_process(struct sos_process * proc) { sos_ret_t retval; sos_pid_t pid; /* First of all: look for a free PID */ while (TRUE) { pid = sos_bitmap_ffc(bitmap_pid, SOS_PROCESS_PID_BITMAP_NBITS, next_base_pid); if (pid <= 0) { if (next_base_pid > 1) { /* try again from the beginning */ next_base_pid = 1; continue; } else /* Bad news: no PID left at all ! */ return -SOS_EAGAIN; } if (! sos_bitmap_test_and_set(bitmap_pid, pid)) /* Could allocate it ! */ break; } /* Set the base PID for next process */ if (pid < SOS_PROCESS_PID_BITMAP_NBITS) next_base_pid = pid + 1; else next_base_pid = 1; /* Now bind the process into the process hash */ proc->pid = pid; retval = sos_hash_insert(process_hash, proc); if (SOS_OK != retval) { /* Bad news: cannot register in hash */ SOS_ASSERT_FATAL(sos_bitmap_test_and_clear(bitmap_pid, pid)); return retval; } return SOS_OK; } /** Helper function to deallocate a PID entry and unbind it to the given process */ static sos_ret_t unregister_process(struct sos_process * proc) { SOS_ASSERT_FATAL(SOS_OK == sos_hash_remove(process_hash, proc)); SOS_ASSERT_FATAL(sos_bitmap_test_and_clear(bitmap_pid, proc->pid)); proc->pid = 0; return SOS_OK; } struct sos_process *sos_process_create(const char *name, sos_bool_t do_copy_current_process) { sos_ret_t retval = SOS_OK; sos_ui32_t flags; struct sos_process *proc; proc = (struct sos_process*) sos_kmem_cache_alloc(cache_struct_process, 0); if (! proc) return NULL; /* proc is already filled with 0 (cache has SOS_KSLAB_CREATE_ZERO flag) */ /* Copy the file descriptors when needed */ if (do_copy_current_process) { struct sos_process * myself = sos_thread_get_current()->process; int fd; for (fd = 0 ; fd < SOS_PROCESS_MAX_OPENED_FILES ; fd++) if (NULL != myself->fds[fd]) { retval = sos_fs_duplicate_opened_file(myself->fds[fd], proc, & proc->fds[fd]); if (SOS_OK != retval) goto end_create_proc; } retval = sos_fs_duplicate_opened_file(myself->root, proc, & proc->root); if (SOS_OK != retval) goto end_create_proc; retval = sos_fs_duplicate_opened_file(myself->cwd, proc, & proc->cwd); if (SOS_OK != retval) goto end_create_proc; } if (do_copy_current_process) { struct sos_process * myself = sos_thread_get_current()->process; proc->address_space = sos_umem_vmm_duplicate_as(sos_process_get_address_space(myself), proc); } else proc->address_space = sos_umem_vmm_create_empty_as(proc); if (NULL == proc->address_space) { /* Error */ retval = -SOS_ENOMEM; goto end_create_proc; } if (!name) { struct sos_thread * cur_thr = sos_thread_get_current(); if (do_copy_current_process) name = cur_thr->process->name; else name = "[UNNAMED]"; } strzcpy(proc->name, name, SOS_PROCESS_MAX_NAMELEN); /* Add it to the global list of processes */ sos_disable_IRQs(flags); retval = register_process(proc); if (SOS_OK == retval) proc->ref_cnt = 1; /* Mark the process as referenced */ sos_restore_IRQs(flags); end_create_proc: if (SOS_OK != retval) { int fd; /* Close the file descriptors */ for (fd = 0 ; fd < SOS_PROCESS_MAX_OPENED_FILES ; fd++) if (NULL != proc->fds[fd]) { sos_fs_close(proc->fds[fd]); proc->fds[fd] = NULL; } if (proc->root) sos_fs_close(proc->root); if (proc->cwd) sos_fs_close(proc->cwd); proc->root = proc->cwd = NULL; sos_kmem_cache_free((sos_vaddr_t) proc); proc = NULL; } return proc; } inline sos_ret_t sos_process_ref(struct sos_process *proc) { sos_ui32_t flags; sos_disable_IRQs(flags); proc->ref_cnt ++; sos_restore_IRQs(flags); return SOS_OK; } sos_pid_t sos_process_get_pid(const struct sos_process *proc) { return proc->pid; } sos_count_t sos_process_get_nb_threads(const struct sos_process *proc) { sos_count_t retval; sos_ui32_t flags; sos_disable_IRQs(flags); retval = proc->nb_threads; sos_restore_IRQs(flags); return retval; } struct sos_mm_context * sos_process_get_mm_context(const struct sos_process *proc) { return sos_umem_vmm_get_mm_context(proc->address_space); } struct sos_umem_vmm_as * sos_process_get_address_space(const struct sos_process *proc) { return proc->address_space; } sos_ret_t sos_process_set_address_space(struct sos_process *proc, struct sos_umem_vmm_as *new_as) { int fd; /* Close the FD that are not allowed to be duplicated */ for (fd = 0 ; fd < SOS_PROCESS_MAX_OPENED_FILES ; fd++) if ( (NULL != proc->fds[fd]) && (proc->fds[fd]->open_flags & SOS_FS_OPEN_CLOSEONEXEC) ) { sos_fs_close(proc->fds[fd]); proc->fds[fd] = NULL; } if (proc->address_space) { sos_ret_t retval = sos_umem_vmm_delete_as(proc->address_space); if (SOS_OK != retval) return retval; } proc->address_space = new_as; return SOS_OK; } struct sos_fs_opened_file * sos_process_get_root(const struct sos_process *proc) { return proc->root; } struct sos_fs_opened_file * sos_process_get_cwd(const struct sos_process *proc) { return proc->cwd; } struct sos_fs_opened_file * sos_process_get_opened_file(const struct sos_process *proc, int fd) { if ((fd < 0) || (fd >= SOS_PROCESS_MAX_OPENED_FILES)) return NULL; return proc->fds[fd]; } sos_ret_t sos_process_chroot(struct sos_process *proc, struct sos_fs_opened_file * new_root, struct sos_fs_opened_file ** old_root) { *old_root = proc->root; proc->root = new_root; return SOS_OK; } sos_ret_t sos_process_chdir(struct sos_process *proc, struct sos_fs_opened_file * new_cwd, struct sos_fs_opened_file ** old_cwd) { *old_cwd = proc->cwd; proc->cwd = new_cwd; return SOS_OK; } sos_ret_t sos_process_register_opened_file(struct sos_process *proc, struct sos_fs_opened_file * of, int start_search_at_fd) { int i; for (i = start_search_at_fd ; i < SOS_PROCESS_MAX_OPENED_FILES ; i++) if (NULL == proc->fds[i]) { proc->fds[i] = of; return i; } return -SOS_EMFILE; } sos_ret_t sos_process_unregister_opened_file(struct sos_process *proc, int fd) { if ((fd < 0) || (fd >= SOS_PROCESS_MAX_OPENED_FILES)) return -SOS_EBADF; proc->fds[fd] = NULL; return SOS_OK; } /* *************************************************** * Restricted functions */ sos_ret_t sos_process_register_thread(struct sos_process *in_proc, struct sos_thread *thr) { sos_ui32_t flags; /* The process is assumed to be referenced by somebody */ SOS_ASSERT_FATAL(in_proc->ref_cnt > 0); /* Only threads that are being created are allowed to be attached to a process */ SOS_ASSERT_FATAL(thr->state == SOS_THR_CREATED); /* Update the list of the threads in the process */ thr->process = in_proc; /* Increment the reference count for the process */ sos_process_ref(in_proc); /* Add the thread to the process thread's list */ sos_disable_IRQs(flags); list_add_tail_named(in_proc->thread_list, thr, prev_in_process, next_in_process); in_proc->nb_threads ++; sos_restore_IRQs(flags); return SOS_OK; } /** The function responsible for releasing the resources held by the process. */ sos_ret_t sos_process_unref(struct sos_process *proc) { sos_ui32_t flags; sos_ret_t retval; int fd; SOS_ASSERT_FATAL(proc->ref_cnt > 0); sos_disable_IRQs(flags); proc->ref_cnt --; if (proc->ref_cnt > 0) { sos_restore_IRQs(flags); return -SOS_EBUSY; } unregister_process(proc); sos_restore_IRQs(flags); /* Close the file descriptors */ for (fd = 0 ; fd < SOS_PROCESS_MAX_OPENED_FILES ; fd++) if (NULL != proc->fds[fd]) { sos_fs_close(proc->fds[fd]); proc->fds[fd] = NULL; } sos_fs_close(proc->root); sos_fs_close(proc->cwd); proc->root = proc->cwd = NULL; /* First: free the user address space */ retval = sos_umem_vmm_delete_as(proc->address_space); SOS_ASSERT_FATAL(SOS_OK == retval); /* Free the process structure */ sos_kmem_cache_free((sos_vaddr_t)proc); return SOS_OK; } sos_ret_t sos_process_unregister_thread(struct sos_thread *thr) { sos_ui32_t flags; struct sos_process * in_proc = thr->process; SOS_ASSERT_FATAL(thr->state == SOS_THR_ZOMBIE); /* Update the list of the threads in the process */ thr->process = NULL; sos_disable_IRQs(flags); list_delete_named(in_proc->thread_list, thr, prev_in_process, next_in_process); SOS_ASSERT_FATAL(in_proc->nb_threads > 0); in_proc->nb_threads --; sos_restore_IRQs(flags); /* Unreference the process */ sos_process_unref(in_proc); return SOS_OK; } sos_ret_t sos_process_set_name(struct sos_process * proc, const char * new_name) { strzcpy(proc->name, new_name, SOS_PROCESS_MAX_NAMELEN); return SOS_OK; }