aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/os-Linux/skas/process.c
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/um/os-Linux/skas/process.c
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/um/os-Linux/skas/process.c')
-rw-r--r--arch/um/os-Linux/skas/process.c68
1 files changed, 16 insertions, 52 deletions
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)