MOS Source Code
Loading...
Searching...
No Matches
thread.c
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
4#include "mos/mm/mm.h"
5
6#include <errno.h>
7#include <limits.h>
11#include <mos/mm/cow.h>
14#include <mos/syslog/printk.h>
15#include <mos/tasks/process.h>
16#include <mos/tasks/schedule.h>
18#include <mos/tasks/thread.h>
19#include <mos/tasks/wait.h>
20#include <mos_stdlib.h>
21#include <mos_string.h>
22
23hashmap_t thread_table = { 0 }; // tid_t -> thread_t
24
25static tid_t new_thread_id(void)
26{
27 static tid_t next = 1;
28 return (tid_t){ next++ };
29}
30
32{
33 thread_t *t = kmalloc(thread_cache);
35 t->tid = new_thread_id();
36 t->owner = owner;
38 t->mode = tflags;
43 return t;
44}
45
47{
48 MOS_ASSERT_X(thread != current_thread, "you cannot just destroy yourself");
49 if (!thread_is_valid(thread))
50 return;
51
53
54 pr_dinfo2(thread, "destroying thread %pt", (void *) thread);
55 MOS_ASSERT_X(spinlock_is_locked(&thread->state_lock), "thread state lock must be held");
56 MOS_ASSERT_X(thread->state == THREAD_STATE_DEAD, "thread must be dead for destroy");
57
59
60 if (thread->name)
61 kfree(thread->name);
62
63 if (thread->mode == THREAD_MODE_USER)
64 {
65 process_t *const owner = thread->owner;
66 spinlock_acquire(&owner->mm->mm_lock);
67 vmap_t *const stack = vmap_obtain(owner->mm, (ptr_t) thread->u_stack.top - 1, NULL);
68 vmap_destroy(stack);
69 spinlock_release(&owner->mm->mm_lock);
70 }
71
73
74 kfree(thread);
75}
76
77thread_t *thread_new(process_t *owner, thread_mode tmode, const char *name, size_t stack_size, void *explicit_stack_top)
78{
79 thread_t *t = thread_allocate(owner, tmode);
80
81 t->name = strdup(name);
82
83 pr_dinfo2(thread, "creating new thread %pt, owner=%pp", (void *) t, (void *) owner);
84
85 // Kernel stack
87 stack_init(&t->k_stack, (void *) kstack_blk, MOS_STACK_PAGES_KERNEL * MOS_PAGE_SIZE);
88
89 if (tmode != THREAD_MODE_USER)
90 {
91 stack_init(&t->u_stack, NULL, 0); // kernel thread has no user stack
92 return t;
93 }
94
95 // User stack
96 const size_t user_stack_size = stack_size ? stack_size : MOS_STACK_PAGES_USER * MOS_PAGE_SIZE;
97 if (!explicit_stack_top)
98 {
100 stack_init(&t->u_stack, (void *) stack_vmap->vaddr, user_stack_size);
102 return t;
103 }
104
105 // check if the stack is valid
106 mm_lock_ctx_pair(owner->mm, NULL);
107 vmap_t *stack_vmap = vmap_obtain(owner->mm, (ptr_t) explicit_stack_top, NULL);
108 if (!stack_vmap)
109 {
110 pr_warn("invalid stack pointer %pt", explicit_stack_top);
111 goto done_efault;
112 }
113
114 // check if the stack vmap is valid
115 if (stack_vmap->content == VMAP_STACK) // has been claimed by another thread?
116 {
117 pr_warn("stack %pt has been claimed by another thread", explicit_stack_top);
118 goto done_efault;
119 }
120
121 // check if the stack is large enough
122 if (stack_vmap->npages < user_stack_size / MOS_PAGE_SIZE)
123 {
124 pr_warn("stack %pt is too small (size=%zu, required=%zu)", explicit_stack_top, stack_vmap->npages * MOS_PAGE_SIZE, user_stack_size);
125 goto done_efault;
126 }
127
128 // check if the stack is writable
129 if (!(stack_vmap->vmflags & VM_USER_RW))
130 {
131 pr_warn("stack %pt is not writable", explicit_stack_top);
132 goto done_efault;
133 }
134
135 const ptr_t stack_bottom = ALIGN_UP_TO_PAGE((ptr_t) explicit_stack_top) - user_stack_size;
136 vmap_t *second = vmap_split(stack_vmap, (stack_bottom - stack_vmap->vaddr) / MOS_PAGE_SIZE);
137 spinlock_release(&stack_vmap->lock);
138 stack_vmap = second;
139
140 stack_vmap->content = VMAP_STACK;
141 stack_vmap->type = VMAP_TYPE_PRIVATE;
142 spinlock_release(&stack_vmap->lock);
143 mm_unlock_ctx_pair(owner->mm, NULL);
144 stack_init(&t->u_stack, (void *) stack_bottom, user_stack_size);
145 t->u_stack.head = (ptr_t) explicit_stack_top;
146 return t;
147
148done_efault:
149 spinlock_release(&stack_vmap->lock);
150 mm_unlock_ctx_pair(owner->mm, NULL);
153 return ERR_PTR(-EFAULT); // invalid stack pointer
154}
155
157{
158 if (!thread_is_valid(thread))
159 return NULL;
160
161 thread_t *old = hashmap_put(&thread_table, thread->tid, thread);
162 MOS_ASSERT(old == NULL);
163 return thread;
164}
165
167{
169 if (thread_is_valid(t))
170 return t;
171
172 return NULL;
173}
174
176{
177 thread_t *target = thread_get(tid);
178 if (target == NULL)
179 {
180 pr_warn("wait_for_tid(%d) from pid %d (%s) but thread does not exist", tid, current_process->pid, current_process->name);
181 return false;
182 }
183
184 if (target->owner != current_process)
185 {
186 pr_warn("wait_for_tid(%d) from process %pp but thread belongs to %pp", tid, (void *) current_process, (void *) target->owner);
187 return false;
188 }
189
190 bool ok = reschedule_for_waitlist(&target->waiters);
191 MOS_UNUSED(ok); // true: thread is dead, false: thread is already dead at the time of calling
192
193 return true;
194}
195
197{
198 MOS_ASSERT_X(thread_is_valid(t), "thread_handle_exit() called on invalid thread");
201}
202
203[[noreturn]] void thread_exit_locked(thread_t *t)
204{
205 MOS_ASSERT_X(thread_is_valid(t), "thread_exit_locked() called on invalid thread");
206
207 pr_dinfo(thread, "thread %pt is exiting", (void *) t);
208
209 MOS_ASSERT_X(spinlock_is_locked(&t->state_lock), "thread state lock must be held");
210
212
214 waitlist_wake(&t->waiters, INT_MAX);
215
216 while (true)
217 reschedule();
219}
#define MOS_ASSERT_X(cond, msg,...)
Definition assert.h:15
#define MOS_ASSERT(cond)
Definition assert.h:14
#define MOS_UNREACHABLE()
Definition assert.h:11
#define MOS_STACK_PAGES_USER
Definition autoconf.h:29
#define MOS_ADDR_USER_STACK
Definition autoconf.h:1
#define MOS_PAGE_SIZE
Definition autoconf.h:6
#define MOS_STACK_PAGES_KERNEL
Definition autoconf.h:28
vmap_t * cow_allocate_zeroed_pages(mm_context_t *handle, size_t npages, ptr_t vaddr, valloc_flags hints, vm_flags flags)
Allocate zero-on-demand pages at a specific address.
Definition cow.c:79
MOSAPI void stack_init(downwards_stack_t *stack, void *mem_region_bottom, size_t size)
Definition stack.c:8
MOSAPI void * hashmap_get(hashmap_t *map, uintn key)
Definition hashmap.c:96
MOSAPI void * hashmap_put(hashmap_t *map, uintn key, void *value)
Definition hashmap.c:68
MOSAPI void * hashmap_remove(hashmap_t *map, uintn key)
Definition hashmap.c:117
MOSAPI char * strdup(const char *src)
MOSAPI void linked_list_init(list_node_t *head_node)
Initialise a circular double linked list.
Definition list.c:15
MOSAPI void list_node_append(list_node_t *head, list_node_t *item)
Definition list.c:68
#define list_node(element)
Get the ‘list_node’ of a list element. This is exactly the reverse of ‘list_entry’ above.
Definition list.h:68
#define va_phyframe(va)
Definition mm.h:78
#define phyframe_va(frame)
Definition mm.h:79
void vmap_finalise_init(vmap_t *vmap, vmap_content_t content, vmap_type_t type)
Finalize the initialization of a vmap object.
Definition mm.c:257
vmap_t * vmap_obtain(mm_context_t *mmctx, ptr_t vaddr, size_t *out_offset)
Get the vmap object for a virtual address.
Definition mm.c:192
vmap_t * vmap_split(vmap_t *vmap, size_t split)
Split a vmap object into two, at the specified offset.
Definition mm.c:212
#define mm_free_pages(frame, npages)
Definition mm.h:89
void mm_unlock_ctx_pair(mm_context_t *ctx1, mm_context_t *ctx2)
Definition mm.c:111
phyframe_t * mm_get_free_pages(size_t npages)
Definition mm.c:55
void vmap_destroy(vmap_t *vmap)
Destroy a vmap object, and unmmap the region.
Definition mm.c:171
void mm_lock_ctx_pair(mm_context_t *ctx1, mm_context_t *ctx2)
Lock and unlock a pair of mm_context_t objects.
Definition mm.c:95
@ VMAP_TYPE_PRIVATE
Definition mm.h:31
@ VMAP_STACK
Definition mm.h:23
@ VALLOC_DEFAULT
Default allocation flags.
Definition paging.h:21
thread_mode
Definition task_types.h:20
slab_t * thread_cache
Definition task_types.h:94
@ THREAD_MODE_USER
Definition task_types.h:22
void define_syscall thread_exit(void)
Definition ksyscall.c:170
#define ALIGN_UP_TO_PAGE(addr)
Definition mos_global.h:75
#define MOS_UNUSED(x)
Definition mos_global.h:64
#define NULL
Definition pb_syshdr.h:46
#define current_thread
Definition platform.h:30
@ VM_USER_RW
Definition platform.h:54
@ THREAD_STATE_DEAD
thread is dead, and will be cleaned up soon by the scheduler
Definition platform.h:67
@ THREAD_STATE_CREATED
created or forked, but not ever started
Definition platform.h:62
#define current_process
Definition platform.h:31
#define pr_warn(fmt,...)
Definition printk.h:38
#define pr_dinfo(feat, fmt,...)
Definition printk.h:28
#define pr_dinfo2(feat, fmt,...)
Definition printk.h:27
void platform_context_cleanup(thread_t *thread)
__nodiscard bool reschedule_for_waitlist(waitlist_t *waitlist)
Definition schedule.c:174
void reschedule(void)
reschedule.
Definition schedule.c:107
const char * name
Definition slab.c:31
should_inline bool spinlock_is_locked(const spinlock_t *lock)
Definition spinlock.h:68
#define spinlock_acquire(lock)
Definition spinlock.h:61
#define spinlock_release(lock)
Definition spinlock.h:62
A node in a linked list.
Definition list.h:27
spinlock_t mm_lock
protects [pgd] and the [mmaps] list (the list itself, not the vmap_t objects)
Definition platform.h:81
mm_context_t * mm
Definition task_types.h:59
list_head threads
Definition task_types.h:57
list_head pending
list of pending signals
Definition task_types.h:70
u32 magic
Definition task_types.h:76
thread_mode mode
user-mode thread or kernel-mode
Definition task_types.h:81
downwards_stack_t u_stack
user-mode stack
Definition task_types.h:84
waitlist_t waiters
list of threads waiting for this thread to exit
Definition task_types.h:89
downwards_stack_t k_stack
kernel-mode stack
Definition task_types.h:85
const char * name
Definition task_types.h:78
spinlock_t state_lock
protects the thread state
Definition task_types.h:82
process_t * owner
Definition task_types.h:79
tid_t tid
Definition task_types.h:77
thread_state_t state
thread state
Definition task_types.h:83
thread_signal_info_t signal_info
Definition task_types.h:91
Definition mm.h:58
vmap_content_t content
Definition mm.h:70
ptr_t vaddr
Definition mm.h:62
size_t npages
Definition mm.h:63
vm_flags vmflags
Definition mm.h:64
spinlock_t lock
Definition mm.h:60
vmap_type_t type
Definition mm.h:71
void thread_destroy(thread_t *thread)
Definition thread.c:46
hashmap_t thread_table
Definition thread.c:23
thread_t * thread_get(tid_t tid)
Definition thread.c:166
thread_t * thread_new(process_t *owner, thread_mode tmode, const char *name, size_t stack_size, void *explicit_stack_top)
Definition thread.c:77
bool thread_wait_for_tid(tid_t tid)
Definition thread.c:175
thread_t * thread_allocate(process_t *owner, thread_mode tflags)
Definition thread.c:31
void thread_exit_locked(thread_t *t)
Definition thread.c:203
thread_t * thread_complete_init(thread_t *thread)
Definition thread.c:156
static tid_t new_thread_id(void)
Definition thread.c:25
#define THREAD_MAGIC_THRD
Definition thread.h:7
should_inline bool thread_is_valid(const thread_t *thread)
Definition thread.h:13
s32 tid_t
Definition types.h:79
unsigned long ptr_t
Definition types.h:25
size_t waitlist_wake(waitlist_t *list, size_t max_wakeups)
Definition wait.c:45
__BEGIN_DECLS void waitlist_init(waitlist_t *list)
Definition wait.c:23
void waitlist_close(waitlist_t *list)
Definition wait.c:76