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_is_valid(parent)); |
27 | |
28 | auto child_p = Process::New(parent, name: parent->name); |
29 | if (unlikely(!child_p)) |
30 | { |
31 | pr_emerg("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 | pr_demph(process, "process %d forked to %d" , parent->pid, child_p->pid); |
38 | |
39 | mm_lock_ctx_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 | pr_dinfo2(process, "fork vmap %d->%d: %10s, %pvm -> %pvm" , parent->pid, child_p->pid, get_vmap_type_str(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_ctx_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 | fd_type file = parent->files[i]; |
68 | if (io_valid(io: file.io)) |
69 | { |
70 | child_p->files[i].io = io_ref(io: file.io); |
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 | pr_dinfo2(process, "fork: thread %d->%d" , 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 | |