patch-2.4.13 linux/arch/s390/mm/fault.c
Next file: linux/arch/s390/mm/init.c
Previous file: linux/arch/s390/mm/extable.c
Back to the patch index
Back to the overall index
- Lines: 202
- Date:
Thu Oct 11 09:04:57 2001
- Orig file:
v2.4.12/linux/arch/s390/mm/fault.c
- Orig date:
Sun Aug 12 13:27:58 2001
diff -u --recursive --new-file v2.4.12/linux/arch/s390/mm/fault.c linux/arch/s390/mm/fault.c
@@ -4,6 +4,7 @@
* S390 version
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Hartmut Penner (hp@de.ibm.com)
+ * Ulrich Weigand (uweigand@de.ibm.com)
*
* Derived from "arch/i386/mm/fault.c"
* Copyright (C) 1995 Linus Torvalds
@@ -23,6 +24,7 @@
#include <linux/smp_lock.h>
#include <linux/compatmac.h>
#include <linux/init.h>
+#include <linux/console.h>
#include <asm/system.h>
#include <asm/uaccess.h>
@@ -34,6 +36,34 @@
#endif
extern void die(const char *,struct pt_regs *,long);
+static void force_sigsegv(struct task_struct *tsk, int code, void *address);
+
+extern spinlock_t timerlist_lock;
+
+/*
+ * Unlock any spinlocks which will prevent us from getting the
+ * message out (timerlist_lock is acquired through the
+ * console unblank code)
+ */
+void bust_spinlocks(int yes)
+{
+ spin_lock_init(&timerlist_lock);
+ if (yes) {
+ oops_in_progress = 1;
+ } else {
+ int loglevel_save = console_loglevel;
+ oops_in_progress = 0;
+ console_unblank();
+ /*
+ * OK, the message is on the console. Now we call printk()
+ * without oops_in_progress set so that printk will give klogd
+ * a poke. Hold onto your hats...
+ */
+ console_loglevel = 15;
+ printk(" ");
+ console_loglevel = loglevel_save;
+ }
+}
/*
* This routine handles page faults. It determines the address,
@@ -56,18 +86,36 @@
int si_code = SEGV_MAPERR;
int kernel_address = 0;
+ tsk = current;
+ mm = tsk->mm;
+
+ /*
+ * Check for low-address protection. This needs to be treated
+ * as a special case because the translation exception code
+ * field is not guaranteed to contain valid data in this case.
+ */
+ if ((error_code & 0xff) == 4 && !(S390_lowcore.trans_exc_code & 4)) {
+
+ /* Low-address protection hit in kernel mode means
+ NULL pointer write access in kernel mode. */
+ if (!(regs->psw.mask & PSW_PROBLEM_STATE)) {
+ address = 0;
+ kernel_address = 1;
+ goto no_context;
+ }
+
+ /* Low-address protection hit in user mode 'cannot happen'. */
+ die ("Low-address protection", regs, error_code);
+ do_exit(SIGKILL);
+ }
+
/*
* get the failing address
* more specific the segment and page table portion of
* the address
*/
- address = S390_lowcore.trans_exc_code&0x7ffff000;
- tsk = current;
- mm = tsk->mm;
-
- if (in_interrupt() || !mm)
- goto no_context;
+ address = S390_lowcore.trans_exc_code&0x7ffff000;
/*
@@ -99,6 +147,7 @@
}
}
die("page fault via unknown access register", regs, error_code);
+ do_exit(SIGKILL);
break;
case 2: /* Secondary Segment Table Descriptor */
@@ -107,6 +156,11 @@
break;
}
+ /*
+ * Check whether we have a user MM in the first place.
+ */
+ if (in_interrupt() || !mm)
+ goto no_context;
/*
* When we get here, the fault happened in the current
@@ -146,6 +200,7 @@
goto bad_area;
}
+ survive:
/*
* If for any reason at all we couldn't handle the fault,
* make sure we exit gracefully rather than endlessly redo
@@ -176,7 +231,6 @@
/* User mode accesses just cause a SIGSEGV */
if (regs->psw.mask & PSW_PROBLEM_STATE) {
- struct siginfo si;
tsk->thread.prot_addr = address;
tsk->thread.trap_no = error_code;
#ifndef CONFIG_SYSCTL
@@ -193,10 +247,8 @@
show_regs(regs);
}
#endif
- si.si_signo = SIGSEGV;
- si.si_code = si_code;
- si.si_addr = (void*) address;
- force_sig_info(SIGSEGV, &si, tsk);
+
+ force_sigsegv(tsk, si_code, (void *)address);
return;
}
@@ -218,9 +270,6 @@
else
printk(KERN_ALERT "Unable to handle kernel paging request"
" at virtual user address %08lx\n", address);
-/*
- * need to define, which information is useful here
- */
die("Oops", regs, error_code);
do_exit(SIGKILL);
@@ -232,6 +281,12 @@
*/
out_of_memory:
up_read(&mm->mmap_sem);
+ if (tsk->pid == 1) {
+ tsk->policy |= SCHED_YIELD;
+ schedule();
+ down_read(&mm->mmap_sem);
+ goto survive;
+ }
printk("VM: killing process %s\n", tsk->comm);
if (regs->psw.mask & PSW_PROBLEM_STATE)
do_exit(SIGKILL);
@@ -253,6 +308,18 @@
goto no_context;
}
+/*
+ * Send SIGSEGV to task. This is an external routine
+ * to keep the stack usage of do_page_fault small.
+ */
+static void force_sigsegv(struct task_struct *tsk, int code, void *address)
+{
+ struct siginfo si;
+ si.si_signo = SIGSEGV;
+ si.si_code = code;
+ si.si_addr = address;
+ force_sig_info(SIGSEGV, &si, tsk);
+}
typedef struct _pseudo_wait_t {
struct _pseudo_wait_t *next;
@@ -434,7 +501,6 @@
asmlinkage void
pfault_interrupt(struct pt_regs *regs, __u16 error_code)
{
- DECLARE_WAITQUEUE(wait, current);
struct task_struct *tsk;
wait_queue_head_t queue;
wait_queue_head_t *qp;
@@ -447,7 +513,7 @@
* external interrupt.
*/
subcode = S390_lowcore.cpu_addr;
- if ((subcode & 0xff00) != 0x06)
+ if ((subcode & 0xff00) != 0x0600)
return;
/*
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)