const char *test_semaphore1(void)
{
	int i, j, k;
	atomic_count_t consumers;
	atomic_count_t producers;
	
	waitq_initialize(&can_start);
	semaphore_initialize(&sem, AT_ONCE);
	
	for (i = 1; i <= 3; i++) {
		thread_t *thrd;
		
		atomic_set(&items_produced, 0);
		atomic_set(&items_consumed, 0);
		
		consumers = i * CONSUMERS;
		producers = (4 - i) * PRODUCERS;
		
		TPRINTF("Creating %" PRIua " consumers and %" PRIua " producers...",
		    consumers, producers);
		
		for (j = 0; j < (CONSUMERS + PRODUCERS) / 2; j++) {
			for (k = 0; k < i; k++) {
				thrd = thread_create(consumer, NULL, TASK,
				    THREAD_FLAG_NONE, "consumer");
				if (thrd)
					thread_ready(thrd);
				else
					TPRINTF("could not create consumer %d\n", i);
			}
			for (k = 0; k < (4 - i); k++) {
				thrd = thread_create(producer, NULL, TASK,
				    THREAD_FLAG_NONE, "producer");
				if (thrd)
					thread_ready(thrd);
				else
					TPRINTF("could not create producer %d\n", i);
			}
		}
		
		TPRINTF("ok\n");
		
		thread_sleep(1);
		waitq_wakeup(&can_start, WAKEUP_ALL);
		
		while ((items_consumed.count != consumers) || (items_produced.count != producers)) {
			TPRINTF("%" PRIua " consumers remaining, %" PRIua " producers remaining\n",
			    consumers - items_consumed.count, producers - items_produced.count);
			thread_sleep(1);
		}
	}
	
	return NULL;
}
Exemple #2
0
indev_t *kbrd_wire(kbrd_instance_t *instance, indev_t *sink)
{
	ASSERT(instance);
	ASSERT(sink);
	
	instance->sink = sink;
	thread_ready(instance->thread);
	
	return &instance->raw;
}
Exemple #3
0
void complete_test( void )
{
  uint32_t    index;
  rtems_id          task_id;

  benchmark_timer_initialize();
    thread_resume( Middle_tcb );
  thread_resume_time = benchmark_timer_read();

  thread_set_state( Middle_tcb, STATES_WAITING_FOR_MESSAGE );

  benchmark_timer_initialize();
    thread_unblock( Middle_tcb );
  thread_unblock_time = benchmark_timer_read();

  thread_set_state( Middle_tcb, STATES_WAITING_FOR_MESSAGE );

  benchmark_timer_initialize();
    thread_ready( Middle_tcb );
  thread_ready_time = benchmark_timer_read();

  benchmark_timer_initialize();
    for ( index=1 ; index <= OPERATION_COUNT ; index++ )
      (void) benchmark_timer_empty_function();
  overhead = benchmark_timer_read();

  task_id = Middle_tcb->Object.id;

  benchmark_timer_initialize();
    for ( index=1 ; index <= OPERATION_COUNT ; index++ )
      (void) _Thread_Get( task_id, &location );
  thread_get_time = benchmark_timer_read();

  benchmark_timer_initialize();
    for ( index=1 ; index <= OPERATION_COUNT ; index++ )
      (void) _Semaphore_Get( Semaphore_id, &location );
  semaphore_get_time = benchmark_timer_read();

  benchmark_timer_initialize();
    for ( index=1 ; index <= OPERATION_COUNT ; index++ )
      (void) _Thread_Get( 0x3, &location );
  thread_get_invalid_time = benchmark_timer_read();

  /*
   *  This is the running task and we have tricked RTEMS out enough where
   *  we need to set some internal tracking information to match this.
   */

  set_thread_heir( _Thread_Get_executing() );
  set_thread_dispatch_necessary( false );

  for (index = 0; index < 2 * OPERATION_COUNT; ++index) {
    _Thread_Unnest_dispatch();
  }

  /*
   *  Now dump all the times
   */

  put_time(
    "rtems interrupt: _ISR_Disable",
    isr_disable_time,
    1,
    0,
    0
  );

  put_time(
    "rtems interrupt: _ISR_Flash",
    isr_flash_time,
    1,
    0,
    0
  );

  put_time(
    "rtems interrupt: _ISR_Enable",
    isr_enable_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: _Thread_Disable_dispatch",
    thread_disable_dispatch_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: _Thread_Enable_dispatch",
    thread_enable_dispatch_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: _Thread_Set_state",
    thread_set_state_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: _Thread_Dispatch NO FP",
    thread_dispatch_no_fp_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: context switch: no floating point contexts",
    context_switch_no_fp_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: context switch: self",
    context_switch_self_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: context switch to another task",
    context_switch_another_task_time,
    1,
    0,
    0
  );

#if (CPU_HARDWARE_FP == 1) || (CPU_SOFTWARE_FP == 1)
  put_time(
    "rtems internal: fp context switch restore 1st FP task",
    context_switch_restore_1st_fp_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: fp context switch save idle and restore initialized",
    context_switch_save_idle_restore_initted_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: fp context switch save idle, restore idle",
    context_switch_save_restore_idle_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: fp context switch save initialized, restore initialized",
    context_switch_save_restore_initted_time,
    1,
    0,
    0
  );
#else
    puts(
     "rtems internal: fp context switch restore 1st FP task - NA\n"
     "rtems internal: fp context switch save idle restore initialized - NA\n"
     "rtems internal: fp context switch save idle restore idle - NA\n"
     "rtems internal: fp context switch save initialized\n"
                      " restore initialized - NA"
   );
#endif

  put_time(
    "rtems internal: _Thread_Resume",
    thread_resume_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: _Thread_Unblock",
    thread_unblock_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: _Thread_Ready",
    thread_ready_time,
    1,
    0,
    0
  );

  put_time(
    "rtems internal: _Thread_Get",
    thread_get_time,
    OPERATION_COUNT,
    0,
    0
  );

  put_time(
    "rtems internal: _Semaphore_Get",
    semaphore_get_time,
    OPERATION_COUNT,
    0,
    0
  );

  put_time(
    "rtems internal: _Thread_Get: invalid id",
    thread_get_invalid_time,
    OPERATION_COUNT,
    0,
    0
  );

  TEST_END();
  rtems_test_exit( 0 );
}
Exemple #4
0
/** Kernel initialization thread.
 *
 * kinit takes care of higher level kernel
 * initialization (i.e. thread creation,
 * userspace initialization etc.).
 *
 * @param arg Not used.
 */
void kinit(void *arg)
{
	thread_t *thread;
	
	/*
	 * Detach kinit as nobody will call thread_join_timeout() on it.
	 */
	thread_detach(THREAD);
	
	interrupts_disable();
	
#ifdef CONFIG_SMP
	if (config.cpu_count > 1) {
		waitq_initialize(&ap_completion_wq);
		
		/*
		 * Create the kmp thread and wait for its completion.
		 * cpu1 through cpuN-1 will come up consecutively and
		 * not mess together with kcpulb threads.
		 * Just a beautification.
		 */
		thread = thread_create(kmp, NULL, TASK,
		    THREAD_FLAG_UNCOUNTED, "kmp");
		if (thread != NULL) {
			thread_wire(thread, &cpus[0]);
			thread_ready(thread);
		} else
			panic("Unable to create kmp thread.");
		
		thread_join(thread);
		thread_detach(thread);
		
		/*
		 * For each CPU, create its load balancing thread.
		 */
		unsigned int i;
		
		for (i = 0; i < config.cpu_count; i++) {
			thread = thread_create(kcpulb, NULL, TASK,
			    THREAD_FLAG_UNCOUNTED, "kcpulb");
			if (thread != NULL) {
				thread_wire(thread, &cpus[i]);
				thread_ready(thread);
			} else
				printf("Unable to create kcpulb thread for cpu%u\n", i);
		}
	}
#endif /* CONFIG_SMP */
	
	/*
	 * At this point SMP, if present, is configured.
	 */
	arch_post_smp_init();
	
	/* Start thread computing system load */
	thread = thread_create(kload, NULL, TASK, THREAD_FLAG_NONE,
	    "kload");
	if (thread != NULL)
		thread_ready(thread);
	else
		printf("Unable to create kload thread\n");
	
#ifdef CONFIG_KCONSOLE
	if (stdin) {
		/*
		 * Create kernel console.
		 */
		thread = thread_create(kconsole_thread, NULL, TASK,
		    THREAD_FLAG_NONE, "kconsole");
		if (thread != NULL)
			thread_ready(thread);
		else
			printf("Unable to create kconsole thread\n");
	}
#endif /* CONFIG_KCONSOLE */
	
	interrupts_enable();
	
	/*
	 * Create user tasks, load RAM disk images.
	 */
	size_t i;
	program_t programs[CONFIG_INIT_TASKS];
	
	for (i = 0; i < init.cnt; i++) {
		if (init.tasks[i].paddr % FRAME_SIZE) {
			printf("init[%zu]: Address is not frame aligned\n", i);
			programs[i].task = NULL;
			continue;
		}
		
		/*
		 * Construct task name from the 'init:' prefix and the
		 * name stored in the init structure (if any).
		 */
		
		char namebuf[TASK_NAME_BUFLEN];
		
		const char *name = init.tasks[i].name;
		if (name[0] == 0)
			name = "<unknown>";
		
		ASSERT(TASK_NAME_BUFLEN >= INIT_PREFIX_LEN);
		str_cpy(namebuf, TASK_NAME_BUFLEN, INIT_PREFIX);
		str_cpy(namebuf + INIT_PREFIX_LEN,
		    TASK_NAME_BUFLEN - INIT_PREFIX_LEN, name);
		
		/*
		 * Create virtual memory mappings for init task images.
		 */
		uintptr_t page = km_map(init.tasks[i].paddr,
		    init.tasks[i].size,
		    PAGE_READ | PAGE_WRITE | PAGE_CACHEABLE);
		ASSERT(page);
		
		int rc = program_create_from_image((void *) page, namebuf,
		    &programs[i]);
		
		if (rc == 0) {
			if (programs[i].task != NULL) {
				/*
				 * Set capabilities to init userspace tasks.
				 */
				cap_set(programs[i].task, CAP_CAP | CAP_MEM_MANAGER |
				    CAP_IO_MANAGER | CAP_IRQ_REG);
				
				if (!ipc_phone_0)
					ipc_phone_0 = &programs[i].task->answerbox;
			}
			
			/*
			 * If programs[i].task == NULL then it is
			 * the program loader and it was registered
			 * successfully.
			 */
		} else if (i == init.cnt - 1) {
			/*
			 * Assume the last task is the RAM disk.
			 */
			init_rd((void *) init.tasks[i].paddr, init.tasks[i].size);
		} else
			printf("init[%zu]: Init binary load failed "
			    "(error %d, loader status %u)\n", i, rc,
			    programs[i].loader_status);
	}
	
	/*
	 * Run user tasks.
	 */
	for (i = 0; i < init.cnt; i++) {
		if (programs[i].task != NULL)
			program_ready(&programs[i]);
	}
	
#ifdef CONFIG_KCONSOLE
	if (!stdin) {
		thread_sleep(10);
		printf("kinit: No stdin\nKernel alive: .");
		
		unsigned int i = 0;
		while (true) {
			printf("\b%c", alive[i % ALIVE_CHARS]);
			thread_sleep(1);
			i++;
		}
	}
#endif /* CONFIG_KCONSOLE */
}
Exemple #5
0
/** Make program ready.
 *
 * Switch program's main thread to the ready state.
 *
 * @param prg Program to make ready.
 *
 */
void program_ready(program_t *prg)
{
	thread_ready(prg->main_thread);
}
Exemple #6
0
const char *test_smpcall1(void)
{
	/* Number of received calls that were sent by cpu[i]. */
	size_t call_cnt[MAX_CPUS] = {0};
	thread_t *thread[MAX_CPUS] = { NULL };
	
	unsigned int cpu_count = min(config.cpu_active, MAX_CPUS);
	size_t running_thread_cnt = 0;

	TPRINTF("Spawning threads on %u cpus.\n", cpu_count);
	
	/* Create a wired thread on each cpu. */
	for (unsigned int id = 0; id < cpu_count; ++id) {
		thread[id] = thread_create(test_thread, &call_cnt[id], TASK, 
			THREAD_FLAG_NONE, "smp-call-test");
		
		if (thread[id]) {
			thread_wire(thread[id], &cpus[id]);
			++running_thread_cnt;
		} else {
			TPRINTF("Failed to create thread on cpu%u.\n", id);
		}
	}

	size_t exp_calls = calc_exp_calls(running_thread_cnt);
	size_t exp_calls_sum = exp_calls * cpu_count;
	
	TPRINTF("Running %zu wired threads. Expecting %zu calls. Be patient.\n", 
		running_thread_cnt, exp_calls_sum);

	for (unsigned int i = 0; i < cpu_count; ++i) {
		if (thread[i] != NULL) {
			thread_ready(thread[i]);
		}
	}
	
	/* Wait for threads to complete. */
	for (unsigned int i = 0; i < cpu_count; ++i) {
		if (thread[i] != NULL) {
			thread_join(thread[i]);
			thread_detach(thread[i]);
		}
	}

	TPRINTF("Threads finished. Checking number of smp_call()s.\n");
	
	bool ok = true;
	size_t calls_sum = 0;
	
	for (size_t i = 0; i < cpu_count; ++i) {
		if (thread[i] != NULL) {
			if (call_cnt[i] != exp_calls) {
				ok = false;
				TPRINTF("Error: %zu instead of %zu cpu%zu's calls were"
					" acknowledged.\n", call_cnt[i], exp_calls, i);
			} 
		}
		
		calls_sum += call_cnt[i];
	}
	
	if (calls_sum != exp_calls_sum) {
		TPRINTF("Error: total acknowledged sum: %zu instead of %zu.\n",
			calls_sum, exp_calls_sum);
		
		ok = false;
	}
	
	if (ok) {
		TPRINTF("Success: number of received smp_calls is as expected (%zu).\n",
			exp_calls_sum);
		return NULL;
	} else
		return "Failed: incorrect acknowledged smp_calls.\n";
	
}
Exemple #7
0
/** Kernel initialization thread.
 *
 * kinit takes care of higher level kernel
 * initialization (i.e. thread creation,
 * userspace initialization etc.).
 *
 * @param arg Not used.
 */
void kinit(void *arg)
{
	thread_t *thread;
	
	/*
	 * Detach kinit as nobody will call thread_join_timeout() on it.
	 */
	thread_detach(THREAD);

	interrupts_disable();
	
	/* Start processing RCU callbacks. RCU is fully functional afterwards. */
	rcu_kinit_init();
	
	/*
	 * Start processing work queue items. Some may have been queued during boot.
	 */
	workq_global_worker_init();
	
#ifdef CONFIG_SMP
	if (config.cpu_count > 1) {
		waitq_initialize(&ap_completion_wq);
		
		/*
		 * Create the kmp thread and wait for its completion.
		 * cpu1 through cpuN-1 will come up consecutively and
		 * not mess together with kcpulb threads.
		 * Just a beautification.
		 */
		thread = thread_create(kmp, NULL, TASK,
		    THREAD_FLAG_UNCOUNTED, "kmp");
		if (thread != NULL) {
			thread_wire(thread, &cpus[0]);
			thread_ready(thread);
		} else
			panic("Unable to create kmp thread.");
		
		thread_join(thread);
		thread_detach(thread);
		
		/*
		 * For each CPU, create its load balancing thread.
		 */
		unsigned int i;
		
		for (i = 0; i < config.cpu_count; i++) {
			thread = thread_create(kcpulb, NULL, TASK,
			    THREAD_FLAG_UNCOUNTED, "kcpulb");
			if (thread != NULL) {
				thread_wire(thread, &cpus[i]);
				thread_ready(thread);
			} else
				log(LF_OTHER, LVL_ERROR,
				    "Unable to create kcpulb thread for cpu%u", i);
		}
	}
#endif /* CONFIG_SMP */
	
	/*
	 * At this point SMP, if present, is configured.
	 */
	ARCH_OP(post_smp_init);
	
	/* Start thread computing system load */
	thread = thread_create(kload, NULL, TASK, THREAD_FLAG_NONE,
	    "kload");
	if (thread != NULL)
		thread_ready(thread);
	else
		log(LF_OTHER, LVL_ERROR, "Unable to create kload thread");
	
#ifdef CONFIG_KCONSOLE
	if (stdin) {
		/*
		 * Create kernel console.
		 */
		thread = thread_create(kconsole_thread, NULL, TASK,
		    THREAD_FLAG_NONE, "kconsole");
		if (thread != NULL)
			thread_ready(thread);
		else
			log(LF_OTHER, LVL_ERROR,
			    "Unable to create kconsole thread");
	}
#endif /* CONFIG_KCONSOLE */
	
	/*
	 * Store the default stack size in sysinfo so that uspace can create
	 * stack with this default size.
	 */
	sysinfo_set_item_val("default.stack_size", NULL, STACK_SIZE_USER);
	
	interrupts_enable();
	
	/*
	 * Create user tasks, load RAM disk images.
	 */
	size_t i;
	program_t programs[CONFIG_INIT_TASKS];
	
	// FIXME: do not propagate arguments through sysinfo
	// but pass them directly to the tasks
	for (i = 0; i < init.cnt; i++) {
		const char *arguments = init.tasks[i].arguments;
		if (str_length(arguments) == 0)
			continue;
		if (str_length(init.tasks[i].name) == 0)
			continue;
		size_t arguments_size = str_size(arguments);

		void *arguments_copy = malloc(arguments_size, 0);
		if (arguments_copy == NULL)
			continue;
		memcpy(arguments_copy, arguments, arguments_size);

		char item_name[CONFIG_TASK_NAME_BUFLEN + 15];
		snprintf(item_name, CONFIG_TASK_NAME_BUFLEN + 15,
		    "init_args.%s", init.tasks[i].name);

		sysinfo_set_item_data(item_name, NULL, arguments_copy, arguments_size);
	}

	for (i = 0; i < init.cnt; i++) {
		if (init.tasks[i].paddr % FRAME_SIZE) {
			log(LF_OTHER, LVL_ERROR,
			    "init[%zu]: Address is not frame aligned", i);
			programs[i].task = NULL;
			continue;
		}
		
		/*
		 * Construct task name from the 'init:' prefix and the
		 * name stored in the init structure (if any).
		 */
		
		char namebuf[TASK_NAME_BUFLEN];
		
		const char *name = init.tasks[i].name;
		if (name[0] == 0)
			name = "<unknown>";
		
		STATIC_ASSERT(TASK_NAME_BUFLEN >= INIT_PREFIX_LEN);
		str_cpy(namebuf, TASK_NAME_BUFLEN, INIT_PREFIX);
		str_cpy(namebuf + INIT_PREFIX_LEN,
		    TASK_NAME_BUFLEN - INIT_PREFIX_LEN, name);
		
		/*
		 * Create virtual memory mappings for init task images.
		 */
		uintptr_t page = km_map(init.tasks[i].paddr,
		    init.tasks[i].size,
		    PAGE_READ | PAGE_WRITE | PAGE_CACHEABLE);
		ASSERT(page);
		
		int rc = program_create_from_image((void *) page, namebuf,
		    &programs[i]);
		
		if (rc == 0) {
			if (programs[i].task != NULL) {
				/*
				 * Set capabilities to init userspace tasks.
				 */
				cap_set(programs[i].task, CAP_CAP | CAP_MEM_MANAGER |
				    CAP_IO_MANAGER | CAP_IRQ_REG);
				
				if (!ipc_phone_0) {
					ipc_phone_0 = &programs[i].task->answerbox;
					/*
					 * Hold the first task so that the
					 * ipc_phone_0 remains a valid pointer
					 * even if the first task exits for
					 * whatever reason.
					 */
					task_hold(programs[i].task);
				}
			}
			
			/*
			 * If programs[i].task == NULL then it is
			 * the program loader and it was registered
			 * successfully.
			 */
		} else if (i == init.cnt - 1) {
			/*
			 * Assume the last task is the RAM disk.
			 */
			init_rd((void *) init.tasks[i].paddr, init.tasks[i].size);
		} else
			log(LF_OTHER, LVL_ERROR,
			    "init[%zu]: Init binary load failed "
			    "(error %d, loader status %u)", i, rc,
			    programs[i].loader_status);
	}
	
	/*
	 * Run user tasks.
	 */
	for (i = 0; i < init.cnt; i++) {
		if (programs[i].task != NULL)
			program_ready(&programs[i]);
	}
	
#ifdef CONFIG_KCONSOLE
	if (!stdin) {
		thread_sleep(10);
		printf("kinit: No stdin\nKernel alive: .");
		
		unsigned int i = 0;
		while (true) {
			printf("\b%c", alive[i % ALIVE_CHARS]);
			thread_sleep(1);
			i++;
		}
	}
#endif /* CONFIG_KCONSOLE */
}
Exemple #8
0
pid_t proc_fork(void)
{
    /*
     * http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html
     */

    KERROR_DBG("%s(%u)\n", __func__, curproc->pid);

    struct proc_info * const old_proc = curproc;
    struct proc_info * new_proc;
    pid_t retval = 0;

    /* Check that the old process is in valid state. */
    if (!old_proc || old_proc->state == PROC_STATE_INITIAL)
        return -EINVAL;

    new_proc = clone_proc_info(old_proc);
    if (!new_proc)
        return -ENOMEM;

    /* Clear some things required to be zeroed at this point */
    new_proc->state = PROC_STATE_INITIAL;
    new_proc->files = NULL;
    new_proc->pgrp = NULL; /* Must be NULL so we don't free the old ref. */
    memset(&new_proc->tms, 0, sizeof(new_proc->tms));
    /* ..and then start to fix things. */

    /*
     * Process group.
     */
    PROC_LOCK();
    proc_pgrp_insert(old_proc->pgrp, new_proc);
    PROC_UNLOCK();

    /*
     *  Initialize the mm struct.
     */
    retval = vm_mm_init(&new_proc->mm, old_proc->mm.nr_regions);
    if (retval)
        goto out;

    /*
     * Clone the master page table.
     * This is probably something we would like to get rid of but we are
     * stuck with because it's the easiest way to keep some static kernel
     * mappings valid between processes.
     */
    if (mmu_ptcpy(&new_proc->mm.mpt, &old_proc->mm.mpt)) {
        retval = -EAGAIN;
        goto out;
    }

    /*
     * Clone L2 page tables.
     */
    if (vm_ptlist_clone(&new_proc->mm.ptlist_head, &new_proc->mm.mpt,
                        &old_proc->mm.ptlist_head) < 0) {
        retval = -ENOMEM;
        goto out;
    }

    retval = clone_code_region(new_proc, old_proc);
    if (retval)
        goto out;

    /*
     * Clone stack region.
     */
    retval = clone_stack(new_proc, old_proc);
    if (retval) {
        KERROR_DBG("Cloning stack region failed.\n");
        goto out;
    }

    /*
     *  Clone other regions.
     */
    retval = clone_regions_from(new_proc, old_proc, MM_HEAP_REGION);
    if (retval)
        goto out;

    /*
     * Set break values.
     */
    new_proc->brk_start = (void *)(
            (*new_proc->mm.regions)[MM_HEAP_REGION]->b_mmu.vaddr +
            (*new_proc->mm.regions)[MM_HEAP_REGION]->b_bcount);
    new_proc->brk_stop = (void *)(
            (*new_proc->mm.regions)[MM_HEAP_REGION]->b_mmu.vaddr +
            (*new_proc->mm.regions)[MM_HEAP_REGION]->b_bufsize);

    /* fork() signals */
    ksignal_signals_fork_reinit(&new_proc->sigs);

    /*
     * Copy file descriptors.
     */
    KERROR_DBG("Copy file descriptors\n");
    int nofile_max = old_proc->rlim[RLIMIT_NOFILE].rlim_max;
    if (nofile_max < 0) {
#if configRLIMIT_NOFILE < 0
#error configRLIMIT_NOFILE can't be negative.
#endif
        nofile_max = configRLIMIT_NOFILE;
    }
    new_proc->files = fs_alloc_files(nofile_max, nofile_max);
    if (!new_proc->files) {
        KERROR_DBG(
               "\tENOMEM when tried to allocate memory for file descriptors\n");
        retval = -ENOMEM;
        goto out;
    }
    /* Copy and ref old file descriptors */
    for (int i = 0; i < old_proc->files->count; i++) {
        new_proc->files->fd[i] = old_proc->files->fd[i];
        fs_fildes_ref(new_proc->files, i, 1); /* null pointer safe */
    }
    KERROR_DBG("All file descriptors copied\n");

    /*
     * Select PID.
     */
    if (likely(nprocs != 1)) { /* Tecnically it would be good idea to have lock
                                * on nprocs before reading it but I think this
                                * should work fine... */
        new_proc->pid = proc_get_random_pid();
    } else { /* Proc is init */
        KERROR_DBG("Assuming this process to be init\n");
        new_proc->pid = 1;
    }

    if (new_proc->cwd) {
        KERROR_DBG("Increment refcount for the cwd\n");
        vref(new_proc->cwd); /* Increment refcount for the cwd */
    }

    /* Update inheritance attributes */
    set_proc_inher(old_proc, new_proc);

    /* Insert the new process into the process array */
    procarr_insert(new_proc);

    /*
     * A process shall be created with a single thread. If a multi-threaded
     * process calls fork(), the new process shall contain a replica of the
     * calling thread.
     * We left main_thread null if calling process has no main thread.
     */
    KERROR_DBG("Handle main_thread\n");
    if (old_proc->main_thread) {
        KERROR_DBG("Call thread_fork() to get a new main thread for the fork.\n");
        if (!(new_proc->main_thread = thread_fork(new_proc->pid))) {
            KERROR_DBG("\tthread_fork() failed\n");
            retval = -EAGAIN;
            goto out;
        }

        KERROR_DBG("\tthread_fork() fork OK\n");

        /*
         * We set new proc's mpt as the current mpt because the new main thread
         * is going to return directly to the user space.
         */
        new_proc->main_thread->curr_mpt = &new_proc->mm.mpt;
    } else {
        KERROR_DBG("No thread to fork.\n");
        new_proc->main_thread = NULL;
    }
    retval = new_proc->pid;

    new_proc->state = PROC_STATE_READY;

#ifdef configPROCFS
    procfs_mkentry(new_proc);
#endif

    if (new_proc->main_thread) {
        KERROR_DBG("Set the new main_thread (%d) ready\n",
                   new_proc->main_thread->id);
        thread_ready(new_proc->main_thread->id);
    }

    KERROR_DBG("Fork %d -> %d created.\n", old_proc->pid, new_proc->pid);

out:
    if (unlikely(retval < 0)) {
        _proc_free(new_proc);
    }
    return retval;
}