Index: trunk/kernel/syscalls/sys_fork.c
===================================================================
--- trunk/kernel/syscalls/sys_fork.c	(revision 690)
+++ trunk/kernel/syscalls/sys_fork.c	(revision 1)
@@ -1,6 +1,8 @@
 /*
- * sys_fork.c - Kernel function implementing the "fork" system call.
+ * sys_fork.c - fork the current process
  * 
- * Authors  Alain Greiner  (2016,2017,2018,2019,2020)
+ * Authors  Ghassan Almaless (2008,2009,2010,2011,2012)
+ *          Mohamed Lamine Karaoui (2015)
+ *          Alain Greiner (2016)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -22,13 +24,9 @@
  */
 
-#include <kernel_config.h>
-#include <hal_kernel_types.h>
-#include <hal_context.h>
-#include <hal_switch.h>
-#include <hal_atomic.h>
 #include <errno.h>
-#include <printk.h>
-#include <core.h>
+#include <config.h>
+#include <cpu.h>
 #include <cluster.h>
+#include <event.h>
 #include <list.h>
 #include <thread.h>
@@ -38,175 +36,218 @@
 #include <process.h>
 
-#include <syscalls.h>
-
-////////////////////
-int sys_fork( void )
+#if CONFIG_FORK_DEBUG
+#define fork_debug(...) printk(__VA_ARGS__)
+#else
+#define fork_debug(...) /**/
+#endif
+
+/***********************************************************************************************
+ * This kernel function implement the "fork" system call.
+ * The calling process descriptor (parent process), and the associated thread descriptor are
+ * replicated in the same cluster as the calling thread, but the new process (child process)
+ * is registered in another target cluster, that will become the process owner. 
+ * The child process and the associated main thread will be migrated to the target cluster
+ * later, when the child process makes an "exec" or any other system call. 
+ * The target cluster depends on the "fork_user" flag and "fork_cxy" variable that can be
+ * stored in the calling thread descriptor by the specific fork_place() system call.
+ * If not, the sys_fork() function makes a query to the DQDT to select the target cluster. 
+ * @ returns child process PID if success / returns -1 if failure
+ **********************************************************************************************/
+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
-    cxy_t             parent_cxy;           // parent thread cluster
-
-    pid_t             child_pid;            // child process identifier
-    thread_t        * child_thread_ptr;     // local pointer on remote child thread 
-    cxy_t             child_cxy;            // target cluster for forked child process 
+	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
+    core_t             * child_core;      // pointer on core for child main thread
+    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;
+
+    cluster_t          * parent_cluster = LOCAL_CLUSTER;
+
+    // get pointers on parent process and thread
+	parent_thread  = CURRENT_THREAD;
+	parent_process = parent_thread->process;
+
+    // check parent process children number
+	if( hal_atomic_add( &parent_process->childs_nr , 1 ) >= CONFIG_PROCESS_CHILDS_MAX_NR )
+	{
+	    printk("ERROR in %s : too much children processes\n", __FUNCTION__);
+	    hal_atomic_add ( &parent_process->childs_nr , -1 );
+        return EAGAIN;
+	}
+
+	fork_debug("INFO : %s enters for process %d at cycle [%d]\n",
+		  __FUNCTION__, parent_process->pid, hal_time_stamp());
+
+    // 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_debug("INFO : %s save FPU\n", __FUNCTION__);
+	}
+
+    // Select target cluster for future migration of child process and main thread.
+    // The placement can be specified by user. If placement is not user-defined,
+    // the placement is defined by the DQDT. 
+    // The two first processes ("init" and "sh") on boot cluster will not migrate. 
+	if( parent_threads->fork_user )
+	{
+        // user defined placement
+        target_cxy = parent->thread.fork_cxy;
+        parent->thread.fork_cxy = false;
+	}
+    else if( (LPID_FROM_PID(parent_process->pid) < 2 )  
+             && ( parent_cluster->cxy == parent_cluster->boot_cxy ) )
+    {
+        // 2 first process stay in boot cluster
+        target_cxy = parent_cluster->cxy;
+    }
+	else
+	{
+        // DQDT placement
+		target_cxy = dqdt_get_cluster_for_process();
+	}
+
+	fork_debug("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("ERROR in %s : cannot allocate memory for child process\n", __FUNCTION__ );
+	    hal_atomic_add ( &parent_process->childs_nr , -1 );
+        return EAGAIN;
+	}
+
+    // get a new PID for child process
+    // it requires an RPC if target cluster is remote
+    xptr_t xp = XPTR( target_cxy , child_process ); 
+    if( target_cxy == parent_cluster->cxy )   // local cluster
+    {
+        error = process_pid_alloc( xp , &child_pid );
+    }
+    else                            // remote cluster
+    {
+        rpc_process_pid_alloc_server( target_cxy , xp , &error , &child_pid );
+    }
+    if( error )
+    {
+	    printk("ERROR in %s : cannot allocate PID\n", __FUNCTION__ );
+	    atomic_add ( &parent_process->childs_nr , -1 );
+        process_destroy( child_process );
+        return EAGAIN;
+    }
+
+    // initialize and register the child process descriptor
+    error = process_reference_init( child_process , child_pid , parent_pid );
+    if( error )
+    {
+	    printk("ERROR in %s : cannot initialise child process\n", __FUNCTION__ );
+	    atomic_add ( &parent_process->childs_nr , -1 );
+        process_destroy( child_process );
+        return EAGAIN;
+    }
+
+	fork_debug("INFO : %s created child process : pid = %x / ppid = %x\n", 
+              __FUNCTION__, child_pid , parent_pid );
+
+    // set IS_REFERENCE flag in child process descriptor
+    child_process->flags = PDF_IS_REFERENCE;
+
+    // initialises child process standard files structures
+    // ( root / cwd / bin ) from parent process descriptor
+	spinlock_lock( &parent_process->cwd_lock );
+
+	vfs_file_count_up( &parent_process->vfs_root );
+	child_process->vfs_root = parent_process->vfs_root;
+
+	vfs_file_count_up( &parent_process->vfs_cwd );
+	child_process->vfs_cwd  = parent_process->vfs_cwd;
+
+	vfs_file_count_up( &parent_process->vfs_bin );
+    child_process->vfs_bin = parent_process->vfs_bin;
+
+	spinlock_unlock( &parent_process->cwd_lock );
+  
+    // copy the parent process fd_array to the child process fd_array
+	process_fd_fork( child_process , parent_process );
+
+    // initialise child process signal manager TODO ???
+	signal_manager_init( child_process );
+  
+	fork_debug("INFO : %s duplicated child process from parent process\n",
+                  __FUNCTION__ );
+
+    // replicates virtual memory manager
+	error = vmm_dup( &child_process->vmm , &parent_process->vmm );
+	if( error )
+    {
+	    printk("ERROR in %s : cannot duplicate VMM\n", __FUNCTION__ );
+	    atomic_add ( &parent_process->childs_nr , -1 );
+        process_destroy( child_process );
+        return EAGAIN;
+    }
+  
+	fork_debug("INFO : %s: parent vmm duplicated in child process\n", __FUNCTION__ );
+
+    // create child main thread descriptor in local cluster TODO stack ???
+    error = thread_user_fork( &child_thread , process , parent_thread );
+	if( error )
+    {
+	    printk("ERROR in %s : cannot duplicate thread\n", __FUNCTION__ );
+	    atomic_add ( &parent_process->childs_nr , -1 );
+        process_destroy( child_process );
+        return EAGAIN;
+    }
+
+    // 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( &process->th_lock );
+
+    if( error ) 
+    {
+        printk("ERROR in %s : cannot register thread\n", __FUNCTION__ );
+	    atomic_add ( &parent_process->childs_nr , -1 );
+        thread_destroy( child_thread );
+        process_destroy( child_process );
+        return EAGAIN;
+    }
  
-    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;
-    parent_cxy         = local_cxy;
-
-#if DEBUG_SYS_FORK || DEBUG_SYSCALLS_ERROR || CONFIG_INSTRUMENTATION_SYSCALLS
-uint64_t     tm_start = hal_get_cycles();
-#endif
-
-#if DEBUG_SYS_FORK
-if( DEBUG_SYS_FORK < (uint32_t)tm_start )
-printk("\n[%s] thread[%x,%x] enter / cycle =  %d\n",
-__FUNCTION__, parent_pid, parent_thread_ptr->trdid, (uint32_t)tm_start );
-#endif
-
-    // get infos on reference parent process
-    ref_process_xp  = parent_process_ptr->ref_xp;
-    ref_process_cxy = GET_CXY( ref_process_xp );
-    ref_process_ptr = 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 )
-	{
-
-#if DEBUG_SYSCALLS_ERROR
-if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
-printk("\n[ERROR] in %s : thread[%x,%x] / too much children\n",
-__FUNCTION__, parent_pid, parent_thread_ptr->trdid );
-#endif
-	    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, it is defined by the DQDT. 
-	if( parent_thread_ptr->fork_user )
-	{
-        child_cxy = parent_thread_ptr->fork_cxy;
-        parent_thread_ptr->fork_user = false;
-	}
-	else                                  // DQDT placement
-	{
-		child_cxy = dqdt_get_cluster_for_thread( LOCAL_CLUSTER->dqdt_root_xp );
-	}
-
-#if (DEBUG_SYS_FORK & 1 )
-if( DEBUG_SYS_FORK < (uint32_t)tm_start )
-printk("\n[%s] thread[%x,%x] selected cluster %x\n",
-__FUNCTION__, parent_pid, parent_thread_ptr->trdid, child_cxy );
-#endif
-
-    // call process_make_fork in target cluster
-    if( child_cxy == local_cxy )
-    {
-        error = process_make_fork( ref_process_xp,
-                                   parent_thread_xp,
-                                   &child_pid,
-                                   &child_thread_ptr );
-    }
-    else
-    {
-        rpc_process_make_fork_client( child_cxy,
-                                      ref_process_xp,
-                                      parent_thread_xp,
-                                      &child_pid,
-                                      &child_thread_ptr,
-                                      &error );
-    }
-
-    if( error )
-    {
-
-#if DEBUG_SYSCALLS_ERROR
-if( DEBUG_SYSCALLS_ERROR < (uint32_t)tm_start )
-printk("\n[ERROR] in %s : thread[%x,%x] cannot fork\n",
-__FUNCTION__, parent_pid, parent_thread_ptr->trdid );
-#endif
-        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( child_cxy , child_thread_ptr ) );
-	}
-
-    // set the remote child CPU context from parent register values,
-    // set the remote child uzone from 
-    // replicates the parent thread kernel stack to the child thread descriptor,
-    // and finally unblock the child thread. 
-    hal_cpu_context_fork( XPTR( child_cxy , child_thread_ptr ) );
-
-    // From this point, both parent and child threads execute the following code,
-    // but child thread will only execute it after being unblocked by parent thread.
-    // They can be distinguished by the (CURRENT_THREAD,local_cxy) values.
-    // - parent return child PID to user application.
-    // - child  return 0 to user application 
-
-    thread_t * current = CURRENT_THREAD;
-
-#if (DEBUG_SYS_FORK || CONFIG_INSTRUMENTATION_SYSCALLS)
-uint64_t     tm_end = hal_get_cycles();
-#endif
-
-    if( (current == parent_thread_ptr) && (local_cxy == parent_cxy) )   // parent thread
-    {
-
-#if DEBUG_SYS_FORK
-if( DEBUG_SYS_FORK < (uint32_t)tm_end )
-printk("\n[%s] parent thread[%x,%x] exit / child_pid %x / cycle %d\n",
-__FUNCTION__, current->process->pid, current->trdid, child_pid, (uint32_t)tm_end );
-#endif
-
-// only parent display the parent and child VMM
-#if (DEBUG_SYS_FORK & 1 )
-if( DEBUG_SYS_FORK < (uint32_t)tm_end )
-{
-    process_t * child_process_ptr = hal_remote_lpt( XPTR( child_cxy , 
-                                                          &child_thread_ptr->process ) );
-    xptr_t      child_process_xp  = XPTR( child_cxy , child_process_ptr );
-
-    hal_vmm_display( ref_process_xp , true ); 
-    hal_vmm_display( child_process_xp , true );
-}
-#endif
-
-// only parent contribute to syscalls instrumentation
-#if CONFIG_INSTRUMENTATION_SYSCALLS
-hal_atomic_add( &syscalls_cumul_cost[SYS_FORK] , tm_end - tm_start );
-hal_atomic_add( &syscalls_occurences[SYS_FORK] , 1 );
-#endif
-        return child_pid;
-    }
-	else                                                               // child_thread
-    {
-
-#if DEBUG_SYS_FORK
-if( DEBUG_SYS_FORK < (uint32_t)tm_end )
-printk("\n[%s] child thread[%x,%x] exit / child_pid %x / cycle %d\n",
-__FUNCTION__, current->process->pid, current->trdid, child_pid, (uint32_t)tm_end );
-#endif
-
-        return 0;
-    }
+    // get a local core to execute child thread
+    child_core_lid = cluster_select_local_core();
+
+	// Update child thread descriptor
+	child_thread->core    = process->core_tbl[child_core_lid];
+	child_thread->process = child_process;
+    child_thread->trdid   = chid_trdid;
+
+	fork_debug("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( trdid );
+	child_process->th_tbl[ltid] = XPTR( local_cxy , child_thread );
+	child_process->threads_nr = 1;
+
+    // register child thread in scheduler 
+	sched_register( child_thread->core , child_thread );
+  
+	fork_debug("INFO : %s registered main thread in scheduler\n", __FUNCTION__);
+
+	// update DQDT for the child thread 
+    dqdt_update_threads_number( 1 );
+
+	fork_debug("INFO :%s completed / parent pid = %x / child pid = %x / at cycle [%d]\n",
+	          __FUNCTION__, parent_process->pid, child_process->pid, hal_time_stamp() );
+
+	return child_process->pid;
 
 }  // end sys_fork()
