Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 55896
b: refs/heads/master
c: c14b849
h: refs/heads/master
v: v3
  • Loading branch information
Jeff Dike authored and Linus Torvalds committed May 11, 2007
1 parent 9ee3c5a commit 37d6854
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 15 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 2ea5bc5e5bb51492f189bba44045e0de7decf4a0
refs/heads/master: c14b84949e127560084c7c56b365931c71c60768
3 changes: 3 additions & 0 deletions trunk/arch/um/include/kern_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,7 @@ extern void sigio_handler(int sig, union uml_pt_regs *regs);

extern void copy_sc(union uml_pt_regs *regs, void *from);

unsigned long to_irq_stack(int sig, unsigned long *mask_out);
unsigned long from_irq_stack(int nested);

#endif
2 changes: 2 additions & 0 deletions trunk/arch/um/kernel/dyn.lds.S
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ SECTIONS
.data : {
. = ALIGN(KERNEL_STACK_SIZE); /* init_task */
*(.data.init_task)
. = ALIGN(KERNEL_STACK_SIZE);
*(.data.init_irqstack)
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
Expand Down
16 changes: 10 additions & 6 deletions trunk/arch/um/kernel/init_task.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,intel.linux}.com)
* Licensed under the GPL
*/

Expand Down Expand Up @@ -33,14 +33,18 @@ EXPORT_SYMBOL(init_task);
/*
* Initial thread structure.
*
* We need to make sure that this is 16384-byte aligned due to the
* We need to make sure that this is aligned due to the
* way process stacks are handled. This is done by having a special
* "init_task" linker map entry..
*/

union thread_union init_thread_union
__attribute__((__section__(".data.init_task"))) =
{ INIT_THREAD_INFO(init_task) };
union thread_union init_thread_union
__attribute__((__section__(".data.init_task"))) =
{ INIT_THREAD_INFO(init_task) };

union thread_union cpu0_irqstack
__attribute__((__section__(".data.init_irqstack"))) =
{ INIT_THREAD_INFO(init_task) };

void unprotect_stack(unsigned long stack)
{
Expand Down
111 changes: 111 additions & 0 deletions trunk/arch/um/kernel/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "sigio.h"
#include "um_malloc.h"
#include "misc_constants.h"
#include "as-layout.h"

/*
* Generic, controller-independent functions:
Expand Down Expand Up @@ -468,3 +469,113 @@ int init_aio_irq(int irq, char *name, irq_handler_t handler)
out:
return err;
}

/*
* IRQ stack entry and exit:
*
* Unlike i386, UML doesn't receive IRQs on the normal kernel stack
* and switch over to the IRQ stack after some preparation. We use
* sigaltstack to receive signals on a separate stack from the start.
* These two functions make sure the rest of the kernel won't be too
* upset by being on a different stack. The IRQ stack has a
* thread_info structure at the bottom so that current et al continue
* to work.
*
* to_irq_stack copies the current task's thread_info to the IRQ stack
* thread_info and sets the tasks's stack to point to the IRQ stack.
*
* from_irq_stack copies the thread_info struct back (flags may have
* been modified) and resets the task's stack pointer.
*
* Tricky bits -
*
* What happens when two signals race each other? UML doesn't block
* signals with sigprocmask, SA_DEFER, or sa_mask, so a second signal
* could arrive while a previous one is still setting up the
* thread_info.
*
* There are three cases -
* The first interrupt on the stack - sets up the thread_info and
* handles the interrupt
* A nested interrupt interrupting the copying of the thread_info -
* can't handle the interrupt, as the stack is in an unknown state
* A nested interrupt not interrupting the copying of the
* thread_info - doesn't do any setup, just handles the interrupt
*
* The first job is to figure out whether we interrupted stack setup.
* This is done by xchging the signal mask with thread_info->pending.
* If the value that comes back is zero, then there is no setup in
* progress, and the interrupt can be handled. If the value is
* non-zero, then there is stack setup in progress. In order to have
* the interrupt handled, we leave our signal in the mask, and it will
* be handled by the upper handler after it has set up the stack.
*
* Next is to figure out whether we are the outer handler or a nested
* one. As part of setting up the stack, thread_info->real_thread is
* set to non-NULL (and is reset to NULL on exit). This is the
* nesting indicator. If it is non-NULL, then the stack is already
* set up and the handler can run.
*/

static unsigned long pending_mask;

unsigned long to_irq_stack(int sig, unsigned long *mask_out)
{
struct thread_info *ti;
unsigned long mask, old;
int nested;

mask = xchg(&pending_mask, 1 << sig);
if(mask != 0){
/* If any interrupts come in at this point, we want to
* make sure that their bits aren't lost by our
* putting our bit in. So, this loop accumulates bits
* until xchg returns the same value that we put in.
* When that happens, there were no new interrupts,
* and pending_mask contains a bit for each interrupt
* that came in.
*/
old = 1 << sig;
do {
old |= mask;
mask = xchg(&pending_mask, old);
} while(mask != old);
return 1;
}

ti = current_thread_info();
nested = (ti->real_thread != NULL);
if(!nested){
struct task_struct *task;
struct thread_info *tti;

task = cpu_tasks[ti->cpu].task;
tti = task_thread_info(task);
*ti = *tti;
ti->real_thread = tti;
task->stack = ti;
}

mask = xchg(&pending_mask, 0);
*mask_out |= mask | nested;
return 0;
}

unsigned long from_irq_stack(int nested)
{
struct thread_info *ti, *to;
unsigned long mask;

ti = current_thread_info();

pending_mask = 1;

to = ti->real_thread;
current->stack = to;
ti->real_thread = NULL;
*to = *ti;

mask = xchg(&pending_mask, 0);
return mask & ~1;
}

4 changes: 4 additions & 0 deletions trunk/arch/um/kernel/skas/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,12 @@ static int start_kernel_proc(void *unused)

extern int userspace_pid[];

extern char cpu0_irqstack[];

int start_uml_skas(void)
{
stack_protections((unsigned long) &cpu0_irqstack);
set_sigstack(cpu0_irqstack, THREAD_SIZE);
if(proc_mm)
userspace_pid[0] = start_userspace(0);

Expand Down
2 changes: 2 additions & 0 deletions trunk/arch/um/kernel/uml.lds.S
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ SECTIONS
{
. = ALIGN(KERNEL_STACK_SIZE); /* init_task */
*(.data.init_task)
. = ALIGN(KERNEL_STACK_SIZE);
*(.data.init_irqstack)
*(.data)
*(.gnu.linkonce.d*)
CONSTRUCTORS
Expand Down
40 changes: 40 additions & 0 deletions trunk/arch/um/os-Linux/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,46 @@ void remove_sigstack(void)

void (*handlers[_NSIG])(int sig, struct sigcontext *sc);

void handle_signal(int sig, struct sigcontext *sc)
{
unsigned long pending = 0;

do {
int nested, bail;

/*
* pending comes back with one bit set for each
* interrupt that arrived while setting up the stack,
* plus a bit for this interrupt, plus the zero bit is
* set if this is a nested interrupt.
* If bail is true, then we interrupted another
* handler setting up the stack. In this case, we
* have to return, and the upper handler will deal
* with this interrupt.
*/
bail = to_irq_stack(sig, &pending);
if(bail)
return;

nested = pending & 1;
pending &= ~1;

while((sig = ffs(pending)) != 0){
sig--;
pending &= ~(1 << sig);
(*handlers[sig])(sig, sc);
}

/* Again, pending comes back with a mask of signals
* that arrived while tearing down the stack. If this
* is non-zero, we just go back, set up the stack
* again, and handle the new interrupts.
*/
if(!nested)
pending = from_irq_stack(nested);
} while(pending);
}

extern void hard_handler(int sig);

void set_handler(int sig, void (*handler)(int), int flags, ...)
Expand Down
8 changes: 3 additions & 5 deletions trunk/arch/um/os-Linux/sys-i386/signal.c
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
/*
* Copyright (C) 2006 Jeff Dike (jdike@addtoit.com)
* Copyright (C) 2006 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/

#include <signal.h>

extern void (*handlers[])(int sig, struct sigcontext *sc);
extern void handle_signal(int sig, struct sigcontext *sc);

void hard_handler(int sig)
{
struct sigcontext *sc = (struct sigcontext *) (&sig + 1);

(*handlers[sig])(sig, sc);
handle_signal(sig, (struct sigcontext *) (&sig + 1));
}
6 changes: 3 additions & 3 deletions trunk/arch/um/os-Linux/sys-x86_64/signal.c
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/*
* Copyright (C) 2006 Jeff Dike (jdike@addtoit.com)
* Copyright (C) 2006 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Licensed under the GPL
*/

#include <signal.h>

extern void (*handlers[])(int sig, struct sigcontext *sc);
extern void handle_signal(int sig, struct sigcontext *sc);

void hard_handler(int sig)
{
struct ucontext *uc;
asm("movq %%rdx, %0" : "=r" (uc));

(*handlers[sig])(sig, (struct sigcontext *) &uc->uc_mcontext);
handle_signal(sig, (struct sigcontext *) &uc->uc_mcontext);
}
2 changes: 2 additions & 0 deletions trunk/include/asm-um/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct thread_info {
0-0xBFFFFFFF for user
0-0xFFFFFFFF for kernel */
struct restart_block restart_block;
struct thread_info *real_thread; /* Points to non-IRQ stack */
};

#define INIT_THREAD_INFO(tsk) \
Expand All @@ -35,6 +36,7 @@ struct thread_info {
.restart_block = { \
.fn = do_no_restart_syscall, \
}, \
.real_thread = NULL, \
}

#define init_thread_info (init_thread_union.thread_info)
Expand Down

0 comments on commit 37d6854

Please sign in to comment.