2 * arch/s390/kernel/s390mach.c
3 * S/390 machine check handler,
4 * currently only channel-reports are supported
7 * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
8 * Author(s): Ingo Adlung (adlung@de.ibm.com)
11 #include <linux/config.h>
12 #include <linux/spinlock.h>
13 #include <linux/init.h>
14 #include <linux/slab.h>
16 #include <linux/smp.h>
20 #include <asm/lowcore.h>
21 #include <asm/semaphore.h>
22 #include <asm/s390io.h>
23 #include <asm/s390dyn.h>
24 #include <asm/s390mach.h>
25 #ifdef CONFIG_MACHCHK_WARNING
26 #include <asm/signal.h>
29 extern void ctrl_alt_del(void);
31 #define S390_MACHCHK_DEBUG
33 static int s390_machine_check_handler( void * parm );
34 static void s390_enqueue_mchchk( mache_t *mchchk );
35 static mache_t *s390_dequeue_mchchk( void );
36 static void s390_enqueue_free_mchchk( mache_t *mchchk );
37 static mache_t *s390_dequeue_free_mchchk( void );
38 static int s390_collect_crw_info( void );
39 #ifdef CONFIG_MACHCHK_WARNING
40 static int s390_post_warning( void );
43 static mache_t *mchchk_queue_head = NULL;
44 static mache_t *mchchk_queue_tail = NULL;
45 static mache_t *mchchk_queue_free = NULL;
46 static crwe_t *crw_buffer_anchor = NULL;
47 static spinlock_t mchchk_queue_lock = SPIN_LOCK_UNLOCKED;
48 static spinlock_t crw_queue_lock = SPIN_LOCK_UNLOCKED;
50 static struct semaphore s_sem;
52 #ifdef CONFIG_MACHCHK_WARNING
53 static int mchchk_wng_posted = 0;
57 * s390_init_machine_check
59 * initialize machine check handling
61 void s390_init_machine_check( void )
63 crwe_t *pcrwe; /* CRW buffer element pointer */
64 mache_t *pmache; /* machine check element pointer */
66 init_MUTEX_LOCKED( &s_sem );
68 pcrwe = kmalloc( MAX_CRW_PENDING * sizeof( crwe_t), GFP_KERNEL);
74 crw_buffer_anchor = pcrwe;
76 for ( i=0; i < MAX_CRW_PENDING-1; i++)
78 pcrwe->crwe_next = (crwe_t *)((unsigned long)pcrwe + sizeof(crwe_t));
79 pcrwe = pcrwe->crwe_next;
83 pcrwe->crwe_next = NULL;
88 panic( "s390_init_machine_check : unable to obtain memory\n");
92 pmache = kmalloc( MAX_MACH_PENDING * sizeof( mache_t), GFP_KERNEL);
98 for ( i=0; i < MAX_MACH_PENDING; i++)
100 s390_enqueue_free_mchchk( pmache );
101 pmache = (mache_t *)((unsigned long)pmache + sizeof(mache_t));
107 panic( "s390_init_machine_check : unable to obtain memory\n");
111 #ifdef S390_MACHCHK_DEBUG
112 printk( KERN_NOTICE "init_mach : starting machine check handler\n");
115 kernel_thread( s390_machine_check_handler, &s_sem, CLONE_FS | CLONE_FILES);
117 ctl_clear_bit( 14, 25 ); // disable damage MCH
119 ctl_set_bit( 14, 26 ); /* enable degradation MCH */
120 ctl_set_bit( 14, 27 ); /* enable system recovery MCH */
122 ctl_set_bit( 14, 28 ); // enable channel report MCH
124 #ifdef CONFIG_MACHCHK_WARNING
125 ctl_set_bit( 14, 24); /* enable warning MCH */
128 #ifdef S390_MACHCHK_DEBUG
129 printk( KERN_DEBUG "init_mach : machine check buffer : head = %08X\n",
130 (unsigned)&mchchk_queue_head);
131 printk( KERN_DEBUG "init_mach : machine check buffer : tail = %08X\n",
132 (unsigned)&mchchk_queue_tail);
133 printk( KERN_DEBUG "init_mach : machine check buffer : free = %08X\n",
134 (unsigned)&mchchk_queue_free);
135 printk( KERN_DEBUG "init_mach : CRW entry buffer anchor = %08X\n",
136 (unsigned)&crw_buffer_anchor);
137 printk( KERN_DEBUG "init_mach : machine check handler ready\n");
143 static void s390_handle_damage(char * msg){
145 unsigned long caller = (unsigned long) __builtin_return_address(0);
147 printk(KERN_EMERG "%s\n", msg);
151 disabled_wait(caller);
156 * s390_do_machine_check
158 * mchine check pre-processor, collecting the machine check info,
159 * queueing it and posting the machine check handler for processing.
161 void s390_do_machine_check( void )
166 #ifdef S390_MACHCHK_DEBUG
167 printk( KERN_INFO "s390_do_machine_check : starting ...\n");
171 &S390_lowcore.mcck_interruption_code,
174 if (mcic.mcc.mcd.sd) /* system damage */
175 s390_handle_damage("received system damage machine check\n");
177 if (mcic.mcc.mcd.pd) /* instruction processing damage */
178 s390_handle_damage("received instruction processing damage machine check\n");
180 if (mcic.mcc.mcd.se) /* storage error uncorrected */
181 s390_handle_damage("received storage error uncorrected machine check\n");
183 if (mcic.mcc.mcd.sc) /* storage error corrected */
184 printk(KERN_WARNING "received storage error corrected machine check\n");
186 if (mcic.mcc.mcd.ke) /* storage key-error uncorrected */
187 s390_handle_damage("received storage key-error uncorrected machine check\n");
189 if (mcic.mcc.mcd.ds && mcic.mcc.mcd.fa) /* storage degradation */
190 s390_handle_damage("received storage degradation machine check\n");
192 if ( mcic.mcc.mcd.cp ) // CRW pending ?
194 crw_count = s390_collect_crw_info();
203 #ifdef CONFIG_MACHCHK_WARNING
205 * The warning may remain for a prolonged period on the bare iron.
206 * (actually till the machine is powered off, or until the problem is gone)
207 * So we just stop listening for the WARNING MCH and prevent continuously
208 * being interrupted. One caveat is however, that we must do this per
209 * processor and cannot use the smp version of ctl_clear_bit().
210 * On VM we only get one interrupt per virtally presented machinecheck.
211 * Though one suffices, we may get one interrupt per (virtual) processor.
213 if ( mcic.mcc.mcd.w ) // WARNING pending ?
215 // Use single machine clear, as we cannot handle smp right now
216 __ctl_clear_bit( 14, 24 ); // Disable WARNING MCH
218 if ( ! mchchk_wng_posted )
220 mchchk_wng_posted = s390_post_warning();
222 if ( mchchk_wng_posted )
233 #ifdef S390_MACHCHK_DEBUG
234 printk( KERN_INFO "s390_do_machine_check : done \n");
241 * s390_machine_check_handler
243 * machine check handler, dequeueing machine check entries
244 * and processing them
246 static int s390_machine_check_handler( void *parm)
248 struct semaphore *sem = parm;
254 /* set name to something sensible */
255 strcpy (current->comm, "kmcheck");
258 /* block all signals */
259 sigfillset(¤t->blocked);
261 #ifdef S390_MACHCHK_DEBUG
262 printk( KERN_NOTICE "mach_handler : ready\n");
267 #ifdef S390_MACHCHK_DEBUG
268 printk( KERN_NOTICE "mach_handler : waiting for wakeup\n");
271 down_interruptible( sem );
273 #ifdef S390_MACHCHK_DEBUG
274 printk( KERN_NOTICE "\nmach_handler : wakeup ... \n");
276 found = 0; /* init ... */
278 __save_flags( flags );
283 pmache = s390_dequeue_mchchk();
289 if ( pmache->mcic.mcc.mcd.cp )
294 s390_do_crw_pending( pmache->mc.crwe );
296 pcrwe_h = pmache->mc.crwe;
297 pcrwe_n = pmache->mc.crwe->crwe_next;
299 pmache->mcic.mcc.mcd.cp = 0;
300 pmache->mc.crwe = NULL;
302 spin_lock( &crw_queue_lock);
306 pcrwe_h->crwe_next = crw_buffer_anchor;
307 crw_buffer_anchor = pcrwe_h;
310 if ( pcrwe_h != NULL )
311 pcrwe_n = pcrwe_h->crwe_next;
315 spin_unlock( &crw_queue_lock);
319 #ifdef CONFIG_MACHCHK_WARNING
320 if ( pmache->mcic.mcc.mcd.w )
322 ctrl_alt_del(); // shutdown NOW!
323 #ifdef S390_MACHCHK_DEBUG
324 printk( KERN_DEBUG "mach_handler : kill -SIGPWR init\n");
329 #ifdef CONFIG_MACHCHK_WARNING
330 if ( pmache->mcic.mcc.mcd.w )
332 ctrl_alt_del(); // shutdown NOW!
333 #ifdef S390_MACHCHK_DEBUG
334 printk( KERN_DEBUG "mach_handler : kill -SIGPWR init\n");
339 s390_enqueue_free_mchchk( pmache );
344 // unconditional surrender ...
345 #ifdef S390_MACHCHK_DEBUG
346 printk( KERN_DEBUG "mach_handler : nothing to do, sleeping\n");
353 __restore_flags( flags );
361 * s390_dequeue_mchchk
363 * Dequeue an entry from the machine check queue
365 * Note : The queue elements provide for a double linked list.
366 * We dequeue entries from the tail, and enqueue entries to
370 static mache_t *s390_dequeue_mchchk( void )
374 spin_lock( &mchchk_queue_lock );
376 qe = mchchk_queue_tail;
380 mchchk_queue_tail = qe->prev;
382 if ( mchchk_queue_tail != NULL )
384 mchchk_queue_tail->next = NULL;
388 mchchk_queue_head = NULL;
394 spin_unlock( &mchchk_queue_lock );
400 * s390_enqueue_mchchk
402 * Enqueue an entry to the machine check queue.
404 * Note : The queue elements provide for a double linked list.
405 * We enqueue entries to the head, and dequeue entries from
409 static void s390_enqueue_mchchk( mache_t *pmache )
411 spin_lock( &mchchk_queue_lock );
413 if ( pmache != NULL )
416 if ( mchchk_queue_head == NULL ) /* first element */
421 mchchk_queue_head = pmache;
422 mchchk_queue_tail = pmache;
427 pmache->next = mchchk_queue_head;
429 mchchk_queue_head->prev = pmache;
430 mchchk_queue_head = pmache;
436 spin_unlock( &mchchk_queue_lock );
443 * s390_enqueue_free_mchchk
445 * Enqueue a free entry to the free queue.
447 * Note : While the queue elements provide for a double linked list,
448 * the free queue entries are only concatenated by means of a
449 * single linked list (forward concatenation).
452 static void s390_enqueue_free_mchchk( mache_t *pmache )
456 memset( pmache, '\0', sizeof( mache_t ));
458 spin_lock( &mchchk_queue_lock );
460 pmache->next = mchchk_queue_free;
462 mchchk_queue_free = pmache;
464 spin_unlock( &mchchk_queue_lock );
472 * s390_dequeue_free_mchchk
474 * Dequeue an entry from the free queue.
476 * Note : While the queue elements provide for a double linked list,
477 * the free queue entries are only concatenated by means of a
478 * single linked list (forward concatenation).
481 static mache_t *s390_dequeue_free_mchchk( void )
485 spin_lock( &mchchk_queue_lock );
487 qe = mchchk_queue_free;
491 mchchk_queue_free = qe->next;
495 spin_unlock( &mchchk_queue_lock );
501 * s390_collect_crw_info
503 * Retrieve CRWs. If a CRW was found a machine check element
504 * is dequeued from the free chain, filled and enqueued to
507 * The function returns the number of CRWs found.
509 * Note : We must always be called disabled ...
511 static int s390_collect_crw_info( void )
513 crw_t tcrw; /* temporarily holds a CRW */
514 int ccode; /* condition code from stcrw() */
515 crwe_t *pcrwe; /* pointer to CRW buffer entry */
517 mache_t *pmache = NULL; /* ptr to mchchk entry */
518 int chain = 0; /* indicate chaining */
519 crwe_t *pccrw = NULL; /* ptr to current CRW buffer entry */
520 int count = 0; /* CRW count */
522 #ifdef S390_MACHCHK_DEBUG
523 printk( KERN_DEBUG "crw_info : looking for CRWs ...\n");
528 ccode = stcrw( (__u32 *)&tcrw);
534 #ifdef S390_MACHCHK_DEBUG
535 printk( KERN_DEBUG "crw_info : CRW reports "
536 "slct=%d, oflw=%d, chn=%d, "
537 "rsc=%X, anc=%d, erc=%X, "
549 * Dequeue a CRW entry from the free chain
552 spin_lock( &crw_queue_lock );
554 pcrwe = crw_buffer_anchor;
558 spin_unlock( &crw_queue_lock );
559 printk( KERN_CRIT"crw_info : "
560 "no CRW buffer entries available\n");
565 crw_buffer_anchor = pcrwe->crwe_next;
566 pcrwe->crwe_next = NULL;
568 spin_unlock( &crw_queue_lock );
570 memcpy( &(pcrwe->crw), &tcrw, sizeof(crw_t));
573 * If it is the first CRW, chain it to the mchchk
574 * buffer entry, otherwise to the last CRW entry.
578 pmache = s390_dequeue_free_mchchk();
580 if ( pmache != NULL )
582 memset( pmache, '\0', sizeof(mache_t));
584 pmache->mcic.mcc.mcd.cp = 1;
585 pmache->mc.crwe = pcrwe;
593 "free mchchk buffer");
599 pccrw->crwe_next = pcrwe;
604 if ( pccrw->crw.chn )
606 #ifdef S390_MACHCHK_DEBUG
607 printk( KERN_DEBUG "crw_info : "
608 "chained CRWs pending ...\n\n");
617 * We can enqueue the mchchk buffer if
618 * there aren't more CRWs chained.
620 s390_enqueue_mchchk( pmache);
626 } while ( ccode == 0 );
631 #ifdef CONFIG_MACHCHK_WARNING
635 * Post a warning type machine check
637 * The function returns 1 when succesfull (panics otherwise)
639 static int s390_post_warning( void )
641 mache_t *pmache = NULL; /* ptr to mchchk entry */
643 pmache = s390_dequeue_free_mchchk();
645 if ( pmache != NULL )
647 memset( pmache, '\0', sizeof(mache_t) );
649 pmache->mcic.mcc.mcd.w = 1;
651 s390_enqueue_mchchk( pmache );
655 panic( "post_warning : "
657 "free mchchk buffer" );
660 #ifdef S390_MACHCHK_DEBUG
661 printk( KERN_DEBUG "post_warning : 1 warning machine check posted\n");