Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5daba1d authored by Heiko Carstens's avatar Heiko Carstens Committed by Martin Schwidefsky
Browse files

[S390] split/move machine check handler code



Split machine check handler code and move it to cio and kernel code
where it belongs to. No functional change.

Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 70193af9
Loading
Loading
Loading
Loading
+68 −0
Original line number Diff line number Diff line
/*
 *   Data definitions for channel report processing
 *    Copyright IBM Corp. 2000,2009
 *    Author(s): Ingo Adlung <adlung@de.ibm.com>,
 *		 Martin Schwidefsky <schwidefsky@de.ibm.com>,
 *		 Cornelia Huck <cornelia.huck@de.ibm.com>,
 *		 Heiko Carstens <heiko.carstens@de.ibm.com>,
 */

#ifndef _ASM_S390_CRW_H
#define _ASM_S390_CRW_H

#include <linux/types.h>

/*
 * Channel Report Word
 */
struct crw {
	__u32 res1 :  1;   /* reserved zero */
	__u32 slct :  1;   /* solicited */
	__u32 oflw :  1;   /* overflow */
	__u32 chn  :  1;   /* chained */
	__u32 rsc  :  4;   /* reporting source code */
	__u32 anc  :  1;   /* ancillary report */
	__u32 res2 :  1;   /* reserved zero */
	__u32 erc  :  6;   /* error-recovery code */
	__u32 rsid : 16;   /* reporting-source ID */
} __attribute__ ((packed));

typedef void (*crw_handler_t)(struct crw *, struct crw *, int);

extern int crw_register_handler(int rsc, crw_handler_t handler);
extern void crw_unregister_handler(int rsc);
extern void crw_handle_channel_report(void);

#define NR_RSCS 16

#define CRW_RSC_MONITOR  0x2  /* monitoring facility */
#define CRW_RSC_SCH	 0x3  /* subchannel */
#define CRW_RSC_CPATH	 0x4  /* channel path */
#define CRW_RSC_CONFIG	 0x9  /* configuration-alert facility */
#define CRW_RSC_CSS	 0xB  /* channel subsystem */

#define CRW_ERC_EVENT	 0x00 /* event information pending */
#define CRW_ERC_AVAIL	 0x01 /* available */
#define CRW_ERC_INIT	 0x02 /* initialized */
#define CRW_ERC_TERROR	 0x03 /* temporary error */
#define CRW_ERC_IPARM	 0x04 /* installed parm initialized */
#define CRW_ERC_TERM	 0x05 /* terminal */
#define CRW_ERC_PERRN	 0x06 /* perm. error, fac. not init */
#define CRW_ERC_PERRI	 0x07 /* perm. error, facility init */
#define CRW_ERC_PMOD	 0x08 /* installed parameters modified */

static inline int stcrw(struct crw *pcrw)
{
	int ccode;

	asm volatile(
		"	stcrw	0(%2)\n"
		"	ipm	%0\n"
		"	srl	%0,28\n"
		: "=d" (ccode), "=m" (*pcrw)
		: "a" (pcrw)
		: "cc" );
	return ccode;
}

#endif /* _ASM_S390_CRW_H */
+66 −0
Original line number Diff line number Diff line
/*
 *   Machine check handler definitions
 *
 *    Copyright IBM Corp. 2000,2009
 *    Author(s): Ingo Adlung <adlung@de.ibm.com>,
 *		 Martin Schwidefsky <schwidefsky@de.ibm.com>,
 *		 Cornelia Huck <cornelia.huck@de.ibm.com>,
 *		 Heiko Carstens <heiko.carstens@de.ibm.com>,
 */

#ifndef _ASM_S390_NMI_H
#define _ASM_S390_NMI_H

#include <linux/types.h>

struct mci {
	__u32 sd :  1; /* 00 system damage */
	__u32 pd :  1; /* 01 instruction-processing damage */
	__u32 sr :  1; /* 02 system recovery */
	__u32	 :  1; /* 03 */
	__u32 cd :  1; /* 04 timing-facility damage */
	__u32 ed :  1; /* 05 external damage */
	__u32	 :  1; /* 06 */
	__u32 dg :  1; /* 07 degradation */
	__u32 w  :  1; /* 08 warning pending */
	__u32 cp :  1; /* 09 channel-report pending */
	__u32 sp :  1; /* 10 service-processor damage */
	__u32 ck :  1; /* 11 channel-subsystem damage */
	__u32	 :  2; /* 12-13 */
	__u32 b  :  1; /* 14 backed up */
	__u32	 :  1; /* 15 */
	__u32 se :  1; /* 16 storage error uncorrected */
	__u32 sc :  1; /* 17 storage error corrected */
	__u32 ke :  1; /* 18 storage-key error uncorrected */
	__u32 ds :  1; /* 19 storage degradation */
	__u32 wp :  1; /* 20 psw mwp validity */
	__u32 ms :  1; /* 21 psw mask and key validity */
	__u32 pm :  1; /* 22 psw program mask and cc validity */
	__u32 ia :  1; /* 23 psw instruction address validity */
	__u32 fa :  1; /* 24 failing storage address validity */
	__u32	 :  1; /* 25 */
	__u32 ec :  1; /* 26 external damage code validity */
	__u32 fp :  1; /* 27 floating point register validity */
	__u32 gr :  1; /* 28 general register validity */
	__u32 cr :  1; /* 29 control register validity */
	__u32	 :  1; /* 30 */
	__u32 st :  1; /* 31 storage logical validity */
	__u32 ie :  1; /* 32 indirect storage error */
	__u32 ar :  1; /* 33 access register validity */
	__u32 da :  1; /* 34 delayed access exception */
	__u32	 :  7; /* 35-41 */
	__u32 pr :  1; /* 42 tod programmable register validity */
	__u32 fc :  1; /* 43 fp control register validity */
	__u32 ap :  1; /* 44 ancillary report */
	__u32	 :  1; /* 45 */
	__u32 ct :  1; /* 46 cpu timer validity */
	__u32 cc :  1; /* 47 clock comparator validity */
	__u32	 : 16; /* 47-63 */
};

struct pt_regs;

extern void s390_handle_mcck(void);
extern void s390_do_machine_check(struct pt_regs *regs);

#endif /* _ASM_S390_NMI_H */
+1 −1
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@ CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w
obj-y	:=  bitmap.o traps.o time.o process.o base.o early.o setup.o \
	    processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
	    s390_ext.o debug.o irq.o ipl.o dis.o diag.o mem_detect.o \
	    vdso.o vtime.o sysinfo.o
	    vdso.o vtime.o sysinfo.o nmi.o

obj-y	+= $(if $(CONFIG_64BIT),entry64.o,entry.o)
obj-y	+= $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
+46 −200
Original line number Diff line number Diff line
/*
 *  drivers/s390/s390mach.c
 *   S/390 machine check handler
 *   Machine check handler
 *
 *    Copyright IBM Corp. 2000,2008
 *    Author(s): Ingo Adlung (adlung@de.ibm.com)
 *		 Martin Schwidefsky (schwidefsky@de.ibm.com)
 *		 Cornelia Huck <cornelia.huck@de.ibm.com>
 *    Copyright IBM Corp. 2000,2009
 *    Author(s): Ingo Adlung <adlung@de.ibm.com>,
 *		 Martin Schwidefsky <schwidefsky@de.ibm.com>,
 *		 Cornelia Huck <cornelia.huck@de.ibm.com>,
 *		 Heiko Carstens <heiko.carstens@de.ibm.com>,
 */

#include <linux/init.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/workqueue.h>
#include <linux/time.h>
#include <linux/device.h>
#include <linux/kthread.h>
#include <asm/etr.h>
#include <linux/module.h>
#include <asm/lowcore.h>
#include <asm/cio.h>
#include <asm/smp.h>
#include <asm/etr.h>
#include <asm/cpu.h>
#include "s390mach.h"

static struct semaphore m_sem;

static NORET_TYPE void
s390_handle_damage(char *msg)
{
#ifdef CONFIG_SMP
	smp_send_stop();
#endif
	disabled_wait((unsigned long) __builtin_return_address(0));
	for(;;);
}

static crw_handler_t crw_handlers[NR_RSCS];

/**
 * s390_register_crw_handler() - register a channel report word handler
 * @rsc: reporting source code to handle
 * @handler: handler to be registered
 *
 * Returns %0 on success and a negative error value otherwise.
 */
int s390_register_crw_handler(int rsc, crw_handler_t handler)
{
	if ((rsc < 0) || (rsc >= NR_RSCS))
		return -EINVAL;
	if (!cmpxchg(&crw_handlers[rsc], NULL, handler))
		return 0;
	return -EBUSY;
}

/**
 * s390_unregister_crw_handler() - unregister a channel report word handler
 * @rsc: reporting source code to handle
 */
void s390_unregister_crw_handler(int rsc)
{
	if ((rsc < 0) || (rsc >= NR_RSCS))
		return;
	xchg(&crw_handlers[rsc], NULL);
	synchronize_sched();
}

/*
 * Retrieve CRWs and call function to handle event.
 */
static int s390_collect_crw_info(void *param)
{
	struct crw crw[2];
	int ccode;
	struct semaphore *sem;
	unsigned int chain;
	int ignore;

	sem = (struct semaphore *)param;
repeat:
	ignore = down_interruptible(sem);
	chain = 0;
	while (1) {
		if (unlikely(chain > 1)) {
			struct crw tmp_crw;

			printk(KERN_WARNING"%s: Code does not support more "
			       "than two chained crws; please report to "
			       "linux390@de.ibm.com!\n", __func__);
			ccode = stcrw(&tmp_crw);
			printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, "
			       "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
			       __func__, tmp_crw.slct, tmp_crw.oflw,
			       tmp_crw.chn, tmp_crw.rsc, tmp_crw.anc,
			       tmp_crw.erc, tmp_crw.rsid);
			printk(KERN_WARNING"%s: This was crw number %x in the "
			       "chain\n", __func__, chain);
			if (ccode != 0)
				break;
			chain = tmp_crw.chn ? chain + 1 : 0;
			continue;
		}
		ccode = stcrw(&crw[chain]);
		if (ccode != 0)
			break;
		printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, "
		       "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
		       crw[chain].slct, crw[chain].oflw, crw[chain].chn,
		       crw[chain].rsc, crw[chain].anc, crw[chain].erc,
		       crw[chain].rsid);
		/* Check for overflows. */
		if (crw[chain].oflw) {
			int i;

			pr_debug("%s: crw overflow detected!\n", __func__);
			for (i = 0; i < NR_RSCS; i++) {
				if (crw_handlers[i])
					crw_handlers[i](NULL, NULL, 1);
			}
			chain = 0;
			continue;
		}
		if (crw[0].chn && !chain) {
			chain++;
			continue;
		}
		if (crw_handlers[crw[chain].rsc])
			crw_handlers[crw[chain].rsc](&crw[0],
						     chain ? &crw[1] : NULL,
						     0);
		/* chain is always 0 or 1 here. */
		chain = crw[chain].chn ? chain + 1 : 0;
	}
	goto repeat;
	return 0;
}
#include <asm/nmi.h>
#include <asm/crw.h>

struct mcck_struct {
	int kill_task;
@@ -142,12 +28,18 @@ struct mcck_struct {

static DEFINE_PER_CPU(struct mcck_struct, cpu_mcck);

static NORET_TYPE void s390_handle_damage(char *msg)
{
	smp_send_stop();
	disabled_wait((unsigned long) __builtin_return_address(0));
	while (1);
}

/*
 * Main machine check handler function. Will be called with interrupts enabled
 * or disabled and machine checks enabled or disabled.
 */
void
s390_handle_mcck(void)
void s390_handle_mcck(void)
{
	unsigned long flags;
	struct mcck_struct mcck;
@@ -166,7 +58,7 @@ s390_handle_mcck(void)
	local_irq_restore(flags);

	if (mcck.channel_report)
		up(&m_sem);
		crw_handle_channel_report();

#ifdef CONFIG_MACHCHK_WARNING
/*
@@ -204,8 +96,7 @@ EXPORT_SYMBOL_GPL(s390_handle_mcck);
 * returns 0 if all registers could be validated
 * returns 1 otherwise
 */
static int
s390_revalidate_registers(struct mci *mci)
static int notrace s390_revalidate_registers(struct mci *mci)
{
	int kill_task;
	u64 tmpclock;
@@ -214,22 +105,21 @@ s390_revalidate_registers(struct mci *mci)

	kill_task = 0;
	zero = 0;
	/* General purpose registers */
	if (!mci->gr)

	if (!mci->gr) {
		/*
		 * General purpose registers couldn't be restored and have
		 * unknown contents. Process needs to be terminated.
		 */
		kill_task = 1;

	/* Revalidate floating point registers */
	if (!mci->fp)
	}
	if (!mci->fp) {
		/*
		 * Floating point registers can't be restored and
		 * therefore the process needs to be terminated.
		 */
		kill_task = 1;

	}
#ifndef CONFIG_64BIT
	asm volatile(
		"	ld	0,0(%0)\n"
@@ -247,7 +137,6 @@ s390_revalidate_registers(struct mci *mci)
		fpt_save_area = (void *) S390_lowcore.extended_save_area_addr;
		fpt_creg_save_area = fpt_save_area + 128;
#endif
		/* Floating point control register */
		if (!mci->fc) {
			/*
			 * Floating point control register can't be restored.
@@ -278,26 +167,25 @@ s390_revalidate_registers(struct mci *mci)
			"	ld	15,120(%0)\n"
			: : "a" (fpt_save_area));
	}

	/* Revalidate access registers */
	asm volatile(
		"	lam	0,15,0(%0)"
		: : "a" (&S390_lowcore.access_regs_save_area));
	if (!mci->ar)
	if (!mci->ar) {
		/*
		 * Access registers have unknown contents.
		 * Terminating task.
		 */
		kill_task = 1;

	}
	/* Revalidate control registers */
	if (!mci->cr)
	if (!mci->cr) {
		/*
		 * Control registers have unknown contents.
		 * Can't recover and therefore stopping machine.
		 */
		s390_handle_damage("invalid control registers.");
	else
	} else {
#ifdef CONFIG_64BIT
		asm volatile(
			"	lctlg	0,15,0(%0)"
@@ -307,12 +195,11 @@ s390_revalidate_registers(struct mci *mci)
			"	lctl	0,15,0(%0)"
			: : "a" (&S390_lowcore.cregs_save_area));
#endif

	}
	/*
	 * We don't even try to revalidate the TOD register, since we simply
	 * can't write something sensible into that register.
	 */

#ifdef CONFIG_64BIT
	/*
	 * See if we can revalidate the TOD programmable register with its
@@ -330,7 +217,6 @@ s390_revalidate_registers(struct mci *mci)
			: : "a" (&S390_lowcore.tod_progreg_save_area)
			: "0", "cc");
#endif

	/* Revalidate clock comparator register */
	asm volatile(
		"	stck	0(%1)\n"
@@ -354,31 +240,35 @@ s390_revalidate_registers(struct mci *mci)
#define MAX_IPD_COUNT	29
#define MAX_IPD_TIME	(5 * 60 * USEC_PER_SEC) /* 5 minutes */

#define ED_STP_ISLAND	6	/* External damage STP island check */
#define ED_STP_SYNC	7	/* External damage STP sync check */
#define ED_ETR_SYNC	12	/* External damage ETR sync check */
#define ED_ETR_SWITCH	13	/* External damage ETR switch to local */

/*
 * machine check handler.
 */
void notrace s390_do_machine_check(struct pt_regs *regs)
{
	static int ipd_count;
	static DEFINE_SPINLOCK(ipd_lock);
	static unsigned long long last_ipd;
	static int ipd_count;
	struct mcck_struct *mcck;
	unsigned long long tmp;
	struct mci *mci;
	struct mcck_struct *mcck;
	int umode;

	lockdep_off();

	s390_idle_check();

	mci = (struct mci *) &S390_lowcore.mcck_interruption_code;
	mcck = &__get_cpu_var(cpu_mcck);
	umode = user_mode(regs);

	if (mci->sd)
	if (mci->sd) {
		/* System damage -> stopping machine */
		s390_handle_damage("received system damage machine check.");

	}
	if (mci->pd) {
		if (mci->b) {
			/* Processing backup -> verify if we can survive this */
@@ -408,24 +298,17 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
			 * Nullifying exigent condition, therefore we might
			 * retry this instruction.
			 */

			spin_lock(&ipd_lock);

			tmp = get_clock();

			if (((tmp - last_ipd) >> 12) < MAX_IPD_TIME)
				ipd_count++;
			else
				ipd_count = 1;

			last_ipd = tmp;

			if (ipd_count == MAX_IPD_COUNT)
				s390_handle_damage("too many ipd retries.");

			spin_unlock(&ipd_lock);
		}
		else {
		} else {
			/* Processing damage -> stopping machine */
			s390_handle_damage("received instruction processing "
					   "damage machine check.");
@@ -440,20 +323,18 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
			mcck->kill_task = 1;
			mcck->mcck_code = *(unsigned long long *) mci;
			set_thread_flag(TIF_MCCK_PENDING);
		}
		else
		} else {
			/*
			 * Couldn't restore all register contents while in
			 * kernel mode -> stopping machine.
			 */
			s390_handle_damage("unable to revalidate registers.");
		}

	}
	if (mci->cd) {
		/* Timing facility damage */
		s390_handle_damage("TOD clock damaged");
	}

	if (mci->ed && mci->ec) {
		/* External damage */
		if (S390_lowcore.external_damage_code & (1U << ED_ETR_SYNC))
@@ -465,28 +346,23 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
		if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND))
			stp_island_check();
	}

	if (mci->se)
		/* Storage error uncorrected */
		s390_handle_damage("received storage error uncorrected "
				   "machine check.");

	if (mci->ke)
		/* Storage key-error uncorrected */
		s390_handle_damage("received storage key-error uncorrected "
				   "machine check.");

	if (mci->ds && mci->fa)
		/* Storage degradation */
		s390_handle_damage("received storage degradation machine "
				   "check.");

	if (mci->cp) {
		/* Channel report word pending */
		mcck->channel_report = 1;
		set_thread_flag(TIF_MCCK_PENDING);
	}

	if (mci->w) {
		/* Warning pending */
		mcck->warning = 1;
@@ -495,15 +371,8 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
	lockdep_on();
}

/*
 * s390_init_machine_check
 *
 * initialize machine check handling
 */
static int
machine_check_init(void)
static int __init machine_check_init(void)
{
	init_MUTEX_LOCKED(&m_sem);
	ctl_set_bit(14, 25);	/* enable external damage MCH */
	ctl_set_bit(14, 27);	/* enable system recovery MCH */
#ifdef CONFIG_MACHCHK_WARNING
@@ -511,27 +380,4 @@ machine_check_init(void)
#endif
	return 0;
}

/*
 * Initialize the machine check handler really early to be able to
 * catch all machine checks that happen during boot
 */
arch_initcall(machine_check_init);

/*
 * Machine checks for the channel subsystem must be enabled
 * after the channel subsystem is initialized
 */
static int __init
machine_check_crw_init (void)
{
	struct task_struct *task;

	task = kthread_run(s390_collect_crw_info, &m_sem, "kmcheck");
	if (IS_ERR(task))
		return PTR_ERR(task);
	ctl_set_bit(14, 28);	/* enable channel report MCH */
	return 0;
}

device_initcall (machine_check_crw_init);
+1 −1
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
#include <asm/processor.h>
#include <asm/irq.h>
#include <asm/timer.h>
#include <asm/nmi.h>
#include "entry.h"

asmlinkage void ret_from_fork(void) asm ("ret_from_fork");
@@ -68,7 +69,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
	return sf->gprs[8];
}

extern void s390_handle_mcck(void);
/*
 * The idle loop on a S390...
 */
Loading