aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorJeff Dike <jdike@addtoit.com>2006-09-27 04:50:40 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-27 11:26:16 -0400
commit3c9173509985b957bea692ea887a8a0e5055cfe8 (patch)
tree9406fb650d6085f992b6d2a0a3fd8763b045cc35 /arch
parent0915ee38c7db57ff41a7a68bcc02d0dd3b7b849b (diff)
[PATCH] uml: thread creation tidying
fork on UML has always somewhat subtle. The underlying cause has been the need to initialize a stack for the new process. The only portable way to initialize a new stack is to set it as the alternate signal stack and take a signal. The signal handler does whatever initialization is needed and jumps back to the original stack, where the fork processing is finished. The basic context switching mechanism is a jmp_buf for each process. You switch to a new process by longjmping to its jmp_buf. Now that UML has its own implementation of setjmp and longjmp, and I can poke around inside a jmp_buf without fear that libc will change the structure, a much simpler mechanism is possible. The jmpbuf can simply be initialized by hand. This eliminates - the need to set up and remove the alternate signal stack sending and handling a signal the signal blocking needed around the stack switching, since there is no stack switching setting up the jmp_buf needed to jump back to the original stack after the new one is set up In addition, since jmp_buf is now defined by UML, and not by libc, it can be embedded in the thread struct. This makes it unnecessary to have it exist on the stack, where it used to be. It also simplifies interfaces, since the switch jmp_buf used to be a void * inside the thread struct, and functions which took it as an argument needed to define a jmp_buf variable and assign it from the void *. Signed-off-by: Jeff Dike <jdike@addtoit.com> Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/um/include/os.h10
-rw-r--r--arch/um/include/skas/skas.h3
-rw-r--r--arch/um/include/sysdep-i386/archsetjmp.h3
-rw-r--r--arch/um/include/sysdep-x86_64/archsetjmp.h3
-rw-r--r--arch/um/kernel/skas/process_kern.c36
-rw-r--r--arch/um/os-Linux/skas/process.c68
6 files changed, 40 insertions, 83 deletions
diff --git a/arch/um/include/os.h b/arch/um/include/os.h
index be71b3d4c961..120ca21a513a 100644
--- a/arch/um/include/os.h
+++ b/arch/um/include/os.h
@@ -14,6 +14,7 @@
14#include "skas/mm_id.h" 14#include "skas/mm_id.h"
15#include "irq_user.h" 15#include "irq_user.h"
16#include "sysdep/tls.h" 16#include "sysdep/tls.h"
17#include "sysdep/archsetjmp.h"
17 18
18#define OS_TYPE_FILE 1 19#define OS_TYPE_FILE 1
19#define OS_TYPE_DIR 2 20#define OS_TYPE_DIR 2
@@ -308,12 +309,9 @@ extern int copy_context_skas0(unsigned long stack, int pid);
308extern void userspace(union uml_pt_regs *regs); 309extern void userspace(union uml_pt_regs *regs);
309extern void map_stub_pages(int fd, unsigned long code, 310extern void map_stub_pages(int fd, unsigned long code,
310 unsigned long data, unsigned long stack); 311 unsigned long data, unsigned long stack);
311extern void new_thread(void *stack, void **switch_buf_ptr, 312extern void new_thread(void *stack, jmp_buf *buf, void (*handler)(void));
312 void **fork_buf_ptr, void (*handler)(int)); 313extern void switch_threads(jmp_buf *me, jmp_buf *you);
313extern void thread_wait(void *sw, void *fb); 314extern int start_idle_thread(void *stack, jmp_buf *switch_buf);
314extern void switch_threads(void *me, void *next);
315extern int start_idle_thread(void *stack, void *switch_buf_ptr,
316 void **fork_buf_ptr);
317extern void initial_thread_cb_skas(void (*proc)(void *), 315extern void initial_thread_cb_skas(void (*proc)(void *),
318 void *arg); 316 void *arg);
319extern void halt_skas(void); 317extern void halt_skas(void);
diff --git a/arch/um/include/skas/skas.h b/arch/um/include/skas/skas.h
index 853b26f148c5..e88926b16072 100644
--- a/arch/um/include/skas/skas.h
+++ b/arch/um/include/skas/skas.h
@@ -14,8 +14,7 @@ extern int proc_mm, ptrace_faultinfo, ptrace_ldt;
14extern int skas_needs_stub; 14extern int skas_needs_stub;
15 15
16extern int user_thread(unsigned long stack, int flags); 16extern int user_thread(unsigned long stack, int flags);
17extern void new_thread_proc(void *stack, void (*handler)(int sig)); 17extern void new_thread_handler(void);
18extern void new_thread_handler(int sig);
19extern void handle_syscall(union uml_pt_regs *regs); 18extern void handle_syscall(union uml_pt_regs *regs);
20extern int new_mm(unsigned long stack); 19extern int new_mm(unsigned long stack);
21extern void get_skas_faultinfo(int pid, struct faultinfo * fi); 20extern void get_skas_faultinfo(int pid, struct faultinfo * fi);
diff --git a/arch/um/include/sysdep-i386/archsetjmp.h b/arch/um/include/sysdep-i386/archsetjmp.h
index ea1ba3d42aee..11bafab669e9 100644
--- a/arch/um/include/sysdep-i386/archsetjmp.h
+++ b/arch/um/include/sysdep-i386/archsetjmp.h
@@ -16,4 +16,7 @@ struct __jmp_buf {
16 16
17typedef struct __jmp_buf jmp_buf[1]; 17typedef struct __jmp_buf jmp_buf[1];
18 18
19#define JB_IP __eip
20#define JB_SP __esp
21
19#endif /* _SETJMP_H */ 22#endif /* _SETJMP_H */
diff --git a/arch/um/include/sysdep-x86_64/archsetjmp.h b/arch/um/include/sysdep-x86_64/archsetjmp.h
index 454fc60aff6d..9a5e1a6ec800 100644
--- a/arch/um/include/sysdep-x86_64/archsetjmp.h
+++ b/arch/um/include/sysdep-x86_64/archsetjmp.h
@@ -18,4 +18,7 @@ struct __jmp_buf {
18 18
19typedef struct __jmp_buf jmp_buf[1]; 19typedef struct __jmp_buf jmp_buf[1];
20 20
21#define JB_IP __rip
22#define JB_SP __rsp
23
21#endif /* _SETJMP_H */ 24#endif /* _SETJMP_H */
diff --git a/arch/um/kernel/skas/process_kern.c b/arch/um/kernel/skas/process_kern.c
index 55caeec8b257..c73d47c57abe 100644
--- a/arch/um/kernel/skas/process_kern.c
+++ b/arch/um/kernel/skas/process_kern.c
@@ -33,7 +33,7 @@ void switch_to_skas(void *prev, void *next)
33 switch_timers(0); 33 switch_timers(0);
34 34
35 switch_threads(&from->thread.mode.skas.switch_buf, 35 switch_threads(&from->thread.mode.skas.switch_buf,
36 to->thread.mode.skas.switch_buf); 36 &to->thread.mode.skas.switch_buf);
37 37
38 arch_switch_to_skas(current->thread.prev_sched, current); 38 arch_switch_to_skas(current->thread.prev_sched, current);
39 39
@@ -43,21 +43,21 @@ void switch_to_skas(void *prev, void *next)
43 43
44extern void schedule_tail(struct task_struct *prev); 44extern void schedule_tail(struct task_struct *prev);
45 45
46void new_thread_handler(int sig) 46/* This is called magically, by its address being stuffed in a jmp_buf
47 * and being longjmp-d to.
48 */
49void new_thread_handler(void)
47{ 50{
48 int (*fn)(void *), n; 51 int (*fn)(void *), n;
49 void *arg; 52 void *arg;
50 53
51 fn = current->thread.request.u.thread.proc;
52 arg = current->thread.request.u.thread.arg;
53 os_usr1_signal(1);
54 thread_wait(&current->thread.mode.skas.switch_buf,
55 current->thread.mode.skas.fork_buf);
56
57 if(current->thread.prev_sched != NULL) 54 if(current->thread.prev_sched != NULL)
58 schedule_tail(current->thread.prev_sched); 55 schedule_tail(current->thread.prev_sched);
59 current->thread.prev_sched = NULL; 56 current->thread.prev_sched = NULL;
60 57
58 fn = current->thread.request.u.thread.proc;
59 arg = current->thread.request.u.thread.arg;
60
61 /* The return value is 1 if the kernel thread execs a process, 61 /* The return value is 1 if the kernel thread execs a process,
62 * 0 if it just exits 62 * 0 if it just exits
63 */ 63 */
@@ -70,22 +70,13 @@ void new_thread_handler(int sig)
70 else do_exit(0); 70 else do_exit(0);
71} 71}
72 72
73void new_thread_proc(void *stack, void (*handler)(int sig))
74{
75 init_new_thread_stack(stack, handler);
76 os_usr1_process(os_getpid());
77}
78
79void release_thread_skas(struct task_struct *task) 73void release_thread_skas(struct task_struct *task)
80{ 74{
81} 75}
82 76
83void fork_handler(int sig) 77/* Called magically, see new_thread_handler above */
78void fork_handler(void)
84{ 79{
85 os_usr1_signal(1);
86 thread_wait(&current->thread.mode.skas.switch_buf,
87 current->thread.mode.skas.fork_buf);
88
89 force_flush_all(); 80 force_flush_all();
90 if(current->thread.prev_sched == NULL) 81 if(current->thread.prev_sched == NULL)
91 panic("blech"); 82 panic("blech");
@@ -109,7 +100,7 @@ int copy_thread_skas(int nr, unsigned long clone_flags, unsigned long sp,
109 unsigned long stack_top, struct task_struct * p, 100 unsigned long stack_top, struct task_struct * p,
110 struct pt_regs *regs) 101 struct pt_regs *regs)
111{ 102{
112 void (*handler)(int); 103 void (*handler)(void);
113 104
114 if(current->thread.forking){ 105 if(current->thread.forking){
115 memcpy(&p->thread.regs.regs.skas, &regs->regs.skas, 106 memcpy(&p->thread.regs.regs.skas, &regs->regs.skas,
@@ -128,7 +119,7 @@ int copy_thread_skas(int nr, unsigned long clone_flags, unsigned long sp,
128 } 119 }
129 120
130 new_thread(task_stack_page(p), &p->thread.mode.skas.switch_buf, 121 new_thread(task_stack_page(p), &p->thread.mode.skas.switch_buf,
131 &p->thread.mode.skas.fork_buf, handler); 122 handler);
132 return(0); 123 return(0);
133} 124}
134 125
@@ -182,8 +173,7 @@ int start_uml_skas(void)
182 init_task.thread.request.u.thread.proc = start_kernel_proc; 173 init_task.thread.request.u.thread.proc = start_kernel_proc;
183 init_task.thread.request.u.thread.arg = NULL; 174 init_task.thread.request.u.thread.arg = NULL;
184 return(start_idle_thread(task_stack_page(&init_task), 175 return(start_idle_thread(task_stack_page(&init_task),
185 &init_task.thread.mode.skas.switch_buf, 176 &init_task.thread.mode.skas.switch_buf));
186 &init_task.thread.mode.skas.fork_buf));
187} 177}
188 178
189int external_pid_skas(struct task_struct *task) 179int external_pid_skas(struct task_struct *task)
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index 42e3d1ed802c..cb9ab54146cc 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -444,56 +444,22 @@ void map_stub_pages(int fd, unsigned long code,
444 } 444 }
445} 445}
446 446
447void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr, 447void new_thread(void *stack, jmp_buf *buf, void (*handler)(void))
448 void (*handler)(int))
449{ 448{
450 unsigned long flags; 449 (*buf)[0].JB_IP = (unsigned long) handler;
451 jmp_buf switch_buf, fork_buf; 450 (*buf)[0].JB_SP = (unsigned long) stack +
452 451 (PAGE_SIZE << UML_CONFIG_KERNEL_STACK_ORDER) - sizeof(void *);
453 *switch_buf_ptr = &switch_buf;
454 *fork_buf_ptr = &fork_buf;
455
456 /* Somewhat subtle - siglongjmp restores the signal mask before doing
457 * the longjmp. This means that when jumping from one stack to another
458 * when the target stack has interrupts enabled, an interrupt may occur
459 * on the source stack. This is bad when starting up a process because
460 * it's not supposed to get timer ticks until it has been scheduled.
461 * So, we disable interrupts around the sigsetjmp to ensure that
462 * they can't happen until we get back here where they are safe.
463 */
464 flags = get_signals();
465 block_signals();
466 if(UML_SETJMP(&fork_buf) == 0)
467 new_thread_proc(stack, handler);
468
469 remove_sigstack();
470
471 set_signals(flags);
472} 452}
473 453
474#define INIT_JMP_NEW_THREAD 0 454#define INIT_JMP_NEW_THREAD 0
475#define INIT_JMP_REMOVE_SIGSTACK 1 455#define INIT_JMP_CALLBACK 1
476#define INIT_JMP_CALLBACK 2 456#define INIT_JMP_HALT 2
477#define INIT_JMP_HALT 3 457#define INIT_JMP_REBOOT 3
478#define INIT_JMP_REBOOT 4
479
480void thread_wait(void *sw, void *fb)
481{
482 jmp_buf buf, **switch_buf = sw, *fork_buf;
483
484 *switch_buf = &buf;
485 fork_buf = fb;
486 if(UML_SETJMP(&buf) == 0)
487 UML_LONGJMP(fork_buf, INIT_JMP_REMOVE_SIGSTACK);
488}
489 458
490void switch_threads(void *me, void *next) 459void switch_threads(jmp_buf *me, jmp_buf *you)
491{ 460{
492 jmp_buf my_buf, **me_ptr = me, *next_buf = next; 461 if(UML_SETJMP(me) == 0)
493 462 UML_LONGJMP(you, 1);
494 *me_ptr = &my_buf;
495 if(UML_SETJMP(&my_buf) == 0)
496 UML_LONGJMP(next_buf, 1);
497} 463}
498 464
499static jmp_buf initial_jmpbuf; 465static jmp_buf initial_jmpbuf;
@@ -503,23 +469,21 @@ static void (*cb_proc)(void *arg);
503static void *cb_arg; 469static void *cb_arg;
504static jmp_buf *cb_back; 470static jmp_buf *cb_back;
505 471
506int start_idle_thread(void *stack, void *switch_buf_ptr, void **fork_buf_ptr) 472int start_idle_thread(void *stack, jmp_buf *switch_buf)
507{ 473{
508 jmp_buf **switch_buf = switch_buf_ptr;
509 int n; 474 int n;
510 475
511 set_handler(SIGWINCH, (__sighandler_t) sig_handler, 476 set_handler(SIGWINCH, (__sighandler_t) sig_handler,
512 SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGALRM, 477 SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGALRM,
513 SIGVTALRM, -1); 478 SIGVTALRM, -1);
514 479
515 *fork_buf_ptr = &initial_jmpbuf;
516 n = UML_SETJMP(&initial_jmpbuf); 480 n = UML_SETJMP(&initial_jmpbuf);
517 switch(n){ 481 switch(n){
518 case INIT_JMP_NEW_THREAD: 482 case INIT_JMP_NEW_THREAD:
519 new_thread_proc((void *) stack, new_thread_handler); 483 (*switch_buf)[0].JB_IP = (unsigned long) new_thread_handler;
520 break; 484 (*switch_buf)[0].JB_SP = (unsigned long) stack +
521 case INIT_JMP_REMOVE_SIGSTACK: 485 (PAGE_SIZE << UML_CONFIG_KERNEL_STACK_ORDER) -
522 remove_sigstack(); 486 sizeof(void *);
523 break; 487 break;
524 case INIT_JMP_CALLBACK: 488 case INIT_JMP_CALLBACK:
525 (*cb_proc)(cb_arg); 489 (*cb_proc)(cb_arg);
@@ -534,7 +498,7 @@ int start_idle_thread(void *stack, void *switch_buf_ptr, void **fork_buf_ptr)
534 default: 498 default:
535 panic("Bad sigsetjmp return in start_idle_thread - %d\n", n); 499 panic("Bad sigsetjmp return in start_idle_thread - %d\n", n);
536 } 500 }
537 UML_LONGJMP(*switch_buf, 1); 501 UML_LONGJMP(switch_buf, 1);
538} 502}
539 503
540void initial_thread_cb_skas(void (*proc)(void *), void *arg) 504void initial_thread_cb_skas(void (*proc)(void *), void *arg)