Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3095

kernel-2.6.18-194.11.1.el5.src.rpm

From: Hans-Joachim Picht <hpicht@redhat.com>
Date: Tue, 30 Oct 2007 16:51:44 +0100
Subject: [s390] cio: reipl fails after channel path reset
Message-id: 20071030155144.GA6604@redhat.com
O-Subject: [RHEL5.2 PATCH 1/5] s390 cio: reipl fails after channel path reset
Bugzilla: 231306

Problem:
=========

Re-IPL channel program is terminated by a asynchronous channel path
reset operation which is started during shutdown.
The result in a hanging LPAR on reboot.
The problem is fixed by  waiting for the completion of the notification
of the channel path reset operation.

Bugzilla
=========

BZ 231306
https://bugzilla.redhat.com/show_bug.cgi?id=231306

Upstream status of the patch:
=============================
Code was upstream but later on changed completely because of code rewrites

Test status:
============
Kernel with patch was built and successfully tested

Please ACK.

With best regards,

Hans

diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index 0c712b7..f7bc855 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -785,6 +785,20 @@ mcck_return:
 
         RESTORE_ALL __LC_RETURN_MCCK_PSW,0
 
+/*
+ * Reset channel path machine check handler.
+ */
+	.globl rchp_mcck_int_handler
+rchp_mcck_int_handler:
+	SAVE_ALL_BASE __LC_SAVE_AREA+32
+	SAVE_ALL_SYNC __LC_MCK_OLD_PSW,__LC_SAVE_AREA+32
+	CREATE_STACK_FRAME __LC_MCK_OLD_PSW,__LC_SAVE_AREA+32
+
+	l	%r1,BASED(.Ldo_rchp_mcck)
+	basr	%r14,%r1		# call machine check handler
+
+	RESTORE_ALL __LC_RETURN_MCCK_PSW,0
+
 #ifdef CONFIG_SMP
 /*
  * Restart interruption handler, kick starter for additional CPUs
@@ -1031,6 +1045,7 @@ cleanup_io_leave_insn:
 .Ldo_IRQ:      .long  do_IRQ
 .Ldo_extint:   .long  do_extint
 .Ldo_signal:   .long  do_signal
+.Ldo_rchp_mcck:.long  do_rchp_mcck
 .Lhandle_per:  .long  do_single_step
 .Ljump_table:  .long  pgm_check_table
 .Lschedule:    .long  schedule
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S
index 8b956d1..1dbb84f 100644
--- a/arch/s390/kernel/entry64.S
+++ b/arch/s390/kernel/entry64.S
@@ -790,6 +790,19 @@ mcck_return:
 #endif
 	lpswe	__LC_RETURN_MCCK_PSW	# back to caller
 
+/*
+ * Reset channel path machine check handler.
+ */
+	.globl rchp_mcck_int_handler
+rchp_mcck_int_handler:
+	SAVE_ALL_BASE __LC_SAVE_AREA+64
+	SAVE_ALL_SYNC __LC_MCK_OLD_PSW,__LC_SAVE_AREA+64
+	CREATE_STACK_FRAME __LC_MCK_OLD_PSW,__LC_SAVE_AREA+64
+
+	brasl	%r14,do_rchp_mcck
+
+	RESTORE_ALL __LC_RETURN_MCCK_PSW,0
+
 #ifdef CONFIG_SMP
 /*
  * Restart interruption handler, kick starter for additional CPUs
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 8117c41..b941d4a 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -15,12 +15,15 @@
 #include <linux/device.h>
 
 #include <asm/cio.h>
+#include <asm/lowcore.h>
+#include <asm/ptrace.h>
 
 #include "css.h"
 #include "cio.h"
 #include "cio_debug.h"
 #include "ioasm.h"
 #include "chsc.h"
+#include "../s390mach.h"
 
 static void *sei_page;
 
@@ -1508,24 +1511,65 @@ static int reset_channel_path(struct channel_path *chp)
 	}
 }
 
+static atomic_t chpid_count;
+
+void do_rchp_mcck(struct pt_regs *regs)
+{
+	struct crw crw;
+	struct mci *mci;
+
+	/* Check for pending channel report word; */
+	mci = (struct mci *) &S390_lowcore.mcck_interruption_code;
+	if (!mci->cp)
+		return;
+	/* Process channel report words. */
+	while (stcrw(&crw) == 0)
+		/* Count RCHP responses. */
+		if (crw.slct && crw.rsc == CRW_RSC_CPATH)
+			atomic_dec(&chpid_count);
+}
+
+#define RCHP_TIMEOUT	(30 * USEC_PER_SEC)
+
 static void reset_channel_paths_css(struct channel_subsystem *css)
 {
 	int i;
 
-	for (i = 0; i <= __MAX_CHPID; i++) {
+	for (i = 0; i <= __MAX_CHPID; i++)
 		if (css->chps[i])
-			reset_channel_path(css->chps[i]);
-	}
+			if (reset_channel_path(css->chps[i]) == 0)
+				atomic_inc(&chpid_count);
 }
 
+extern void rchp_mcck_int_handler(void);
+
 void cio_reset_channel_paths(void)
 {
 	int i;
-
+	unsigned long long timeout;
+
+	/* Disable lowcore protection. */
+	__ctl_clear_bit(0,28);
+	/* Set local machine check handler. */
+	local_mcck_disable();
+	S390_lowcore.mcck_new_psw.mask =
+                PSW_KERNEL_BITS & ~PSW_MASK_MCHECK;
+	S390_lowcore.mcck_new_psw.addr =
+		PSW_ADDR_AMODE | (unsigned long) &rchp_mcck_int_handler;
+	local_mcck_enable();
+	/* Reset known channel paths. */
+	atomic_set(&chpid_count, 0);
 	for (i = 0; i <= __MAX_CSSID; i++) {
 		if (css[i] && css[i]->valid)
 			reset_channel_paths_css(css[i]);
 	}
+	/* Wait for reset acknowledgment. */
+	timeout = get_clock() + (RCHP_TIMEOUT << 12);
+	while (atomic_read(&chpid_count) != 0) {
+		if (get_clock() > timeout)
+			break;
+		cpu_relax();
+	}
 }
 
 static int __init