diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/exit.c | 46 | ||||
| -rw-r--r-- | kernel/fork.c | 46 | ||||
| -rw-r--r-- | kernel/ksyms.c | 8 | ||||
| -rw-r--r-- | kernel/pid.c | 9 | ||||
| -rw-r--r-- | kernel/sched.c | 17 | ||||
| -rw-r--r-- | kernel/timer.c | 14 |
6 files changed, 109 insertions, 31 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index 7189e9bce6d4..6ed07def4c62 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -32,6 +32,7 @@ int getrusage(struct task_struct *, int, struct rusage *); static struct dentry * __unhash_process(struct task_struct *p) { struct dentry *proc_dentry; + nr_threads--; detach_pid(p, PIDTYPE_PID); detach_pid(p, PIDTYPE_TGID); @@ -57,31 +58,31 @@ static struct dentry * __unhash_process(struct task_struct *p) void release_task(struct task_struct * p) { struct dentry *proc_dentry; + task_t *leader; - if (p->state != TASK_ZOMBIE) + if (p->state < TASK_ZOMBIE) BUG(); if (p != current) wait_task_inactive(p); atomic_dec(&p->user->processes); security_ops->task_free_security(p); free_uid(p->user); - if (unlikely(p->ptrace)) { - write_lock_irq(&tasklist_lock); + write_lock_irq(&tasklist_lock); + if (unlikely(p->ptrace)) __ptrace_unlink(p); - write_unlock_irq(&tasklist_lock); - } BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children)); - write_lock_irq(&tasklist_lock); __exit_sighand(p); proc_dentry = __unhash_process(p); /* * If we are the last non-leader member of the thread * group, and the leader is zombie, then notify the - * group leader's parent process. + * group leader's parent process. (if it wants notification.) */ - if (p->group_leader != p && thread_group_empty(p)) - do_notify_parent(p->group_leader, p->group_leader->exit_signal); + leader = p->group_leader; + if (leader != p && thread_group_empty(leader) && + leader->state == TASK_ZOMBIE && leader->exit_signal != -1) + do_notify_parent(leader, leader->exit_signal); p->parent->cutime += p->utime + p->cutime; p->parent->cstime += p->stime + p->cstime; @@ -159,7 +160,7 @@ static int __will_become_orphaned_pgrp(int pgrp, task_t *ignored_task) for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) { if (p == ignored_task - || p->state == TASK_ZOMBIE + || p->state >= TASK_ZOMBIE || p->real_parent->pid == 1) continue; if (p->real_parent->pgrp != pgrp @@ -435,8 +436,11 @@ void exit_mm(struct task_struct *tsk) static inline void choose_new_parent(task_t *p, task_t *reaper, task_t *child_reaper) { - /* Make sure we're not reparenting to ourselves. */ - if (p == reaper) + /* + * Make sure we're not reparenting to ourselves and that + * the parent is not a zombie. + */ + if (p == reaper || reaper->state >= TASK_ZOMBIE) p->real_parent = child_reaper; else p->real_parent = reaper; @@ -774,9 +778,10 @@ static int eligible_child(pid_t pid, int options, task_t *p) asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru) { - int flag, retval; DECLARE_WAITQUEUE(wait, current); struct task_struct *tsk; + unsigned long state; + int flag, retval; if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL)) return -EINVAL; @@ -827,7 +832,15 @@ repeat: */ if (ret == 2) continue; + /* + * Try to move the task's state to DEAD + * only one thread is allowed to do this: + */ + state = xchg(&p->state, TASK_DEAD); + if (state != TASK_ZOMBIE) + continue; read_unlock(&tasklist_lock); + retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; if (!retval && stat_addr) { if (p->sig->group_exit) @@ -835,13 +848,16 @@ repeat: else retval = put_user(p->exit_code, stat_addr); } - if (retval) - goto end_wait4; + if (retval) { + p->state = TASK_ZOMBIE; + goto end_wait4; + } retval = p->pid; if (p->real_parent != p->parent) { write_lock_irq(&tasklist_lock); __ptrace_unlink(p); do_notify_parent(p, SIGCHLD); + p->state = TASK_ZOMBIE; write_unlock_irq(&tasklist_lock); } else release_task(p); diff --git a/kernel/fork.c b/kernel/fork.c index 062a4d1f9c3e..5880309f3fee 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -103,6 +103,52 @@ void remove_wait_queue(wait_queue_head_t *q, wait_queue_t * wait) spin_unlock_irqrestore(&q->lock, flags); } +void prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state) +{ + unsigned long flags; + + __set_current_state(state); + wait->flags &= ~WQ_FLAG_EXCLUSIVE; + spin_lock_irqsave(&q->lock, flags); + if (list_empty(&wait->task_list)) + __add_wait_queue(q, wait); + spin_unlock_irqrestore(&q->lock, flags); +} + +void +prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state) +{ + unsigned long flags; + + __set_current_state(state); + wait->flags |= WQ_FLAG_EXCLUSIVE; + spin_lock_irqsave(&q->lock, flags); + if (list_empty(&wait->task_list)) + __add_wait_queue_tail(q, wait); + spin_unlock_irqrestore(&q->lock, flags); +} + +void finish_wait(wait_queue_head_t *q, wait_queue_t *wait) +{ + unsigned long flags; + + __set_current_state(TASK_RUNNING); + if (!list_empty(&wait->task_list)) { + spin_lock_irqsave(&q->lock, flags); + list_del_init(&wait->task_list); + spin_unlock_irqrestore(&q->lock, flags); + } +} + +int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync) +{ + int ret = default_wake_function(wait, mode, sync); + + if (ret) + list_del_init(&wait->task_list); + return ret; +} + void __init fork_init(unsigned long mempages) { /* create a slab on which task_structs can be allocated */ diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 557ae8f7ded2..0409fc676f29 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -400,6 +400,10 @@ EXPORT_SYMBOL(irq_stat); EXPORT_SYMBOL(add_wait_queue); EXPORT_SYMBOL(add_wait_queue_exclusive); EXPORT_SYMBOL(remove_wait_queue); +EXPORT_SYMBOL(prepare_to_wait); +EXPORT_SYMBOL(prepare_to_wait_exclusive); +EXPORT_SYMBOL(finish_wait); +EXPORT_SYMBOL(autoremove_wake_function); /* completion handling */ EXPORT_SYMBOL(wait_for_completion); @@ -493,7 +497,9 @@ EXPORT_SYMBOL(jiffies_64); EXPORT_SYMBOL(xtime); EXPORT_SYMBOL(do_gettimeofday); EXPORT_SYMBOL(do_settimeofday); - +#ifdef CONFIG_DEBUG_KERNEL +EXPORT_SYMBOL(__might_sleep); +#endif #if !defined(__ia64__) EXPORT_SYMBOL(loops_per_jiffy); #endif diff --git a/kernel/pid.c b/kernel/pid.c index b4da62f0aef2..0005a8cc36cb 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -53,6 +53,8 @@ static pidmap_t pidmap_array[PIDMAP_ENTRIES] = static pidmap_t *map_limit = pidmap_array + PIDMAP_ENTRIES; +static spinlock_t pidmap_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; + inline void free_pidmap(int pid) { pidmap_t *map = pidmap_array + pid / BITS_PER_PAGE; @@ -77,8 +79,13 @@ static inline pidmap_t *next_free_map(pidmap_t *map, int *max_steps) * Free the page if someone raced with us * installing it: */ - if (cmpxchg(&map->page, NULL, (void *) page)) + spin_lock(&pidmap_lock); + if (map->page) free_page(page); + else + map->page = (void *)page; + spin_unlock(&pidmap_lock); + if (!map->page) break; } diff --git a/kernel/sched.c b/kernel/sched.c index 304f90fd4bdf..9965e5f7549e 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2150,3 +2150,20 @@ void __init sched_init(void) enter_lazy_tlb(&init_mm, current, smp_processor_id()); } +#ifdef CONFIG_DEBUG_KERNEL +void __might_sleep(char *file, int line) +{ +#if defined(in_atomic) + static unsigned long prev_jiffy; /* ratelimiting */ + + if (in_atomic()) { + if (time_before(jiffies, prev_jiffy + HZ)) + return; + prev_jiffy = jiffies; + printk("Sleeping function called from illegal" + " context at %s:%d\n", file, line); + dump_stack(); + } +#endif +} +#endif diff --git a/kernel/timer.c b/kernel/timer.c index 3b4be840f931..55c14c11c901 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -888,20 +888,6 @@ asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp) if (t.tv_nsec >= 1000000000L || t.tv_nsec < 0 || t.tv_sec < 0) return -EINVAL; - - if (t.tv_sec == 0 && t.tv_nsec <= 2000000L && - current->policy != SCHED_NORMAL) - { - /* - * Short delay requests up to 2 ms will be handled with - * high precision by a busy wait for all real-time processes. - * - * Its important on SMP not to do this holding locks. - */ - udelay((t.tv_nsec + 999) / 1000); - return 0; - } - expire = timespec_to_jiffies(&t) + (t.tv_sec || t.tv_nsec); current->state = TASK_INTERRUPTIBLE; |
