/* * sys_fork.c - Kernel function implementing the "fork" system call. * * 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_ptr; // pointer on local parent process descriptor xptr_t parent_thread_xp; // extended pointer on parent thread descriptor pid_t parent_pid; // parent process identifier thread_t * parent_thread_ptr; // local pointer on local parent thread descriptor pid_t child_pid; // child process identifier thread_t * child_thread_ptr; // local pointer on remote child thread descriptor cxy_t target_cxy; // target cluster for forked child process xptr_t ref_process_xp; // extended pointer on reference parent process cxy_t ref_process_cxy; // cluster of reference parent process process_t * ref_process_ptr; // local pointer on reference parent process error_t error; // get pointers on local parent process and thread parent_thread_ptr = CURRENT_THREAD; parent_thread_xp = XPTR( local_cxy , parent_thread_ptr ); parent_process_ptr = parent_thread_ptr->process; parent_pid = parent_process_ptr->pid; #if CONFIG_DEBUG_SYS_FORK uint64_t tm_start; uint64_t tm_end; tm_start = hal_get_cycles(); if( CONFIG_DEBUG_SYS_FORK < tm_start ) printk("\n[DBG] %s : thread %x enter / parent %x / cycle = %d\n", __FUNCTION__, parent_thread_ptr, parent_pid, (uint32_t)tm_start ); #endif // get infos on reference process ref_process_xp = parent_process_ptr->ref_xp; ref_process_cxy = GET_CXY( ref_process_xp ); ref_process_ptr = (process_t *)GET_PTR( ref_process_xp ); // check parent process children number from reference xptr_t children_xp = XPTR( ref_process_cxy , &ref_process_ptr->children_nr ); if( hal_remote_atomic_add( children_xp , 1 ) >= CONFIG_PROCESS_MAX_CHILDREN ) { printk("\n[ERROR] in %s : too much children processes\n", __FUNCTION__); hal_remote_atomic_add ( children_xp , -1 ); parent_thread_ptr->errno = EAGAIN; return -1; } // Select target cluster for child process and main thread. // If placement is not user-defined, the placement is defined by the DQDT. if( parent_thread_ptr->fork_user ) // user defined placement { target_cxy = parent_thread_ptr->fork_cxy; parent_thread_ptr->fork_user = false; } else // DQDT placement { target_cxy = dqdt_get_cluster_for_process(); } // call process_make_fork in target cluster if( target_cxy == local_cxy ) { error = process_make_fork( ref_process_xp, parent_thread_xp, &child_pid, &child_thread_ptr ); } else { rpc_process_make_fork_client( target_cxy, ref_process_xp, parent_thread_xp, &child_pid, &child_thread_ptr, &error ); } if( error ) { printk("\n[ERROR] in %s : cannot fork process %x in cluster %x\n", __FUNCTION__, parent_pid, local_cxy ); parent_thread_ptr->errno = EAGAIN; return -1; } // set remote child FPU_context from parent_thread register values // only when the parent thread is the FPU owner if( CURRENT_THREAD->core->fpu_owner == parent_thread_ptr ) { hal_fpu_context_save( XPTR( target_cxy , child_thread_ptr ) ); } // set remote child CPU context from parent_thread register values hal_cpu_context_fork( XPTR( target_cxy , child_thread_ptr ) ); // From this point, both parent and child threads execute the following code. // They can be distinguished by the CURRENT_THREAD value, and child will only // execute it when it is unblocked by parent. // - parent unblock child, and return child PID to user application. // - child thread does nothing, and return 0 to user pplication thread_t * current = CURRENT_THREAD; if( current == parent_thread_ptr ) // current == parent thread { // parent_thread unblock child_thread thread_unblock( XPTR( target_cxy , child_thread_ptr ) , THREAD_BLOCKED_GLOBAL ); #if CONFIG_DEBUG_SYS_FORK tm_end = hal_get_cycles(); if( CONFIG_DEBUG_SYS_FORK < tm_end ) printk("\n[DBG] %s : parent_thread %x exit / cost = %d / cycle %d\n", __FUNCTION__ , parent_thread_ptr, (uint32_t)(tm_end - tm_start), (uint32_t)tm_end ); #endif return child_pid; } else // current == child_thread { #if CONFIG_DEBUG_SYS_FORK tm_end = hal_get_cycles(); if( CONFIG_DEBUG_SYS_FORK < tm_end ) printk("\n[DBG] %s : child_thread %x exit / cost = %d / cycle %d\n", __FUNCTION__ , child_thread_ptr, (uint32_t)(tm_end - tm_start), (uint32_t)tm_end ); #endif return 0; } } // end sys_fork()