/* * 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 ////////////// 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 trdid_t child_trdid; // child main thread identifier lid_t child_core_lid; // core local index for the child main thread cxy_t target_cxy; // final target cluster for forked child process error_t error; // get pointers on parent process and thread parent_thread = CURRENT_THREAD; parent_process = parent_thread->process; parent_pid = parent_process->pid; // 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; } fork_dmsg("\n[INFO] %s : enters for process %d at cycle [%d]\n", __FUNCTION__, parent_process->pid, hal_get_cycles()); // save FPU state in fpu_context if parent process is FPU owner // because we want the child process to share the FPU context if( CURRENT_CORE->fpu_owner == parent_thread ) { hal_fpu_context_save( parent_thread ); fork_dmsg("\n[INFO] %s : save FPU\n", __FUNCTION__); } // 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(); } fork_dmsg("INFO : %s select target_cluster = %x\n", __FUNCTION__ , 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 , parent_pid ); fork_dmsg("\n[INFO] : %s created child process : pid = %x / ppid = %x\n", __FUNCTION__, child_pid , parent_pid ); // 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 ) ); fork_dmsg("\n[INFO] %s : duplicated child process from parent process\n", __FUNCTION__ ); // replicates virtual memory manager 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; } fork_dmsg("\n[INFO] %s : parent vmm duplicated in child process\n", __FUNCTION__ ); // create child main thread descriptor in local cluster error = thread_user_fork( parent_process , &child_thread ); if( error ) { printk("\n[ERROR] in %s : cannot duplicate thread\n", __FUNCTION__ ); hal_atomic_add( &parent_process->children_nr , -1 ); process_destroy( child_process ); return ENOMEM; } // register child thread in child process, and get a TRDID spinlock_lock( &child_process->th_lock ); error = process_register_thread( child_process, child_thread , &child_trdid ); spinlock_unlock( &child_process->th_lock ); if( error ) { printk("\n[ERROR] in %s : cannot register thread\n", __FUNCTION__ ); hal_atomic_add ( &parent_process->children_nr , -1 ); thread_destroy( child_thread ); process_destroy( child_process ); return EAGAIN; } // get a local core to execute child thread child_core_lid = cluster_select_local_core(); // Update child thread descriptor child_thread->core = &LOCAL_CLUSTER->core_tbl[child_core_lid]; child_thread->process = child_process; child_thread->trdid = child_trdid; fork_dmsg("\n[INFO] %s : initialised child main thread\n", __FUNCTION__ ); // register local child thread into local child process th_tbl[] // we don't use the th_lock because there is no concurrent access ltid_t ltid = LTID_FROM_TRDID( child_trdid ); child_process->th_tbl[ltid] = child_thread; child_process->th_nr = 1; // register child thread in scheduler sched_register_thread( child_thread->core , child_thread ); fork_dmsg("\n[INFO] %s : registered main thread in scheduler\n", __FUNCTION__); // update DQDT for the child thread dqdt_local_update_threads( 1 ); fork_dmsg("\n[INFO] %s : completed / parent pid = %x / child pid = %x / at cycle [%d]\n", __FUNCTION__, parent_process->pid, child_process->pid, hal_get_cycles() ); return child_process->pid; } // end sys_fork()