[1] | 1 | /* |
---|
[23] | 2 | * sys_fork.c - Fork the current process. |
---|
[1] | 3 | * |
---|
[23] | 4 | * Authors Alain Greiner (2016,2017) |
---|
[1] | 5 | * |
---|
| 6 | * Copyright (c) UPMC Sorbonne Universites |
---|
| 7 | * |
---|
| 8 | * This file is part of ALMOS-MKH. |
---|
| 9 | * |
---|
| 10 | * ALMOS-MKH.is free software; you can redistribute it and/or modify it |
---|
| 11 | * under the terms of the GNU General Public License as published by |
---|
| 12 | * the Free Software Foundation; version 2.0 of the License. |
---|
| 13 | * |
---|
| 14 | * ALMOS-MKH.is distributed in the hope that it will be useful, but |
---|
| 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
| 17 | * General Public License for more details. |
---|
| 18 | * |
---|
| 19 | * You should have received a copy of the GNU General Public License |
---|
| 20 | * along with ALMOS-MKH.; if not, write to the Free Software Foundation, |
---|
| 21 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
---|
| 22 | */ |
---|
| 23 | |
---|
[23] | 24 | #include <kernel_config.h> |
---|
| 25 | #include <hal_types.h> |
---|
[407] | 26 | #include <hal_context.h> |
---|
| 27 | #include <hal_switch.h> |
---|
[23] | 28 | #include <hal_atomic.h> |
---|
[1] | 29 | #include <errno.h> |
---|
[23] | 30 | #include <printk.h> |
---|
| 31 | #include <core.h> |
---|
[1] | 32 | #include <cluster.h> |
---|
| 33 | #include <list.h> |
---|
| 34 | #include <thread.h> |
---|
| 35 | #include <scheduler.h> |
---|
| 36 | #include <kmem.h> |
---|
| 37 | #include <dqdt.h> |
---|
| 38 | #include <process.h> |
---|
| 39 | |
---|
[23] | 40 | ////////////// |
---|
| 41 | int sys_fork() |
---|
[1] | 42 | { |
---|
| 43 | process_t * parent_process; // pointer on parent process descriptor |
---|
| 44 | pid_t parent_pid; // parent process identifier |
---|
| 45 | thread_t * parent_thread; // pointer on parent thread descriptor |
---|
| 46 | process_t * child_process; // pointer on child process descriptor |
---|
| 47 | pid_t child_pid; // child process identifier |
---|
| 48 | thread_t * child_thread; // pointer on child main thread descriptor |
---|
[407] | 49 | cxy_t target_cxy; // target cluster for forked child process |
---|
[1] | 50 | error_t error; |
---|
| 51 | |
---|
[407] | 52 | uint64_t tm_start; |
---|
| 53 | uint64_t tm_end; |
---|
| 54 | |
---|
| 55 | tm_start = hal_get_cycles(); |
---|
| 56 | |
---|
[1] | 57 | // get pointers on parent process and thread |
---|
| 58 | parent_thread = CURRENT_THREAD; |
---|
| 59 | parent_process = parent_thread->process; |
---|
[23] | 60 | parent_pid = parent_process->pid; |
---|
[1] | 61 | |
---|
[407] | 62 | fork_dmsg("\n[DBG] %s : core[%x,%d] enters for process %x / cycle %d\n", |
---|
| 63 | __FUNCTION__ , local_cxy , parent_thread->core->lid , parent_pid , (uint32_t)tm_start ); |
---|
| 64 | |
---|
[1] | 65 | // check parent process children number |
---|
[23] | 66 | if( hal_atomic_add( &parent_process->children_nr , 1 ) >= CONFIG_PROCESS_MAX_CHILDREN ) |
---|
[1] | 67 | { |
---|
[23] | 68 | printk("\n[ERROR] in %s : too much children processes\n", __FUNCTION__); |
---|
| 69 | hal_atomic_add ( &parent_process->children_nr , -1 ); |
---|
[1] | 70 | return EAGAIN; |
---|
| 71 | } |
---|
| 72 | |
---|
| 73 | // Select target cluster for future migration of child process and main thread. |
---|
[23] | 74 | // If placement is not user-defined, the placement is defined by the DQDT. |
---|
| 75 | // The two first processes ("init" and "sh") on boot cluster do not migrate. |
---|
| 76 | |
---|
| 77 | if( parent_thread->fork_user ) |
---|
[1] | 78 | { |
---|
| 79 | // user defined placement |
---|
[23] | 80 | target_cxy = parent_thread->fork_cxy; |
---|
| 81 | parent_thread->fork_user = false; |
---|
[1] | 82 | } |
---|
[23] | 83 | else if( (LPID_FROM_PID(parent_process->pid) < 2) && (local_cxy == 0) ) |
---|
[1] | 84 | { |
---|
| 85 | // 2 first process stay in boot cluster |
---|
[23] | 86 | target_cxy = local_cxy; |
---|
[1] | 87 | } |
---|
| 88 | else |
---|
| 89 | { |
---|
| 90 | // DQDT placement |
---|
| 91 | target_cxy = dqdt_get_cluster_for_process(); |
---|
| 92 | } |
---|
| 93 | |
---|
[407] | 94 | //printk("\n[DBG] %s : core[%x,%d] for process %x selects target_cluster = %x\n", |
---|
| 95 | //__FUNCTION__ , local_cxy , parent_thread->core->lid , parent_pid , target_cxy ); |
---|
[1] | 96 | |
---|
| 97 | // allocates memory in local cluster for the child process descriptor |
---|
| 98 | child_process = process_alloc(); |
---|
[23] | 99 | |
---|
[1] | 100 | if( child_process == NULL ) |
---|
| 101 | { |
---|
[23] | 102 | printk("\n[ERROR] in %s : cannot allocate child process\n", __FUNCTION__ ); |
---|
| 103 | hal_atomic_add ( &parent_process->children_nr , -1 ); |
---|
[1] | 104 | return EAGAIN; |
---|
| 105 | } |
---|
| 106 | |
---|
[23] | 107 | // get a new PID for child process, |
---|
| 108 | if( target_cxy == local_cxy ) // target cluster is local |
---|
[1] | 109 | { |
---|
[23] | 110 | error = cluster_pid_alloc( XPTR( target_cxy , child_process ) , &child_pid ); |
---|
[1] | 111 | } |
---|
[23] | 112 | else // target cluster is remote |
---|
[1] | 113 | { |
---|
[23] | 114 | rpc_process_pid_alloc_client( target_cxy , child_process , &error , &child_pid ); |
---|
[1] | 115 | } |
---|
[23] | 116 | |
---|
[1] | 117 | if( error ) |
---|
| 118 | { |
---|
[23] | 119 | printk("\n[ERROR] in %s : cannot allocate PID\n", __FUNCTION__ ); |
---|
| 120 | hal_atomic_add ( &parent_process->children_nr , -1 ); |
---|
[1] | 121 | process_destroy( child_process ); |
---|
| 122 | return EAGAIN; |
---|
| 123 | } |
---|
| 124 | |
---|
| 125 | // initialize and register the child process descriptor |
---|
[407] | 126 | process_reference_init( child_process , child_pid , XPTR(local_cxy, parent_process) ); |
---|
[1] | 127 | |
---|
| 128 | // initialises child process standard files structures |
---|
| 129 | // ( root / cwd / bin ) from parent process descriptor |
---|
| 130 | |
---|
[23] | 131 | vfs_file_count_up( parent_process->vfs_root_xp ); |
---|
| 132 | child_process->vfs_root_xp = parent_process->vfs_root_xp; |
---|
[1] | 133 | |
---|
[23] | 134 | vfs_file_count_up( parent_process->vfs_cwd_xp ); |
---|
| 135 | child_process->vfs_cwd_xp = parent_process->vfs_cwd_xp; |
---|
[1] | 136 | |
---|
[23] | 137 | vfs_file_count_up( parent_process->vfs_bin_xp ); |
---|
| 138 | child_process->vfs_bin_xp = parent_process->vfs_bin_xp; |
---|
[1] | 139 | |
---|
| 140 | // copy the parent process fd_array to the child process fd_array |
---|
[23] | 141 | process_fd_remote_copy( XPTR( local_cxy , &child_process->fd_array ), |
---|
| 142 | XPTR( local_cxy , &parent_process->fd_array ) ); |
---|
[1] | 143 | |
---|
[407] | 144 | //printk("\n[DBG] %s : core[%x,%d] for process %x created child process %x\n", |
---|
| 145 | //__FUNCTION__ , local_cxy , parent_thread->core->lid , parent_pid , child_pid ); |
---|
[1] | 146 | |
---|
[407] | 147 | // replicate VMM |
---|
[23] | 148 | error = vmm_copy( child_process , parent_process ); |
---|
| 149 | |
---|
[1] | 150 | if( error ) |
---|
| 151 | { |
---|
[23] | 152 | printk("\n[ERROR] in %s : cannot duplicate VMM\n", __FUNCTION__ ); |
---|
| 153 | hal_atomic_add ( &parent_process->children_nr , -1 ); |
---|
[1] | 154 | process_destroy( child_process ); |
---|
[23] | 155 | return ENOMEM; |
---|
[1] | 156 | } |
---|
| 157 | |
---|
[407] | 158 | //printk("\n[DBG] %s : core[%x,%d] for process %x duplicated vmm in child process\n", |
---|
| 159 | //__FUNCTION__ , local_cxy , parent_thread->core->lid , parent_pid ); |
---|
| 160 | //vmm_display( parent_process , true ); |
---|
| 161 | //vmm_display( child_process , true ); |
---|
[1] | 162 | |
---|
[407] | 163 | // create child main thread in local cluster |
---|
| 164 | error = thread_user_fork( child_process, |
---|
| 165 | parent_thread->u_stack_size, |
---|
| 166 | parent_thread->u_stack_base, |
---|
| 167 | &child_thread ); |
---|
[1] | 168 | if( error ) |
---|
| 169 | { |
---|
[407] | 170 | printk("\n[ERROR] in %s : cannot duplicate main thread\n", __FUNCTION__ ); |
---|
[23] | 171 | hal_atomic_add( &parent_process->children_nr , -1 ); |
---|
[1] | 172 | process_destroy( child_process ); |
---|
[23] | 173 | return ENOMEM; |
---|
[1] | 174 | } |
---|
| 175 | |
---|
[407] | 176 | //printk("\n[DBG] %s : core[%x,%d] initialised child main thread\n", |
---|
| 177 | //__FUNCTION__ , local_cxy , parent_thread->core->lid ); |
---|
[1] | 178 | |
---|
[407] | 179 | // update DQDT for the child thread |
---|
| 180 | dqdt_local_update_threads( 1 ); |
---|
[1] | 181 | |
---|
[407] | 182 | // set child_thread FPU_context from parent_thread register values |
---|
| 183 | // only when the parent process is the FPU owner |
---|
| 184 | if( CURRENT_THREAD->core->fpu_owner == parent_thread ) |
---|
| 185 | { |
---|
| 186 | hal_fpu_context_save( child_thread->fpu_context ); |
---|
| 187 | } |
---|
[1] | 188 | |
---|
[407] | 189 | // set child_thread CPU context from parent_thread register values |
---|
| 190 | hal_do_cpu_save( child_thread->cpu_context, |
---|
| 191 | child_thread, |
---|
| 192 | (int)((intptr_t)child_thread - (intptr_t)parent_thread) ); |
---|
[1] | 193 | |
---|
| 194 | |
---|
[407] | 195 | // from this point, both parent and child threads execute the following code |
---|
| 196 | // but child execute it only when it has been unblocked by its parent |
---|
[1] | 197 | |
---|
[407] | 198 | thread_t * current = CURRENT_THREAD; |
---|
[1] | 199 | |
---|
[407] | 200 | if( current == parent_thread ) |
---|
| 201 | { |
---|
| 202 | // parent_thread unblock child_thread |
---|
| 203 | thread_unblock( XPTR( local_cxy , child_thread ) , THREAD_BLOCKED_GLOBAL ); |
---|
[1] | 204 | |
---|
[407] | 205 | tm_end = hal_get_cycles(); |
---|
[1] | 206 | |
---|
[407] | 207 | fork_dmsg("\n[DBG] %s : core[%x,%d] parent_process %x exit / cycle %d\n" |
---|
| 208 | " child_process %x / child_thread = %x / cost = %d\n", |
---|
| 209 | __FUNCTION__, local_cxy, parent_thread->core->lid, parent_pid, (uint32_t)tm_start, |
---|
| 210 | child_pid, child_thread->trdid , (uint32_t)(tm_end - tm_start) ); |
---|
| 211 | |
---|
| 212 | return child_pid; |
---|
| 213 | } |
---|
| 214 | else // current == child_thread |
---|
| 215 | { |
---|
| 216 | assert( (current == child_thread) , __FUNCTION__ , |
---|
| 217 | "current thread %x is not the child thread %x\n", current , child_thread ); |
---|
| 218 | |
---|
| 219 | fork_dmsg("\n[DBG] %s : core[%x,%d] child process %x exit / cycle %d\n", |
---|
| 220 | __FUNCTION__, local_cxy, parent_thread->core->lid, child_pid, (uint32_t)hal_get_cycles() ); |
---|
| 221 | |
---|
| 222 | return 0; |
---|
| 223 | } |
---|
| 224 | |
---|
[1] | 225 | } // end sys_fork() |
---|
| 226 | |
---|