patch-2.4.13 linux/arch/s390x/mm/fault.c

Next file: linux/arch/s390x/mm/init.c
Previous file: linux/arch/s390x/mm/extable.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.12/linux/arch/s390x/mm/fault.c linux/arch/s390x/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
@@ -22,6 +23,7 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/init.h>
+#include <linux/console.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -33,33 +35,33 @@
 #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
+ * 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;
-#ifdef CONFIG_SMP
-                atomic_set(&global_irq_lock,0);
-#endif
-        } else {
-                int loglevel_save = console_loglevel;
-                oops_in_progress = 0;
-                /*
-                 * 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;          /* NMI oopser may have shut the console up */
-                printk(" ");
-                console_loglevel = loglevel_save;
-        }
+	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;
+	}
 }
 
 /*
@@ -84,6 +86,29 @@
 	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 
@@ -92,11 +117,6 @@
 
         address = S390_lowcore.trans_exc_code&-4096L;
 
-        tsk = current;
-        mm = tsk->mm;
-
-        if (in_interrupt() || !mm)
-                goto no_context;
 
 	/*
 	 * Check which address space the address belongs to
@@ -127,6 +147,7 @@
 			}
 		}
 		die("page fault via unknown access register", regs, error_code);
+        	do_exit(SIGKILL);
 		break;
 
 	case 2: /* Secondary Segment Table Descriptor */
@@ -135,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
@@ -144,10 +170,8 @@
         down_read(&mm->mmap_sem);
 
         vma = find_vma(mm, address);
-        if (!vma) {
-	        printk("no vma for address %lX\n",address);
+        if (!vma)
                 goto bad_area;
-        }
         if (vma->vm_start <= address) 
                 goto good_area;
         if (!(vma->vm_flags & VM_GROWSDOWN))
@@ -177,6 +201,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
@@ -207,7 +232,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
@@ -224,10 +248,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;
 	}
 
@@ -242,6 +264,7 @@
  * Oops. The kernel tried to access some bad page. We'll have to
  * terminate things with extreme prejudice.
  */
+
         if (kernel_address)
                 printk(KERN_ALERT "Unable to handle kernel pointer dereference"
         	       " at virtual kernel address %016lx\n", address);
@@ -249,10 +272,6 @@
                 printk(KERN_ALERT "Unable to handle kernel paging request"
 		       " at virtual user address %016lx\n", address);
 
-/*
- * need to define, which information is useful here
- */
-
         die("Oops", regs, error_code);
         do_exit(SIGKILL);
 
@@ -263,6 +282,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);
@@ -284,6 +309,20 @@
 		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);
+}
+
+
 #ifdef CONFIG_PFAULT
 /*
  * 'pfault' pseudo page faults routines.
@@ -316,13 +355,11 @@
        int resolved;
 } pseudo_wait_t;
 
-static pseudo_wait_t *pseudo_lock_queue = NULL;
-static spinlock_t pseudo_wait_spinlock; /* spinlock to protect lock queue */
-
 int pfault_init(void)
 {
 	pfault_refbk_t refbk =
-	{ 0x258, 0, 5, 2, __LC_KERNEL_STACK, 1ULL << 48, 1ULL << 48, 0ULL };
+	{ 0x258, 0, 5, 2, __LC_KERNEL_STACK, 1ULL << 48, 1ULL << 48,
+          0x8000000000000000ULL };
         int rc;
 
 	if (pfault_disable)
@@ -362,7 +399,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;
@@ -375,7 +411,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)