/* * sys_fork.c - Fork the current process. * * Authors Alain Greiner (2016,2017) * * 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 ////////////// int sys_fork() { process_t * parent_process; // pointer on parent process descriptor pid_t parent_pid; // parent process identifier thread_t * parent_thread; // pointer on parent thread descriptor process_t * child_process; // pointer on child process descriptor pid_t child_pid; // child process identifier thread_t * child_thread; // pointer on child main thread descriptor cxy_t target_cxy; // target cluster for forked child process error_t error; uint64_t tm_start; uint64_t tm_end; tm_start = hal_get_cycles(); // get pointers on parent process and thread parent_thread = CURRENT_THREAD; parent_process = parent_thread->process; parent_pid = parent_process->pid; fork_dmsg("\n[DBG] %s : core[%x,%d] enters for process %x / cycle %d\n", __FUNCTION__ , local_cxy , parent_thread->core->lid , parent_pid , (uint32_t)tm_start ); // check parent process children number if( hal_atomic_add( &parent_process->children_nr , 1 ) >= CONFIG_PROCESS_MAX_CHILDREN ) { printk("\n[ERROR] in %s : too much children processes\n", __FUNCTION__); hal_atomic_add ( &parent_process->children_nr , -1 ); return EAGAIN; } // Select target cluster for future migration of child process and main thread. // If placement is not user-defined, the placement is defined by the DQDT. // The two first processes ("init" and "sh") on boot cluster do not migrate. if( parent_thread->fork_user ) { // user defined placement target_cxy = parent_thread->fork_cxy; parent_thread->fork_user = false; } else if( (LPID_FROM_PID(parent_process->pid) < 2) && (local_cxy == 0) ) { // 2 first process stay in boot cluster target_cxy = local_cxy; } else { // DQDT placement target_cxy = dqdt_get_cluster_for_process(); } //printk("\n[DBG] %s : core[%x,%d] for process %x selects target_cluster = %x\n", //__FUNCTION__ , local_cxy , parent_thread->core->lid , parent_pid , target_cxy ); // allocates memory in local cluster for the child process descriptor child_process = process_alloc(); if( child_process == NULL ) { printk("\n[ERROR] in %s : cannot allocate child process\n", __FUNCTION__ ); hal_atomic_add ( &parent_process->children_nr , -1 ); return EAGAIN; } // get a new PID for child process, if( target_cxy == local_cxy ) // target cluster is local { error = cluster_pid_alloc( XPTR( target_cxy , child_process ) , &child_pid ); } else // target cluster is remote { rpc_process_pid_alloc_client( target_cxy , child_process , &error , &child_pid ); } if( error ) { printk("\n[ERROR] in %s : cannot allocate PID\n", __FUNCTION__ ); hal_atomic_add ( &parent_process->children_nr , -1 ); process_destroy( child_process ); return EAGAIN; } // initialize and register the child process descriptor process_reference_init( child_process , child_pid , XPTR(local_cxy, parent_process) ); // initialises child process standard files structures // ( root / cwd / bin ) from parent process descriptor vfs_file_count_up( parent_process->vfs_root_xp ); child_process->vfs_root_xp = parent_process->vfs_root_xp; vfs_file_count_up( parent_process->vfs_cwd_xp ); child_process->vfs_cwd_xp = parent_process->vfs_cwd_xp; vfs_file_count_up( parent_process->vfs_bin_xp ); child_process->vfs_bin_xp = parent_process->vfs_bin_xp; // copy the parent process fd_array to the child process fd_array process_fd_remote_copy( XPTR( local_cxy , &child_process->fd_array ), XPTR( local_cxy , &parent_process->fd_array ) ); //printk("\n[DBG] %s : core[%x,%d] for process %x created child process %x\n", //__FUNCTION__ , local_cxy , parent_thread->core->lid , parent_pid , child_pid ); // replicate VMM error = vmm_copy( child_process , parent_process ); if( error ) { printk("\n[ERROR] in %s : cannot duplicate VMM\n", __FUNCTION__ ); hal_atomic_add ( &parent_process->children_nr , -1 ); process_destroy( child_process ); return ENOMEM; } //printk("\n[DBG] %s : core[%x,%d] for process %x duplicated vmm in child process\n", //__FUNCTION__ , local_cxy , parent_thread->core->lid , parent_pid ); //vmm_display( parent_process , true ); //vmm_display( child_process , true ); // create child main thread in local cluster error = thread_user_fork( child_process, parent_thread->u_stack_size, parent_thread->u_stack_base, &child_thread ); if( error ) { printk("\n[ERROR] in %s : cannot duplicate main thread\n", __FUNCTION__ ); hal_atomic_add( &parent_process->children_nr , -1 ); process_destroy( child_process ); return ENOMEM; } //printk("\n[DBG] %s : core[%x,%d] initialised child main thread\n", //__FUNCTION__ , local_cxy , parent_thread->core->lid ); // update DQDT for the child thread dqdt_local_update_threads( 1 ); // set child_thread FPU_context from parent_thread register values // only when the parent process is the FPU owner if( CURRENT_THREAD->core->fpu_owner == parent_thread ) { hal_fpu_context_save( child_thread->fpu_context ); } // set child_thread CPU context from parent_thread register values hal_do_cpu_save( child_thread->cpu_context, child_thread, (int)((intptr_t)child_thread - (intptr_t)parent_thread) ); // from this point, both parent and child threads execute the following code // but child execute it only when it has been unblocked by its parent thread_t * current = CURRENT_THREAD; if( current == parent_thread ) { // parent_thread unblock child_thread thread_unblock( XPTR( local_cxy , child_thread ) , THREAD_BLOCKED_GLOBAL ); tm_end = hal_get_cycles(); fork_dmsg("\n[DBG] %s : core[%x,%d] parent_process %x exit / cycle %d\n" " child_process %x / child_thread = %x / cost = %d\n", __FUNCTION__, local_cxy, parent_thread->core->lid, parent_pid, (uint32_t)tm_start, child_pid, child_thread->trdid , (uint32_t)(tm_end - tm_start) ); return child_pid; } else // current == child_thread { assert( (current == child_thread) , __FUNCTION__ , "current thread %x is not the child thread %x\n", current , child_thread ); fork_dmsg("\n[DBG] %s : core[%x,%d] child process %x exit / cycle %d\n", __FUNCTION__, local_cxy, parent_thread->core->lid, child_pid, (uint32_t)hal_get_cycles() ); return 0; } } // end sys_fork()