Rev 3022 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 3022 | Rev 4055 | ||
|---|---|---|---|
| Line 65... | Line 65... | ||
| 65 | #include <mm/slab.h> |
65 | #include <mm/slab.h> |
| 66 | #include <debug.h> |
66 | #include <debug.h> |
| 67 | #include <main/uinit.h> |
67 | #include <main/uinit.h> |
| 68 | #include <syscall/copy.h> |
68 | #include <syscall/copy.h> |
| 69 | #include <errno.h> |
69 | #include <errno.h> |
| - | 70 | ||
| - | 71 | ||
| 70 | #include <console/klog.h> |
72 | #ifndef LOADED_PROG_STACK_PAGES_NO |
| - | 73 | #define LOADED_PROG_STACK_PAGES_NO 1 |
|
| - | 74 | #endif |
|
| 71 | 75 | ||
| 72 | 76 | ||
| 73 | /** Thread states */ |
77 | /** Thread states */ |
| 74 | char *thread_states[] = { |
78 | char *thread_states[] = { |
| 75 | "Invalid", |
79 | "Invalid", |
| Line 96... | Line 100... | ||
| 96 | 100 | ||
| 97 | SPINLOCK_INITIALIZE(tidlock); |
101 | SPINLOCK_INITIALIZE(tidlock); |
| 98 | thread_id_t last_tid = 0; |
102 | thread_id_t last_tid = 0; |
| 99 | 103 | ||
| 100 | static slab_cache_t *thread_slab; |
104 | static slab_cache_t *thread_slab; |
| 101 | #ifdef ARCH_HAS_FPU |
105 | #ifdef CONFIG_FPU |
| 102 | slab_cache_t *fpu_context_slab; |
106 | slab_cache_t *fpu_context_slab; |
| 103 | #endif |
107 | #endif |
| 104 | 108 | ||
| 105 | /** Thread wrapper. |
109 | /** Thread wrapper. |
| 106 | * |
110 | * |
| Line 155... | Line 159... | ||
| 155 | link_initialize(&t->th_link); |
159 | link_initialize(&t->th_link); |
| 156 | 160 | ||
| 157 | /* call the architecture-specific part of the constructor */ |
161 | /* call the architecture-specific part of the constructor */ |
| 158 | thr_constructor_arch(t); |
162 | thr_constructor_arch(t); |
| 159 | 163 | ||
| 160 | #ifdef ARCH_HAS_FPU |
164 | #ifdef CONFIG_FPU |
| 161 | #ifdef CONFIG_FPU_LAZY |
165 | #ifdef CONFIG_FPU_LAZY |
| 162 | t->saved_fpu_context = NULL; |
166 | t->saved_fpu_context = NULL; |
| 163 | #else |
167 | #else |
| 164 | t->saved_fpu_context = slab_alloc(fpu_context_slab, kmflags); |
168 | t->saved_fpu_context = slab_alloc(fpu_context_slab, kmflags); |
| 165 | if (!t->saved_fpu_context) |
169 | if (!t->saved_fpu_context) |
| 166 | return -1; |
170 | return -1; |
| 167 | #endif |
171 | #endif |
| 168 | #endif |
172 | #endif |
| 169 | 173 | ||
| 170 | t->kstack = (uint8_t *) frame_alloc(STACK_FRAMES, FRAME_KA | kmflags); |
174 | t->kstack = (uint8_t *) frame_alloc(STACK_FRAMES, FRAME_KA | kmflags); |
| 171 | if (!t->kstack) { |
175 | if (!t->kstack) { |
| 172 | #ifdef ARCH_HAS_FPU |
176 | #ifdef CONFIG_FPU |
| 173 | if (t->saved_fpu_context) |
177 | if (t->saved_fpu_context) |
| 174 | slab_free(fpu_context_slab, t->saved_fpu_context); |
178 | slab_free(fpu_context_slab, t->saved_fpu_context); |
| 175 | #endif |
179 | #endif |
| 176 | return -1; |
180 | return -1; |
| 177 | } |
181 | } |
| 178 | 182 | ||
| - | 183 | #ifdef CONFIG_UDEBUG |
|
| - | 184 | mutex_initialize(&t->udebug.lock, MUTEX_PASSIVE); |
|
| - | 185 | #endif |
|
| - | 186 | ||
| 179 | return 0; |
187 | return 0; |
| 180 | } |
188 | } |
| 181 | 189 | ||
| 182 | /** Destruction of thread_t object */ |
190 | /** Destruction of thread_t object */ |
| 183 | static int thr_destructor(void *obj) |
191 | static int thr_destructor(void *obj) |
| Line 186... | Line 194... | ||
| 186 | 194 | ||
| 187 | /* call the architecture-specific part of the destructor */ |
195 | /* call the architecture-specific part of the destructor */ |
| 188 | thr_destructor_arch(t); |
196 | thr_destructor_arch(t); |
| 189 | 197 | ||
| 190 | frame_free(KA2PA(t->kstack)); |
198 | frame_free(KA2PA(t->kstack)); |
| 191 | #ifdef ARCH_HAS_FPU |
199 | #ifdef CONFIG_FPU |
| 192 | if (t->saved_fpu_context) |
200 | if (t->saved_fpu_context) |
| 193 | slab_free(fpu_context_slab, t->saved_fpu_context); |
201 | slab_free(fpu_context_slab, t->saved_fpu_context); |
| 194 | #endif |
202 | #endif |
| 195 | return 1; /* One page freed */ |
203 | return 1; /* One page freed */ |
| 196 | } |
204 | } |
| Line 201... | Line 209... | ||
| 201 | * |
209 | * |
| 202 | */ |
210 | */ |
| 203 | void thread_init(void) |
211 | void thread_init(void) |
| 204 | { |
212 | { |
| 205 | THREAD = NULL; |
213 | THREAD = NULL; |
| 206 | atomic_set(&nrdy,0); |
214 | atomic_set(&nrdy, 0); |
| 207 | thread_slab = slab_cache_create("thread_slab", sizeof(thread_t), 0, |
215 | thread_slab = slab_cache_create("thread_slab", sizeof(thread_t), 0, |
| 208 | thr_constructor, thr_destructor, 0); |
216 | thr_constructor, thr_destructor, 0); |
| 209 | 217 | ||
| 210 | #ifdef ARCH_HAS_FPU |
218 | #ifdef CONFIG_FPU |
| 211 | fpu_context_slab = slab_cache_create("fpu_slab", sizeof(fpu_context_t), |
219 | fpu_context_slab = slab_cache_create("fpu_slab", sizeof(fpu_context_t), |
| 212 | FPU_CONTEXT_ALIGN, NULL, NULL, 0); |
220 | FPU_CONTEXT_ALIGN, NULL, NULL, 0); |
| 213 | #endif |
221 | #endif |
| 214 | 222 | ||
| 215 | avltree_create(&threads_tree); |
223 | avltree_create(&threads_tree); |
| Line 269... | Line 277... | ||
| 269 | * @param arg Thread's implementing function argument. |
277 | * @param arg Thread's implementing function argument. |
| 270 | * @param task Task to which the thread belongs. The caller must |
278 | * @param task Task to which the thread belongs. The caller must |
| 271 | * guarantee that the task won't cease to exist during the |
279 | * guarantee that the task won't cease to exist during the |
| 272 | * call. The task's lock may not be held. |
280 | * call. The task's lock may not be held. |
| 273 | * @param flags Thread flags. |
281 | * @param flags Thread flags. |
| 274 | * @param name Symbolic name. |
282 | * @param name Symbolic name (a copy is made). |
| 275 | * @param uncounted Thread's accounting doesn't affect accumulated task |
283 | * @param uncounted Thread's accounting doesn't affect accumulated task |
| 276 | * accounting. |
284 | * accounting. |
| 277 | * |
285 | * |
| 278 | * @return New thread's structure on success, NULL on failure. |
286 | * @return New thread's structure on success, NULL on failure. |
| 279 | * |
287 | * |
| Line 287... | Line 295... | ||
| 287 | t = (thread_t *) slab_alloc(thread_slab, 0); |
295 | t = (thread_t *) slab_alloc(thread_slab, 0); |
| 288 | if (!t) |
296 | if (!t) |
| 289 | return NULL; |
297 | return NULL; |
| 290 | 298 | ||
| 291 | /* Not needed, but good for debugging */ |
299 | /* Not needed, but good for debugging */ |
| 292 | memsetb((uintptr_t) t->kstack, THREAD_STACK_SIZE * 1 << STACK_FRAMES, |
300 | memsetb(t->kstack, THREAD_STACK_SIZE * 1 << STACK_FRAMES, 0); |
| 293 | 0); |
- | |
| 294 | 301 | ||
| 295 | ipl = interrupts_disable(); |
302 | ipl = interrupts_disable(); |
| 296 | spinlock_lock(&tidlock); |
303 | spinlock_lock(&tidlock); |
| 297 | t->tid = ++last_tid; |
304 | t->tid = ++last_tid; |
| 298 | spinlock_unlock(&tidlock); |
305 | spinlock_unlock(&tidlock); |
| Line 307... | Line 314... | ||
| 307 | ipl = interrupts_disable(); |
314 | ipl = interrupts_disable(); |
| 308 | t->saved_context.ipl = interrupts_read(); |
315 | t->saved_context.ipl = interrupts_read(); |
| 309 | interrupts_restore(ipl); |
316 | interrupts_restore(ipl); |
| 310 | 317 | ||
| 311 | memcpy(t->name, name, THREAD_NAME_BUFLEN); |
318 | memcpy(t->name, name, THREAD_NAME_BUFLEN); |
| - | 319 | t->name[THREAD_NAME_BUFLEN - 1] = '\0'; |
|
| 312 | 320 | ||
| 313 | t->thread_code = func; |
321 | t->thread_code = func; |
| 314 | t->thread_arg = arg; |
322 | t->thread_arg = arg; |
| 315 | t->ticks = -1; |
323 | t->ticks = -1; |
| 316 | t->cycles = 0; |
324 | t->cycles = 0; |
| Line 342... | Line 350... | ||
| 342 | t->fpu_context_engaged = 0; |
350 | t->fpu_context_engaged = 0; |
| 343 | 351 | ||
| 344 | avltree_node_initialize(&t->threads_tree_node); |
352 | avltree_node_initialize(&t->threads_tree_node); |
| 345 | t->threads_tree_node.key = (uintptr_t) t; |
353 | t->threads_tree_node.key = (uintptr_t) t; |
| 346 | 354 | ||
| - | 355 | #ifdef CONFIG_UDEBUG |
|
| - | 356 | /* Init debugging stuff */ |
|
| - | 357 | udebug_thread_initialize(&t->udebug); |
|
| - | 358 | #endif |
|
| - | 359 | ||
| 347 | /* might depend on previous initialization */ |
360 | /* might depend on previous initialization */ |
| 348 | thread_create_arch(t); |
361 | thread_create_arch(t); |
| 349 | 362 | ||
| 350 | if (!(flags & THREAD_FLAG_NOATTACH)) |
363 | if (!(flags & THREAD_FLAG_NOATTACH)) |
| 351 | thread_attach(t, task); |
364 | thread_attach(t, task); |
| Line 404... | Line 417... | ||
| 404 | void thread_attach(thread_t *t, task_t *task) |
417 | void thread_attach(thread_t *t, task_t *task) |
| 405 | { |
418 | { |
| 406 | ipl_t ipl; |
419 | ipl_t ipl; |
| 407 | 420 | ||
| 408 | /* |
421 | /* |
| 409 | * Attach to the current task. |
422 | * Attach to the specified task. |
| 410 | */ |
423 | */ |
| 411 | ipl = interrupts_disable(); |
424 | ipl = interrupts_disable(); |
| 412 | spinlock_lock(&task->lock); |
425 | spinlock_lock(&task->lock); |
| - | 426 | ||
| 413 | atomic_inc(&task->refcount); |
427 | atomic_inc(&task->refcount); |
| - | 428 | ||
| - | 429 | /* Must not count kbox thread into lifecount */ |
|
| - | 430 | if (t->flags & THREAD_FLAG_USPACE) |
|
| 414 | atomic_inc(&task->lifecount); |
431 | atomic_inc(&task->lifecount); |
| - | 432 | ||
| 415 | list_append(&t->th_link, &task->th_head); |
433 | list_append(&t->th_link, &task->th_head); |
| 416 | spinlock_unlock(&task->lock); |
434 | spinlock_unlock(&task->lock); |
| 417 | 435 | ||
| 418 | /* |
436 | /* |
| 419 | * Register this thread in the system-wide list. |
437 | * Register this thread in the system-wide list. |
| Line 432... | Line 450... | ||
| 432 | */ |
450 | */ |
| 433 | void thread_exit(void) |
451 | void thread_exit(void) |
| 434 | { |
452 | { |
| 435 | ipl_t ipl; |
453 | ipl_t ipl; |
| 436 | 454 | ||
| - | 455 | if (THREAD->flags & THREAD_FLAG_USPACE) { |
|
| - | 456 | #ifdef CONFIG_UDEBUG |
|
| - | 457 | /* Generate udebug THREAD_E event */ |
|
| - | 458 | udebug_thread_e_event(); |
|
| - | 459 | #endif |
|
| 437 | if (atomic_predec(&TASK->lifecount) == 0) { |
460 | if (atomic_predec(&TASK->lifecount) == 0) { |
| 438 | /* |
461 | /* |
| 439 | * We are the last thread in the task that still has not exited. |
462 | * We are the last userspace thread in the task that |
| - | 463 | * still has not exited. With the exception of the |
|
| 440 | * With the exception of the moment the task was created, new |
464 | * moment the task was created, new userspace threads |
| 441 | * threads can only be created by threads of the same task. |
465 | * can only be created by threads of the same task. |
| 442 | * We are safe to perform cleanup. |
466 | * We are safe to perform cleanup. |
| 443 | */ |
467 | */ |
| 444 | if (THREAD->flags & THREAD_FLAG_USPACE) { |
- | |
| 445 | ipc_cleanup(); |
468 | ipc_cleanup(); |
| 446 | futex_cleanup(); |
469 | futex_cleanup(); |
| 447 | klog_printf("Cleanup of task %llu completed.", |
470 | LOG("Cleanup of task %" PRIu64" completed.", TASK->taskid); |
| 448 | TASK->taskid); |
- | |
| 449 | } |
471 | } |
| 450 | } |
472 | } |
| 451 | 473 | ||
| 452 | restart: |
474 | restart: |
| 453 | ipl = interrupts_disable(); |
475 | ipl = interrupts_disable(); |
| Line 579... | Line 601... | ||
| 579 | interrupts_restore(ipl); |
601 | interrupts_restore(ipl); |
| 580 | } |
602 | } |
| 581 | 603 | ||
| 582 | static bool thread_walker(avltree_node_t *node, void *arg) |
604 | static bool thread_walker(avltree_node_t *node, void *arg) |
| 583 | { |
605 | { |
| 584 | thread_t *t; |
- | |
| 585 | - | ||
| 586 | t = avltree_get_instance(node, thread_t, threads_tree_node); |
606 | thread_t *t = avltree_get_instance(node, thread_t, threads_tree_node); |
| 587 | 607 | ||
| 588 | uint64_t cycles; |
608 | uint64_t cycles; |
| 589 | char suffix; |
609 | char suffix; |
| 590 | order(t->cycles, &cycles, &suffix); |
610 | order(t->cycles, &cycles, &suffix); |
| 591 | 611 | ||
| 592 | if (sizeof(void *) == 4) |
612 | #ifdef __32_BITS__ |
| 593 | printf("%-6llu %-10s %#10zx %-8s %#10zx %-3ld %#10zx %#10zx %9llu%c ", |
613 | printf("%-6" PRIu64" %-10s %10p %-8s %10p %-3" PRIu32 " %10p %10p %9" PRIu64 "%c ", |
| 594 | t->tid, t->name, t, thread_states[t->state], t->task, |
614 | t->tid, t->name, t, thread_states[t->state], t->task, |
| 595 | t->task->context, t->thread_code, t->kstack, cycles, suffix); |
615 | t->task->context, t->thread_code, t->kstack, cycles, suffix); |
| 596 | else |
616 | #endif |
| - | 617 | ||
| - | 618 | #ifdef __64_BITS__ |
|
| 597 | printf("%-6llu %-10s %#18zx %-8s %#18zx %-3ld %#18zx %#18zx %9llu%c ", |
619 | printf("%-6" PRIu64" %-10s %18p %-8s %18p %-3" PRIu32 " %18p %18p %9" PRIu64 "%c ", |
| 598 | t->tid, t->name, t, thread_states[t->state], t->task, |
620 | t->tid, t->name, t, thread_states[t->state], t->task, |
| 599 | t->task->context, t->thread_code, t->kstack, cycles, suffix); |
621 | t->task->context, t->thread_code, t->kstack, cycles, suffix); |
| - | 622 | #endif |
|
| 600 | 623 | ||
| 601 | if (t->cpu) |
624 | if (t->cpu) |
| 602 | printf("%-4zd", t->cpu->id); |
625 | printf("%-4u", t->cpu->id); |
| 603 | else |
626 | else |
| 604 | printf("none"); |
627 | printf("none"); |
| 605 | 628 | ||
| 606 | if (t->state == Sleeping) { |
629 | if (t->state == Sleeping) { |
| 607 | if (sizeof(uintptr_t) == 4) |
630 | #ifdef __32_BITS__ |
| 608 | printf(" %#10zx", t->sleep_queue); |
631 | printf(" %10p", t->sleep_queue); |
| 609 | else |
632 | #endif |
| - | 633 | ||
| - | 634 | #ifdef __64_BITS__ |
|
| 610 | printf(" %#18zx", t->sleep_queue); |
635 | printf(" %18p", t->sleep_queue); |
| - | 636 | #endif |
|
| 611 | } |
637 | } |
| 612 | 638 | ||
| 613 | printf("\n"); |
639 | printf("\n"); |
| 614 | 640 | ||
| 615 | return true; |
641 | return true; |
| Line 621... | Line 647... | ||
| 621 | ipl_t ipl; |
647 | ipl_t ipl; |
| 622 | 648 | ||
| 623 | /* Messing with thread structures, avoid deadlock */ |
649 | /* Messing with thread structures, avoid deadlock */ |
| 624 | ipl = interrupts_disable(); |
650 | ipl = interrupts_disable(); |
| 625 | spinlock_lock(&threads_lock); |
651 | spinlock_lock(&threads_lock); |
| 626 | 652 | ||
| 627 | if (sizeof(uintptr_t) == 4) { |
653 | #ifdef __32_BITS__ |
| 628 | printf("tid name address state task " |
654 | printf("tid name address state task " |
| 629 | "ctx code stack cycles cpu " |
655 | "ctx code stack cycles cpu " |
| 630 | "waitqueue\n"); |
656 | "waitqueue\n"); |
| 631 | printf("------ ---------- ---------- -------- ---------- " |
657 | printf("------ ---------- ---------- -------- ---------- " |
| 632 | "--- ---------- ---------- ---------- ---- " |
658 | "--- ---------- ---------- ---------- ---- " |
| 633 | "----------\n"); |
659 | "----------\n"); |
| 634 | } else { |
660 | #endif |
| - | 661 | ||
| - | 662 | #ifdef __64_BITS__ |
|
| 635 | printf("tid name address state task " |
663 | printf("tid name address state task " |
| 636 | "ctx code stack cycles cpu " |
664 | "ctx code stack cycles cpu " |
| 637 | "waitqueue\n"); |
665 | "waitqueue\n"); |
| 638 | printf("------ ---------- ------------------ -------- ------------------ " |
666 | printf("------ ---------- ------------------ -------- ------------------ " |
| 639 | "--- ------------------ ------------------ ---------- ---- " |
667 | "--- ------------------ ------------------ ---------- ---- " |
| 640 | "------------------\n"); |
668 | "------------------\n"); |
| 641 | } |
669 | #endif |
| 642 | 670 | ||
| 643 | avltree_walk(&threads_tree, thread_walker, NULL); |
671 | avltree_walk(&threads_tree, thread_walker, NULL); |
| 644 | 672 | ||
| 645 | spinlock_unlock(&threads_lock); |
673 | spinlock_unlock(&threads_lock); |
| 646 | interrupts_restore(ipl); |
674 | interrupts_restore(ipl); |
| Line 662... | Line 690... | ||
| 662 | node = avltree_search(&threads_tree, (avltree_key_t) ((uintptr_t) t)); |
690 | node = avltree_search(&threads_tree, (avltree_key_t) ((uintptr_t) t)); |
| 663 | 691 | ||
| 664 | return node != NULL; |
692 | return node != NULL; |
| 665 | } |
693 | } |
| 666 | 694 | ||
| 667 | - | ||
| 668 | /** Update accounting of current thread. |
695 | /** Update accounting of current thread. |
| 669 | * |
696 | * |
| 670 | * Note that thread_lock on THREAD must be already held and |
697 | * Note that thread_lock on THREAD must be already held and |
| 671 | * interrupts must be already disabled. |
698 | * interrupts must be already disabled. |
| 672 | * |
699 | * |
| Line 680... | Line 707... | ||
| 680 | 707 | ||
| 681 | /** Process syscall to create new thread. |
708 | /** Process syscall to create new thread. |
| 682 | * |
709 | * |
| 683 | */ |
710 | */ |
| 684 | unative_t sys_thread_create(uspace_arg_t *uspace_uarg, char *uspace_name, |
711 | unative_t sys_thread_create(uspace_arg_t *uspace_uarg, char *uspace_name, |
| 685 | thread_id_t *uspace_thread_id) |
712 | size_t name_len, thread_id_t *uspace_thread_id) |
| 686 | { |
713 | { |
| 687 | thread_t *t; |
714 | thread_t *t; |
| 688 | char namebuf[THREAD_NAME_BUFLEN]; |
715 | char namebuf[THREAD_NAME_BUFLEN]; |
| 689 | uspace_arg_t *kernel_uarg; |
716 | uspace_arg_t *kernel_uarg; |
| 690 | int rc; |
717 | int rc; |
| 691 | 718 | ||
| - | 719 | if (name_len > THREAD_NAME_BUFLEN - 1) |
|
| - | 720 | name_len = THREAD_NAME_BUFLEN - 1; |
|
| - | 721 | ||
| 692 | rc = copy_from_uspace(namebuf, uspace_name, THREAD_NAME_BUFLEN); |
722 | rc = copy_from_uspace(namebuf, uspace_name, name_len); |
| 693 | if (rc != 0) |
723 | if (rc != 0) |
| 694 | return (unative_t) rc; |
724 | return (unative_t) rc; |
| 695 | 725 | ||
| - | 726 | namebuf[name_len] = '\0'; |
|
| - | 727 | ||
| 696 | /* |
728 | /* |
| 697 | * In case of failure, kernel_uarg will be deallocated in this function. |
729 | * In case of failure, kernel_uarg will be deallocated in this function. |
| 698 | * In case of success, kernel_uarg will be freed in uinit(). |
730 | * In case of success, kernel_uarg will be freed in uinit(). |
| 699 | */ |
731 | */ |
| 700 | kernel_uarg = (uspace_arg_t *) malloc(sizeof(uspace_arg_t), 0); |
732 | kernel_uarg = (uspace_arg_t *) malloc(sizeof(uspace_arg_t), 0); |
| Line 729... | Line 761... | ||
| 729 | free(kernel_uarg); |
761 | free(kernel_uarg); |
| 730 | 762 | ||
| 731 | return (unative_t) rc; |
763 | return (unative_t) rc; |
| 732 | } |
764 | } |
| 733 | } |
765 | } |
| - | 766 | #ifdef CONFIG_UDEBUG |
|
| - | 767 | /* |
|
| - | 768 | * Generate udebug THREAD_B event and attach the thread. |
|
| - | 769 | * This must be done atomically (with the debug locks held), |
|
| - | 770 | * otherwise we would either miss some thread or receive |
|
| - | 771 | * THREAD_B events for threads that already existed |
|
| - | 772 | * and could be detected with THREAD_READ before. |
|
| - | 773 | */ |
|
| - | 774 | udebug_thread_b_event_attach(t, TASK); |
|
| - | 775 | #else |
|
| 734 | thread_attach(t, TASK); |
776 | thread_attach(t, TASK); |
| - | 777 | #endif |
|
| 735 | thread_ready(t); |
778 | thread_ready(t); |
| 736 | 779 | ||
| 737 | return 0; |
780 | return 0; |
| 738 | } else |
781 | } else |
| 739 | free(kernel_uarg); |
782 | free(kernel_uarg); |