/* * process.c - process related management * * Authors Ghassan Almaless (2008,2009,2010,2011,2012) * Mohamed Lamine Karaoui (2015) * Alain Greiner (2016) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH.. * * ALMOS-MKH. 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; version 2.0 of the License. * * ALMOS-MKH. 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 ALMOS-MKH.; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////////////////// // Extern global variables ////////////////////////////////////////////////////////////////////////////////////////// extern process_t process_zero; ////////////////////////////////////////////////////////////////////////////////////////// // Process initialisation related functions ////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////// process_t * process_alloc() { kmem_req_t req; req.type = KMEM_PROCESS; req.size = sizeof(process_t); req.flags = AF_KERNEL; return (process_t *)kmem_alloc( &req ); } //////////////////////////////////////// void process_free( process_t * process ) { kmem_req_t req; req.type = KMEM_PROCESS; req.ptr = process; kmem_free( &req ); } //////////////////////////////////////////// void process_zero_init( boot_info_t * info ) { // reset process descriptor memset( &process_zero , 0 , sizeof(process_t) ); // initialize kernel code & data vsegs base addresses process_zero.vmm.code_vpn_base = (vpn_t)(info->kernel_code_start >> CONFIG_PPM_PAGE_SHIFT); process_zero.vmm.data_vpn_base = (vpn_t)(info->kernel_data_start >> CONFIG_PPM_PAGE_SHIFT); // reset threads and childs number process_zero.th_nr = 0; process_zero.children_nr = 0; // set PID process_zero.pid = 0; } ///////////////////////////////////////////////// void process_reference_init( process_t * process, pid_t pid, pid_t ppid ) { // reset signal manager TODO [AG] // reset the file descriptors array process_fd_init( process ); // reset the process files structures and cd_lock process->vfs_root_xp = XPTR_NULL; process->vfs_cwd_xp = XPTR_NULL; process->vfs_bin_xp = XPTR_NULL; spinlock_init( &process->cd_lock ); // reset children list root xlist_root_init( XPTR( local_cxy , &process->children_root ) ); process->children_nr = 0; // reset semaphore list root xlist_root_init( XPTR( local_cxy , &process->sem_root ) ); process->sem_nr = 0; // register new process in the parent children list xptr_t entry = XPTR( local_cxy , &process->brothers_list ); xptr_t root = XPTR( local_cxy , &process->children_root ); xlist_add_first( root , entry ); // reset th_tbl[] array uint32_t i; for( i = 0 ; i < CONFIG_THREAD_MAX_PER_CLUSTER ; i++ ) { process->th_tbl[i] = NULL; } process->th_nr = 0; spinlock_init( &process->th_lock ); // reset process VMM memset( &process->vmm , 0 , sizeof(vmm_t) ); // initialize PID and PPID process->pid = pid; process->ppid = ppid; // set ref_xp field and is_ref flag process->is_ref = true; process->ref_xp = XPTR( local_cxy , process ); // register new process descriptor in local cluster manager local_list cluster_process_local_link( process ); // register new process descriptor in owner cluster manager copies_list cluster_process_copies_link( process ); hal_wbflush(); } // end process_reference_init() ///////////////////////////////////////////////////// error_t process_copy_init( process_t * local_process, xptr_t reference_process_xp ) { // replicate the remote process descriptor in new process descriptor xptr_t local_process_xp = XPTR( local_cxy , local_process ); hal_remote_memcpy( local_process_xp , reference_process_xp , sizeof(process_t) ); // initalise signal manager TODO [AG] // initialise file descriptors array TODO [AG] // initialise process files structures TODO [AG] // set the ref_xp field and clear the is_ref flag local_process->ref_xp = reference_process_xp; local_process->is_ref = false; // reset children list root (not used in a process descriptor copy) xlist_root_init( XPTR( local_cxy , &local_process->children_root ) ); local_process->children_nr = 0; // reset brothers list (not used in a process descriptor copy) xlist_entry_init( XPTR( local_cxy , &local_process->brothers_list ) ); // reset semaphores list root (not used in a process descriptor copy) xlist_root_init( XPTR( local_cxy , &local_process->sem_root ) ); local_process->sem_nr = 0; // initialize th_tbl[] array as empty uint32_t i; for( i = 0 ; i < CONFIG_THREAD_MAX_PER_CLUSTER ; i++ ) { local_process->th_tbl[i] = NULL; } local_process->th_nr = 0; spinlock_init( &local_process->th_lock ); // initialise process VMM TODO [AG] // register new process descriptor in local cluster manager local_list cluster_process_local_link( local_process ); // register new process descriptor in owner cluster manager copies_list cluster_process_copies_link( local_process ); hal_wbflush(); return 0; } // end process_copy_init() /////////////////////////////////////////// void process_destroy( process_t * process ) { if( process->th_nr != 0 ) { printk("\n[PANIC] in %s : process %x in cluster %x has still active threads\n", __FUNCTION__ , process->pid , local_cxy ); hal_core_sleep(); } // get local process manager pointer pmgr_t * pmgr = &LOCAL_CLUSTER->pmgr; // remove the process descriptor from local_list in local cluster manager spinlock_lock( &pmgr->local_lock ); xlist_unlink( XPTR( local_cxy , &process->local_list ) ); spinlock_unlock( &pmgr->local_lock ); // get extended pointer on copies_lock in owner cluster manager cxy_t owner_cxy = CXY_FROM_PID( process->pid ); lpid_t lpid = LPID_FROM_PID( process->pid ); xptr_t copies_lock = hal_remote_lwd( XPTR( owner_cxy , &pmgr->copies_lock[lpid] ) ); // remove the local process descriptor from copies_list remote_spinlock_lock( copies_lock ); xlist_unlink( XPTR( local_cxy , &process->copies_list ) ); remote_spinlock_unlock( copies_lock ); // synchronize memory hal_wbflush(); // From this point, the process descriptor is unreachable // release signal manager TODO [AG] // delete all open file descriptors process_fd_destroy( process ); // Close bin file and decrease refcount for root and cwd if( process->vfs_bin_xp != XPTR_NULL ) vfs_close( process->vfs_bin_xp , NULL ); vfs_file_count_down( process->vfs_root_xp ); vfs_file_count_down( process->vfs_cwd_xp ); // Destroy VMM vmm_destroy( process ); process_dmsg("\n[INFO] %s for pid %d / page_faults = %d\n", __FUNCTION__ , process->pid, process->vmm.pgfault_nr ); } // end process_destroy() //////////////////////////////////////// void process_kill( process_t * process ) { thread_t * thread; // pointer on current thead descriptor uint32_t ltid; // index in process th_tbl uint32_t count; // thread counter // get lock protecting th_tbl[] spinlock_lock( &process->th_lock ); // first loop on threads to send the THREAD_SIG_KILL signal to all process threads // we use both "ltid" and "count" indexes, because it can exist "holes" in th_tbl for( ltid = 0 , count = 0 ; (ltid < CONFIG_THREAD_MAX_PER_CLUSTER) && (count < process->th_nr) ; ltid++ ) { thread = process->th_tbl[ltid]; if( thread != NULL ) { thread_kill( thread ); count++; } } volatile uint32_t ko; // second loop on threads to wait acknowledge from scheduler, // unlink thread from process and parent thread, and release thread descriptor for( ltid = 0 , count = 0 ; (ltid < CONFIG_THREAD_MAX_PER_CLUSTER) && (count < process->th_nr) ; ltid++ ) { thread = process->th_tbl[ltid]; if( thread != NULL ) { // wait scheduler acknowledge do { ko = (thread->signals & THREAD_SIG_KILL); } while( ko ); // unlink thread from brothers list if required if( (thread->flags & THREAD_FLAG_DETACHED) == 0 ) xlist_unlink( XPTR( local_cxy , &thread->brothers_list ) ); // unlink thread from process process_remove_thread( thread ); // release memory for thread descriptor thread_destroy( thread ); count++; } } // release lock protecting th_tbl[] spinlock_unlock( &process->th_lock ); // release memory allocated for process descriptor process_destroy( process ); } // end process_kill() /////////////////////////////////////////////// process_t * process_get_local_copy( pid_t pid ) { error_t error; bool_t found; list_entry_t * iter; process_t * process; // pointer on local copy cluster_t * cluster = LOCAL_CLUSTER; // get lock protecting local list of processes spinlock_lock( &cluster->pmgr.local_lock ); // scan the local list of process descriptors to find the process found = false; LIST_FOREACH( &cluster->pmgr.local_root , iter ) { process = LIST_ELEMENT( iter , process_t , local_list ); if( process->pid == pid ) { found = true; break; } } // release lock protecting local list of processes spinlock_unlock( &cluster->pmgr.local_lock ); // allocate memory for a local process descriptor // and initialise it from reference if required if( !found ) { // get extended pointer on reference process descriptor xptr_t reference = cluster_get_reference_process_from_pid( pid ); // allocate memory for local process descriptor process = process_alloc(); if( process == NULL ) return NULL; // initialize local process descriptor copy error = process_copy_init( process, reference ); if( error ) return NULL; // register process in global copies_list } return process; } // end process_get_local_copy() ////////////////////////////////////////////////////////////////////////////////////////// // File descriptor array related functions ////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////// void process_fd_init( process_t * process ) { uint32_t fd; remote_spinlock_init( XPTR( local_cxy , &process->fd_array.lock ) ); process->fd_array.max = CONFIG_PROCESS_FILE_MAX_NR; process->fd_array.current = 0; // initialize array for ( fd = 0 ; fd < process->fd_array.max ; fd++ ) { process->fd_array.array[fd] = XPTR_NULL; } } ////////////////////////////////////////////// void process_fd_destroy( process_t * process ) { uint32_t fd; // loop on all open file descriptors to close all open files for( fd = 0 ; fd < process->fd_array.max ; fd++ ) { xptr_t file_xp = process->fd_array.array[fd]; if ( file_xp != XPTR_NULL ) vfs_close( file_xp , NULL ); } } /////////////////////////////////////////////////// bool_t process_fd_array_full( process_t * process ) { return ( process->fd_array.current >= process->fd_array.max); } ///////////////////////////////////////////////// error_t process_fd_allocate( process_t * process, xptr_t file_xp, uint32_t * ret_fd ) { bool_t found; uint32_t fd; remote_spinlock_lock( XPTR( local_cxy , &process->fd_array.lock ) ); found = false; for ( fd = 0; fd < process->fd_array.max; fd++ ) { if ( process->fd_array.array[fd] == XPTR_NULL ) { found = true; process->fd_array.array[fd] = file_xp; process->fd_array.current++; *ret_fd = fd; break; } } remote_spinlock_unlock( XPTR( local_cxy , &process->fd_array.lock ) ); if ( !found ) return EMFILE; else return 0; } //////////////////////////////////////////////// error_t process_fd_release( process_t * process, uint32_t fd ) { if ( (fd < 0) || (fd > process->fd_array.max) ) return EBADF; remote_spinlock_lock( XPTR( local_cxy , &process->fd_array.lock ) ); process->fd_array.array[fd] = XPTR_NULL; process->fd_array.current--; remote_spinlock_unlock( XPTR( local_cxy , &process->fd_array.lock ) ); return 0; } /////////////////////////////////////// void process_fd_copy( fd_array_t * dst, fd_array_t * src ) { uint32_t fd; xptr_t entry; remote_spinlock_lock( XPTR( local_cxy , &src->lock ) ); // loop on all entries in source process fd_array for( fd = 0 ; fd < src->max ; fd++ ) { entry = src->array[fd]; if( entry != XPTR_NULL ) { // increment file descriptor ref count vfs_file_count_up( entry ); // copy entry in destination process fd_array dst->array[fd] = entry; } } // release lock on source process fd_array remote_spinlock_unlock( XPTR( local_cxy , &src->lock ) ); } /////////////////////////////////////////// void process_fd_remote_copy( xptr_t dst_xp, xptr_t src_xp ) { uint32_t fd; xptr_t entry; // get cluster and local pointer for src fd_array cxy_t src_cxy = GET_CXY( src_xp ); fd_array_t * src_ptr = (fd_array_t *)GET_PTR( src_xp ); // get cluster and local pointer for dst fd_array cxy_t dst_cxy = GET_CXY( dst_xp ); fd_array_t * dst_ptr = (fd_array_t *)GET_PTR( dst_xp ); // get the remote lock protecting the src fd_array remote_spinlock_lock( XPTR( src_cxy , &src_ptr->lock ) ); // get number of entries in fd_array uint32_t max = hal_remote_lw( XPTR( src_cxy , &src_ptr->max ) ); // loop on all entries in source process fd_array for( fd = 0 ; fd < max ; fd++ ) { entry = (xptr_t)hal_remote_lwd( XPTR( src_cxy , &src_ptr->array[fd] ) ); if( entry != XPTR_NULL ) { // increment file descriptor ref count vfs_file_count_up( entry ); // copy entry in destination process fd_array hal_remote_swd( XPTR( dst_cxy , &dst_ptr->array[fd] ) , entry ); } } // release lock on source process fd_array remote_spinlock_unlock( XPTR( src_cxy , &src_ptr->lock ) ); } //////////////////////////////////////////////////////////////////////////////////// // Thread related functions //////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////// error_t process_register_thread( process_t * process, thread_t * thread, trdid_t * trdid ) { ltid_t ltid; bool_t found; if( process == NULL ) { printk("\n[PANIC] in %s : process argument is NULL\n", __FUNCTION__ ); hal_core_sleep(); } if( thread == NULL ) { printk("\n[PANIC] in %s : thread argument is NULL\n", __FUNCTION__ ); hal_core_sleep(); } // search a free slot in th_tbl[] found = false; for( ltid = 0 ; ltid < CONFIG_THREAD_MAX_PER_CLUSTER ; ltid++ ) { if( process->th_tbl[ltid] == NULL ) { found = true; break; } } if( found ) { // register thread in th_tbl[] process->th_tbl[ltid] = thread; process->th_nr++; // returns trdid *trdid = TRDID( local_cxy , ltid ); } return (found) ? 0 : ENOMEM; } /////////////////////////////////////////////// void process_remove_thread( thread_t * thread ) { if( thread == NULL ) { printk("\n[PANIC] in %s : thread argument is NULL\n", __FUNCTION__ ); hal_core_sleep(); } process_t * process = thread->process; // get thread local index ltid_t ltid = LTID_FROM_TRDID( thread->trdid ); // remove thread from th_tbl[] process->th_tbl[ltid] = NULL; process->th_nr--; } ///////////////////////////////////////////////////// error_t process_make_exec( exec_info_t * exec_info ) { char * path; // pathname to .elf file process_t * process; // pointer on new process pid_t pid; // new process pid thread_t * thread; // pointer on new thread pthread_attr_t attr; // main thread attributes core_t * core; // pointer on selected core lid_t lid; // selected core local index error_t error; // get pid and pathname to .elf file path = exec_info->path; pid = exec_info->pid; if( CXY_FROM_PID( pid ) != local_cxy ) { printk("\n[PANIC] in %s : illegal process PID %x in cluster %x\n", __FUNCTION__ , pid , local_cxy ); hal_core_sleep(); } exec_dmsg("\n[INFO] %s enters in cluster %x for process %x / path = %s\n", __FUNCTION__ , local_cxy , pid , path ); // create new process descriptor process = process_alloc(); if( process == NULL ) { printk("\n[ERROR] in %s : no memory in cluster %x for process %x / path = %s\n", __FUNCTION__ , local_cxy , pid , path ); return ENOMEM; } // initialize the process descriptor as the reference process_reference_init( process , pid , exec_info->ppid ); // restore from exec_info the extended pointer on vfs root, cwd, and bin process->vfs_root_xp = exec_info->vfs_root_xp; process->vfs_cwd_xp = exec_info->vfs_cwd_xp; process->vfs_bin_xp = exec_info->vfs_bin_xp; // restore from exec_info the embedded fd_array process_fd_remote_copy( XPTR( local_cxy , &process->fd_array ), exec_info->fd_array_xp ); exec_dmsg("\n[INFO] %s restaured fd-array in cluster %x for process %x / path = %s\n", __FUNCTION__, local_cxy , pid , path ); // initialize signal manager TODO ??? [AG] // signal_manager_init( process ); // initialize process VMM vmm_init( process ); exec_dmsg("\n[INFO] %s initialized VMM in cluster %x for process %x / path = %s\n", __FUNCTION__ , local_cxy , pid , path ); // register "code" and "data" vsegs as well a the process entry-point in VMM, // using informations contained in the elf file. error = elf_load_process( path , process ); if( error ) { printk("\n[ERROR] in %s : failed to access elf file for process %x / path = %s\n", __FUNCTION__, pid , path ); process_destroy( process ); return error; } // create "heap" vseg descriptor vseg_t * heap_vseg = vmm_create_vseg( process, CONFIG_VMM_HEAP_BASE, CONFIG_VMM_HEAP_SIZE, VSEG_TYPE_HEAP ); if( heap_vseg == NULL ) { printk("\n[ERROR] in %s : cannot create heap vseg for process %x / path = %s\n", __FUNCTION__, pid , path ); process_destroy( process ); return error; } // create "stack" vseg descriptor for associated main thread vseg_t * stack_vseg = vmm_create_vseg( process, 0, // base defined by VMM 0, // size defined by VMM VSEG_TYPE_STACK ); if( stack_vseg == NULL ) { printk("\n[ERROR] in %s : cannot create stack vseg for process %x / path = %s\n", __FUNCTION__, pid , path ); process_destroy( process ); return error; } // select a core in cluster lid = cluster_select_local_core(); core = &LOCAL_CLUSTER->core_tbl[lid]; // initialize pthread attributes for main thread attr.pid = pid; attr.entry_func = (void*)process->vmm.entry_point; attr.entry_args = exec_info->args_pointers; attr.flags = PT_FLAG_DETACH; // main thread always detached attr.cxy = local_cxy; attr.lid = lid; // create and initialise thread descriptor, link thread & process error = thread_user_create( &thread, &attr, stack_vseg->min, stack_vseg->max - stack_vseg->min ); if( error ) { printk("\n[ERROR] in %s : cannot create thread for process %x / path = %s\n", __FUNCTION__, pid , path ); process_destroy( process ); return error; } // Register thread in scheduler sched_register_thread( core , thread ); exec_dmsg("\n[INFO] %s created thread for process %x on core %d in cluster %x\n", __FUNCTION__ , process->pid , core->lid , local_cxy ); // activate new thread thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_GLOBAL ); return 0; } // end process_make_exec() ////////////////////////// void process_init_create() { process_t * process_init; // process_init descriptor thread_t * thread_init; // process_init main thread descriptor pid_t init_pid; // process_init pid exec_info_t exec_info; // structure to be passed to process_make_exec() error_t error1 = 0; error_t error2 = 0; error_t error3 = 0; process_dmsg("\n[INFO] %s enters in cluster %x\n", __FUNCTION__ , local_cxy ); // allocate memory for process descriptor process_init = process_alloc(); if( process_init == NULL ) { printk("\n[PANIC] in %s : no memory for process descriptor in cluster %x\n", __FUNCTION__ , local_cxy ); hal_core_sleep(); } // get a pid from the local cluster xptr_t xp_process = XPTR( local_cxy , process_init ); error1 = cluster_pid_alloc( xp_process , &init_pid ); if( error1 ) { printk("\n[PANIC] in %s : cannot get PID in cluster %x\n", __FUNCTION__ , local_cxy ); hal_core_sleep(); } // initializes process_init descriptor as the reference process_reference_init( process_init , init_pid , process_zero.pid ); // initialize process_init VMM vmm_init( process_init ); // initializes vfs_root and vfs_cwd from process_zero vfs_file_count_up( process_zero.vfs_root_xp ); process_init->vfs_root_xp = process_zero.vfs_root_xp; vfs_file_count_up( process_zero.vfs_cwd_xp ); process_init->vfs_cwd_xp = process_zero.vfs_cwd_xp; // update children list in process_zero xlist_add_last( XPTR( local_cxy , &process_zero.children_root ), XPTR( local_cxy , &process_init->brothers_list ) ); process_zero.children_nr = 1; // TODO open stdin / stdout / stderr pseudo-files xptr_t stdin_xp; xptr_t stdout_xp; xptr_t stderr_xp; // error1 = vfs_open( process_init->vfs_cwd_xp, NULL, VFS_O_RDONLY, &stdin_xp ); // error2 = vfs_open( process_init->vfs_cwd_xp, NULL, VFS_O_WRONLY, &stdout_xp ); // error3 = vfs_open( process_init->vfs_cwd_xp, NULL, VFS_O_WRONLY, &stderr_xp ); if( error1 || error2 || error3 ) { if( !error1 ) vfs_close( stdin_xp , NULL ); if( !error2 ) vfs_close( stdout_xp , NULL ); if( !error3 ) vfs_close( stderr_xp , NULL ); printk("\n[PANIC] in %s : cannot open stdin/stdout/stderr in cluster %x\n", __FUNCTION__ , local_cxy ); hal_core_sleep(); } // register stdin / stdout / stderr in the fd_array 3 first slots process_init->fd_array.array[0] = stdin_xp; process_init->fd_array.array[1] = stdout_xp; process_init->fd_array.array[2] = stderr_xp; process_init->fd_array.current = 3; // initialize the exec_info structure exec_info.pid = process_init->pid; exec_info.ppid = process_init->ppid; exec_info.fd_array_xp = XPTR( local_cxy , &process_init->fd_array ); exec_info.vfs_root_xp = process_init->vfs_root_xp; exec_info.vfs_cwd_xp = process_init->vfs_cwd_xp; exec_info.vfs_bin_xp = process_init->vfs_bin_xp; // TODO thread_init ??? // exec_info.args_nr = 1; // exec_info.envs_nr = 1; // strcpy( exec_info.args[0] , "init" ); // strcpy( exec_info.envs[0] , "ALMOS-MKH.CONFIG = "CONFIG_ALMOS_VERSION ); // strcpy( exec_info.path , INIT_PATHNAME ); // create process_init and thread_init error1 = process_make_exec( &exec_info ); if( error1 ) { printk("\n[PANIC] in %s : cannot create main thread in cluster %x\n", __FUNCTION__ , local_cxy ); hal_core_sleep(); } process_dmsg("\n[INFO] %s successfully exit in cluster %x\n", __FUNCTION__ , local_cxy ); hal_wbflush(); } // end process_init_create()