| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "mos/filesystem/dentry.hpp" |
| 4 | #include "mos/filesystem/vfs.hpp" |
| 5 | #include "mos/mm/mm.hpp" |
| 6 | #include "mos/tasks/schedule.hpp" |
| 7 | #include "mos/tasks/signal.hpp" |
| 8 | |
| 9 | #include <mos/lib/structures/hashmap.hpp> |
| 10 | #include <mos/lib/structures/list.hpp> |
| 11 | #include <mos/lib/structures/stack.hpp> |
| 12 | #include <mos/lib/sync/spinlock.hpp> |
| 13 | #include <mos/mm/cow.hpp> |
| 14 | #include <mos/mm/paging/paging.hpp> |
| 15 | #include <mos/mos_global.h> |
| 16 | #include <mos/platform/platform.hpp> |
| 17 | #include <mos/syslog/printk.hpp> |
| 18 | #include <mos/tasks/process.hpp> |
| 19 | #include <mos/tasks/task_types.hpp> |
| 20 | #include <mos/tasks/thread.hpp> |
| 21 | #include <mos_stdlib.hpp> |
| 22 | #include <mos_string.hpp> |
| 23 | |
| 24 | Process *process_do_fork(Process *parent) |
| 25 | { |
| 26 | MOS_ASSERT(Process::IsValid(parent)); |
| 27 | |
| 28 | auto child_p = Process::New(parent, name: parent->name); |
| 29 | if (unlikely(!child_p)) |
| 30 | { |
| 31 | mEmerg << "failed to allocate process for fork" ; |
| 32 | return NULL; |
| 33 | } |
| 34 | |
| 35 | child_p->working_directory = dentry_ref_up_to(dentry: parent->working_directory, root: root_dentry); |
| 36 | |
| 37 | dEmph<process> << "process " << parent->pid << " forked to " << child_p->pid; |
| 38 | |
| 39 | mm_lock_context_pair(ctx1: parent->mm, ctx2: child_p->mm); |
| 40 | list_foreach(vmap_t, vmap_p, parent->mm->mmaps) |
| 41 | { |
| 42 | PtrResult<vmap_t> child_vmap = [&]() |
| 43 | { |
| 44 | switch (vmap_p->type) |
| 45 | { |
| 46 | case VMAP_TYPE_SHARED: return mm_clone_vmap_locked(src_vmap: vmap_p, dst_ctx: child_p->mm); break; |
| 47 | case VMAP_TYPE_PRIVATE: return cow_clone_vmap_locked(target_mmctx: child_p->mm, source_vmap: vmap_p); break; |
| 48 | default: mos_panic("unknown vmap" ); break; |
| 49 | } |
| 50 | }(); |
| 51 | |
| 52 | if (child_vmap.isErr()) |
| 53 | { |
| 54 | mos_panic("failed to clone vmap" ); |
| 55 | } |
| 56 | |
| 57 | dInfo2<process> << "fork vmap " << parent->pid << "->" << child_p->pid << ": " << get_vmap_type_str(type: vmap_p->type) << ", " << (void *) vmap_p << " -> " |
| 58 | << (void *) child_vmap.get(); |
| 59 | vmap_finalise_init(vmap: child_vmap.get(), content: vmap_p->content, type: vmap_p->type); |
| 60 | } |
| 61 | |
| 62 | mm_unlock_context_pair(ctx1: parent->mm, ctx2: child_p->mm); |
| 63 | |
| 64 | // copy the parent's files |
| 65 | for (int i = 0; i < MOS_PROCESS_MAX_OPEN_FILES; i++) |
| 66 | { |
| 67 | const auto &file = parent->files[i]; |
| 68 | if (IO::IsValid(io: file.io)) |
| 69 | { |
| 70 | child_p->files[i].io = file.io->ref(); |
| 71 | child_p->files[i].flags = file.flags; |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | child_p->signal_info = parent->signal_info; |
| 76 | waitlist_init(list: &child_p->signal_info.sigchild_waitlist); |
| 77 | |
| 78 | // copy the thread |
| 79 | const auto parent_thread = current_thread; |
| 80 | const auto child_t = thread_allocate(owner: child_p, tflags: parent_thread->mode); |
| 81 | dInfo2<process> << "fork: thread " << parent_thread->tid << "->" << child_t->tid; |
| 82 | child_t->u_stack = parent_thread->u_stack; |
| 83 | child_t->name = parent_thread->name; |
| 84 | const ptr_t kstack_blk = phyframe_va(mm_get_free_pages(MOS_STACK_PAGES_KERNEL)); |
| 85 | stack_init(stack: &child_t->k_stack, mem_region_bottom: (void *) kstack_blk, MOS_STACK_PAGES_KERNEL * MOS_PAGE_SIZE); |
| 86 | spinlock_acquire(&parent_thread->signal_info.lock); |
| 87 | child_t->signal_info.mask = parent_thread->signal_info.mask; |
| 88 | list_foreach(sigpending_t, sig, parent_thread->signal_info.pending) |
| 89 | { |
| 90 | sigpending_t *new_sig = mos::create<sigpending_t>(); |
| 91 | linked_list_init(list_node(new_sig)); |
| 92 | new_sig->signal = sig->signal; |
| 93 | list_node_prepend(head: &child_t->signal_info.pending, list_node(new_sig)); |
| 94 | } |
| 95 | spinlock_release(&parent_thread->signal_info.lock); |
| 96 | |
| 97 | platform_context_clone(from: parent_thread, to: child_t); |
| 98 | |
| 99 | ProcessTable.insert(key: child_p->pid, value: child_p); |
| 100 | thread_complete_init(thread: child_t); |
| 101 | scheduler_add_thread(thread: child_t); |
| 102 | return child_p; |
| 103 | } |
| 104 | |