Commit e58d8e30 authored by Linus Torvalds's avatar Linus Torvalds

Fix x86 "switch_to()" to properly set the previous task information,

which is needed to keep track of process usage counts correctly and
efficiently.
parent 91e2be46
......@@ -173,9 +173,10 @@ ENTRY(lcall27)
ENTRY(ret_from_fork)
# NOTE: this function takes a parameter but it's unused on x86.
pushl %eax
call schedule_tail
GET_THREAD_INFO(%ebp)
popl %eax
jmp syscall_exit
/*
......
......@@ -423,8 +423,12 @@ int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs)
* so the performance issues may eventually be a valid point.
* More important, however, is the fact that this allows us much
* more flexibility.
*
* The return value (in %eax) will be the "prev" task after
* the task-switch, and shows up in ret_from_fork in entry.S,
* for example.
*/
void __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
struct task_struct * __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
{
struct thread_struct *prev = &prev_p->thread,
*next = &next_p->thread;
......@@ -495,6 +499,7 @@ void __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
*/
tss->bitmap = INVALID_IO_BITMAP_OFFSET;
}
return prev_p;
}
asmlinkage int sys_fork(struct pt_regs regs)
......
......@@ -9,26 +9,24 @@
#ifdef __KERNEL__
struct task_struct; /* one of the stranger aspects of C forward declarations.. */
extern void FASTCALL(__switch_to(struct task_struct *prev, struct task_struct *next));
extern struct task_struct * FASTCALL(__switch_to(struct task_struct *prev, struct task_struct *next));
#define switch_to(prev,next,last) do { \
unsigned long esi,edi; \
asm volatile("pushfl\n\t" \
"pushl %%esi\n\t" \
"pushl %%edi\n\t" \
"pushl %%ebp\n\t" \
"movl %%esp,%0\n\t" /* save ESP */ \
"movl %2,%%esp\n\t" /* restore ESP */ \
"movl %5,%%esp\n\t" /* restore ESP */ \
"movl $1f,%1\n\t" /* save EIP */ \
"pushl %3\n\t" /* restore EIP */ \
"pushl %6\n\t" /* restore EIP */ \
"jmp __switch_to\n" \
"1:\t" \
"popl %%ebp\n\t" \
"popl %%edi\n\t" \
"popl %%esi\n\t" \
"popfl\n\t" \
:"=m" (prev->thread.esp),"=m" (prev->thread.eip) \
"popfl" \
:"=m" (prev->thread.esp),"=m" (prev->thread.eip), \
"=a" (last),"=S" (esi),"=D" (edi) \
:"m" (next->thread.esp),"m" (next->thread.eip), \
"a" (prev), "d" (next)); \
"2" (prev), "d" (next)); \
} while (0)
#define _set_base(addr,base) do { unsigned long __pr; \
......
......@@ -62,6 +62,7 @@
{ \
.state = 0, \
.thread_info = &init_thread_info, \
.usage = ATOMIC_INIT(2), \
.flags = 0, \
.lock_depth = -1, \
.prio = MAX_PRIO-20, \
......
......@@ -74,6 +74,9 @@ int nr_processes(void)
void __put_task_struct(struct task_struct *tsk)
{
WARN_ON(!(tsk->state & (TASK_DEAD | TASK_ZOMBIE)));
WARN_ON(atomic_read(&tsk->usage));
if (tsk != current) {
free_thread_info(tsk->thread_info);
kmem_cache_free(task_struct_cachep,tsk);
......@@ -217,7 +220,9 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
*tsk = *orig;
tsk->thread_info = ti;
ti->task = tsk;
atomic_set(&tsk->usage,1);
/* One for us, one for whoever does the "release_task()" (usually parent) */
atomic_set(&tsk->usage,2);
return tsk;
}
......
......@@ -581,6 +581,8 @@ static inline void finish_task_switch(task_t *prev)
finish_arch_switch(rq, prev);
if (mm)
mmdrop(mm);
if (prev->state & (TASK_DEAD | TASK_ZOMBIE))
put_task_struct(prev);
}
/**
......@@ -1185,7 +1187,7 @@ asmlinkage void schedule(void)
* schedule() atomically, we ignore that path for now.
* Otherwise, whine if we are scheduling when we should not be.
*/
if (likely(current->state != TASK_ZOMBIE)) {
if (likely(!(current->state & (TASK_DEAD | TASK_ZOMBIE)))) {
if (unlikely(in_atomic())) {
printk(KERN_ERR "bad: scheduling while atomic!\n");
dump_stack();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment