summaryrefslogtreecommitdiffstats
path: root/kernel/exit.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
committer <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
commitbeb116954b9b7f3bb56412b2494b562f02b864b1 (patch)
tree120e997879884e1b9d93b265221b939d2ef1ade1 /kernel/exit.c
parent908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff)
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'kernel/exit.c')
-rw-r--r--kernel/exit.c293
1 files changed, 220 insertions, 73 deletions
diff --git a/kernel/exit.c b/kernel/exit.c
index 59c0b075b..d2fdbdc4a 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -4,7 +4,7 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#define DEBUG_PROC_TREE
+#undef DEBUG_PROC_TREE
#include <linux/wait.h>
#include <linux/errno.h>
@@ -15,56 +15,85 @@
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/malloc.h>
+#include <linux/interrupt.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
-#include <asm/segment.h>
extern void sem_exit (void);
+extern void acct_process (long exitcode);
+extern void kerneld_exit(void);
int getrusage(struct task_struct *, int, struct rusage *);
-static int generate(unsigned long sig, struct task_struct * p)
+static inline void generate(unsigned long sig, struct task_struct * p)
{
unsigned long mask = 1 << (sig-1);
- struct sigaction * sa = sig + p->sigaction - 1;
+ struct sigaction * sa = sig + p->sig->action - 1;
- /* always generate signals for traced processes ??? */
- if (p->flags & PF_PTRACED) {
- p->signal |= mask;
- return 1;
+ /*
+ * Optimize away the signal, if it's a signal that can
+ * be handled immediately (ie non-blocked and untraced)
+ * and that is ignored (either explicitly or by default)
+ */
+ if (!(mask & p->blocked) && !(p->flags & PF_PTRACED)) {
+ /* don't bother with ignored signals (but SIGCHLD is special) */
+ if (sa->sa_handler == SIG_IGN && sig != SIGCHLD)
+ return;
+ /* some signals are ignored by default.. (but SIGCONT already did its deed) */
+ if ((sa->sa_handler == SIG_DFL) &&
+ (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH || sig == SIGURG))
+ return;
}
- /* don't bother with ignored signals (but SIGCHLD is special) */
- if (sa->sa_handler == SIG_IGN && sig != SIGCHLD)
- return 0;
- /* some signals are ignored by default.. (but SIGCONT already did its deed) */
- if ((sa->sa_handler == SIG_DFL) &&
- (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH))
- return 0;
p->signal |= mask;
- return 1;
+ if (p->state == TASK_INTERRUPTIBLE && (p->signal & ~p->blocked))
+ wake_up_process(p);
}
+/*
+ * Force a signal that the process can't ignore: if necessary
+ * we unblock the signal and change any SIG_IGN to SIG_DFL.
+ */
+void force_sig(unsigned long sig, struct task_struct * p)
+{
+ sig--;
+ if (p->sig) {
+ unsigned long mask = 1UL << sig;
+ struct sigaction *sa = p->sig->action + sig;
+ p->signal |= mask;
+ p->blocked &= ~mask;
+ if (sa->sa_handler == SIG_IGN)
+ sa->sa_handler = SIG_DFL;
+ if (p->state == TASK_INTERRUPTIBLE)
+ wake_up_process(p);
+ }
+}
+
+
int send_sig(unsigned long sig,struct task_struct * p,int priv)
{
if (!p || sig > 32)
return -EINVAL;
if (!priv && ((sig != SIGCONT) || (current->session != p->session)) &&
- (current->euid != p->euid) && (current->euid != p->uid) && !suser())
+ (current->euid ^ p->suid) && (current->euid ^ p->uid) &&
+ (current->uid ^ p->suid) && (current->uid ^ p->uid) &&
+ !suser())
return -EPERM;
if (!sig)
return 0;
/*
* Forget it if the process is already zombie'd.
*/
- if (p->state == TASK_ZOMBIE)
+ if (!p->sig)
return 0;
if ((sig == SIGKILL) || (sig == SIGCONT)) {
if (p->state == TASK_STOPPED)
- p->state = TASK_RUNNING;
+ wake_up_process(p);
p->exit_code = 0;
p->signal &= ~( (1<<(SIGSTOP-1)) | (1<<(SIGTSTP-1)) |
(1<<(SIGTTIN-1)) | (1<<(SIGTTOU-1)) );
}
- /* Depends on order SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU */
- if ((sig >= SIGSTOP) && (sig <= SIGTTOU))
+ if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
p->signal &= ~(1<<(SIGCONT-1));
/* Actually generate the signal */
generate(sig,p);
@@ -73,7 +102,7 @@ int send_sig(unsigned long sig,struct task_struct * p,int priv)
void notify_parent(struct task_struct * tsk)
{
- if (tsk->p_pptr == task[1])
+ if (tsk->p_pptr == task[smp_num_cpus]) /* Init */
tsk->exit_signal = SIGCHLD;
send_sig(tsk->exit_signal, tsk->p_pptr, 1);
wake_up_interruptible(&tsk->p_pptr->wait_chldexit);
@@ -94,10 +123,14 @@ void release(struct task_struct * p)
nr_tasks--;
task[i] = NULL;
REMOVE_LINKS(p);
+ release_thread(p);
if (STACK_MAGIC != *(unsigned long *)p->kernel_stack_page)
printk(KERN_ALERT "release: %s kernel stack corruption. Aiee\n", p->comm);
- free_page(p->kernel_stack_page);
- free_page((long) p);
+ free_kernel_stack(p->kernel_stack_page);
+ current->cmin_flt += p->min_flt + p->cmin_flt;
+ current->cmaj_flt += p->maj_flt + p->cmaj_flt;
+ current->cnswap += p->nswap + p->cnswap;
+ kfree(p);
return;
}
panic("trying to release non-existent task");
@@ -313,12 +346,12 @@ asmlinkage int sys_kill(int pid,int sig)
*
* "I ask you, have you ever known what it is to be an orphan?"
*/
-int is_orphaned_pgrp(int pgrp)
+static int will_become_orphaned_pgrp(int pgrp, struct task_struct * ignored_task)
{
struct task_struct *p;
for_each_task(p) {
- if ((p->pgrp != pgrp) ||
+ if ((p == ignored_task) || (p->pgrp != pgrp) ||
(p->state == TASK_ZOMBIE) ||
(p->p_pptr->pid == 1))
continue;
@@ -329,7 +362,12 @@ int is_orphaned_pgrp(int pgrp)
return(1); /* (sighing) "Often!" */
}
-static int has_stopped_jobs(int pgrp)
+int is_orphaned_pgrp(int pgrp)
+{
+ return will_become_orphaned_pgrp(pgrp, 0);
+}
+
+static inline int has_stopped_jobs(int pgrp)
{
struct task_struct * p;
@@ -342,65 +380,140 @@ static int has_stopped_jobs(int pgrp)
return(0);
}
-static void forget_original_parent(struct task_struct * father)
+static inline void forget_original_parent(struct task_struct * father)
{
struct task_struct * p;
for_each_task(p) {
if (p->p_opptr == father)
- if (task[1])
- p->p_opptr = task[1];
+ if (task[smp_num_cpus]) /* init */
+ p->p_opptr = task[smp_num_cpus];
else
p->p_opptr = task[0];
}
}
-static void exit_files(void)
+static inline void close_files(struct files_struct * files)
{
- int i;
+ int i, j;
+
+ j = 0;
+ for (;;) {
+ unsigned long set = files->open_fds.fds_bits[j];
+ i = j * __NFDBITS;
+ j++;
+ if (i >= NR_OPEN)
+ break;
+ while (set) {
+ if (set & 1)
+ close_fp(files->fd[i]);
+ i++;
+ set >>= 1;
+ }
+ }
+}
- for (i=0 ; i<NR_OPEN ; i++)
- if (current->files->fd[i])
- sys_close(i);
+static inline void __exit_files(struct task_struct *tsk)
+{
+ struct files_struct * files = tsk->files;
+
+ if (files) {
+ tsk->files = NULL;
+ if (!--files->count) {
+ close_files(files);
+ kfree(files);
+ }
+ }
}
-static void exit_fs(void)
+void exit_files(struct task_struct *tsk)
{
- iput(current->fs->pwd);
- current->fs->pwd = NULL;
- iput(current->fs->root);
- current->fs->root = NULL;
+ __exit_files(tsk);
}
-NORET_TYPE void do_exit(long code)
+static inline void __exit_fs(struct task_struct *tsk)
{
- struct task_struct *p;
+ struct fs_struct * fs = tsk->fs;
+
+ if (fs) {
+ tsk->fs = NULL;
+ if (!--fs->count) {
+ iput(fs->root);
+ iput(fs->pwd);
+ kfree(fs);
+ }
+ }
+}
- if (intr_count) {
- printk("Aiee, killing interrupt handler\n");
- intr_count = 0;
+void exit_fs(struct task_struct *tsk)
+{
+ __exit_fs(tsk);
+}
+
+static inline void __exit_sighand(struct task_struct *tsk)
+{
+ struct signal_struct * sig = tsk->sig;
+
+ if (sig) {
+ tsk->sig = NULL;
+ if (!--sig->count) {
+ kfree(sig);
+ }
}
-fake_volatile:
- current->flags |= PF_EXITING;
- sem_exit();
- exit_mmap(current);
- free_page_tables(current);
- exit_files();
- exit_fs();
- exit_thread();
+}
+
+void exit_sighand(struct task_struct *tsk)
+{
+ __exit_sighand(tsk);
+}
+
+static inline void __exit_mm(struct task_struct * tsk)
+{
+ struct mm_struct * mm = tsk->mm;
+
+ /* Set us up to use the kernel mm state */
+ if (mm != &init_mm) {
+ flush_cache_mm(mm);
+ flush_tlb_mm(mm);
+ tsk->mm = &init_mm;
+ tsk->swappable = 0;
+ SET_PAGE_DIR(tsk, swapper_pg_dir);
+
+ /* free the old state - not used any more */
+ if (!--mm->count) {
+ exit_mmap(mm);
+ free_page_tables(mm);
+ kfree(mm);
+ }
+ }
+}
+
+void exit_mm(struct task_struct *tsk)
+{
+ __exit_mm(tsk);
+}
+
+/*
+ * Send signals to all our closest relatives so that they know
+ * to properly mourn us..
+ */
+static void exit_notify(void)
+{
+ struct task_struct * p;
+
forget_original_parent(current);
/*
* Check to see if any process groups have become orphaned
* as a result of our exiting, and if they have any stopped
- * jobs, send them a SIGUP and then a SIGCONT. (POSIX 3.2.2.2)
+ * jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2)
*
* Case i: Our father is in a different pgrp than we are
* and we were the only connection outside, so our pgrp
* is about to become orphaned.
- */
+ */
if ((current->p_pptr->pgrp != current->pgrp) &&
(current->p_pptr->session == current->session) &&
- is_orphaned_pgrp(current->pgrp) &&
+ will_become_orphaned_pgrp(current->pgrp, current) &&
has_stopped_jobs(current->pgrp)) {
kill_pg(current->pgrp,SIGHUP,1);
kill_pg(current->pgrp,SIGCONT,1);
@@ -420,8 +533,8 @@ fake_volatile:
current->p_cptr = p->p_osptr;
p->p_ysptr = NULL;
p->flags &= ~(PF_PTRACED|PF_TRACESYS);
- if (task[1] && task[1] != current)
- p->p_pptr = task[1];
+ if (task[smp_num_cpus] && task[smp_num_cpus] != current) /* init */
+ p->p_pptr = task[smp_num_cpus];
else
p->p_pptr = task[0];
p->p_osptr = p->p_pptr->p_cptr;
@@ -445,11 +558,28 @@ fake_volatile:
}
if (current->leader)
disassociate_ctty(1);
- if (last_task_used_math == current)
- last_task_used_math = NULL;
+}
+
+NORET_TYPE void do_exit(long code)
+{
+ if (intr_count) {
+ printk("Aiee, killing interrupt handler\n");
+ intr_count = 0;
+ }
+fake_volatile:
+ acct_process(code);
+ current->flags |= PF_EXITING;
+ del_timer(&current->real_timer);
+ sem_exit();
+ kerneld_exit();
+ __exit_mm(current);
+ __exit_files(current);
+ __exit_fs(current);
+ __exit_sighand(current);
+ exit_thread();
current->state = TASK_ZOMBIE;
current->exit_code = code;
- current->mm->rss = 0;
+ exit_notify();
#ifdef DEBUG_PROC_TREE
audit_ptree();
#endif
@@ -486,10 +616,18 @@ asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct
struct task_struct *p;
if (stat_addr) {
- flag = verify_area(VERIFY_WRITE, stat_addr, 4);
+ flag = verify_area(VERIFY_WRITE, stat_addr, sizeof(*stat_addr));
+ if (flag)
+ return flag;
+ }
+ if (ru) {
+ flag = verify_area(VERIFY_WRITE, ru, sizeof(*ru));
if (flag)
return flag;
}
+ if (options & ~(WNOHANG|WUNTRACED|__WCLONE))
+ return -EINVAL;
+
add_wait_queue(&current->wait_chldexit,&wait);
repeat:
flag=0;
@@ -514,24 +652,22 @@ repeat:
continue;
if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED))
continue;
+ if (ru != NULL)
+ getrusage(p, RUSAGE_BOTH, ru);
if (stat_addr)
- put_fs_long((p->exit_code << 8) | 0x7f,
+ put_user((p->exit_code << 8) | 0x7f,
stat_addr);
p->exit_code = 0;
- if (ru != NULL)
- getrusage(p, RUSAGE_BOTH, ru);
retval = p->pid;
goto end_wait4;
case TASK_ZOMBIE:
current->cutime += p->utime + p->cutime;
current->cstime += p->stime + p->cstime;
- current->mm->cmin_flt += p->mm->min_flt + p->mm->cmin_flt;
- current->mm->cmaj_flt += p->mm->maj_flt + p->mm->cmaj_flt;
if (ru != NULL)
getrusage(p, RUSAGE_BOTH, ru);
- flag = p->pid;
if (stat_addr)
- put_fs_long(p->exit_code, stat_addr);
+ put_user(p->exit_code, stat_addr);
+ retval = p->pid;
if (p->p_opptr != p->p_pptr) {
REMOVE_LINKS(p);
p->p_pptr = p->p_opptr;
@@ -542,7 +678,6 @@ repeat:
#ifdef DEBUG_PROC_TREE
audit_ptree();
#endif
- retval = flag;
goto end_wait4;
default:
continue;
@@ -552,12 +687,11 @@ repeat:
retval = 0;
if (options & WNOHANG)
goto end_wait4;
- current->state=TASK_INTERRUPTIBLE;
- schedule();
- current->signal &= ~(1<<(SIGCHLD-1));
retval = -ERESTARTSYS;
if (current->signal & ~current->blocked)
goto end_wait4;
+ current->state=TASK_INTERRUPTIBLE;
+ schedule();
goto repeat;
}
retval = -ECHILD;
@@ -566,6 +700,8 @@ end_wait4:
return retval;
}
+#ifndef __alpha__
+
/*
* sys_waitpid() remains for compatibility. waitpid() should be
* implemented by calling sys_wait4() from libc.a.
@@ -574,3 +710,14 @@ asmlinkage int sys_waitpid(pid_t pid,unsigned int * stat_addr, int options)
{
return sys_wait4(pid, stat_addr, options, NULL);
}
+
+#endif
+
+/*
+ * sys_wait() has been added for compatibility. wait() should be
+ * implemented by calling sys_wait4() from libc.a.
+ */
+asmlinkage int sys_wait(unsigned int * stat_addr)
+{
+ return sys_wait4(-1, stat_addr, 0, NULL);
+}